326 lines
11 KiB
C#
326 lines
11 KiB
C#
using System;
|
|
using System.Collections.Specialized;
|
|
using System.ComponentModel;
|
|
using CoreGraphics;
|
|
using Foundation;
|
|
using UIKit;
|
|
using Xamarin.Forms;
|
|
using Xamarin.Forms.Platform.iOS;
|
|
|
|
namespace Gallery.iOS.Renderers.AppShellSection
|
|
{
|
|
public class AppShellSectionRootHeader : UICollectionViewController, IAppearanceObserver, IShellSectionRootHeader
|
|
{
|
|
#region IAppearanceObserver
|
|
|
|
readonly Color _defaultBackgroundColor = new(0.964);
|
|
readonly Color _defaultForegroundColor = Color.Black;
|
|
readonly Color _defaultUnselectedColor = Color.Black.MultiplyAlpha(0.7);
|
|
|
|
void IAppearanceObserver.OnAppearanceChanged(ShellAppearance appearance)
|
|
{
|
|
if (appearance == null)
|
|
ResetAppearance();
|
|
else
|
|
SetAppearance(appearance);
|
|
}
|
|
|
|
protected virtual void ResetAppearance()
|
|
{
|
|
SetValues(_defaultBackgroundColor, _defaultForegroundColor, _defaultUnselectedColor);
|
|
}
|
|
|
|
protected virtual void SetAppearance(ShellAppearance appearance)
|
|
{
|
|
SetValues(appearance.BackgroundColor.IsDefault ? _defaultBackgroundColor : appearance.BackgroundColor,
|
|
appearance.ForegroundColor.IsDefault ? _defaultForegroundColor : appearance.ForegroundColor,
|
|
appearance.UnselectedColor.IsDefault ? _defaultUnselectedColor : appearance.UnselectedColor);
|
|
}
|
|
|
|
void SetValues(Color backgroundColor, Color foregroundColor, Color unselectedColor)
|
|
{
|
|
CollectionView.BackgroundColor = new Color(backgroundColor.R, backgroundColor.G, backgroundColor.B, .663).ToUIColor();
|
|
_bar.BackgroundColor = foregroundColor.ToUIColor();
|
|
|
|
bool reloadData = _selectedColor != foregroundColor || _unselectedColor != unselectedColor;
|
|
|
|
_selectedColor = foregroundColor;
|
|
_unselectedColor = unselectedColor;
|
|
|
|
if (reloadData)
|
|
CollectionView.ReloadData();
|
|
}
|
|
|
|
#endregion IAppearanceObserver
|
|
|
|
static readonly NSString CellId = new("HeaderCell");
|
|
|
|
readonly IShellContext _shellContext;
|
|
UIView _bar;
|
|
UIView _bottomShadow;
|
|
Color _selectedColor;
|
|
Color _unselectedColor;
|
|
bool _isDisposed;
|
|
|
|
public AppShellSectionRootHeader(IShellContext shellContext) : base(new UICollectionViewFlowLayout())
|
|
{
|
|
_shellContext = shellContext;
|
|
}
|
|
|
|
public double SelectedIndex { get; set; }
|
|
public ShellSection ShellSection { get; set; }
|
|
IShellSectionController ShellSectionController => ShellSection;
|
|
|
|
public UIViewController ViewController => this;
|
|
|
|
public override bool CanMoveItem(UICollectionView collectionView, NSIndexPath indexPath)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
public override UICollectionViewCell GetCell(UICollectionView collectionView, NSIndexPath indexPath)
|
|
{
|
|
var reusedCell = (UICollectionViewCell)collectionView.DequeueReusableCell(CellId, indexPath);
|
|
|
|
if (reusedCell is not ShellSectionHeaderCell headerCell)
|
|
return reusedCell;
|
|
|
|
var selectedItems = collectionView.GetIndexPathsForSelectedItems();
|
|
|
|
var shellContent = ShellSectionController.GetItems()[indexPath.Row];
|
|
headerCell.Label.Text = shellContent.Title;
|
|
headerCell.Label.SetNeedsDisplay();
|
|
|
|
headerCell.SelectedColor = _selectedColor.ToUIColor();
|
|
headerCell.UnSelectedColor = _unselectedColor.ToUIColor();
|
|
|
|
if (selectedItems.Length > 0 && selectedItems[0].Row == indexPath.Row)
|
|
headerCell.Selected = true;
|
|
else
|
|
headerCell.Selected = false;
|
|
|
|
return headerCell;
|
|
}
|
|
|
|
public override nint GetItemsCount(UICollectionView collectionView, nint section)
|
|
{
|
|
return ShellSectionController.GetItems().Count;
|
|
}
|
|
|
|
public override void ItemDeselected(UICollectionView collectionView, NSIndexPath indexPath)
|
|
{
|
|
if (CollectionView.CellForItem(indexPath) is ShellSectionHeaderCell cell)
|
|
cell.Label.TextColor = _unselectedColor.ToUIColor();
|
|
}
|
|
|
|
public override void ItemSelected(UICollectionView collectionView, NSIndexPath indexPath)
|
|
{
|
|
var row = indexPath.Row;
|
|
|
|
var item = ShellSectionController.GetItems()[row];
|
|
|
|
if (item != ShellSection.CurrentItem)
|
|
ShellSection.SetValueFromRenderer(ShellSection.CurrentItemProperty, item);
|
|
|
|
if (CollectionView.CellForItem(indexPath) is ShellSectionHeaderCell cell)
|
|
cell.Label.TextColor = _selectedColor.ToUIColor();
|
|
}
|
|
|
|
public override nint NumberOfSections(UICollectionView collectionView)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
public override bool ShouldSelectItem(UICollectionView collectionView, NSIndexPath indexPath)
|
|
{
|
|
var row = indexPath.Row;
|
|
var item = ShellSectionController.GetItems()[row];
|
|
IShellController shellController = _shellContext.Shell;
|
|
|
|
if (item == ShellSection.CurrentItem)
|
|
return true;
|
|
return shellController.ProposeNavigation(ShellNavigationSource.ShellContentChanged, (ShellItem)ShellSection.Parent, ShellSection, item, ShellSection.Stack, true);
|
|
}
|
|
|
|
public override void ViewDidLayoutSubviews()
|
|
{
|
|
if (_isDisposed)
|
|
return;
|
|
|
|
base.ViewDidLayoutSubviews();
|
|
|
|
LayoutBar();
|
|
|
|
_bottomShadow.Frame = new CGRect(0, CollectionView.Frame.Bottom, CollectionView.Frame.Width, 0.5);
|
|
}
|
|
|
|
public override void ViewDidLoad()
|
|
{
|
|
if (_isDisposed)
|
|
return;
|
|
|
|
base.ViewDidLoad();
|
|
|
|
CollectionView.ScrollsToTop = false;
|
|
CollectionView.Bounces = false;
|
|
CollectionView.AlwaysBounceHorizontal = false;
|
|
CollectionView.ShowsHorizontalScrollIndicator = false;
|
|
CollectionView.ClipsToBounds = false;
|
|
|
|
_bar = new UIView(new CGRect(0, 0, 20, 20));
|
|
_bar.Layer.ZPosition = 9001; //its over 9000!
|
|
CollectionView.AddSubview(_bar);
|
|
|
|
_bottomShadow = new UIView(new CGRect(0, 0, 10, 1))
|
|
{
|
|
BackgroundColor = Color.Black.MultiplyAlpha(0.3).ToUIColor()
|
|
};
|
|
_bottomShadow.Layer.ZPosition = 9002;
|
|
CollectionView.AddSubview(_bottomShadow);
|
|
|
|
var flowLayout = Layout as UICollectionViewFlowLayout;
|
|
flowLayout.ScrollDirection = UICollectionViewScrollDirection.Horizontal;
|
|
flowLayout.MinimumInteritemSpacing = 0;
|
|
flowLayout.MinimumLineSpacing = 0;
|
|
flowLayout.EstimatedItemSize = new CGSize(70, 35);
|
|
|
|
CollectionView.RegisterClassForCell(GetCellType(), CellId);
|
|
|
|
((IShellController)_shellContext.Shell).AddAppearanceObserver(this, ShellSection);
|
|
ShellSectionController.ItemsCollectionChanged += OnShellSectionItemsChanged;
|
|
|
|
UpdateSelectedIndex();
|
|
ShellSection.PropertyChanged += OnShellSectionPropertyChanged;
|
|
}
|
|
|
|
protected virtual Type GetCellType()
|
|
{
|
|
return typeof(ShellSectionHeaderCell);
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
if (_isDisposed)
|
|
return;
|
|
|
|
if (disposing)
|
|
{
|
|
((IShellController)_shellContext.Shell).RemoveAppearanceObserver(this);
|
|
ShellSectionController.ItemsCollectionChanged -= OnShellSectionItemsChanged;
|
|
ShellSection.PropertyChanged -= OnShellSectionPropertyChanged;
|
|
|
|
ShellSection = null;
|
|
_bar.RemoveFromSuperview();
|
|
RemoveFromParentViewController();
|
|
_bar.Dispose();
|
|
_bar = null;
|
|
}
|
|
|
|
_isDisposed = true;
|
|
base.Dispose(disposing);
|
|
}
|
|
|
|
protected void LayoutBar()
|
|
{
|
|
if (SelectedIndex < 0)
|
|
return;
|
|
|
|
if (ShellSectionController.GetItems().IndexOf(ShellSection.CurrentItem) != SelectedIndex)
|
|
return;
|
|
|
|
var layout = CollectionView.GetLayoutAttributesForItem(NSIndexPath.FromItemSection((int)SelectedIndex, 0));
|
|
|
|
if (layout == null)
|
|
return;
|
|
|
|
var frame = layout.Frame;
|
|
|
|
if (_bar.Frame.Height != 2)
|
|
{
|
|
_bar.Frame = new CGRect(frame.X, frame.Bottom - 2, frame.Width, 2);
|
|
}
|
|
else
|
|
{
|
|
UIView.Animate(.25, () => _bar.Frame = new CGRect(frame.X, frame.Bottom - 2, frame.Width, 2));
|
|
}
|
|
}
|
|
|
|
protected virtual void OnShellSectionPropertyChanged(object sender, PropertyChangedEventArgs e)
|
|
{
|
|
if (e.PropertyName == ShellSection.CurrentItemProperty.PropertyName)
|
|
{
|
|
UpdateSelectedIndex();
|
|
}
|
|
}
|
|
|
|
protected virtual void UpdateSelectedIndex(bool animated = false)
|
|
{
|
|
if (ShellSection.CurrentItem == null)
|
|
return;
|
|
|
|
SelectedIndex = ShellSectionController.GetItems().IndexOf(ShellSection.CurrentItem);
|
|
|
|
if (SelectedIndex < 0)
|
|
return;
|
|
|
|
LayoutBar();
|
|
|
|
CollectionView.SelectItem(NSIndexPath.FromItemSection((int)SelectedIndex, 0), false, UICollectionViewScrollPosition.CenteredHorizontally);
|
|
}
|
|
|
|
void OnShellSectionItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
|
|
{
|
|
if (_isDisposed)
|
|
return;
|
|
|
|
CollectionView.ReloadData();
|
|
}
|
|
|
|
public class ShellSectionHeaderCell : UICollectionViewCell
|
|
{
|
|
public UIColor SelectedColor { get; set; }
|
|
public UIColor UnSelectedColor { get; set; }
|
|
|
|
public ShellSectionHeaderCell()
|
|
{
|
|
|
|
}
|
|
|
|
[Export("initWithFrame:")]
|
|
public ShellSectionHeaderCell(CGRect frame) : base(frame)
|
|
{
|
|
Label = new UILabel
|
|
{
|
|
TextAlignment = UITextAlignment.Center,
|
|
Font = UIFont.BoldSystemFontOfSize(14)
|
|
};
|
|
ContentView.AddSubview(Label);
|
|
}
|
|
|
|
public override bool Selected
|
|
{
|
|
get => base.Selected;
|
|
set
|
|
{
|
|
base.Selected = value;
|
|
Label.TextColor = value ? SelectedColor : UnSelectedColor;
|
|
}
|
|
}
|
|
|
|
public UILabel Label { get; }
|
|
|
|
public override void LayoutSubviews()
|
|
{
|
|
base.LayoutSubviews();
|
|
|
|
Label.Frame = Bounds;
|
|
}
|
|
|
|
public override CGSize SizeThatFits(CGSize size)
|
|
{
|
|
return new CGSize(Label.SizeThatFits(size).Width + 30, 35);
|
|
}
|
|
}
|
|
}
|
|
}
|