I need a specific MAUI page to be in Landscape only orientation. I found this tutorial about forcing device orientation and I am using the multi-targeting feature of MAUI to implement the device specific code needed to force this orientation. The tutorial says that they didn't test the iOS version. I have the tutorial working for Android (allows programmatic forcing of Landscape orientation for a single page through a singleton service) but not for iOS.
using System;
namespace ScoreKeepersBoard.DeviceServices;
public partial class DeviceOrientationService : IDeviceOrientationService
{
public partial void SetDeviceOrientation(DisplayOrientation displayOrientation);
}
Here is where I inject my device orientation service into my view model and set the orientation to landscape:
public partial class NewGameViewModel : ObservableObject
{
IGameTypeDataAccess gameTypeDataAccess;
ITeamDataAccess teamDataAccess;
IDeviceOrientationService deviceOrientationService;
[ObservableProperty]
IList<GameType> gameTypes = new List<GameType>();
[ObservableProperty]
private GameType selectedGameType;
[ObservableProperty]
private string gameTypeSelectionError;
[ObservableProperty]
private ObservableCollection<Team> teamOneTeams = new ObservableCollection<Team>();
[ObservableProperty]
private Team teamOneSelection;
[ObservableProperty]
private string teamOneSelectionError;
[ObservableProperty]
private ObservableCollection<Team> teamTwoTeams = new ObservableCollection<Team>();
[ObservableProperty]
private Team teamTwoSelection;
[ObservableProperty]
private string teamTwoSelectionError;
private ObservableCollection<Team> allTeams = new ObservableCollection<Team>();
private bool react = true;
public NewGameViewModel(IGameTypeDataAccess iGameTypeDataAccess, ITeamDataAccess iTeamDataAccess, IDeviceOrientationService iDeviceOrientationService)
{
gameTypeDataAccess = iGameTypeDataAccess;
teamDataAccess = iTeamDataAccess;
deviceOrientationService = iDeviceOrientationService;
deviceOrientationService.SetDeviceOrientation(DisplayOrientation.Landscape);
}
}
And here is my multi targeted code in the /Platforms/Android folder:
using System;
using Android.Content.PM;
namespace ScoreKeepersBoard.DeviceServices;
public partial class DeviceOrientationService
{
private static readonly IReadOnlyDictionary<DisplayOrientation, ScreenOrientation> _androidDisplayOrientationMap =
new Dictionary<DisplayOrientation, ScreenOrientation>
{
[DisplayOrientation.Landscape] = ScreenOrientation.Landscape,
[DisplayOrientation.Portrait] = ScreenOrientation.Portrait,
};
public partial void SetDeviceOrientation(DisplayOrientation displayOrientation)
{
var currentActivity = ActivityStateManager.Default.GetCurrentActivity();
if(currentActivity is not null)
{
if(_androidDisplayOrientationMap.TryGetValue(displayOrientation, out ScreenOrientation screenOrientation))
{
currentActivity.RequestedOrientation = screenOrientation;
}
}
}
}
I have similar setup for multi-targeted to iOS in /Platforms/iOS. UPDATE: I Edited my code according to the answer from Dongzhi Wang-MSFT
using System;
using Foundation;
using UIKit;
namespace ScoreKeepersBoard.DeviceServices;
public partial class DeviceOrientationService
{
private static readonly IReadOnlyDictionary<DisplayOrientation, UIInterfaceOrientation> _iosDisplayOrientationMap =
new Dictionary<DisplayOrientation, UIInterfaceOrientation>
{
[DisplayOrientation.Landscape] = UIInterfaceOrientation.LandscapeLeft,
[DisplayOrientation.Portrait] = UIInterfaceOrientation.Portrait,
};
public partial void SetDeviceOrientation(DisplayOrientation displayOrientation)
{
if (UIDevice.CurrentDevice.CheckSystemVersion(16, 0))
{
var scene = (UIApplication.SharedApplication.ConnectedScenes.ToArray()[0] as UIWindowScene);
if (scene != null)
{
var uiAppplication = UIApplication.SharedApplication;
var test = UIApplication.SharedApplication.KeyWindow?.RootViewController;
if (test != null)
{
test.SetNeedsUpdateOfSupportedInterfaceOrientations();
scene.RequestGeometryUpdate(
new UIWindowSceneGeometryPreferencesIOS(UIInterfaceOrientationMask.Portrait), error => { });
}
}
}
else
{
UIDevice.CurrentDevice.SetValueForKey(new NSNumber((int)UIInterfaceOrientation.Portrait), new NSString("orientation"));
}
}
}
This forces the orientation to Portrait but when I switch from Portrait to Landscape the layout first switches to Landscape and then gets forced into Portrait as shown in the GIF image below.
How can I KEEP it in Portrait as the user changes the orientation?
UPDATE: I updated my .NET MAUI and the update required me to use XCODE 14.2 and now my virtual emulators are all running iOS 16.2 and now the iOS version of the code doesn't work at all and doesn't lock the screen into any orientation. I get this warning now in the iOS platform specific code:
It looks like for iOS version 16.2 this solution doesn't work anymore!
if (_iosDisplayOrientationMap.TryGetValue...
, then step through the code, doesSetStatusBarOrientation
line get executed? – Paniculate