maui-linux/Xamarin.Forms.Platform.iOS/Renderers/PageRenderer.cs

350 строки
8.7 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using UIKit;
using PageUIStatusBarAnimation = Xamarin.Forms.PlatformConfiguration.iOSSpecific.UIStatusBarAnimation;
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
namespace Xamarin.Forms.Platform.iOS
{
public class PageRenderer : UIViewController, IVisualElementRenderer, IEffectControlProvider
{
bool _appeared;
bool _disposed;
EventTracker _events;
VisualElementPackager _packager;
VisualElementTracker _tracker;
Page Page => Element as Page;
bool UsingSafeArea => (Forms.IsiOS11OrNewer) ? Page.On<PlatformConfiguration.iOS>().UsingSafeArea() : false;
Thickness SafeAreaInsets => Page.On<PlatformConfiguration.iOS>().SafeAreaInsets();
public PageRenderer()
{
}
void IEffectControlProvider.RegisterEffect(Effect effect)
{
VisualElementRenderer<VisualElement>.RegisterEffect(effect, View);
}
public VisualElement Element { get; private set; }
public event EventHandler<VisualElementChangedEventArgs> ElementChanged;
public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
{
return NativeView.GetSizeRequest(widthConstraint, heightConstraint);
}
public UIView NativeView
{
get { return _disposed ? null : View; }
}
public void SetElement(VisualElement element)
{
VisualElement oldElement = Element;
Element = element;
UpdateTitle();
OnElementChanged(new VisualElementChangedEventArgs(oldElement, element));
if (Element != null && !string.IsNullOrEmpty(Element.AutomationId))
SetAutomationId(Element.AutomationId);
if (element != null)
element.SendViewInitialized(NativeView);
EffectUtilities.RegisterEffectControlProvider(this, oldElement, element);
}
public void SetElementSize(Size size)
{
Element.Layout(new Rectangle(Element.X, Element.Y, size.Width, size.Height));
}
public override void ViewDidLayoutSubviews()
{
base.ViewDidLayoutSubviews();
if (Element.Parent is BaseShellItem)
Element.Layout(View.Bounds.ToRectangle());
UpdateShellInsetPadding();
}
public override void ViewSafeAreaInsetsDidChange()
{
UpdateShellInsetPadding();
var page = (Element as Page);
if (page != null && Forms.IsiOS11OrNewer)
{
var insets = NativeView.SafeAreaInsets;
if (page.Parent is TabbedPage)
{
insets.Bottom = 0;
}
page.On<PlatformConfiguration.iOS>().SetSafeAreaInsets(new Thickness(insets.Left, insets.Top, insets.Right, insets.Bottom));
}
base.ViewSafeAreaInsetsDidChange();
}
public UIViewController ViewController => _disposed ? null : this;
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
if (_appeared || _disposed)
return;
_appeared = true;
UpdateStatusBarPrefersHidden();
if (Element.Parent is CarouselPage)
return;
Page.SendAppearing();
}
public override void ViewDidDisappear(bool animated)
{
base.ViewDidDisappear(animated);
if (!_appeared || _disposed)
return;
_appeared = false;
if (Element.Parent is CarouselPage)
return;
Page.SendDisappearing();
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
var uiTapGestureRecognizer = new UITapGestureRecognizer(a => View.EndEditing(true));
uiTapGestureRecognizer.ShouldRecognizeSimultaneously = (recognizer, gestureRecognizer) => true;
uiTapGestureRecognizer.ShouldReceiveTouch = OnShouldReceiveTouch;
uiTapGestureRecognizer.DelaysTouchesBegan =
uiTapGestureRecognizer.DelaysTouchesEnded = uiTapGestureRecognizer.CancelsTouchesInView = false;
View.AddGestureRecognizer(uiTapGestureRecognizer);
UpdateBackground();
_packager = new VisualElementPackager(this);
_packager.Load();
Element.PropertyChanged += OnHandlePropertyChanged;
_tracker = new VisualElementTracker(this, !(Element.Parent is BaseShellItem));
_events = new EventTracker(this);
_events.LoadEvents(View);
Element.SendViewInitialized(View);
}
public override void ViewWillDisappear(bool animated)
{
base.ViewWillDisappear(animated);
View.Window?.EndEditing(true);
}
protected override void Dispose(bool disposing)
{
if (disposing && !_disposed)
{
Element.PropertyChanged -= OnHandlePropertyChanged;
Platform.SetRenderer(Element, null);
if (_appeared)
Page.SendDisappearing();
_appeared = false;
if (_events != null)
{
_events.Dispose();
_events = null;
}
if (_packager != null)
{
_packager.Dispose();
_packager = null;
}
if (_tracker != null)
{
_tracker.Dispose();
_tracker = null;
}
Element = null;
_disposed = true;
}
base.Dispose(disposing);
}
protected virtual void OnElementChanged(VisualElementChangedEventArgs e)
{
ElementChanged?.Invoke(this, e);
}
protected virtual void SetAutomationId(string id)
{
if (NativeView != null)
NativeView.AccessibilityIdentifier = id;
}
void OnHandlePropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
UpdateBackground();
else if (e.PropertyName == Page.BackgroundImageProperty.PropertyName)
UpdateBackground();
else if (e.PropertyName == Page.TitleProperty.PropertyName)
UpdateTitle();
else if (e.PropertyName == PlatformConfiguration.iOSSpecific.Page.PrefersStatusBarHiddenProperty.PropertyName)
UpdateStatusBarPrefersHidden();
else if (Forms.IsiOS11OrNewer && e.PropertyName == PlatformConfiguration.iOSSpecific.Page.UseSafeAreaProperty.PropertyName)
UpdateUseSafeArea();
else if (Forms.IsiOS11OrNewer && e.PropertyName == PlatformConfiguration.iOSSpecific.Page.SafeAreaInsetsProperty.PropertyName)
UpdateUseSafeArea();
}
public override UIKit.UIStatusBarAnimation PreferredStatusBarUpdateAnimation
{
get
{
var animation = ((Page)Element).OnThisPlatform().PreferredStatusBarUpdateAnimation();
switch (animation)
{
case (PageUIStatusBarAnimation.Fade):
return UIKit.UIStatusBarAnimation.Fade;
case (PageUIStatusBarAnimation.Slide):
return UIKit.UIStatusBarAnimation.Slide;
case (PageUIStatusBarAnimation.None):
default:
return UIKit.UIStatusBarAnimation.None;
}
}
}
void UpdateUseSafeArea()
{
if (!Forms.IsiOS11OrNewer) return;
if (!UsingSafeArea)
{
var safeAreaInsets = SafeAreaInsets;
if (safeAreaInsets == Page.Padding)
Page.Padding = default(Thickness);
}
else
{
Page.Padding = SafeAreaInsets;
}
}
void UpdateShellInsetPadding()
{
var setInsets = Shell.GetSetPaddingInsets(Element);
if (setInsets)
{
nfloat topPadding = 0;
nfloat bottomPadding = 0;
if (Forms.IsiOS11OrNewer)
{
topPadding = View.SafeAreaInsets.Top;
bottomPadding = View.SafeAreaInsets.Bottom;
}
else
{
topPadding = TopLayoutGuide.Length;
bottomPadding = BottomLayoutGuide.Length;
}
(Element as Page).Padding = new Thickness(0, topPadding, 0, bottomPadding);
}
}
void UpdateStatusBarPrefersHidden()
{
if (Element == null)
return;
var animation = ((Page)Element).OnThisPlatform().PreferredStatusBarUpdateAnimation();
if (animation == PageUIStatusBarAnimation.Fade || animation == PageUIStatusBarAnimation.Slide)
UIView.Animate(0.25, () => SetNeedsStatusBarAppearanceUpdate());
else
SetNeedsStatusBarAppearanceUpdate();
View.SetNeedsLayout();
}
bool OnShouldReceiveTouch(UIGestureRecognizer recognizer, UITouch touch)
{
foreach (UIView v in ViewAndSuperviewsOfView(touch.View))
{
if (v is UITableView || v is UITableViewCell || v.CanBecomeFirstResponder)
return false;
}
return true;
}
public override bool PrefersStatusBarHidden()
{
var mode = ((Page)Element).OnThisPlatform().PrefersStatusBarHidden();
switch (mode)
{
case (StatusBarHiddenMode.True):
return true;
case (StatusBarHiddenMode.False):
return false;
case (StatusBarHiddenMode.Default):
default:
return base.PrefersStatusBarHidden();
}
}
void UpdateBackground()
{
string bgImage = ((Page)Element).BackgroundImage;
if (!string.IsNullOrEmpty(bgImage))
{
View.BackgroundColor = UIColor.FromPatternImage(UIImage.FromBundle(bgImage) ?? throw new Exception($"Image: File '{bgImage}' not found in app bundle"));
return;
}
Color bgColor = Element.BackgroundColor;
if (bgColor.IsDefault)
View.BackgroundColor = UIColor.White;
else
View.BackgroundColor = bgColor.ToUIColor();
}
void UpdateTitle()
{
if (!string.IsNullOrWhiteSpace(((Page)Element).Title))
NavigationItem.Title = ((Page)Element).Title;
}
IEnumerable<UIView> ViewAndSuperviewsOfView(UIView view)
{
while (view != null)
{
yield return view;
view = view.Superview;
}
}
}
}