* Flyout Content * - uitest * - ios fix * - fix ios * - fix ios * - fix offset * - fix measuring issues * - fix ScrollView Check * - Cleanup iOS * - remove comments * - fix up ui test * Remove UWP for UI Tests * - fix merge * - Add Flyout Items * - fix uwp flyout items from rebinding * - cleanup code * Update ShellFlyoutContentRenderer.cs
This commit is contained in:
Родитель
75f9a3fdf9
Коммит
4048fab83b
|
@ -397,7 +397,7 @@
|
|||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
<UserProperties XamarinHotReloadUnhandledDeviceExceptionXamarinFormsControlGalleryAndroidHideInfoBar="True" TriggeredFromHotReload="False" />
|
||||
<UserProperties TriggeredFromHotReload="False" XamarinHotReloadUnhandledDeviceExceptionXamarinFormsControlGalleryAndroidHideInfoBar="True" />
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
</Project>
|
|
@ -110,8 +110,11 @@ namespace Xamarin.Forms.Controls.Issues
|
|||
{
|
||||
Device.InvokeOnMainThreadAsync(() =>
|
||||
{
|
||||
var page = AddBottomTab("Success");
|
||||
page.Content = new Label() { Text = "Success" };
|
||||
var page = AddBottomTab("Flyout Item");
|
||||
page.Content = new Label()
|
||||
{
|
||||
Text = "Success"
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,214 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.Forms.CustomAttributes;
|
||||
using Xamarin.Forms.Internals;
|
||||
using Xamarin.Forms.PlatformConfiguration;
|
||||
using Xamarin.Forms.PlatformConfiguration.iOSSpecific;
|
||||
|
||||
|
||||
#if UITEST
|
||||
using Xamarin.UITest;
|
||||
using NUnit.Framework;
|
||||
using Xamarin.Forms.Core.UITests;
|
||||
#endif
|
||||
|
||||
namespace Xamarin.Forms.Controls.Issues
|
||||
{
|
||||
[Preserve(AllMembers = true)]
|
||||
[Issue(IssueTracker.None, 0, "Shell Flyout Content",
|
||||
PlatformAffected.All)]
|
||||
#if UITEST
|
||||
[NUnit.Framework.Category(UITestCategories.Shell)]
|
||||
[NUnit.Framework.Category(UITestCategories.UwpIgnore)]
|
||||
#endif
|
||||
public class ShellFlyoutContent : TestShell
|
||||
{
|
||||
protected override void Init()
|
||||
{
|
||||
var page = new ContentPage();
|
||||
|
||||
this.BindingContext = this;
|
||||
AddFlyoutItem(page, "Flyout Item Top");
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
AddFlyoutItem($"Flyout Item :{i}");
|
||||
Items[i].AutomationId = "Flyout Item";
|
||||
}
|
||||
|
||||
Items.Add(new MenuItem() { Text = "Menu Item" });
|
||||
AddFlyoutItem("Flyout Item Bottom");
|
||||
|
||||
var layout = new StackLayout()
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label()
|
||||
{
|
||||
Text = "Open the Flyout and Toggle the Content, Header and Footer. If it changes after each click test has passed",
|
||||
AutomationId = "PageLoaded"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
page.Content = layout;
|
||||
|
||||
layout.Children.Add(new Button()
|
||||
{
|
||||
Text = "Toggle Flyout Content Template",
|
||||
Command = new Command(() =>
|
||||
{
|
||||
if (FlyoutContentTemplate == null)
|
||||
{
|
||||
FlyoutContentTemplate = new DataTemplate(() =>
|
||||
{
|
||||
var collectionView = new CollectionView();
|
||||
|
||||
collectionView.SetBinding(CollectionView.ItemsSourceProperty, "FlyoutItems");
|
||||
collectionView.IsGrouped = true;
|
||||
|
||||
collectionView.ItemTemplate =
|
||||
new DataTemplate(() =>
|
||||
{
|
||||
var label = new Label();
|
||||
|
||||
label.SetBinding(Label.TextProperty, "Title");
|
||||
|
||||
var button = new Button()
|
||||
{
|
||||
Text = "Click to Reset",
|
||||
AutomationId = "ContentView",
|
||||
Command = new Command(() =>
|
||||
{
|
||||
FlyoutContentTemplate = null;
|
||||
})
|
||||
};
|
||||
|
||||
return new StackLayout()
|
||||
{
|
||||
Children =
|
||||
{
|
||||
label,
|
||||
button
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
return collectionView;
|
||||
});
|
||||
}
|
||||
else if (FlyoutContentTemplate != null)
|
||||
{
|
||||
FlyoutContentTemplate = null;
|
||||
}
|
||||
}),
|
||||
AutomationId = "ToggleFlyoutContentTemplate"
|
||||
});
|
||||
|
||||
layout.Children.Add(new Button()
|
||||
{
|
||||
Text = "Toggle Flyout Content",
|
||||
Command = new Command(() =>
|
||||
{
|
||||
if (FlyoutContent != null)
|
||||
{
|
||||
FlyoutContent = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
var stackLayout = new StackLayout()
|
||||
{
|
||||
Background = SolidColorBrush.Green
|
||||
};
|
||||
|
||||
FlyoutContent = new ScrollView()
|
||||
{
|
||||
Content = stackLayout
|
||||
};
|
||||
|
||||
AddButton("Top Button");
|
||||
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
AddButton("Content View");
|
||||
}
|
||||
|
||||
AddButton("Bottom Button");
|
||||
|
||||
void AddButton(string text)
|
||||
{
|
||||
stackLayout.Children.Add(new Button()
|
||||
{
|
||||
Text = text,
|
||||
AutomationId = "ContentView",
|
||||
Command = new Command(() =>
|
||||
{
|
||||
FlyoutContent = null;
|
||||
}),
|
||||
TextColor = Color.White
|
||||
});
|
||||
}
|
||||
}
|
||||
}),
|
||||
AutomationId = "ToggleContent"
|
||||
});
|
||||
|
||||
layout.Children.Add(new Button()
|
||||
{
|
||||
Text = "Toggle Header/Footer View",
|
||||
Command = new Command(() =>
|
||||
{
|
||||
if (FlyoutHeader != null)
|
||||
{
|
||||
FlyoutHeader = null;
|
||||
FlyoutFooter = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
FlyoutHeader = new StackLayout()
|
||||
{
|
||||
Children = {
|
||||
new Label() { Text = "Header" }
|
||||
},
|
||||
AutomationId = "Header View",
|
||||
Background = SolidColorBrush.Yellow
|
||||
};
|
||||
|
||||
FlyoutFooter = new StackLayout()
|
||||
{
|
||||
Background = SolidColorBrush.Orange,
|
||||
Orientation = StackOrientation.Horizontal,
|
||||
Children = {
|
||||
new Label() { Text = "Footer" }
|
||||
},
|
||||
AutomationId = "Footer View"
|
||||
};
|
||||
}
|
||||
}),
|
||||
AutomationId = "ToggleHeaderFooter"
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
#if UITEST
|
||||
|
||||
[Test]
|
||||
public void FlyoutContentTests()
|
||||
{
|
||||
RunningApp.WaitForElement("PageLoaded");
|
||||
TapInFlyout("Flyout Item");
|
||||
RunningApp.Tap("ToggleContent");
|
||||
TapInFlyout("ContentView");
|
||||
TapInFlyout("Flyout Item");
|
||||
RunningApp.Tap("ToggleFlyoutContentTemplate");
|
||||
TapInFlyout("ContentView");
|
||||
TapInFlyout("Flyout Item");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
|
@ -663,7 +663,7 @@ namespace Xamarin.Forms.Controls
|
|||
ContentPage page = new ContentPage();
|
||||
if (Items.Count == 0)
|
||||
{
|
||||
var item = AddContentPage(page);
|
||||
var item = AddContentPage(page, title);
|
||||
item.Items[0].Items[0].Title = title ?? page.Title;
|
||||
item.Items[0].Title = title ?? page.Title;
|
||||
return page;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
<Compile Include="$(MSBuildThisFileDirectory)CollectionViewGroupTypeIssue.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue11214.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue13109.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)ShellFlyoutContent.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue4720.cs" />
|
||||
<Compile Include="$(MSBuildThisFileDirectory)Issue10897.xaml.cs">
|
||||
<DependentUpon>Issue10897.xaml</DependentUpon>
|
||||
|
|
|
@ -165,9 +165,7 @@ namespace Xamarin.Forms.Core.UnitTests
|
|||
flyoutItem.CurrentItem.CurrentItem.MenuItems.Add(CreateNonVisibleMenuItem());
|
||||
shell.Items.Add(flyoutItem);
|
||||
|
||||
|
||||
IShellController shellController = (IShellController)shell;
|
||||
var groups = shellController.GenerateFlyoutGrouping();
|
||||
var groups = shell.Controller.GenerateFlyoutGrouping();
|
||||
Assert.AreEqual(groups.SelectMany(x => x.OfType<IMenuItemController>()).Count(), 0);
|
||||
}
|
||||
|
||||
|
@ -221,6 +219,89 @@ namespace Xamarin.Forms.Core.UnitTests
|
|||
Assert.AreNotSame(flyoutItems, flyoutItems2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FlyoutItemsBasicSyncTest()
|
||||
{
|
||||
var shell = new TestShell();
|
||||
shell.Items.Add(CreateShellItem<FlyoutItem>());
|
||||
shell.Items.Add(CreateShellItem<FlyoutItem>());
|
||||
shell.Items.Add(CreateShellItem<FlyoutItem>());
|
||||
shell.Items.Add(CreateShellItem<FlyoutItem>());
|
||||
shell.Items[3].IsVisible = false;
|
||||
|
||||
var flyoutItems = shell.GenerateTestFlyoutItems();
|
||||
Assert.AreEqual(shell.Items[0], flyoutItems[0][0]);
|
||||
Assert.AreEqual(shell.Items[1], flyoutItems[0][1]);
|
||||
Assert.AreEqual(shell.Items[2], flyoutItems[0][2]);
|
||||
Assert.AreEqual(3, flyoutItems[0].Count);
|
||||
Assert.AreEqual(1, flyoutItems.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FlyoutItemsGroupTest()
|
||||
{
|
||||
var shell = new TestShell();
|
||||
shell.Items.Add(CreateShellItem<FlyoutItem>());
|
||||
shell.Items.Add(CreateShellItem<FlyoutItem>());
|
||||
var sec1 = shell.Items[0].Items[0];
|
||||
var sec2 = CreateShellSection<Tab>();
|
||||
var sec3 = CreateShellSection<Tab>();
|
||||
|
||||
shell.Items[0].FlyoutDisplayOptions = FlyoutDisplayOptions.AsMultipleItems;
|
||||
shell.Items[0].Items.Add(sec2);
|
||||
shell.Items[0].Items.Add(sec3);
|
||||
|
||||
var flyoutItems = shell.GenerateTestFlyoutItems();
|
||||
Assert.AreEqual(sec1, flyoutItems[0][0]);
|
||||
Assert.AreEqual(sec2, flyoutItems[0][1]);
|
||||
Assert.AreEqual(sec3, flyoutItems[0][2]);
|
||||
Assert.AreEqual(shell.Items[1], flyoutItems[1][0]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FlyoutItemsGroupTestWithRemove()
|
||||
{
|
||||
var shell = new TestShell();
|
||||
shell.Items.Add(CreateShellItem<FlyoutItem>());
|
||||
shell.Items.Add(CreateShellItem<FlyoutItem>());
|
||||
var sec1 = shell.Items[0].Items[0];
|
||||
var sec2 = CreateShellSection<Tab>();
|
||||
var sec3 = CreateShellSection<Tab>();
|
||||
|
||||
shell.Items[0].FlyoutDisplayOptions = FlyoutDisplayOptions.AsMultipleItems;
|
||||
shell.Items[0].Items.Add(sec2);
|
||||
shell.Items[0].Items.Add(sec3);
|
||||
shell.Items.RemoveAt(0);
|
||||
|
||||
var flyoutItems = shell.GenerateTestFlyoutItems();
|
||||
Assert.AreEqual(shell.Items[0], flyoutItems[0][0]);
|
||||
Assert.AreEqual(1, flyoutItems.Count);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FlyoutItemsGroupTestMoveGroup()
|
||||
{
|
||||
var shell = new TestShell();
|
||||
shell.Items.Add(CreateShellItem<FlyoutItem>());
|
||||
shell.Items.Add(CreateShellItem<FlyoutItem>());
|
||||
var sec1 = shell.Items[0].Items[0];
|
||||
var sec2 = CreateShellSection<Tab>();
|
||||
var sec3 = CreateShellSection<Tab>();
|
||||
|
||||
shell.Items[0].FlyoutDisplayOptions = FlyoutDisplayOptions.AsMultipleItems;
|
||||
shell.Items[0].Items.Add(sec2);
|
||||
shell.Items[0].Items.Add(sec3);
|
||||
|
||||
var item1 = shell.Items[0];
|
||||
shell.Items.RemoveAt(0);
|
||||
shell.Items.Add(item1);
|
||||
var flyoutItems = shell.GenerateTestFlyoutItems();
|
||||
Assert.AreEqual(sec1, flyoutItems[1][0]);
|
||||
Assert.AreEqual(sec2, flyoutItems[1][1]);
|
||||
Assert.AreEqual(sec3, flyoutItems[1][2]);
|
||||
Assert.AreEqual(shell.Items[0], flyoutItems[0][0]);
|
||||
}
|
||||
|
||||
MenuItem CreateNonVisibleMenuItem()
|
||||
{
|
||||
MenuItem item = new MenuItem();
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.ComponentModel;
|
||||
|
@ -300,6 +301,18 @@ namespace Xamarin.Forms.Core.UnitTests
|
|||
|
||||
public IShellController Controller => this;
|
||||
|
||||
public List<List<Element>> GenerateTestFlyoutItems()
|
||||
{
|
||||
List<List<Element>> returnValue = new List<List<Element>>();
|
||||
|
||||
|
||||
FlyoutItems
|
||||
.OfType<IEnumerable>()
|
||||
.ForEach(l => returnValue.Add(l.OfType<Element>().ToList()));
|
||||
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
public TestShell()
|
||||
{
|
||||
this.Navigated += (_, __) => NavigatedCount++;
|
||||
|
|
|
@ -165,7 +165,6 @@ namespace Xamarin.Forms
|
|||
|
||||
if (titleViewPart2TheNavBar != null)
|
||||
yield return titleViewPart2TheNavBar;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,8 @@ namespace Xamarin.Forms
|
|||
|
||||
View FlyoutFooter { get; }
|
||||
|
||||
View FlyoutContent { get; }
|
||||
|
||||
ImageSource FlyoutIcon { get; }
|
||||
|
||||
void AddAppearanceObserver(IAppearanceObserver observer, Element pivot);
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Xamarin.Forms.Internals;
|
||||
using Xamarin.Forms.StyleSheets;
|
||||
|
||||
namespace Xamarin.Forms
|
||||
{
|
||||
|
@ -296,14 +295,13 @@ namespace Xamarin.Forms
|
|||
|
||||
event EventHandler IShellController.FlyoutItemsChanged
|
||||
{
|
||||
add { _flyoutItemsChanged += value; }
|
||||
remove { _flyoutItemsChanged -= value; }
|
||||
add { _flyoutManager.FlyoutItemsChanged += value; }
|
||||
remove { _flyoutManager.FlyoutItemsChanged -= value; }
|
||||
}
|
||||
|
||||
event EventHandler _flyoutItemsChanged;
|
||||
|
||||
View IShellController.FlyoutHeader => FlyoutHeaderView;
|
||||
View IShellController.FlyoutFooter => FlyoutFooterView;
|
||||
View IShellController.FlyoutContent => FlyoutContentView;
|
||||
|
||||
IShellController ShellController => this;
|
||||
|
||||
|
@ -440,7 +438,7 @@ namespace Xamarin.Forms
|
|||
|
||||
bool IShellController.ProposeNavigation(ShellNavigationSource source, ShellItem shellItem, ShellSection shellSection, ShellContent shellContent, IReadOnlyList<Page> stack, bool canCancel)
|
||||
{
|
||||
return _navigationManager.ProposeNavigationOutsideGotoAsync(source, shellItem, shellSection, shellContent, stack, canCancel);
|
||||
return _navigationManager.ProposeNavigationOutsideGotoAsync(source, shellItem, shellSection, shellContent, stack, canCancel, true);
|
||||
}
|
||||
|
||||
bool IShellController.RemoveAppearanceObserver(IAppearanceObserver observer)
|
||||
|
@ -546,8 +544,8 @@ namespace Xamarin.Forms
|
|||
|
||||
View _flyoutHeaderView;
|
||||
View _flyoutFooterView;
|
||||
List<List<Element>> _currentFlyoutViews;
|
||||
ShellNavigationManager _navigationManager;
|
||||
ShellFlyoutItemsManager _flyoutManager;
|
||||
|
||||
public Shell()
|
||||
{
|
||||
|
@ -564,6 +562,7 @@ namespace Xamarin.Forms
|
|||
OnNavigating(args);
|
||||
};
|
||||
|
||||
_flyoutManager = new ShellFlyoutItemsManager(this);
|
||||
Navigation = new NavigationImpl(this);
|
||||
Route = Routing.GenerateImplicitRoute("shell");
|
||||
Initialize();
|
||||
|
@ -822,166 +821,18 @@ namespace Xamarin.Forms
|
|||
|
||||
if (FlyoutFooterView != null)
|
||||
SetInheritedBindingContext(FlyoutFooterView, BindingContext);
|
||||
|
||||
if (FlyoutContentView != null)
|
||||
SetInheritedBindingContext(FlyoutContentView, BindingContext);
|
||||
}
|
||||
|
||||
|
||||
internal void SendFlyoutItemsChanged()
|
||||
{
|
||||
if (UpdateFlyoutGroupings())
|
||||
_flyoutItemsChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
internal void SendFlyoutItemsChanged() => _flyoutManager.CheckIfFlyoutItemsChanged();
|
||||
|
||||
List<List<Element>> IShellController.GenerateFlyoutGrouping()
|
||||
{
|
||||
if (_currentFlyoutViews == null)
|
||||
UpdateFlyoutGroupings();
|
||||
public IEnumerable FlyoutItems => _flyoutManager.FlyoutItems;
|
||||
|
||||
return _currentFlyoutViews;
|
||||
}
|
||||
|
||||
bool UpdateFlyoutGroupings()
|
||||
{
|
||||
// The idea here is to create grouping such that the Flyout would
|
||||
// render correctly if it renderered each item in the groups in order
|
||||
// but put a line between the groups. This is needed because our grouping can
|
||||
// actually go 3 layers deep.
|
||||
|
||||
// Ideally this lets us control where lines are drawn in the core code
|
||||
// just by changing how we generate these groupings
|
||||
|
||||
var result = new List<List<Element>>();
|
||||
|
||||
var currentGroup = new List<Element>();
|
||||
|
||||
foreach (var shellItem in ShellController.GetItems())
|
||||
{
|
||||
if (!ShowInFlyoutMenu(shellItem))
|
||||
continue;
|
||||
|
||||
if (Routing.IsImplicit(shellItem) || shellItem.FlyoutDisplayOptions == FlyoutDisplayOptions.AsMultipleItems)
|
||||
{
|
||||
if (shellItem.FlyoutDisplayOptions == FlyoutDisplayOptions.AsMultipleItems)
|
||||
IncrementGroup();
|
||||
|
||||
foreach (var shellSection in (shellItem as IShellItemController).GetItems())
|
||||
{
|
||||
if (!ShowInFlyoutMenu(shellSection))
|
||||
continue;
|
||||
|
||||
var shellContents = ((IShellSectionController)shellSection).GetItems();
|
||||
if (Routing.IsImplicit(shellSection) || shellSection.FlyoutDisplayOptions == FlyoutDisplayOptions.AsMultipleItems)
|
||||
{
|
||||
foreach (var shellContent in shellContents)
|
||||
{
|
||||
if (!ShowInFlyoutMenu(shellContent))
|
||||
continue;
|
||||
|
||||
currentGroup.Add(shellContent);
|
||||
if (shellContent == shellSection.CurrentItem)
|
||||
{
|
||||
AddMenuItems(shellContent.MenuItems);
|
||||
}
|
||||
}
|
||||
|
||||
if (shellSection.FlyoutDisplayOptions == FlyoutDisplayOptions.AsMultipleItems)
|
||||
IncrementGroup();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(shellSection.Parent is TabBar))
|
||||
{
|
||||
if (Routing.IsImplicit(shellSection) && shellContents.Count == 1)
|
||||
{
|
||||
if (!ShowInFlyoutMenu(shellContents[0]))
|
||||
continue;
|
||||
|
||||
currentGroup.Add(shellContents[0]);
|
||||
}
|
||||
else
|
||||
currentGroup.Add(shellSection);
|
||||
}
|
||||
|
||||
// If we have only a single child we will also show the items menu items
|
||||
if (shellContents.Count == 1 && shellSection == shellItem.CurrentItem && shellSection.CurrentItem.MenuItems.Count > 0)
|
||||
{
|
||||
AddMenuItems(shellSection.CurrentItem.MenuItems);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shellItem.FlyoutDisplayOptions == FlyoutDisplayOptions.AsMultipleItems)
|
||||
IncrementGroup();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(shellItem is TabBar))
|
||||
currentGroup.Add(shellItem);
|
||||
}
|
||||
}
|
||||
|
||||
IncrementGroup();
|
||||
|
||||
// If the flyout groupings haven't changed just return
|
||||
// the same instance so the caller knows it hasn't changed
|
||||
// at a later point this will all get converted to an observable collection
|
||||
if (_currentFlyoutViews?.Count == result.Count)
|
||||
{
|
||||
bool hasChanged = false;
|
||||
for (var i = 0; i < result.Count && !hasChanged; i++)
|
||||
{
|
||||
var topLevelNew = result[i];
|
||||
var topLevelPrevious = _currentFlyoutViews[i];
|
||||
|
||||
if (topLevelNew.Count != topLevelPrevious.Count)
|
||||
{
|
||||
hasChanged = true;
|
||||
break;
|
||||
}
|
||||
|
||||
for (var j = 0; j > topLevelNew.Count; j++)
|
||||
{
|
||||
if (topLevelNew[j] != topLevelPrevious[j])
|
||||
{
|
||||
hasChanged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!hasChanged)
|
||||
return false;
|
||||
}
|
||||
|
||||
_currentFlyoutViews = result;
|
||||
return true;
|
||||
|
||||
bool ShowInFlyoutMenu(BindableObject bo)
|
||||
{
|
||||
if (bo is MenuShellItem msi)
|
||||
return Shell.GetFlyoutItemIsVisible(msi.MenuItem);
|
||||
|
||||
return Shell.GetFlyoutItemIsVisible(bo);
|
||||
}
|
||||
|
||||
void AddMenuItems(MenuItemCollection menuItems)
|
||||
{
|
||||
foreach (var item in menuItems)
|
||||
{
|
||||
if (ShowInFlyoutMenu(item))
|
||||
currentGroup.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
void IncrementGroup()
|
||||
{
|
||||
if (currentGroup.Count > 0)
|
||||
{
|
||||
result.Add(currentGroup);
|
||||
currentGroup = new List<Element>();
|
||||
}
|
||||
}
|
||||
}
|
||||
List<List<Element>> IShellController.GenerateFlyoutGrouping() =>
|
||||
_flyoutManager.GenerateFlyoutGrouping();
|
||||
|
||||
internal void SendStructureChanged()
|
||||
{
|
||||
|
@ -1070,7 +921,7 @@ namespace Xamarin.Forms
|
|||
var shellSection = shellItem.CurrentItem;
|
||||
var shellContent = shellSection.CurrentItem;
|
||||
var stack = shellSection.Stack;
|
||||
shell._navigationManager.ProposeNavigationOutsideGotoAsync(ShellNavigationSource.ShellItemChanged, shellItem, shellSection, shellContent, stack, false);
|
||||
shell._navigationManager.ProposeNavigationOutsideGotoAsync(ShellNavigationSource.ShellItemChanged, shellItem, shellSection, shellContent, stack, false, true);
|
||||
}
|
||||
|
||||
static void UpdateChecked(Element root, bool isChecked = true)
|
||||
|
@ -1346,6 +1197,85 @@ namespace Xamarin.Forms
|
|||
}
|
||||
|
||||
|
||||
#region Shell Flyout Content
|
||||
|
||||
|
||||
public static readonly BindableProperty FlyoutContentProperty =
|
||||
BindableProperty.Create(nameof(FlyoutContent), typeof(object), typeof(Shell), null, BindingMode.OneTime, propertyChanging: OnFlyoutContentChanging);
|
||||
|
||||
public static readonly BindableProperty FlyoutContentTemplateProperty =
|
||||
BindableProperty.Create(nameof(FlyoutContentTemplate), typeof(DataTemplate), typeof(Shell), null, BindingMode.OneTime, propertyChanging: OnFlyoutContentTemplateChanging);
|
||||
|
||||
View _flyoutContentView;
|
||||
|
||||
public View FlyoutContent
|
||||
{
|
||||
get => (View)GetValue(FlyoutContentProperty);
|
||||
set => SetValue(FlyoutContentProperty, value);
|
||||
}
|
||||
|
||||
public DataTemplate FlyoutContentTemplate
|
||||
{
|
||||
get => (DataTemplate)GetValue(FlyoutContentTemplateProperty);
|
||||
set => SetValue(FlyoutContentTemplateProperty, value);
|
||||
}
|
||||
|
||||
View FlyoutContentView
|
||||
{
|
||||
get => _flyoutContentView;
|
||||
set
|
||||
{
|
||||
if (_flyoutContentView == value)
|
||||
return;
|
||||
|
||||
if (_flyoutContentView != null)
|
||||
OnChildRemoved(_flyoutContentView, -1);
|
||||
_flyoutContentView = value;
|
||||
if (_flyoutContentView != null)
|
||||
OnChildAdded(_flyoutContentView);
|
||||
}
|
||||
}
|
||||
|
||||
void OnFlyoutContentChanged(object oldVal, object newVal)
|
||||
{
|
||||
if (FlyoutContentTemplate == null)
|
||||
{
|
||||
if (newVal is View newFlyoutContent)
|
||||
FlyoutContentView = newFlyoutContent;
|
||||
else
|
||||
FlyoutContentView = null;
|
||||
}
|
||||
}
|
||||
|
||||
void OnFlyoutContentTemplateChanged(DataTemplate oldValue, DataTemplate newValue)
|
||||
{
|
||||
if (newValue == null)
|
||||
{
|
||||
if (FlyoutContent is View flyoutContentView)
|
||||
FlyoutContentView = flyoutContentView;
|
||||
else
|
||||
FlyoutContentView = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
var newContentView = (View)newValue.CreateContent(FlyoutContent, this);
|
||||
FlyoutContentView = newContentView;
|
||||
}
|
||||
}
|
||||
|
||||
static void OnFlyoutContentChanging(BindableObject bindable, object oldValue, object newValue)
|
||||
{
|
||||
var shell = (Shell)bindable;
|
||||
shell.OnFlyoutContentChanged(oldValue, newValue);
|
||||
}
|
||||
|
||||
static void OnFlyoutContentTemplateChanging(BindableObject bindable, object oldValue, object newValue)
|
||||
{
|
||||
var shell = (Shell)bindable;
|
||||
shell.OnFlyoutContentTemplateChanged((DataTemplate)oldValue, (DataTemplate)newValue);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static void VerifyShellUWPFlagEnabled(
|
||||
|
|
|
@ -0,0 +1,248 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Xamarin.Forms
|
||||
{
|
||||
internal class ShellFlyoutItemsManager
|
||||
{
|
||||
readonly Shell _shell;
|
||||
List<List<Element>> _lastGeneratedFlyoutItems;
|
||||
public event EventHandler FlyoutItemsChanged;
|
||||
IShellController ShellController => _shell;
|
||||
ReadOnlyObservableCollectionWithSource<IReadOnlyList<Element>> _flyoutItemsReadonly;
|
||||
|
||||
public IEnumerable FlyoutItems => _flyoutItemsReadonly;
|
||||
public ShellFlyoutItemsManager(Shell shell)
|
||||
{
|
||||
_shell = shell;
|
||||
_flyoutItemsReadonly = new ReadOnlyObservableCollectionWithSource<IReadOnlyList<Element>>();
|
||||
}
|
||||
|
||||
|
||||
void SyncFlyoutItemsToReadOnlyCollection()
|
||||
{
|
||||
var flyoutItems = _flyoutItemsReadonly.List;
|
||||
|
||||
// sync the number of groups
|
||||
for (var i = flyoutItems.Count; i < _lastGeneratedFlyoutItems.Count; i++)
|
||||
flyoutItems.Add(new ReadOnlyObservableCollectionWithSource<Element>());
|
||||
|
||||
for (var i = _lastGeneratedFlyoutItems.Count; i < flyoutItems.Count; i++)
|
||||
flyoutItems.RemoveAt(i);
|
||||
|
||||
for (var i = 0; i < _lastGeneratedFlyoutItems.Count; i++)
|
||||
{
|
||||
var source = _lastGeneratedFlyoutItems[i];
|
||||
var dest = ((ReadOnlyObservableCollectionWithSource<Element>)flyoutItems[i]).List;
|
||||
|
||||
for (var j = dest.Count - 1; j >= 0; j--)
|
||||
{
|
||||
var item = dest[j];
|
||||
if (!source.Contains(item))
|
||||
dest.RemoveAt(j);
|
||||
}
|
||||
|
||||
for (var j = 0; j < source.Count; j++)
|
||||
{
|
||||
var item = source[j];
|
||||
var destIndex = dest.IndexOf(item);
|
||||
|
||||
if (destIndex == -1)
|
||||
{
|
||||
if (j < dest.Count)
|
||||
dest.Insert(j, item);
|
||||
else
|
||||
dest.Add(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (j < dest.Count)
|
||||
{
|
||||
if(destIndex != j)
|
||||
dest.Move(destIndex, j);
|
||||
}
|
||||
else
|
||||
dest.Add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void CheckIfFlyoutItemsChanged()
|
||||
{
|
||||
if (UpdateFlyoutGroupings())
|
||||
{
|
||||
FlyoutItemsChanged?.Invoke(this, EventArgs.Empty);
|
||||
SyncFlyoutItemsToReadOnlyCollection();
|
||||
}
|
||||
}
|
||||
|
||||
public List<List<Element>> GenerateFlyoutGrouping()
|
||||
{
|
||||
if (_lastGeneratedFlyoutItems == null)
|
||||
UpdateFlyoutGroupings();
|
||||
|
||||
return _lastGeneratedFlyoutItems;
|
||||
}
|
||||
|
||||
bool UpdateFlyoutGroupings()
|
||||
{
|
||||
// The idea here is to create grouping such that the Flyout would
|
||||
// render correctly if it renderered each item in the groups in order
|
||||
// but put a line between the groups. This is needed because our grouping can
|
||||
// actually go 3 layers deep.
|
||||
|
||||
// Ideally this lets us control where lines are drawn in the core code
|
||||
// just by changing how we generate these groupings
|
||||
|
||||
var result = new List<List<Element>>();
|
||||
|
||||
var currentGroup = new List<Element>();
|
||||
|
||||
foreach (var shellItem in ShellController.GetItems())
|
||||
{
|
||||
if (!ShowInFlyoutMenu(shellItem))
|
||||
continue;
|
||||
|
||||
if (Routing.IsImplicit(shellItem) || shellItem.FlyoutDisplayOptions == FlyoutDisplayOptions.AsMultipleItems)
|
||||
{
|
||||
if (shellItem.FlyoutDisplayOptions == FlyoutDisplayOptions.AsMultipleItems)
|
||||
IncrementGroup();
|
||||
|
||||
foreach (var shellSection in (shellItem as IShellItemController).GetItems())
|
||||
{
|
||||
if (!ShowInFlyoutMenu(shellSection))
|
||||
continue;
|
||||
|
||||
var shellContents = ((IShellSectionController)shellSection).GetItems();
|
||||
if (Routing.IsImplicit(shellSection) || shellSection.FlyoutDisplayOptions == FlyoutDisplayOptions.AsMultipleItems)
|
||||
{
|
||||
foreach (var shellContent in shellContents)
|
||||
{
|
||||
if (!ShowInFlyoutMenu(shellContent))
|
||||
continue;
|
||||
|
||||
currentGroup.Add(shellContent);
|
||||
if (shellContent == shellSection.CurrentItem)
|
||||
{
|
||||
AddMenuItems(shellContent.MenuItems);
|
||||
}
|
||||
}
|
||||
|
||||
if (shellSection.FlyoutDisplayOptions == FlyoutDisplayOptions.AsMultipleItems)
|
||||
IncrementGroup();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(shellSection.Parent is TabBar))
|
||||
{
|
||||
if (Routing.IsImplicit(shellSection) && shellContents.Count == 1)
|
||||
{
|
||||
if (!ShowInFlyoutMenu(shellContents[0]))
|
||||
continue;
|
||||
|
||||
currentGroup.Add(shellContents[0]);
|
||||
}
|
||||
else
|
||||
currentGroup.Add(shellSection);
|
||||
}
|
||||
|
||||
// If we have only a single child we will also show the items menu items
|
||||
if (shellContents.Count == 1 && shellSection == shellItem.CurrentItem && shellSection.CurrentItem.MenuItems.Count > 0)
|
||||
{
|
||||
AddMenuItems(shellSection.CurrentItem.MenuItems);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shellItem.FlyoutDisplayOptions == FlyoutDisplayOptions.AsMultipleItems)
|
||||
IncrementGroup();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(shellItem is TabBar))
|
||||
currentGroup.Add(shellItem);
|
||||
}
|
||||
}
|
||||
|
||||
IncrementGroup();
|
||||
|
||||
// If the flyout groupings haven't changed just return
|
||||
// the same instance so the caller knows it hasn't changed
|
||||
// at a later point this will all get converted to an observable collection
|
||||
if (_lastGeneratedFlyoutItems?.Count == result.Count)
|
||||
{
|
||||
bool hasChanged = false;
|
||||
for (var i = 0; i < result.Count && !hasChanged; i++)
|
||||
{
|
||||
var topLevelNew = result[i];
|
||||
var topLevelPrevious = _lastGeneratedFlyoutItems[i];
|
||||
|
||||
if (topLevelNew.Count != topLevelPrevious.Count)
|
||||
{
|
||||
hasChanged = true;
|
||||
break;
|
||||
}
|
||||
|
||||
for (var j = 0; j > topLevelNew.Count; j++)
|
||||
{
|
||||
if (topLevelNew[j] != topLevelPrevious[j])
|
||||
{
|
||||
hasChanged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!hasChanged)
|
||||
return false;
|
||||
}
|
||||
|
||||
_lastGeneratedFlyoutItems = result;
|
||||
return true;
|
||||
|
||||
bool ShowInFlyoutMenu(BindableObject bo)
|
||||
{
|
||||
if (bo is MenuShellItem msi)
|
||||
return Shell.GetFlyoutItemIsVisible(msi.MenuItem);
|
||||
|
||||
return Shell.GetFlyoutItemIsVisible(bo);
|
||||
}
|
||||
|
||||
void AddMenuItems(MenuItemCollection menuItems)
|
||||
{
|
||||
foreach (var item in menuItems)
|
||||
{
|
||||
if (ShowInFlyoutMenu(item))
|
||||
currentGroup.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
void IncrementGroup()
|
||||
{
|
||||
if (currentGroup.Count > 0)
|
||||
{
|
||||
result.Add(currentGroup);
|
||||
currentGroup = new List<Element>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ReadOnlyObservableCollectionWithSource<T> : ReadOnlyObservableCollection<T>
|
||||
{
|
||||
public ReadOnlyObservableCollectionWithSource() : this(new ObservableCollection<T>())
|
||||
{
|
||||
}
|
||||
|
||||
public ReadOnlyObservableCollectionWithSource(ObservableCollection<T> list) : base(list)
|
||||
{
|
||||
List = list;
|
||||
}
|
||||
|
||||
public ObservableCollection<T> List { get; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -51,7 +51,7 @@ namespace Xamarin.Forms
|
|||
// This scenario only comes up from UI iniated navigation (i.e. switching tabs)
|
||||
if (deferredArgs == null)
|
||||
{
|
||||
var navigatingArgs = ProposeNavigation(source, state, _shell.CurrentState != null);
|
||||
var navigatingArgs = ProposeNavigation(source, state, _shell.CurrentState != null, animate ?? true);
|
||||
|
||||
bool accept = !navigatingArgs.NavigationDelayedOrCancelled;
|
||||
if (navigatingArgs.DeferredTask != null)
|
||||
|
@ -273,13 +273,20 @@ namespace Xamarin.Forms
|
|||
// This is used for cases where the user is navigating via native UI navigation i.e. clicking on Tabs
|
||||
// If the user defers this type of navigation we generate the equivalent GotoAsync call
|
||||
// so when the deferral is completed the same navigation can complete
|
||||
public bool ProposeNavigationOutsideGotoAsync(ShellNavigationSource source, ShellItem shellItem, ShellSection shellSection, ShellContent shellContent, IReadOnlyList<Page> stack, bool canCancel)
|
||||
public bool ProposeNavigationOutsideGotoAsync(
|
||||
ShellNavigationSource source,
|
||||
ShellItem shellItem,
|
||||
ShellSection shellSection,
|
||||
ShellContent shellContent,
|
||||
IReadOnlyList<Page> stack,
|
||||
bool canCancel,
|
||||
bool isAnimated)
|
||||
{
|
||||
if (_accumulateNavigatedEvents)
|
||||
return true;
|
||||
|
||||
var proposedState = GetNavigationState(shellItem, shellSection, shellContent, stack, shellSection.Navigation.ModalStack);
|
||||
var navArgs = ProposeNavigation(source, proposedState, canCancel);
|
||||
var navArgs = ProposeNavigation(source, proposedState, canCancel, isAnimated);
|
||||
|
||||
if (navArgs.DeferralCount > 0)
|
||||
{
|
||||
|
@ -302,12 +309,20 @@ namespace Xamarin.Forms
|
|||
return !navArgs.NavigationDelayedOrCancelled;
|
||||
}
|
||||
|
||||
ShellNavigatingEventArgs ProposeNavigation(ShellNavigationSource source, ShellNavigationState proposedState, bool canCancel)
|
||||
ShellNavigatingEventArgs ProposeNavigation(
|
||||
ShellNavigationSource source,
|
||||
ShellNavigationState proposedState,
|
||||
bool canCancel,
|
||||
bool isAnimated)
|
||||
{
|
||||
if (_accumulateNavigatedEvents)
|
||||
return null;
|
||||
|
||||
var navArgs = new ShellNavigatingEventArgs(_shell.CurrentState, proposedState, source, canCancel);
|
||||
var navArgs = new ShellNavigatingEventArgs(_shell.CurrentState, proposedState, source, canCancel)
|
||||
{
|
||||
Animate = isAnimated
|
||||
};
|
||||
|
||||
HandleNavigating(navArgs);
|
||||
|
||||
return navArgs;
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Android.Content;
|
||||
using Android.Runtime;
|
||||
using Android.Util;
|
||||
using AndroidX.CoordinatorLayout.Widget;
|
||||
|
||||
namespace Xamarin.Forms.Platform.Android
|
||||
{
|
||||
class ShellFlyoutLayout : CoordinatorLayout
|
||||
{
|
||||
public ShellFlyoutLayout(Context context) : base(context)
|
||||
{
|
||||
}
|
||||
|
||||
public ShellFlyoutLayout(Context context, IAttributeSet attrs) : base(context, attrs)
|
||||
{
|
||||
}
|
||||
|
||||
public ShellFlyoutLayout(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
|
||||
{
|
||||
}
|
||||
|
||||
protected ShellFlyoutLayout(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
|
||||
{
|
||||
}
|
||||
|
||||
public Action LayoutChanging { get; set; }
|
||||
protected override void OnLayout(bool changed, int left, int top, int right, int bottom)
|
||||
{
|
||||
LayoutChanging?.Invoke();
|
||||
base.OnLayout(changed, left, top, right, bottom);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
LayoutChanging = null;
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,12 +31,13 @@ namespace Xamarin.Forms.Platform.Android
|
|||
Drawable _defaultBackgroundColor;
|
||||
ImageView _bgImage;
|
||||
AppBarLayout _appBar;
|
||||
RecyclerView _recycler;
|
||||
ShellFlyoutRecyclerAdapter _adapter;
|
||||
AView _flyoutContentView;
|
||||
ShellViewRenderer _contentView;
|
||||
View _flyoutHeader;
|
||||
ShellViewRenderer _footerView;
|
||||
int _actionBarHeight;
|
||||
ScrollLayoutManager _layoutManager;
|
||||
int _flyoutHeight;
|
||||
int _flyoutWidth;
|
||||
|
||||
protected IShellContext ShellContext => _shellContext;
|
||||
protected AView FooterView => _footerView?.NativeView;
|
||||
|
@ -62,10 +63,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
return;
|
||||
}
|
||||
|
||||
var coordinator = LayoutInflater.FromContext(context).Inflate(Resource.Layout.FlyoutContent, null);
|
||||
|
||||
Profile.FramePartition("Find Recycler");
|
||||
_recycler = coordinator.FindViewById<RecyclerView>(Resource.Id.flyoutcontent_recycler);
|
||||
var coordinator = (ViewGroup)LayoutInflater.FromContext(context).Inflate(Resource.Layout.FlyoutContent, null);
|
||||
|
||||
Profile.FramePartition("Find AppBar");
|
||||
_appBar = coordinator.FindViewById<AppBarLayout>(Resource.Id.flyoutcontent_appbar);
|
||||
|
@ -79,12 +77,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
_actionBarHeight = (int)context.ToPixels(56);
|
||||
UpdateFlyoutHeader();
|
||||
|
||||
Profile.FramePartition("Recycler.SetAdapter");
|
||||
_adapter = new ShellFlyoutRecyclerAdapter(shellContext, OnElementSelected);
|
||||
_recycler.SetClipToPadding(false);
|
||||
_recycler.SetLayoutManager(_layoutManager = new ScrollLayoutManager(context, (int)Orientation.Vertical, false));
|
||||
_recycler.SetLayoutManager(new LinearLayoutManager(context, (int)Orientation.Vertical, false));
|
||||
_recycler.SetAdapter(_adapter);
|
||||
UpdateFlyoutContent();
|
||||
|
||||
Profile.FramePartition("Initialize BgImage");
|
||||
var metrics = context.Resources.DisplayMetrics;
|
||||
|
@ -121,6 +114,9 @@ namespace Xamarin.Forms.Platform.Android
|
|||
UpdateFlyoutFooter();
|
||||
|
||||
Profile.FrameEnd();
|
||||
|
||||
if (View is ShellFlyoutLayout sfl)
|
||||
sfl.LayoutChanging += OnFlyoutViewLayoutChanged;
|
||||
}
|
||||
|
||||
void OnFlyoutHeaderMeasureInvalidated(object sender, EventArgs e)
|
||||
|
@ -154,6 +150,65 @@ namespace Xamarin.Forms.Platform.Android
|
|||
Shell.FlyoutFooterProperty,
|
||||
Shell.FlyoutFooterTemplateProperty))
|
||||
UpdateFlyoutFooter();
|
||||
else if (e.IsOneOf(
|
||||
Shell.FlyoutContentProperty,
|
||||
Shell.FlyoutContentTemplateProperty))
|
||||
UpdateFlyoutContent();
|
||||
}
|
||||
|
||||
protected virtual void UpdateFlyoutContent()
|
||||
{
|
||||
if (!_rootView.IsAlive())
|
||||
return;
|
||||
|
||||
var index = 0;
|
||||
if (_flyoutContentView != null)
|
||||
{
|
||||
index = _rootView.IndexOfChild(_flyoutContentView);
|
||||
_rootView.RemoveView(_flyoutContentView);
|
||||
}
|
||||
|
||||
_flyoutContentView = CreateFlyoutContent(_rootView);
|
||||
if (_flyoutContentView == null)
|
||||
return;
|
||||
|
||||
_rootView.AddView(_flyoutContentView, index);
|
||||
UpdateContentLayout();
|
||||
}
|
||||
|
||||
AView CreateFlyoutContent(ViewGroup rootView)
|
||||
{
|
||||
_rootView = rootView;
|
||||
if (_contentView != null)
|
||||
{
|
||||
var oldContentView = _contentView;
|
||||
_contentView = null;
|
||||
oldContentView.TearDown();
|
||||
}
|
||||
|
||||
var content = ((IShellController)ShellContext.Shell).FlyoutContent;
|
||||
if (content == null)
|
||||
{
|
||||
var lp = new CoordinatorLayout.LayoutParams(CoordinatorLayout.LayoutParams.MatchParent, CoordinatorLayout.LayoutParams.MatchParent);
|
||||
lp.Behavior = new AppBarLayout.ScrollingViewBehavior();
|
||||
var context = ShellContext.AndroidContext;
|
||||
Profile.FramePartition("Recycler.SetAdapter");
|
||||
var recyclerView = new RecyclerViewContainer(context, new ShellFlyoutRecyclerAdapter(ShellContext, OnElementSelected))
|
||||
{
|
||||
LayoutParameters = lp
|
||||
};
|
||||
|
||||
return recyclerView;
|
||||
}
|
||||
|
||||
_contentView = new ShellViewRenderer(ShellContext.AndroidContext, content);
|
||||
|
||||
_contentView.NativeView.LayoutParameters = new CoordinatorLayout.LayoutParams(LP.MatchParent, LP.MatchParent)
|
||||
{
|
||||
Behavior = new AppBarLayout.ScrollingViewBehavior()
|
||||
};
|
||||
|
||||
return _contentView.NativeView;
|
||||
}
|
||||
|
||||
protected virtual void UpdateFlyoutHeader()
|
||||
|
@ -186,6 +241,8 @@ namespace Xamarin.Forms.Platform.Android
|
|||
};
|
||||
_appBar.AddView(_headerView);
|
||||
UpdateFlyoutHeaderBehavior();
|
||||
|
||||
UpdateContentLayout();
|
||||
}
|
||||
|
||||
protected virtual void UpdateFlyoutFooter()
|
||||
|
@ -205,23 +262,76 @@ namespace Xamarin.Forms.Platform.Android
|
|||
|
||||
_footerView = new ShellViewRenderer(_shellContext.AndroidContext, footer);
|
||||
|
||||
_footerView.NativeView.LayoutParameters = new CoordinatorLayout.LayoutParams(LP.MatchParent, LP.WrapContent)
|
||||
{
|
||||
Gravity = (int)(GravityFlags.Bottom | GravityFlags.End)
|
||||
};
|
||||
|
||||
_footerView.LayoutView(_shellContext.AndroidContext.FromPixels(_rootView.LayoutParameters.Width), double.PositiveInfinity);
|
||||
_rootView.AddView(_footerView.NativeView);
|
||||
if (_recycler?.LayoutParameters is CoordinatorLayout.LayoutParams cl)
|
||||
|
||||
if (_footerView.NativeView.LayoutParameters is CoordinatorLayout.LayoutParams cl)
|
||||
cl.Gravity = (int)(GravityFlags.Bottom | GravityFlags.End);
|
||||
|
||||
UpdateFooterLayout();
|
||||
UpdateContentLayout();
|
||||
UpdateContentBottomMargin();
|
||||
}
|
||||
|
||||
void UpdateFooterLayout()
|
||||
{
|
||||
if (_footerView != null)
|
||||
{
|
||||
cl.BottomMargin = (int)_shellContext.AndroidContext.ToPixels(_footerView.View.Height);
|
||||
_footerView.LayoutView(_shellContext.AndroidContext.FromPixels(_rootView.LayoutParameters.Width), double.PositiveInfinity);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateContentLayout()
|
||||
{
|
||||
if (_contentView != null)
|
||||
{
|
||||
if (_contentView == null)
|
||||
return;
|
||||
|
||||
var height =
|
||||
(View.MeasuredHeight) -
|
||||
(FooterView?.MeasuredHeight ?? 0) -
|
||||
(_headerView?.MeasuredHeight ?? 0);
|
||||
|
||||
var width = View.MeasuredWidth;
|
||||
|
||||
_contentView.LayoutView(
|
||||
ShellContext.AndroidContext.FromPixels(width),
|
||||
ShellContext.AndroidContext.FromPixels(height));
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateContentBottomMargin()
|
||||
{
|
||||
if (_flyoutContentView?.LayoutParameters is CoordinatorLayout.LayoutParams cl)
|
||||
{
|
||||
cl.BottomMargin = (int)_shellContext.AndroidContext.ToPixels(_footerView?.View.Height ?? 0);
|
||||
}
|
||||
}
|
||||
|
||||
void OnFlyoutViewLayoutChanged()
|
||||
{
|
||||
|
||||
if (View?.MeasuredHeight > 0 &&
|
||||
View?.MeasuredWidth > 0 &&
|
||||
(_flyoutHeight != View.MeasuredHeight ||
|
||||
_flyoutWidth != View.MeasuredWidth)
|
||||
)
|
||||
{
|
||||
_flyoutHeight = View.MeasuredHeight;
|
||||
_flyoutWidth = View.MeasuredWidth;
|
||||
|
||||
UpdateFooterLayout();
|
||||
UpdateContentLayout();
|
||||
UpdateContentBottomMargin();
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateVerticalScrollMode()
|
||||
{
|
||||
if (_layoutManager != null)
|
||||
_layoutManager.ScrollVertically = _shellContext.Shell.FlyoutVerticalScrollMode;
|
||||
if (_flyoutContentView is RecyclerView rv && rv.GetLayoutManager() is ScrollLayoutManager lm)
|
||||
{
|
||||
lm.ScrollVertically = _shellContext.Shell.FlyoutVerticalScrollMode;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void UpdateFlyoutBackground()
|
||||
|
@ -367,30 +477,25 @@ namespace Xamarin.Forms.Platform.Android
|
|||
if (_rootView != null && _footerView?.NativeView != null)
|
||||
_rootView.RemoveView(_footerView.NativeView);
|
||||
|
||||
if (_recycler != null)
|
||||
{
|
||||
_recycler.SetLayoutManager(null);
|
||||
_recycler.SetAdapter(null);
|
||||
_recycler.Dispose();
|
||||
}
|
||||
if (View != null && View is ShellFlyoutLayout sfl)
|
||||
sfl.LayoutChanging -= OnFlyoutViewLayoutChanged;
|
||||
|
||||
_adapter?.Dispose();
|
||||
_contentView?.TearDown();
|
||||
_flyoutContentView?.Dispose();
|
||||
_headerView.Dispose();
|
||||
_footerView?.TearDown();
|
||||
_rootView.Dispose();
|
||||
_layoutManager?.Dispose();
|
||||
_defaultBackgroundColor?.Dispose();
|
||||
_bgImage?.Dispose();
|
||||
|
||||
_contentView = null;
|
||||
_flyoutHeader = null;
|
||||
_rootView = null;
|
||||
_headerView = null;
|
||||
_shellContext = null;
|
||||
_appBar = null;
|
||||
_recycler = null;
|
||||
_adapter = null;
|
||||
_flyoutContentView = null;
|
||||
_defaultBackgroundColor = null;
|
||||
_layoutManager = null;
|
||||
_bgImage = null;
|
||||
_footerView = null;
|
||||
}
|
||||
|
@ -478,6 +583,41 @@ namespace Xamarin.Forms.Platform.Android
|
|||
}
|
||||
}
|
||||
|
||||
class RecyclerViewContainer : RecyclerView
|
||||
{
|
||||
bool _disposed;
|
||||
ShellFlyoutRecyclerAdapter _shellFlyoutRecyclerAdapter;
|
||||
ScrollLayoutManager _layoutManager;
|
||||
|
||||
public RecyclerViewContainer(Context context, ShellFlyoutRecyclerAdapter shellFlyoutRecyclerAdapter) : base(context)
|
||||
{
|
||||
_shellFlyoutRecyclerAdapter = shellFlyoutRecyclerAdapter;
|
||||
SetClipToPadding(false);
|
||||
SetLayoutManager(_layoutManager = new ScrollLayoutManager(context, (int)Orientation.Vertical, false));
|
||||
SetLayoutManager(new LinearLayoutManager(context, (int)Orientation.Vertical, false));
|
||||
SetAdapter(_shellFlyoutRecyclerAdapter);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
_disposed = true;
|
||||
if (disposing)
|
||||
{
|
||||
SetLayoutManager(null);
|
||||
SetAdapter(null);
|
||||
_shellFlyoutRecyclerAdapter?.Dispose();
|
||||
_layoutManager?.Dispose();
|
||||
_shellFlyoutRecyclerAdapter = null;
|
||||
_layoutManager = null;
|
||||
}
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
internal class ScrollLayoutManager : LinearLayoutManager
|
||||
{
|
||||
public ScrollMode ScrollVertically { get; set; } = ScrollMode.Auto;
|
||||
|
|
|
@ -128,6 +128,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
|
||||
Fragment IShellObservableFragment.Fragment => this;
|
||||
public ShellSection ShellSection { get; set; }
|
||||
protected IShellContext ShellContext => _shellContext;
|
||||
IShellSectionController SectionController => (IShellSectionController)ShellSection;
|
||||
|
||||
public override AView OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
|
||||
|
|
|
@ -50,7 +50,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
SearchHandler _searchHandler;
|
||||
IShellSearchView _searchView;
|
||||
ContainerView _titleViewContainer;
|
||||
IShellContext _shellContext;
|
||||
protected IShellContext ShellContext { get; private set; }
|
||||
//assume the default
|
||||
Color _tintColor = Color.Default;
|
||||
Toolbar _toolbar;
|
||||
|
@ -62,7 +62,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
|
||||
public ShellToolbarTracker(IShellContext shellContext, Toolbar toolbar, DrawerLayout drawerLayout)
|
||||
{
|
||||
_shellContext = shellContext ?? throw new ArgumentNullException(nameof(shellContext));
|
||||
ShellContext = shellContext ?? throw new ArgumentNullException(nameof(shellContext));
|
||||
_toolbar = toolbar ?? throw new ArgumentNullException(nameof(toolbar));
|
||||
_drawerLayout = drawerLayout ?? throw new ArgumentNullException(nameof(drawerLayout));
|
||||
_appBar = _toolbar.Parent.GetParentOfType<AppBarLayout>();
|
||||
|
@ -70,7 +70,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
_globalLayoutListener = new GenericGlobalLayoutListener(() => UpdateNavBarHasShadow(Page));
|
||||
_appBar.ViewTreeObserver.AddOnGlobalLayoutListener(_globalLayoutListener);
|
||||
_toolbar.SetNavigationOnClickListener(this);
|
||||
((IShellController)_shellContext.Shell).AddFlyoutBehaviorObserver(this);
|
||||
((IShellController)ShellContext.Shell).AddFlyoutBehaviorObserver(this);
|
||||
}
|
||||
|
||||
public bool CanNavigateBack
|
||||
|
@ -138,7 +138,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
else if (CanNavigateBack)
|
||||
OnNavigateBack();
|
||||
else
|
||||
_shellContext.Shell.FlyoutIsPresented = !_shellContext.Shell.FlyoutIsPresented;
|
||||
ShellContext.Shell.FlyoutIsPresented = !ShellContext.Shell.FlyoutIsPresented;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,9 +161,9 @@ namespace Xamarin.Forms.Platform.Android
|
|||
|
||||
_toolbar.DisposeMenuItems(_currentToolbarItems, OnToolbarItemPropertyChanged);
|
||||
|
||||
((IShellController)_shellContext.Shell)?.RemoveFlyoutBehaviorObserver(this);
|
||||
((IShellController)ShellContext.Shell)?.RemoveFlyoutBehaviorObserver(this);
|
||||
|
||||
UpdateTitleView(_shellContext.AndroidContext, _toolbar, null);
|
||||
UpdateTitleView(ShellContext.AndroidContext, _toolbar, null);
|
||||
|
||||
if (_searchView != null)
|
||||
{
|
||||
|
@ -184,7 +184,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
_globalLayoutListener = null;
|
||||
_backButtonBehavior = null;
|
||||
SearchHandler = null;
|
||||
_shellContext = null;
|
||||
ShellContext = null;
|
||||
_drawerToggle = null;
|
||||
_searchView = null;
|
||||
Page = null;
|
||||
|
@ -197,7 +197,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
|
||||
protected virtual IShellSearchView GetSearchView(Context context)
|
||||
{
|
||||
return new ShellSearchView(context, _shellContext);
|
||||
return new ShellSearchView(context, ShellContext);
|
||||
}
|
||||
|
||||
protected async virtual void OnNavigateBack()
|
||||
|
@ -427,7 +427,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
|
||||
|
||||
//this needs to be set after SyncState
|
||||
UpdateToolbarIconAccessibilityText(toolbar, _shellContext.Shell);
|
||||
UpdateToolbarIconAccessibilityText(toolbar, ShellContext.Shell);
|
||||
}
|
||||
|
||||
|
||||
|
@ -467,7 +467,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
|
||||
protected virtual void UpdateMenuItemIcon(Context context, IMenuItem menuItem, ToolbarItem toolBarItem)
|
||||
{
|
||||
_shellContext.ApplyDrawableAsync(toolBarItem, ToolbarItem.IconImageSourceProperty, baseDrawable =>
|
||||
ShellContext.ApplyDrawableAsync(toolBarItem, ToolbarItem.IconImageSourceProperty, baseDrawable =>
|
||||
{
|
||||
if (baseDrawable != null)
|
||||
{
|
||||
|
@ -547,12 +547,12 @@ namespace Xamarin.Forms.Platform.Android
|
|||
var menu = toolbar.Menu;
|
||||
var sortedItems = page.ToolbarItems.OrderBy(x => x.Order);
|
||||
|
||||
toolbar.UpdateMenuItems(sortedItems, _shellContext.AndroidContext, TintColor, OnToolbarItemPropertyChanged, _currentMenuItems, _currentToolbarItems);
|
||||
toolbar.UpdateMenuItems(sortedItems, ShellContext.AndroidContext, TintColor, OnToolbarItemPropertyChanged, _currentMenuItems, _currentToolbarItems);
|
||||
|
||||
SearchHandler = Shell.GetSearchHandler(page);
|
||||
if (SearchHandler != null && SearchHandler.SearchBoxVisibility != SearchBoxVisibility.Hidden)
|
||||
{
|
||||
var context = _shellContext.AndroidContext;
|
||||
var context = ShellContext.AndroidContext;
|
||||
if (_searchView == null)
|
||||
{
|
||||
_searchView = GetSearchView(context);
|
||||
|
@ -609,7 +609,7 @@ namespace Xamarin.Forms.Platform.Android
|
|||
void OnToolbarItemPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
var sortedItems = Page.ToolbarItems.OrderBy(x => x.Order).ToList();
|
||||
_toolbar.OnToolbarItemPropertyChanged(e, (ToolbarItem)sender, sortedItems, _shellContext.AndroidContext, TintColor, OnToolbarItemPropertyChanged, _currentMenuItems, _currentToolbarItems);
|
||||
_toolbar.OnToolbarItemPropertyChanged(e, (ToolbarItem)sender, sortedItems, ShellContext.AndroidContext, TintColor, OnToolbarItemPropertyChanged, _currentMenuItems, _currentToolbarItems);
|
||||
}
|
||||
|
||||
void OnSearchViewAttachedToWindow(object sender, AView.ViewAttachedToWindowEventArgs e)
|
||||
|
@ -636,12 +636,12 @@ namespace Xamarin.Forms.Platform.Android
|
|||
|
||||
void UpdateLeftBarButtonItem()
|
||||
{
|
||||
UpdateLeftBarButtonItem(_shellContext.AndroidContext, _toolbar, _drawerLayout, Page);
|
||||
UpdateLeftBarButtonItem(ShellContext.AndroidContext, _toolbar, _drawerLayout, Page);
|
||||
}
|
||||
|
||||
void UpdateTitleView()
|
||||
{
|
||||
UpdateTitleView(_shellContext.AndroidContext, _toolbar, Shell.GetTitleView(Page));
|
||||
UpdateTitleView(ShellContext.AndroidContext, _toolbar, Shell.GetTitleView(Page));
|
||||
}
|
||||
|
||||
void UpdateToolbarItems()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
<xamarin.forms.platform.android.ShellFlyoutLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -14,10 +14,4 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/flyoutcontent_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
</xamarin.forms.platform.android.ShellFlyoutLayout>
|
||||
|
|
|
@ -18,6 +18,8 @@ namespace Xamarin.Forms.Platform.UWP
|
|||
new PropertyMetadata(default(bool), IsSelectedChanged));
|
||||
|
||||
View _content;
|
||||
object _previousDataContext;
|
||||
double _previousWidth;
|
||||
FrameworkElement FrameworkElement { get; set; }
|
||||
|
||||
public ShellFlyoutItemRenderer()
|
||||
|
@ -34,6 +36,11 @@ namespace Xamarin.Forms.Platform.UWP
|
|||
|
||||
void OnDataContextChanged(Windows.UI.Xaml.FrameworkElement sender, Windows.UI.Xaml.DataContextChangedEventArgs args)
|
||||
{
|
||||
if (_previousDataContext == args.NewValue)
|
||||
return;
|
||||
|
||||
_previousWidth = -1;
|
||||
_previousDataContext = args.NewValue;
|
||||
if (_content != null)
|
||||
{
|
||||
if (_content.BindingContext is INotifyPropertyChanged inpc)
|
||||
|
@ -95,12 +102,6 @@ namespace Xamarin.Forms.Platform.UWP
|
|||
OnMeasureInvalidated();
|
||||
}
|
||||
|
||||
protected override Windows.Foundation.Size MeasureOverride(Windows.Foundation.Size availableSize)
|
||||
{
|
||||
return base.MeasureOverride(availableSize);
|
||||
}
|
||||
|
||||
double _previousWidth;
|
||||
private void OnLayoutUpdated(object sender, object e)
|
||||
{
|
||||
if (this.ActualWidth > 0 && this.ActualWidth != _content.Width && _previousWidth != this.ActualWidth)
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
UIView _footerView;
|
||||
View _footer;
|
||||
ShellTableViewController _tableViewController;
|
||||
ShellFlyoutLayoutManager _shellFlyoutContentManager;
|
||||
|
||||
public event EventHandler WillAppear;
|
||||
public event EventHandler WillDisappear;
|
||||
|
@ -22,10 +23,10 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
{
|
||||
_shellContext = context;
|
||||
_tableViewController = CreateShellTableViewController();
|
||||
_shellFlyoutContentManager = _tableViewController?.ShellFlyoutContentManager;
|
||||
AddChildViewController(_tableViewController);
|
||||
|
||||
context.Shell.PropertyChanged += HandleShellPropertyChanged;
|
||||
|
||||
}
|
||||
|
||||
protected virtual ShellTableViewController CreateShellTableViewController()
|
||||
|
@ -55,6 +56,12 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
{
|
||||
UpdateFlyoutFooter();
|
||||
}
|
||||
else if (e.IsOneOf(
|
||||
Shell.FlyoutContentProperty,
|
||||
Shell.FlyoutContentTemplateProperty))
|
||||
{
|
||||
UpdateFlyoutContent();
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateFlowDirection()
|
||||
|
@ -123,7 +130,6 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
|
||||
View.AddSubview(_footerView);
|
||||
_footerView.ClipsToBounds = true;
|
||||
_tableViewController.FooterView = _footerView;
|
||||
_footer.MeasureInvalidated += OnFooterMeasureInvalidated;
|
||||
}
|
||||
|
||||
|
@ -170,6 +176,7 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
{
|
||||
base.ViewWillLayoutSubviews();
|
||||
UpdateFooterPosition();
|
||||
UpdateFlyoutContent();
|
||||
}
|
||||
|
||||
protected virtual void UpdateBackground()
|
||||
|
@ -251,7 +258,6 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
{
|
||||
base.ViewDidLoad();
|
||||
|
||||
View.AddSubview(_tableViewController.View);
|
||||
|
||||
UpdateFlyoutHeader();
|
||||
UpdateFlyoutFooter();
|
||||
|
@ -273,6 +279,19 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
UpdateFlowDirection();
|
||||
}
|
||||
|
||||
void UpdateFlyoutContent()
|
||||
{
|
||||
var view = (_shellContext.Shell as IShellController).FlyoutContent;
|
||||
|
||||
if (view != null)
|
||||
_shellFlyoutContentManager.SetCustomContent(view);
|
||||
else
|
||||
_shellFlyoutContentManager.SetDefaultContent(_tableViewController.TableView);
|
||||
|
||||
if(_shellFlyoutContentManager.ContentView != null)
|
||||
View.InsertSubview(_shellFlyoutContentManager.ContentView, 0);
|
||||
}
|
||||
|
||||
public override void ViewWillAppear(bool animated)
|
||||
{
|
||||
UpdateFlowDirection();
|
||||
|
|
|
@ -0,0 +1,332 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using CoreAnimation;
|
||||
using CoreGraphics;
|
||||
using Foundation;
|
||||
using UIKit;
|
||||
|
||||
namespace Xamarin.Forms.Platform.iOS
|
||||
{
|
||||
class ShellFlyoutLayoutManager
|
||||
{
|
||||
double _headerMin = 56;
|
||||
double _headerOffset = 0;
|
||||
UIView _contentView;
|
||||
UIScrollView ScrollView { get; set; }
|
||||
UIContainerView _headerView;
|
||||
UIView _footerView;
|
||||
double _headerSize;
|
||||
readonly IShellContext _context;
|
||||
Action removeScolledEvent;
|
||||
|
||||
IShellController ShellController => _context.Shell;
|
||||
public ShellFlyoutLayoutManager(IShellContext context)
|
||||
{
|
||||
_context = context;
|
||||
_context.Shell.PropertyChanged += OnShellPropertyChanged;
|
||||
ShellController.StructureChanged += OnStructureChanged;
|
||||
}
|
||||
|
||||
public void SetCustomContent(View content)
|
||||
{
|
||||
if (content == Content)
|
||||
return;
|
||||
|
||||
removeScolledEvent?.Invoke();
|
||||
removeScolledEvent = null;
|
||||
|
||||
if (Content != null)
|
||||
{
|
||||
var oldRenderer = Platform.GetRenderer(Content);
|
||||
var oldContentView = ContentView;
|
||||
var oldContent = Content;
|
||||
|
||||
Content = null;
|
||||
ContentView = null;
|
||||
oldContent.ClearValue(Platform.RendererProperty);
|
||||
oldContentView?.RemoveFromSuperview();
|
||||
oldRenderer?.Dispose();
|
||||
}
|
||||
// If the user hasn't defined custom content then only the ContentView is set
|
||||
else if(ContentView != null)
|
||||
{
|
||||
var oldContentView = ContentView;
|
||||
ContentView = null;
|
||||
oldContentView.RemoveFromSuperview();
|
||||
}
|
||||
|
||||
Content = content;
|
||||
if (Content != null)
|
||||
{
|
||||
var renderer = Platform.CreateRenderer(Content);
|
||||
ContentView = renderer.NativeView;
|
||||
Platform.SetRenderer(Content, renderer);
|
||||
ContentView.ClipsToBounds = true;
|
||||
|
||||
// not sure if there's a more efficient way to do this
|
||||
// I can test the native control to see if it inherits from UIScrollView
|
||||
// But the CollectionViewRenderer doesn't inherit from UIScrollView
|
||||
if (Content is ScrollView sv)
|
||||
{
|
||||
sv.Scrolled += ScrollViewScrolled;
|
||||
removeScolledEvent = () => sv.Scrolled -= ScrollViewScrolled;
|
||||
void ScrollViewScrolled(object sender, ScrolledEventArgs e) =>
|
||||
OnScrolled((nfloat)sv.ScrollY);
|
||||
}
|
||||
else if(Content is CollectionView cv)
|
||||
{
|
||||
cv.Scrolled += CollectionViewScrolled;
|
||||
removeScolledEvent = () => cv.Scrolled -= CollectionViewScrolled;
|
||||
void CollectionViewScrolled(object sender, ItemsViewScrolledEventArgs e) =>
|
||||
OnScrolled((nfloat)e.VerticalOffset);
|
||||
}
|
||||
else if (Content is ListView lv)
|
||||
{
|
||||
lv.Scrolled += ListViewScrolled;
|
||||
removeScolledEvent = () => lv.Scrolled -= ListViewScrolled;
|
||||
void ListViewScrolled(object sender, ScrolledEventArgs e) =>
|
||||
OnScrolled((nfloat)e.ScrollY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetDefaultContent(UIView view)
|
||||
{
|
||||
if (ContentView == view)
|
||||
return;
|
||||
|
||||
SetCustomContent(null);
|
||||
ContentView = view;
|
||||
}
|
||||
|
||||
public View Content
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public UIView ContentView
|
||||
{
|
||||
get
|
||||
{
|
||||
return _contentView;
|
||||
}
|
||||
private set
|
||||
{
|
||||
_contentView = value;
|
||||
|
||||
if (ContentView is UIScrollView sv1)
|
||||
ScrollView = sv1;
|
||||
else if (ContentView is IVisualElementRenderer ver && ver.NativeView is UIScrollView uIScroll)
|
||||
ScrollView = uIScroll;
|
||||
|
||||
if (ScrollView != null && Forms.IsiOS11OrNewer)
|
||||
ScrollView.ContentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentBehavior.Never;
|
||||
|
||||
if (ScrollView != null)
|
||||
{
|
||||
LayoutParallax();
|
||||
SetHeaderContentInset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual UIContainerView HeaderView
|
||||
{
|
||||
get => _headerView;
|
||||
set
|
||||
{
|
||||
if (_headerView == value)
|
||||
return;
|
||||
|
||||
if (_headerView != null)
|
||||
_headerView.HeaderSizeChanged -= OnHeaderFooterSizeChanged;
|
||||
|
||||
_headerView = value;
|
||||
|
||||
if (_headerView != null)
|
||||
_headerView.HeaderSizeChanged += OnHeaderFooterSizeChanged;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual UIView FooterView
|
||||
{
|
||||
get => _footerView;
|
||||
set
|
||||
{
|
||||
if (_footerView == value)
|
||||
return;
|
||||
|
||||
_footerView = value;
|
||||
}
|
||||
}
|
||||
|
||||
void OnHeaderFooterSizeChanged(object sender, EventArgs e)
|
||||
{
|
||||
_headerSize = HeaderMax;
|
||||
SetHeaderContentInset();
|
||||
LayoutParallax();
|
||||
}
|
||||
|
||||
internal void SetHeaderContentInset()
|
||||
{
|
||||
if (ScrollView == null)
|
||||
return;
|
||||
|
||||
var offset = ScrollView.ContentInset.Top;
|
||||
|
||||
if (HeaderView != null)
|
||||
ScrollView.ContentInset = new UIEdgeInsets((nfloat)HeaderMax, 0, 0, 0);
|
||||
else
|
||||
ScrollView.ContentInset = new UIEdgeInsets(Platform.SafeAreaInsetsForWindow.Top, 0, 0, 0);
|
||||
|
||||
offset -= ScrollView.ContentInset.Top;
|
||||
|
||||
ScrollView.ContentOffset =
|
||||
new CGPoint(ScrollView.ContentOffset.X, ScrollView.ContentOffset.Y + offset);
|
||||
|
||||
UpdateVerticalScrollMode();
|
||||
}
|
||||
|
||||
public void UpdateVerticalScrollMode()
|
||||
{
|
||||
if (ScrollView == null)
|
||||
return;
|
||||
|
||||
switch (_context.Shell.FlyoutVerticalScrollMode)
|
||||
{
|
||||
case ScrollMode.Auto:
|
||||
ScrollView.ScrollEnabled = true;
|
||||
ScrollView.AlwaysBounceVertical = false;
|
||||
break;
|
||||
case ScrollMode.Enabled:
|
||||
ScrollView.ScrollEnabled = true;
|
||||
ScrollView.AlwaysBounceVertical = true;
|
||||
break;
|
||||
case ScrollMode.Disabled:
|
||||
ScrollView.ScrollEnabled = false;
|
||||
ScrollView.AlwaysBounceVertical = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void LayoutParallax()
|
||||
{
|
||||
var parent = ContentView?.Superview;
|
||||
if (parent == null)
|
||||
return;
|
||||
|
||||
nfloat footerHeight = 0;
|
||||
|
||||
if (FooterView != null)
|
||||
footerHeight = FooterView.Frame.Height;
|
||||
|
||||
var contentViewYOffset = HeaderView?.Frame.Height ?? 0;
|
||||
if (ScrollView != null)
|
||||
{
|
||||
if (Content == null)
|
||||
{
|
||||
ContentView.Frame =
|
||||
new CGRect(parent.Bounds.X, HeaderTopMargin, parent.Bounds.Width, parent.Bounds.Height - HeaderTopMargin - footerHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
ContentView.Frame =
|
||||
new CGRect(parent.Bounds.X, HeaderTopMargin, parent.Bounds.Width, parent.Bounds.Height - HeaderTopMargin - footerHeight);
|
||||
|
||||
if (Content != null)
|
||||
Layout.LayoutChildIntoBoundingRegion(Content, new Rectangle(0, 0, ContentView.Frame.Width, ContentView.Frame.Height - contentViewYOffset));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ContentView.Frame =
|
||||
new CGRect(parent.Bounds.X, HeaderTopMargin + contentViewYOffset, parent.Bounds.Width, parent.Bounds.Height - HeaderTopMargin - footerHeight - contentViewYOffset);
|
||||
|
||||
if (Content != null)
|
||||
Layout.LayoutChildIntoBoundingRegion(Content, new Rectangle(0, 0, ContentView.Frame.Width, ContentView.Frame.Height));
|
||||
}
|
||||
|
||||
if (HeaderView != null && !double.IsNaN(_headerSize))
|
||||
{
|
||||
var margin = HeaderView.Margin;
|
||||
var leftMargin = margin.Left - margin.Right;
|
||||
|
||||
HeaderView.Frame = new CGRect(leftMargin, _headerOffset, parent.Frame.Width, _headerSize + HeaderTopMargin);
|
||||
|
||||
if (_context.Shell.FlyoutHeaderBehavior == FlyoutHeaderBehavior.Scroll && HeaderTopMargin > 0 && _headerOffset < 0)
|
||||
{
|
||||
var headerHeight = Math.Max(_headerMin, _headerSize + _headerOffset);
|
||||
CAShapeLayer shapeLayer = new CAShapeLayer();
|
||||
CGRect rect = new CGRect(0, _headerOffset * -1, parent.Frame.Width, headerHeight);
|
||||
var path = CGPath.FromRect(rect);
|
||||
shapeLayer.Path = path;
|
||||
HeaderView.Layer.Mask = shapeLayer;
|
||||
}
|
||||
else if (HeaderView.Layer.Mask != null)
|
||||
HeaderView.Layer.Mask = null;
|
||||
}
|
||||
}
|
||||
|
||||
void OnStructureChanged(object sender, EventArgs e) => UpdateVerticalScrollMode();
|
||||
|
||||
void OnShellPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Is(Shell.FlyoutHeaderBehaviorProperty))
|
||||
{
|
||||
SetHeaderContentInset();
|
||||
LayoutParallax();
|
||||
}
|
||||
else if (e.Is(Shell.FlyoutVerticalScrollModeProperty))
|
||||
UpdateVerticalScrollMode();
|
||||
}
|
||||
|
||||
public void ViewDidLoad()
|
||||
{
|
||||
HeaderView?.MeasureIfNeeded();
|
||||
SetHeaderContentInset();
|
||||
}
|
||||
|
||||
public void OnScrolled(nfloat contentOffsetY)
|
||||
{
|
||||
var headerBehavior = _context.Shell.FlyoutHeaderBehavior;
|
||||
|
||||
switch (headerBehavior)
|
||||
{
|
||||
case FlyoutHeaderBehavior.Default:
|
||||
case FlyoutHeaderBehavior.Fixed:
|
||||
_headerSize = HeaderMax;
|
||||
_headerOffset = 0;
|
||||
break;
|
||||
|
||||
case FlyoutHeaderBehavior.Scroll:
|
||||
_headerSize = HeaderMax;
|
||||
_headerOffset = Math.Min(0, -(HeaderMax + contentOffsetY));
|
||||
break;
|
||||
|
||||
case FlyoutHeaderBehavior.CollapseOnScroll:
|
||||
_headerSize = Math.Max(_headerMin, -contentOffsetY);
|
||||
_headerOffset = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
LayoutParallax();
|
||||
}
|
||||
|
||||
double HeaderMax => HeaderView?.MeasuredHeight ?? 0;
|
||||
double HeaderTopMargin => (HeaderView != null) ? HeaderView.Margin.Top - HeaderView.Margin.Bottom : 0;
|
||||
|
||||
public void TearDown()
|
||||
{
|
||||
_context.Shell.PropertyChanged -= OnShellPropertyChanged;
|
||||
ShellController.StructureChanged -= OnStructureChanged;
|
||||
SetCustomContent(null);
|
||||
ContentView = null;
|
||||
HeaderView = null;
|
||||
FooterView = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,60 +11,48 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
{
|
||||
readonly IShellContext _context;
|
||||
readonly ShellTableViewSource _source;
|
||||
double _headerMin = 56;
|
||||
double _headerOffset = 0;
|
||||
double _headerSize;
|
||||
bool _isDisposed;
|
||||
Action<Element> _onElementSelected;
|
||||
UIContainerView _headerView;
|
||||
UIView _footerView;
|
||||
|
||||
IShellController ShellController => ((IShellController)_context.Shell);
|
||||
IShellController ShellController => _context.Shell;
|
||||
|
||||
public ShellTableViewController(IShellContext context, UIContainerView headerView, Action<Element> onElementSelected) : this(context, onElementSelected)
|
||||
{
|
||||
ShellFlyoutContentManager = new ShellFlyoutLayoutManager(context);
|
||||
HeaderView = headerView;
|
||||
}
|
||||
|
||||
public ShellTableViewController(IShellContext context, Action<Element> onElementSelected)
|
||||
{
|
||||
ShellFlyoutContentManager = ShellFlyoutContentManager ?? new ShellFlyoutLayoutManager(context);
|
||||
_context = context;
|
||||
_onElementSelected = onElementSelected;
|
||||
_source = CreateShellTableViewSource();
|
||||
_source.ScrolledEvent += OnScrolled;
|
||||
|
||||
ShellController.FlyoutItemsChanged += OnFlyoutItemsChanged;
|
||||
_context.Shell.PropertyChanged += OnShellPropertyChanged;
|
||||
_source.ScrolledEvent += OnScrolled;
|
||||
}
|
||||
|
||||
internal ShellFlyoutLayoutManager ShellFlyoutContentManager
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
void OnScrolled(object sender, UIScrollView e)
|
||||
{
|
||||
ShellFlyoutContentManager.OnScrolled(e.ContentOffset.Y);
|
||||
}
|
||||
|
||||
public virtual UIContainerView HeaderView
|
||||
{
|
||||
get => _headerView;
|
||||
set
|
||||
{
|
||||
if (_headerView == value)
|
||||
return;
|
||||
|
||||
if (_headerView != null)
|
||||
_headerView.HeaderSizeChanged -= OnHeaderFooterSizeChanged;
|
||||
|
||||
_headerView = value;
|
||||
|
||||
if (_headerView != null)
|
||||
_headerView.HeaderSizeChanged += OnHeaderFooterSizeChanged;
|
||||
}
|
||||
get => ShellFlyoutContentManager.HeaderView;
|
||||
set => ShellFlyoutContentManager.HeaderView = value;
|
||||
}
|
||||
|
||||
public virtual UIView FooterView
|
||||
{
|
||||
get => _footerView;
|
||||
set
|
||||
{
|
||||
if (_footerView == value)
|
||||
return;
|
||||
|
||||
_footerView = value;
|
||||
}
|
||||
get => ShellFlyoutContentManager.FooterView;
|
||||
set => ShellFlyoutContentManager.FooterView = value;
|
||||
}
|
||||
|
||||
protected ShellTableViewSource CreateShellTableViewSource()
|
||||
|
@ -72,106 +60,26 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
return new ShellTableViewSource(_context, _onElementSelected);
|
||||
}
|
||||
|
||||
void OnShellPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.Is(Shell.FlyoutHeaderBehaviorProperty))
|
||||
{
|
||||
SetHeaderContentInset();
|
||||
LayoutParallax();
|
||||
}
|
||||
else if (e.Is(Shell.FlyoutVerticalScrollModeProperty))
|
||||
UpdateVerticalScrollMode();
|
||||
}
|
||||
|
||||
void OnHeaderFooterSizeChanged(object sender, EventArgs e)
|
||||
{
|
||||
_headerSize = HeaderMax;
|
||||
SetHeaderContentInset();
|
||||
LayoutParallax();
|
||||
}
|
||||
|
||||
void OnFlyoutItemsChanged(object sender, EventArgs e)
|
||||
{
|
||||
_source.ClearCache();
|
||||
TableView.ReloadData();
|
||||
UpdateVerticalScrollMode();
|
||||
ShellFlyoutContentManager.UpdateVerticalScrollMode();
|
||||
}
|
||||
|
||||
void UpdateVerticalScrollMode()
|
||||
{
|
||||
switch (_context.Shell.FlyoutVerticalScrollMode)
|
||||
{
|
||||
case ScrollMode.Auto:
|
||||
TableView.ScrollEnabled = true;
|
||||
TableView.AlwaysBounceVertical = false;
|
||||
break;
|
||||
case ScrollMode.Enabled:
|
||||
TableView.ScrollEnabled = true;
|
||||
TableView.AlwaysBounceVertical = true;
|
||||
break;
|
||||
case ScrollMode.Disabled:
|
||||
TableView.ScrollEnabled = false;
|
||||
TableView.AlwaysBounceVertical = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void LayoutParallax()
|
||||
{
|
||||
if (TableView?.Superview == null)
|
||||
return;
|
||||
|
||||
var parent = TableView.Superview;
|
||||
|
||||
nfloat footerHeight = 0;
|
||||
|
||||
if (FooterView != null)
|
||||
footerHeight = FooterView.Frame.Height;
|
||||
|
||||
TableView.Frame =
|
||||
new CGRect(parent.Bounds.X, HeaderTopMargin, parent.Bounds.Width, parent.Bounds.Height - HeaderTopMargin - footerHeight);
|
||||
|
||||
if (HeaderView != null && !double.IsNaN(_headerSize))
|
||||
{
|
||||
var margin = HeaderView.Margin;
|
||||
var leftMargin = margin.Left - margin.Right;
|
||||
|
||||
HeaderView.Frame = new CGRect(leftMargin, _headerOffset + HeaderTopMargin, parent.Frame.Width, _headerSize);
|
||||
|
||||
if (_context.Shell.FlyoutHeaderBehavior == FlyoutHeaderBehavior.Scroll && HeaderTopMargin > 0 && _headerOffset < 0)
|
||||
{
|
||||
var headerHeight = Math.Max(_headerMin, _headerSize + _headerOffset);
|
||||
CAShapeLayer shapeLayer = new CAShapeLayer();
|
||||
CGRect rect = new CGRect(0, _headerOffset * -1, parent.Frame.Width, headerHeight);
|
||||
var path = CGPath.FromRect(rect);
|
||||
shapeLayer.Path = path;
|
||||
HeaderView.Layer.Mask = shapeLayer;
|
||||
}
|
||||
else if (HeaderView.Layer.Mask != null)
|
||||
HeaderView.Layer.Mask = null;
|
||||
}
|
||||
}
|
||||
|
||||
void SetHeaderContentInset()
|
||||
{
|
||||
if (HeaderView != null)
|
||||
TableView.ContentInset = new UIEdgeInsets((nfloat)HeaderMax, 0, 0, 0);
|
||||
else
|
||||
TableView.ContentInset = new UIEdgeInsets(Platform.SafeAreaInsetsForWindow.Top, 0, 0, 0);
|
||||
UpdateVerticalScrollMode();
|
||||
}
|
||||
public void LayoutParallax() =>
|
||||
ShellFlyoutContentManager.LayoutParallax();
|
||||
|
||||
public override void ViewDidLoad()
|
||||
{
|
||||
{
|
||||
base.ViewDidLoad();
|
||||
HeaderView?.MeasureIfNeeded();
|
||||
|
||||
TableView.SeparatorStyle = UITableViewCellSeparatorStyle.None;
|
||||
if (Forms.IsiOS11OrNewer)
|
||||
TableView.ContentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentBehavior.Never;
|
||||
|
||||
SetHeaderContentInset();
|
||||
TableView.Source = _source;
|
||||
ShellFlyoutContentManager.ViewDidLoad();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
|
@ -187,50 +95,12 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
if (_source != null)
|
||||
_source.ScrolledEvent -= OnScrolled;
|
||||
|
||||
if (HeaderView != null)
|
||||
HeaderView.HeaderSizeChanged -= OnHeaderFooterSizeChanged;
|
||||
|
||||
_context.Shell.PropertyChanged -= OnShellPropertyChanged;
|
||||
|
||||
ShellFlyoutContentManager.TearDown();
|
||||
_onElementSelected = null;
|
||||
}
|
||||
|
||||
|
||||
_isDisposed = true;
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
|
||||
void OnScrolled(object sender, UIScrollView e)
|
||||
{
|
||||
if (HeaderView == null)
|
||||
return;
|
||||
|
||||
var headerBehavior = _context.Shell.FlyoutHeaderBehavior;
|
||||
|
||||
switch (headerBehavior)
|
||||
{
|
||||
case FlyoutHeaderBehavior.Default:
|
||||
case FlyoutHeaderBehavior.Fixed:
|
||||
_headerSize = HeaderMax;
|
||||
_headerOffset = 0;
|
||||
break;
|
||||
|
||||
case FlyoutHeaderBehavior.Scroll:
|
||||
_headerSize = HeaderMax;
|
||||
_headerOffset = Math.Min(0, -(HeaderMax + e.ContentOffset.Y));
|
||||
break;
|
||||
|
||||
case FlyoutHeaderBehavior.CollapseOnScroll:
|
||||
_headerSize = Math.Max(_headerMin, -e.ContentOffset.Y);
|
||||
_headerOffset = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
LayoutParallax();
|
||||
}
|
||||
|
||||
double HeaderMax => HeaderView?.MeasuredHeight ?? 0;
|
||||
double HeaderTopMargin => (HeaderView != null) ? HeaderView.Margin.Top - HeaderView.Margin.Bottom : 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
readonly Action<Element> _onElementSelected;
|
||||
List<List<Element>> _groups;
|
||||
Dictionary<Element, UIContainerCell> _cells;
|
||||
|
||||
IShellController ShellController => _context.Shell;
|
||||
|
||||
public ShellTableViewSource(IShellContext context, Action<Element> onElementSelected)
|
||||
|
@ -213,4 +212,4 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,11 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
ClipsToBounds = true;
|
||||
view.MeasureInvalidated += OnMeasureInvalidated;
|
||||
MeasuredHeight = double.NaN;
|
||||
_view.BatchCommitted += _view_BatchCommitted;
|
||||
}
|
||||
|
||||
private void _view_BatchCommitted(object sender, Internals.EventArg<VisualElement> e)
|
||||
{
|
||||
}
|
||||
|
||||
internal View View => _view;
|
||||
|
@ -30,7 +35,10 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
|
||||
internal bool MeasureIfNeeded()
|
||||
{
|
||||
if (double.IsNaN(MeasuredHeight))
|
||||
if (View == null)
|
||||
return false;
|
||||
|
||||
if (double.IsNaN(MeasuredHeight) || Frame.Width != View.Width)
|
||||
{
|
||||
ReMeasure();
|
||||
return true;
|
||||
|
@ -44,7 +52,12 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
{
|
||||
if(!_view.IsSet(View.MarginProperty))
|
||||
{
|
||||
_view.Margin = new Thickness(0, (float)Platform.SafeAreaInsetsForWindow.Top, 0, 0);
|
||||
var newMargin = new Thickness(0, (float)Platform.SafeAreaInsetsForWindow.Top, 0, 0);
|
||||
|
||||
if (newMargin != _view.Margin)
|
||||
{
|
||||
_view.Margin = newMargin;
|
||||
}
|
||||
}
|
||||
|
||||
return _view.Margin;
|
||||
|
@ -54,7 +67,6 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
void ReMeasure()
|
||||
{
|
||||
var request = _view.Measure(Frame.Width, double.PositiveInfinity, MeasureFlags.None);
|
||||
Layout.LayoutChildIntoBoundingRegion(_view, new Rectangle(0, 0, Frame.Width, request.Request.Height));
|
||||
MeasuredHeight = request.Request.Height;
|
||||
HeaderSizeChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
@ -62,12 +74,18 @@ namespace Xamarin.Forms.Platform.iOS
|
|||
void OnMeasureInvalidated(object sender, System.EventArgs e)
|
||||
{
|
||||
ReMeasure();
|
||||
LayoutSubviews();
|
||||
}
|
||||
|
||||
public override void WillMoveToSuperview(UIView newsuper)
|
||||
{
|
||||
base.WillMoveToSuperview(newsuper);
|
||||
ReMeasure();
|
||||
}
|
||||
|
||||
public override void LayoutSubviews()
|
||||
{
|
||||
if(!MeasureIfNeeded())
|
||||
_view.Layout(Bounds.ToRectangle());
|
||||
_view.Layout(new Rectangle(0, Margin.Top, Frame.Width, MeasuredHeight));
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
|
|
|
@ -192,6 +192,7 @@
|
|||
<Compile Include="Renderers\PageContainer.cs" />
|
||||
<Compile Include="Renderers\CheckBoxRendererBase.cs" />
|
||||
<Compile Include="Renderers\PhoneFlyoutPageRenderer.cs" />
|
||||
<Compile Include="Renderers\ShellFlyoutLayoutManager.cs" />
|
||||
<Compile Include="Renderers\TabletFlyoutPageRenderer.cs" />
|
||||
<Compile Include="Renderers\WkWebViewRenderer.cs" />
|
||||
<Compile Include="Renderers\ElementSelectedEventArgs.cs" />
|
||||
|
|
Загрузка…
Ссылка в новой задаче