如何在iOS 16上使用.NET MAUI和XAMARIN.FORMS锁定方向
#dotnet #ios #maui #xamarinforms

旧的方式

在iOS 16之前,将Page锁定在某个方向上很容易。基本上只是一行代码(如果您不计算DependencyService样板代码):

UIDevice.CurrentDevice.SetValueForKey(new NSNumber((int)UIInterfaceOrientation.Portrait), new NSString("orientation"));

每当分配页面的大小时调用此方法,我们就可以在运行时锁定Xamarin.forms的方向。使用iOS 16,即使在本机iOS应用程序上也不再起作用。

新方法

要了解新方法的原因,您必须了解iOS 13中引入的SceneDelegate Architecture Apple。

现在我们知道SceneDelegate是,我们可以继续进行实施。

页面实现

Xamarin.forms和.net Maui实现了SceneDelegate架构。这就是为什么我们可以与本机iOS实现的样子相似地更新代码:

var rootWindowScene = (UIApplication.SharedApplication.ConnectedScenes.ToArray()?.FirstOrDefault()) as UIWindowScene;

if (rootWindowScene == null)
    return;

rootWindowScene.RequestGeometryUpdate(new UIWindowSceneGeometryPreferencesIOS(UIInterfaceOrientationMask.Portrait),
error =>
{
    Debug.WriteLine("Error while attempting to lock orientation: {Error}", error.LocalizedDescription);
});

最重要的是,我们必须告诉基础的ViewController也更新其方向:

var rootViewController = UIApplication.SharedApplication.KeyWindow?.RootViewController;

if (rootViewController == null)
    return;

rootViewController.SetNeedsUpdateOfSupportedInterfaceOrientations();
rootViewController.NavigationController?.SetNeedsUpdateOfSupportedInterfaceOrientations();

可以通过SetNeedsUpdateOfSupportedInterfaceOrientations方法告知ViewController需要重新绘制其视图。如果我们将所有这些放在一起,我们可以为我们的DeviceOrientationService实施提供可重复使用的实现:

private void SetOrientation(UIInterfaceOrientationMask uiInterfaceOrientationMask)
{
    var rootWindowScene = (UIApplication.SharedApplication.ConnectedScenes.ToArray()?.FirstOrDefault()) as UIWindowScene;

    if (rootWindowScene == null)
        return;

    var rootViewController = UIApplication.SharedApplication.KeyWindow?.RootViewController;

    if (rootViewController == null)
        return;

    rootWindowScene.RequestGeometryUpdate(new UIWindowSceneGeometryPreferencesIOS(uiInterfaceOrientationMask),
    error =>
    {
        Debug.WriteLine("Error while attempting to lock orientation: {Error}", error.LocalizedDescription);
    });

    rootViewController.SetNeedsUpdateOfSupportedInterfaceOrientations();
    rootViewController.NavigationController?.SetNeedsUpdateOfSupportedInterfaceOrientations();
}

还可以使我们的现有代码适用于较旧的iOS版本。现在,我们只是检查我们是否使用iOS 16并致电我们的新方法,下面我们仍然可以使用我们的传统方式:

public void LockPortrait()
{
    if (UIDevice.CurrentDevice.CheckSystemVersion(16, 0))
    {
        _applicationDelegate.CurrentLockedOrientation = UIInterfaceOrientationMask.Portrait;

        SetOrientation(UIInterfaceOrientationMask.Portrait);
    }
    else
    {
        UIDevice.CurrentDevice.SetValueForKey(new NSNumber((int)UIInterfaceOrientation.Portrait), new NSString("orientation"));
    }
}

但是,如果没有最后一个非常重要的步骤,这将无能为力。您可能已经注意到上面的应用程序代表成员的CurrentLockedOrientation属性。

每次应用程序必须决定是否旋转时,application:supportedInterfaceOrientationsForWindow:都会要求提供支持的方向。只有当应用程序和ViewController就支持的方向达成协议时,该操作将被执行。

扩展我们的AppDelegate

就像对本机iOS一样,我们需要在我们的AppDelegate中实现上述方法。 Xamarin和.net Maui通过Export属性来执行此操作,该属性告诉编译器覆盖最终现有的本机实现。

对于我的解决方案,我创建了FormsApplicationDelegate / MauiUIApplicationDelegate类的派生版本,将当前所需的UIInterfaceOrientationMask值传递给CurrentLockedOrientation属性。最后,我实现了GetSupportedInterfaceOrientationsForWindow方法,只返回CurrentLockedOrientation属性的值:

public abstract class AppDelegateEx : MauiUIApplicationDelegate
{
    public virtual UIInterfaceOrientationMask CurrentLockedOrientation { get; set; }

    //according to the Apple docs, Application and ViewController have to agree on the supported orientation, this forces it
    //https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623107-application?language=objc
    [Foundation.Export("application:supportedInterfaceOrientationsForWindow:")]
    public virtual UIInterfaceOrientationMask GetSupportedInterfaceOrientationsForWindow(UIApplication application, UIWindow forWindow)
        => this.CurrentLockedOrientation;
}

现在,我们只制作AppDelegate衍生自AppDelegateEx(或任何您称之为),以完成方向锁定的实现。最后,锁定方向在iOS 16上也有效。

样品

我为Xamarin.forms创建了两个样本,一个用于.net Maui。示例在两个平台上的工作都相似,我以可重复使用的方式编写了代码。您可以找到样品in the corresponding GitHub repo

结论

我花了一些时间来弄清楚为什么锁定方向的传统方式不再起作用。经过一些研究和一些反复试验的编码,我能够提出一种干净易于使用的解决方案,这也可以重复使用。我还学到了一些新事物,例如Maui如何实现ScenesViewControllers,并更好地了解了较新的OS版本的iOS应用程序结构和生命周期。

一如既往,我希望这篇文章也对你们中的某些人有所帮助。

直到下一篇文章,愉快的编码!


有用的链接:


Title Image created via Bing Create with AI


How to lock orientation at runtime on iOS 16 with .NET MAUI and Xamarin.Forms帖子首先出现在MSicc's Blog