Skip to content

Instantly share code, notes, and snippets.

@rotorgames
Last active September 6, 2018 10:37
Show Gist options
  • Select an option

  • Save rotorgames/3e5ffa1d32a38fffeabfda742b0535e0 to your computer and use it in GitHub Desktop.

Select an option

Save rotorgames/3e5ffa1d32a38fffeabfda742b0535e0 to your computer and use it in GitHub Desktop.
orientation
using System;
using System.ComponentModel;
using CoreFoundation;
using Foundation;
using Rg.Forms.Enumerables.Device;
using Rg.Forms.Events.Device;
using Rg.Forms.Extensions.Device;
using Rg.Forms.iOS.Extensions.Device;
using Rg.Forms.iOS.Renderers.Pages;
using Rg.Forms.Interfaces.Contracts.Services.Device;
using Rg.Forms.Services.Device;
using Rg.Forms.Views.Pages.OrientationPage;
using UIKit;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(RgOrientationPage), typeof(RgOrientationPageRenderer))]
namespace Rg.Forms.iOS.Renderers.Pages
{
public class RgOrientationPageRenderer : PageRenderer
{
public static UIInterfaceOrientationMask SupportedInterfaceOrientations { get; private set; }
#region Private
private readonly IDeviceOrientationService _deviceOrientationService;
private UIInterfaceOrientationMask _supportedInterfaceOrientations;
private ScreenOrientation _lastDeviceOrientation;
private NSObject _applicationEnterBackgroundObserver;
private NSObject _applicationEnterForegroundObserver;
private RgOrientationPage _Element => (RgOrientationPage)Element;
private RequestScreenOrientation RequestOrientation => _Element.RequestOrientation;
#endregion
#region Main Methods
public RgOrientationPageRenderer()
{
_deviceOrientationService = DeviceOrientationService.CreateInstance();
_deviceOrientationService.UpdatingDelay = 200;
_deviceOrientationService.OrientationChanged += OnSensorOrientationChanged;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
OnPaused();
_deviceOrientationService.OrientationChanged -= OnSensorOrientationChanged;
_deviceOrientationService.Dispose();
}
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
if (e.OldElement != null)
{
e.OldElement.PropertyChanged -= OnElementPropertyChanged;
}
if (e.NewElement != null)
{
e.NewElement.PropertyChanged += OnElementPropertyChanged;
UpdateOrientationListeners();
UpdateOrientation(true);
}
}
private void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case nameof(_Element.RequestOrientation):
UpdateOrientation(true);
break;
case nameof(_Element.UseDeviceSensorOrientation):
UpdateOrientationListeners();
UpdateOrientation(true);
break;
}
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
_applicationEnterForegroundObserver = NSNotificationCenter.DefaultCenter.AddObserver(UIApplication.DidBecomeActiveNotification, OnApplicationEnterForeground);
_applicationEnterBackgroundObserver = NSNotificationCenter.DefaultCenter.AddObserver(UIApplication.WillResignActiveNotification, OnApplicationEnterBackground);
}
public override void ViewWillDisappear(bool animated)
{
base.ViewWillDisappear(animated);
NSNotificationCenter.DefaultCenter.RemoveObserver(_applicationEnterForegroundObserver);
NSNotificationCenter.DefaultCenter.RemoveObserver(_applicationEnterBackgroundObserver);
}
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
OnResumed();
}
public override void ViewDidDisappear(bool animated)
{
base.ViewDidDisappear(animated);
OnPaused();
}
#endregion
#region Lifecycle Methods
private void OnApplicationEnterForeground(NSNotification obj)
{
OnResumed();
}
private void OnApplicationEnterBackground(NSNotification obj)
{
OnPaused();
}
/// <summary>
/// <para xml:lang="en">
/// Invoked when application enters in background, display is blocked or when page disappears
/// </para>
/// <para xml:lang="ru">
/// Вызывается когда приложение уходит в фоновый режим, дисплей блокируется или когда страница становится невидимой
/// </para>
/// </summary>
protected virtual void OnResumed()
{
UpdateOrientationListeners();
UpdateOrientation(true);
}
/// <summary>
/// <para xml:lang="en">
/// Invoked when application enters in foreground, display is unlocked or when page appearing
/// </para>
/// <para xml:lang="ru">
/// Вызывается когда приложение выходина на передний план, дисплей разблокируется или когда страница становится видимой
/// </para>
/// </summary>
protected virtual void OnPaused()
{
StopSensorOrientationListening();
}
#endregion
#region Base Orinetation Listeners
private void UpdateOrientationListeners()
{
var isDeviceSendosEnabled = _Element.UseDeviceSensorOrientation;
if (isDeviceSendosEnabled)
{
StartSensorOrientationListening();
}
else
{
StopSensorOrientationListening();
}
}
#endregion
#region Device Sensor Orientation Listeners
private void StartSensorOrientationListening()
{
// TODO: Если у IDeviceOrientationService будет проверка которая указывает поддерживается сенсор или нет,
// TODO: тогда добавить проверку сюда
if (true)
{
if (!_deviceOrientationService.IsListening)
_deviceOrientationService.StartListening();
}
else
{
throw new NotSupportedException("Device doesn't support detect orientation");
}
}
private void StopSensorOrientationListening()
{
if (_deviceOrientationService.IsListening)
_deviceOrientationService.StopListening();
}
/// <summary>
/// <para xml:lang="en">
/// Invoked if <see cref="RgOrientationPage.UseDeviceSensorOrientation"/> was enabled and sensor orientation was changed.
/// </para>
/// <para xml:lang="ru">
/// Вызывается если <see cref="RgOrientationPage.UseDeviceSensorOrientation"/> был включен и ориентация экрана по датчикам была изменена
/// </para>
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected virtual void OnSensorOrientationChanged(object sender, DeviceOrientationChangedEventArgs e)
{
UpdateOrientation();
}
#endregion
#region Native Orientation Supporting Methods
public override bool ShouldAutorotate()
{
return !RequestOrientation.HasFlag(RequestScreenOrientation.Locked);
}
public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations()
{
if (_supportedInterfaceOrientations == 0)
return base.GetSupportedInterfaceOrientations();
return _supportedInterfaceOrientations;
}
#endregion
#region Orientation Methods
/// <summary>
/// <para xml:lang="en">
/// Returns current orientation of device or screen
/// </para>
/// <para xml:lang="ru">
/// Возвращает текущую ориентацию устройства или экрана
/// </para>
/// </summary>
/// <returns>
/// <para xml:lang="en">
/// Device or screen orientation
/// </para>
/// <para xml:lang="ru">
/// Ориентация устройства или экрана
/// </para>
/// </returns>
protected virtual ScreenOrientation GetCurrentDeviceOrientation()
{
if (_Element.UseDeviceSensorOrientation)
{
var deviceOrientation = _deviceOrientationService.LastOrientation;
var screenOrientation = deviceOrientation.ToScreenOrientation();
if (screenOrientation != ScreenOrientation.Unknown)
_lastDeviceOrientation = screenOrientation;
}
else
{
ScreenOrientation orientation;
var deviceOrientation = UIDevice.CurrentDevice.Orientation;
var statusBarOrientation = UIApplication.SharedApplication.StatusBarOrientation;
if (deviceOrientation != UIDeviceOrientation.Unknown)
orientation = deviceOrientation.ToScreenOrientation();
else
orientation = statusBarOrientation.ToScreenOrientation();
if (orientation != ScreenOrientation.Unknown)
_lastDeviceOrientation = orientation;
}
return _lastDeviceOrientation;
}
/// <summary>
/// <para xml:lang="en">
/// Invoked when screen orientation should be updated
/// </para>
/// <para xml:lang="ru">
/// Вызывается когда ориентация экрана должна быть обновлена
/// </para>
/// </summary>
/// <param name="isForce">
/// <para xml:lang="en">
/// If is <c>true</c> then orientation was changed user, if it is <c>false</c> then device orientation was changed device
/// </para>
/// <para xml:lang="ru">
/// Если равно <c>true</c> значит ориентация была изменена пользователем, если <c>false</c> тогда ориентация была изменена устройством
/// </para>
/// </param>
protected virtual void UpdateOrientation(bool isForce = false)
{
var requestOrientation = RequestOrientation;
var useDeviceSensorOrientation = _Element.UseDeviceSensorOrientation;
var deviceOrientation = GetCurrentDeviceOrientation();
if (deviceOrientation == ScreenOrientation.Unknown)
return;
UIInterfaceOrientation? interfaceOrientation = null;
UIInterfaceOrientationMask? interfaceOrientationMask = null;
if (deviceOrientation.IsPortrait())
{
if (requestOrientation.HasPortrait())
{
if (requestOrientation.HasFlag(RequestScreenOrientation.Portrait)
&& (deviceOrientation == ScreenOrientation.Portrait || (!requestOrientation.HasFlag(RequestScreenOrientation.ReversePortrait) && isForce)))
interfaceOrientation = UIInterfaceOrientation.Portrait;
else if (requestOrientation.HasFlag(RequestScreenOrientation.ReversePortrait)
&& (deviceOrientation == ScreenOrientation.PortraitUpsideDown || (!requestOrientation.HasFlag(RequestScreenOrientation.Portrait) && isForce)))
interfaceOrientation = UIInterfaceOrientation.PortraitUpsideDown;
}
else if (requestOrientation.HasLandscape() && isForce)
{
if (requestOrientation.HasFlag(RequestScreenOrientation.Landscape))
interfaceOrientation = UIInterfaceOrientation.LandscapeRight;
else if (requestOrientation.HasFlag(RequestScreenOrientation.ReverseLandscape))
interfaceOrientation = UIInterfaceOrientation.LandscapeLeft;
}
}
else if (deviceOrientation.IsLandscape())
{
if (requestOrientation.HasLandscape())
{
if (requestOrientation.HasFlag(RequestScreenOrientation.Landscape)
&& (deviceOrientation == ScreenOrientation.LandscapeRight || (!requestOrientation.HasFlag(RequestScreenOrientation.ReverseLandscape) && isForce)))
interfaceOrientation = UIInterfaceOrientation.LandscapeRight;
else if (requestOrientation.HasFlag(RequestScreenOrientation.ReverseLandscape)
&& (deviceOrientation == ScreenOrientation.LandscapeLeft || (!requestOrientation.HasFlag(RequestScreenOrientation.Landscape) && isForce)))
interfaceOrientation = UIInterfaceOrientation.LandscapeLeft;
}
else if (requestOrientation.HasPortrait() && isForce)
{
if (requestOrientation.HasFlag(RequestScreenOrientation.Portrait))
interfaceOrientation = UIInterfaceOrientation.Portrait;
else if (requestOrientation.HasFlag(RequestScreenOrientation.ReversePortrait))
interfaceOrientation = UIInterfaceOrientation.PortraitUpsideDown;
}
}
if (useDeviceSensorOrientation)
{
if (interfaceOrientation == UIInterfaceOrientation.Portrait)
interfaceOrientationMask = UIInterfaceOrientationMask.Portrait;
if (interfaceOrientation == UIInterfaceOrientation.PortraitUpsideDown)
interfaceOrientationMask = UIInterfaceOrientationMask.PortraitUpsideDown;
if (interfaceOrientation == UIInterfaceOrientation.LandscapeRight)
interfaceOrientationMask = UIInterfaceOrientationMask.LandscapeRight;
if (interfaceOrientation == UIInterfaceOrientation.LandscapeLeft)
interfaceOrientationMask = UIInterfaceOrientationMask.LandscapeLeft;
}
else
{
interfaceOrientationMask = 0;
if (requestOrientation.HasFlag(RequestScreenOrientation.Portrait))
interfaceOrientationMask |= UIInterfaceOrientationMask.Portrait;
if (requestOrientation.HasFlag(RequestScreenOrientation.ReversePortrait))
interfaceOrientationMask |= UIInterfaceOrientationMask.PortraitUpsideDown;
if (requestOrientation.HasFlag(RequestScreenOrientation.Landscape))
interfaceOrientationMask |= UIInterfaceOrientationMask.LandscapeRight;
if (requestOrientation.HasFlag(RequestScreenOrientation.ReverseLandscape))
interfaceOrientationMask |= UIInterfaceOrientationMask.LandscapeLeft;
}
if (interfaceOrientationMask == null || interfaceOrientation == null)
return;
//Debug.WriteLine($"\nReques orientation: {requestOrientation} " +
// $"\nDeviceOrientation: {deviceOrientation} " +
// $"\nInterfaceOrientation {interfaceOrientation}" +
// $"\nInterfaceOrientationMask {interfaceOrientationMask}");
SupportedInterfaceOrientations = _supportedInterfaceOrientations = interfaceOrientationMask.Value;
SetScreenOrientation(interfaceOrientation.Value);
}
#endregion
#region Screen Methods
/// <summary>
/// <para xml:lang="en">
/// Sets new screen orientation
/// </para>
/// <para xml:lang="ru">
/// Устанавливает новую ориентацию экрана
/// </para>
/// </summary>
/// <param name="uiOrientation">
/// <para xml:lang="en">
/// New screen orientation
/// </para>
/// <para xml:lang="ru">
/// Новая ориентация экрана
/// </para>
/// </param>
protected virtual void SetScreenOrientation(UIInterfaceOrientation uiOrientation)
{
DispatchQueue.MainQueue.DispatchAsync(() =>
{
NSNumber newOrientationNumber = new NSNumber((int)uiOrientation);
NSString key = new NSString("orientation");
var currentDeviceOrientation = (NSNumber)UIDevice.CurrentDevice.ValueForKey(key);
UIDevice.CurrentDevice.SetValueForKey(newOrientationNumber, key);
UIDevice.CurrentDevice.SetValueForKey(currentDeviceOrientation, key);
AttemptRotationToDeviceOrientation();
//UIDevice.CurrentDevice.BeginGeneratingDeviceOrientationNotifications();
});
}
#endregion
}
}
using System;
using System.Threading.Tasks;
using CoreMotion;
using Foundation;
using Rg.Forms.Enumerables.Device;
using Rg.Forms.Events.Device;
using Rg.Forms.Extensions.Device;
using Rg.Forms.iOS.Services.Device;
using Rg.Forms.Interfaces.Contracts.Services.Device;
using Rg.Forms.Services.Device;
[assembly: Xamarin.Forms.Dependency(typeof(DeviceOrientationServiceImpl))]
namespace Rg.Forms.iOS.Services.Device
{
public class DeviceOrientationServiceImpl : IDeviceOrientationService
{
#region Constants
private const int DefaultUpdatingDelay = DeviceOrientationService.DefaultUpdatingDelay;
private const double FaceAngle = 0.9;
private const double PortraitAndLandscapeAngle = 0.4;
#endregion
#region Private Fields
private CMMotionManager _motionManager;
private bool _isDisposed;
private DeviceOrientation _lastOrientation;
private DeviceOrientation _lastSetOrientation;
#endregion
#region Events
public event EventHandler<DeviceOrientationChangedEventArgs> OrientationChanged;
#endregion
#region Properties
public bool IsListening { get; private set; }
public bool IsUpdatedIfUnknown { get; set; }
public int UpdatingDelay { get; set; } = DefaultUpdatingDelay;
public DeviceOrientation LastOrientation
{
get
{
if (_isDisposed)
throw new ObjectDisposedException(nameof(IDeviceOrientationService));
return _lastOrientation;
}
private set { _lastOrientation = value; }
}
public bool IsLandscape => LastOrientation.IsLandscape();
public bool IsPortrait => LastOrientation.IsPortrait();
public bool IsFace => LastOrientation.IsFace();
#endregion
#region Main Methods
public DeviceOrientationServiceImpl()
{
_motionManager = new CMMotionManager();
}
public void Dispose()
{
if (_isDisposed)
return;
_isDisposed = true;
StopListening();
_motionManager.Dispose();
_motionManager = null;
}
#endregion
#region Listening Methods
public void StartListening()
{
if (IsListening)
return;
IsListening = true;;
if (_motionManager.DeviceMotionAvailable)
{
_motionManager.DeviceMotionUpdateInterval = 0.4;
_motionManager.StartDeviceMotionUpdates(NSOperationQueue.MainQueue, OnDeviceMotion);
}
}
private void OnDeviceMotion(CMDeviceMotion motion, NSError error)
{
var orientation = DeviceOrientation.Unknown;
var gravity = motion.Gravity;
var x = gravity.X;
var y = gravity.Y;
var z = gravity.Z;
if (z <= -FaceAngle)
{
orientation = DeviceOrientation.FaceUp;
}
else if (z >= FaceAngle)
{
orientation = DeviceOrientation.FaceDown;
}
else if (x >= -PortraitAndLandscapeAngle && x <= PortraitAndLandscapeAngle && y <= 0)
{
orientation = DeviceOrientation.Portrait;
}
else if (x >= -PortraitAndLandscapeAngle && x <= PortraitAndLandscapeAngle && y > 0)
{
orientation = DeviceOrientation.PortraitUpsideDown;
}
else if (x < -PortraitAndLandscapeAngle)
{
orientation = DeviceOrientation.LandscapeRight;
}
else if (x > PortraitAndLandscapeAngle)
{
orientation = DeviceOrientation.LandscapeLeft;
}
SetOrientation(orientation);
}
public void StopListening()
{
if(!IsListening)
return;
IsListening = false;
_motionManager.StopDeviceMotionUpdates();
}
#endregion
#region Orientation Methods
private async void SetOrientation(DeviceOrientation orientation)
{
if (_lastSetOrientation == orientation || (!IsUpdatedIfUnknown && orientation == DeviceOrientation.Unknown))
return;
_lastSetOrientation = orientation;
if (LastOrientation != DeviceOrientation.Unknown)
await Task.Delay(UpdatingDelay);
if (_lastSetOrientation != LastOrientation)
{
LastOrientation = _lastSetOrientation;
OrientationChanged?.Invoke(this, new DeviceOrientationChangedEventArgs(_lastSetOrientation));
}
}
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment