旧的方式
在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如何实现Scenes
和ViewControllers
,并更好地了解了较新的OS版本的iOS应用程序结构和生命周期。
一如既往,我希望这篇文章也对你们中的某些人有所帮助。
直到下一篇文章,愉快的编码!
有用的链接:
- Understanding the iOS 13 Scene Delegate – Donny Wals
- swift – How to resolve: ‘keyWindow’ was deprecated in iOS 13.0 – Stack Overflow
- requestGeometryUpdateWithPreferences:errorHandler: | Apple Developer Documentation
- application:supportedInterfaceOrientationsForWindow: | Apple Developer Documentation
- (UIInterfaceOrientationMask)applic… | Apple Developer Forums
- Forcing specific MAUI view to Landscape Orientation using MultiTargeting Feature working for Android but not iOS – Stack Overflow
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。