[iOS] Shell flyout header on iOS now is dynamic based on the header content (#6109) fixes #3875

* fix the flyout header to size based on content opposed to a fixed height

* update storeshell

* added height slider into storeshell

* resize header if underlying measure changes

* unsubscribe
This commit is contained in:
Shane Neuville 2019-05-09 14:16:52 -07:00 коммит произвёл Rui Marinho
Родитель a542264a38
Коммит 3caf60427f
7 изменённых файлов: 86 добавлений и 24 удалений

Двоичные данные
Xamarin.Forms.ControlGallery.iOS/Resources/xamarinstore.jpg Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 16 KiB

Просмотреть файл

@ -327,6 +327,7 @@
<BundleResource Include="Resources\button_add%402x.png" />
<BundleResource Include="Resources\icon_search%402x.png" />
<BundleResource Include="Resources\icon_bookmark%402x.png" />
<BundleResource Include="Resources\xamarinstore.jpg" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />

Просмотреть файл

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Xamarin.Forms.Controls.XamStore.FlyoutHeader"
HeightRequest="200">
HeightRequest="143">
<ContentView.Content>
<Grid BackgroundColor="Black">
<Image Aspect="AspectFill" Source="xamarinstore.jpg" Opacity="0.6" />

Просмотреть файл

@ -243,6 +243,24 @@ namespace Xamarin.Forms.Controls.XamStore
async () => await Shell.Current.GoToAsync(navEntry.Text, true)),
2, 16);
var headerWidth = new Slider
{
Minimum = 0,
Maximum = 400,
Value = (Shell.Current.FlyoutHeader as VisualElement)?.HeightRequest ?? 0
};
headerWidth.ValueChanged += (_, e) =>
{
if (Shell.Current.FlyoutHeader is VisualElement ve)
ve.HeightRequest = e.NewValue;
};
grid.Children.Add(new Label
{
Text = "fly Header",
VerticalOptions = LayoutOptions.CenterAndExpand
}, 0, 17);
grid.Children.Add(headerWidth, 1, 17);
Content = new ScrollView { Content = grid };
//var listView = new ListView();

Просмотреть файл

@ -9,7 +9,7 @@ namespace Xamarin.Forms.Platform.iOS
{
UIVisualEffectView _blurView;
readonly IShellContext _shellContext;
UIView _headerView;
UIContainerView _headerView;
ShellTableViewController _tableViewController;
public event EventHandler WillAppear;

Просмотреть файл

@ -8,29 +8,28 @@ namespace Xamarin.Forms.Platform.iOS
public class ShellTableViewController : UITableViewController
{
readonly IShellContext _context;
readonly UIView _headerView;
readonly UIContainerView _headerView;
readonly ShellTableViewSource _source;
double _headerMax = 200;
double _headerMin = 44;
double _headerMin = 56;
double _headerOffset = 0;
double _headerSize;
public ShellTableViewController(IShellContext context, UIView headerView, Action<Element> onElementSelected)
public ShellTableViewController(IShellContext context, UIContainerView headerView, Action<Element> onElementSelected)
{
if (headerView == null)
{
_headerMax = 20;
_headerMin = 0;
}
_headerSize = _headerMax;
_context = context;
_headerView = headerView;
_source = new ShellTableViewSource(context, onElementSelected);
_source.ScrolledEvent += OnScrolled;
_headerView.HeaderSizeChanged += OnHeaderSizeChanged;
((IShellController)_context.Shell).StructureChanged += OnStructureChanged;
}
void OnHeaderSizeChanged(object sender, EventArgs e)
{
_headerSize = HeaderMax;
TableView.ContentInset = new UIEdgeInsets((nfloat)HeaderMax + SafeAreaOffset, 0, 0, 0);
LayoutParallax();
}
void OnStructureChanged(object sender, EventArgs e)
{
@ -40,16 +39,20 @@ namespace Xamarin.Forms.Platform.iOS
public void LayoutParallax()
{
if (TableView?.Superview == null)
return;
var parent = TableView.Superview;
TableView.Frame = parent.Bounds.Inset(0, SafeAreaOffset);
if (_headerView != null)
{
_headerView.Frame = new CGRect(0, _headerOffset + SafeAreaOffset, parent.Frame.Width, _headerSize);
if (_headerOffset < 0 && _headerSize + _headerOffset >= 0)
var headerHeight = Math.Max(_headerMin, _headerSize + _headerOffset);
if (_headerOffset < 0)
{
CAShapeLayer shapeLayer = new CAShapeLayer();
CGRect rect = new CGRect(0, _headerOffset * -1, parent.Frame.Width, _headerSize + _headerOffset);
CGRect rect = new CGRect(0, _headerOffset * -1, parent.Frame.Width, headerHeight);
var path = CGPath.FromRect(rect);
shapeLayer.Path = path;
_headerView.Layer.Mask = shapeLayer;
@ -60,11 +63,12 @@ namespace Xamarin.Forms.Platform.iOS
public override void ViewDidLoad()
{
base.ViewDidLoad();
_headerView.MeasureIfNeeded();
TableView.SeparatorStyle = UITableViewCellSeparatorStyle.None;
if (Forms.IsiOS11OrNewer)
TableView.ContentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentBehavior.Never;
TableView.ContentInset = new UIEdgeInsets((nfloat)_headerMax + SafeAreaOffset, 0, 0, 0);
TableView.ContentInset = new UIEdgeInsets((nfloat)HeaderMax + SafeAreaOffset, 0, 0, 0);
TableView.Source = _source;
}
@ -74,11 +78,18 @@ namespace Xamarin.Forms.Platform.iOS
{
if ((_context?.Shell as IShellController) != null)
((IShellController)_context.Shell).StructureChanged -= OnStructureChanged;
if(_source != null)
_source.ScrolledEvent -= OnScrolled;
if(_headerView != null)
_headerView.HeaderSizeChanged -= OnHeaderSizeChanged;
}
base.Dispose(disposing);
}
void OnScrolled(object sender, UIScrollView e)
{
var headerBehavior = _context.Shell.FlyoutHeaderBehavior;
@ -87,16 +98,16 @@ namespace Xamarin.Forms.Platform.iOS
{
case FlyoutHeaderBehavior.Default:
case FlyoutHeaderBehavior.Fixed:
_headerSize = _headerMax;
_headerSize = HeaderMax;
break;
case FlyoutHeaderBehavior.Scroll:
_headerSize = _headerMax;
_headerOffset = Math.Min(0, -(_headerMax + e.ContentOffset.Y));
_headerSize = HeaderMax;
_headerOffset = Math.Min(0, -(HeaderMax + e.ContentOffset.Y));
break;
case FlyoutHeaderBehavior.CollapseOnScroll:
_headerSize = Math.Max(_headerMin, Math.Min(_headerMax, _headerMax - e.ContentOffset.Y - _headerMax));
_headerSize = Math.Max(_headerMin, Math.Min(HeaderMax, HeaderMax - e.ContentOffset.Y - HeaderMax));
break;
}
@ -104,5 +115,6 @@ namespace Xamarin.Forms.Platform.iOS
}
float SafeAreaOffset => (float)Platform.SafeAreaInsetsForWindow.Top;
double HeaderMax => _headerView.MeasuredHeight;
}
}

Просмотреть файл

@ -1,4 +1,5 @@
using CoreGraphics;
using System;
using CoreGraphics;
using UIKit;
namespace Xamarin.Forms.Platform.iOS
@ -8,6 +9,7 @@ namespace Xamarin.Forms.Platform.iOS
readonly View _view;
IVisualElementRenderer _renderer;
bool _disposed;
internal event EventHandler HeaderSizeChanged;
public UIContainerView(View view)
{
@ -17,11 +19,40 @@ namespace Xamarin.Forms.Platform.iOS
Platform.SetRenderer(view, _renderer);
AddSubview(_renderer.NativeView);
ClipsToBounds = true;
view.MeasureInvalidated += OnMeasureInvalidated;
MeasuredHeight = double.NaN;
}
internal double MeasuredHeight { get; private set; }
internal bool MeasureIfNeeded()
{
if (double.IsNaN(MeasuredHeight))
{
ReMeasure();
return true;
}
return false;
}
void ReMeasure()
{
var request = _view.Measure(Frame.Width, double.PositiveInfinity, MeasureFlags.IncludeMargins);
Layout.LayoutChildIntoBoundingRegion(_view, new Rectangle(0, 0, Frame.Width, request.Request.Height));
MeasuredHeight = request.Request.Height;
HeaderSizeChanged?.Invoke(this, EventArgs.Empty);
}
void OnMeasureInvalidated(object sender, System.EventArgs e)
{
ReMeasure();
}
public override void LayoutSubviews()
{
_view.Layout(Bounds.ToRectangle());
if(!MeasureIfNeeded())
_view.Layout(Bounds.ToRectangle());
}
protected override void Dispose(bool disposing)