From e2ecabc22496c01ee5824d60a02ccbf05534bab5 Mon Sep 17 00:00:00 2001 From: Tsanie Lily Date: Fri, 8 May 2020 08:47:38 +0800 Subject: [PATCH] feature: segments control --- Pixiview.iOS/Pixiview.iOS.csproj | 1 + Pixiview.iOS/Renderers/AppShellRenderer.cs | 2 +- .../Renderers/SegmentedControlRenderer.cs | 155 ++++++++++++++++++ Pixiview/AppShell.xaml | 15 +- Pixiview/Illust/IllustCollectionPage.cs | 9 +- Pixiview/Illust/RankingPage.xaml | 3 +- Pixiview/Illust/RankingPage.xaml.cs | 3 - Pixiview/Illust/RecommendsPage.xaml | 17 +- Pixiview/Illust/RecommendsPage.xaml.cs | 61 ++++++- Pixiview/Illust/ViewIllustPage.xaml.cs | 13 +- Pixiview/UI/SegmentedControl.cs | 76 +++++++++ 11 files changed, 325 insertions(+), 30 deletions(-) create mode 100644 Pixiview.iOS/Renderers/SegmentedControlRenderer.cs create mode 100644 Pixiview/UI/SegmentedControl.cs diff --git a/Pixiview.iOS/Pixiview.iOS.csproj b/Pixiview.iOS/Pixiview.iOS.csproj index 61894a4..8b5ff94 100644 --- a/Pixiview.iOS/Pixiview.iOS.csproj +++ b/Pixiview.iOS/Pixiview.iOS.csproj @@ -78,6 +78,7 @@ + diff --git a/Pixiview.iOS/Renderers/AppShellRenderer.cs b/Pixiview.iOS/Renderers/AppShellRenderer.cs index ee384fc..904a200 100644 --- a/Pixiview.iOS/Renderers/AppShellRenderer.cs +++ b/Pixiview.iOS/Renderers/AppShellRenderer.cs @@ -35,7 +35,7 @@ namespace Pixiview.iOS.Renderers if (renderer.ViewController is UITabBarController controller) { var tabBar = controller.TabBar; - tabBar.TintColor = UIColor.SecondaryLabelColor.ColorWithAlpha(1); + tabBar.TintColor = UIColor.LabelColor; tabBar.UnselectedItemTintColor = UIColor.SecondaryLabelColor; } } diff --git a/Pixiview.iOS/Renderers/SegmentedControlRenderer.cs b/Pixiview.iOS/Renderers/SegmentedControlRenderer.cs new file mode 100644 index 0000000..c56a1ca --- /dev/null +++ b/Pixiview.iOS/Renderers/SegmentedControlRenderer.cs @@ -0,0 +1,155 @@ +using System; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using Pixiview.iOS.Renderers; +using Pixiview.UI; +using UIKit; +using Xamarin.Forms; +using Xamarin.Forms.Platform.iOS; + +[assembly: ExportRenderer(typeof(SegmentedControl), typeof(SegmentedControlRenderer))] +namespace Pixiview.iOS.Renderers +{ + [SuppressMessage("Code Notifications", "XI0002:Notifies you from using newer Apple APIs when targeting an older OS version", Justification = "")] + public class SegmentedControlRenderer : ViewRenderer + { + private UISegmentedControl nativeControl; + + protected override void OnElementChanged(ElementChangedEventArgs e) + { + base.OnElementChanged(e); + + var element = Element; + if (Control == null && element != null) + { + nativeControl = new UISegmentedControl(); + + for (var i = 0; i < element.Children.Count; i++) + { + nativeControl.InsertSegment(element.Children[i].Text, i, false); + } + + nativeControl.Enabled = element.IsEnabled; + nativeControl.BackgroundColor = element.BackgroundColor.ToUIColor(); + nativeControl.SelectedSegmentTintColor = GetTintColor(element); + SetTextColor(); + nativeControl.SelectedSegment = element.SelectedSegmentIndex; + + SetNativeControl(nativeControl); + } + + if (e.OldElement != null) + { + if (nativeControl != null) + { + nativeControl.ValueChanged -= NativeControl_ValueChanged; + } + } + + if (e.NewElement != null) + { + nativeControl.ValueChanged += NativeControl_ValueChanged; + } + } + + protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) + { + base.OnElementPropertyChanged(sender, e); + + var element = Element; + if (nativeControl == null || element == null) + { + return; + } + + switch (e.PropertyName) + { + case "Renderer": + element.SendValueChanged(); + break; + + case nameof(element.BackgroundColor): + nativeControl.BackgroundColor = element.BackgroundColor.ToUIColor(); + break; + + case nameof(element.SelectedSegmentIndex): + nativeControl.SelectedSegment = element.SelectedSegmentIndex; + break; + + case nameof(element.TintColor): + nativeControl.SelectedSegmentTintColor = GetTintColor(element); + break; + + case nameof(element.IsEnabled): + nativeControl.Enabled = element.IsEnabled; + nativeControl.SelectedSegmentTintColor = GetTintColor(element); + break; + + case nameof(element.SelectedTextColor): + SetTextColor(); + break; + } + } + + private void SetTextColor() + { + var color = Element.SelectedTextColor; + UIColor c = color == default ? UIColor.LabelColor : color.ToUIColor(); + var attribute = new UITextAttributes + { + TextColor = c + }; + nativeControl.SetTitleTextAttributes(attribute, UIControlState.Selected); + attribute = new UITextAttributes + { + TextColor = c.ColorWithAlpha(.6f) + }; + nativeControl.SetTitleTextAttributes(attribute, UIControlState.Normal); + } + + private UIColor GetTintColor(SegmentedControl element) + { + if (element.IsEnabled) + { + var tintColor = element.TintColor; + if (tintColor == default) + { + return UIColor.QuaternaryLabelColor; + } + else + { + return tintColor.ToUIColor().ColorWithAlpha(.3f); + } + } + else + { + var disabledColor = element.DisabledColor; + if (disabledColor == default) + { + return UIColor.SecondaryLabelColor; + } + else + { + return disabledColor.ToUIColor(); + } + } + } + + private void NativeControl_ValueChanged(object sender, EventArgs e) + { + Element.SelectedSegmentIndex = (int)nativeControl.SelectedSegment; + } + + protected override void Dispose(bool disposing) + { + if (nativeControl != null) + { + nativeControl.ValueChanged -= NativeControl_ValueChanged; + nativeControl.Dispose(); + nativeControl = null; + } + + base.Dispose(disposing); + } + } +} diff --git a/Pixiview/AppShell.xaml b/Pixiview/AppShell.xaml index a59005e..e686802 100644 --- a/Pixiview/AppShell.xaml +++ b/Pixiview/AppShell.xaml @@ -67,18 +67,9 @@ - - - - - - - - + Title="{r:Text Recommends}" + Route="{x:Static util:Routes.Recommends}"> + + Unfocused="SearchBar_Unfocused"/> SetValue(KeywordsProperty, value); } - private readonly Thickness totalBarOffset; - private readonly Thickness navigationBarOffset; - public RankingPage() { totalBarOffset = new Thickness(0, AppShell.TotalBarOffset.Top + 50, 0, 0); diff --git a/Pixiview/Illust/RecommendsPage.xaml b/Pixiview/Illust/RecommendsPage.xaml index 8993efb..ec1ee17 100644 --- a/Pixiview/Illust/RecommendsPage.xaml +++ b/Pixiview/Illust/RecommendsPage.xaml @@ -11,13 +11,26 @@ - + + + + + + + + + (int)GetValue(SegmentIndexProperty); + set => SetValue(SegmentIndexProperty, value); + } + + private IllustData illustData; public RecommendsPage() { + totalBarOffset = new Thickness(0, AppShell.TotalBarOffset.Top + 40, 0, 0); + navigationBarOffset = new Thickness(0, AppShell.NavigationBarOffset.Top + 40, 0, 0); + Resources.Add("cardView", GetCardViewTemplate()); InitializeComponent(); } @@ -27,10 +47,39 @@ namespace Pixiview.Illust loaded = false; } + public override void OnOrientationChanged(Orientation orientation) + { + int columns; + switch (orientation) + { + case Orientation.Portrait: + columns = 2; + PageTopMargin = totalBarOffset; + break; + case Orientation.PortraitUpsideDown: + columns = isPhone ? 4 : 2; + PageTopMargin = isPhone ? navigationBarOffset : totalBarOffset; + break; + case Orientation.Unknown: + case Orientation.LandscapeLeft: + case Orientation.LandscapeRight: + default: + columns = 4; + PageTopMargin = navigationBarOffset; + break; + } + if (Columns != columns) + { + App.DebugPrint($"ranking page, change columns to {columns}"); + Columns = columns; + } + } + protected override IEnumerable DoGetIllustList(IllustData data, ICommand command) { - if (ByUser) + if (SegmentIndex == 1) { + // by user return data.body.page.recommendUser.SelectMany(i => i.illustIds) .Select(id => { @@ -44,6 +93,7 @@ namespace Pixiview.Illust } else { + // recommends return data.body.page.recommend.Select(id => { var item = data.body.thumbnails.illust.FirstOrDefault(l => l.illustId == id)?.ConvertToItem(); @@ -58,7 +108,12 @@ namespace Pixiview.Illust protected override IllustData DoLoadIllustData(bool force) { - return Stores.LoadIllustData(force); + if (illustData != null && !force) + { + return illustData; + } + illustData = Stores.LoadIllustData(force); + return illustData; } private void Refresh_Clicked(object sender, EventArgs e) diff --git a/Pixiview/Illust/ViewIllustPage.xaml.cs b/Pixiview/Illust/ViewIllustPage.xaml.cs index fac6e8b..5a743c5 100644 --- a/Pixiview/Illust/ViewIllustPage.xaml.cs +++ b/Pixiview/Illust/ViewIllustPage.xaml.cs @@ -187,14 +187,14 @@ namespace Pixiview.Illust item.OriginalUrl = p.urls.original; } - DoLoadImage(0); + DoLoadImage(0, true); if (items.Length > 1) { DoLoadImage(1); } } - private void DoLoadImage(int index) + private void DoLoadImage(int index, bool force = false) { var items = Illusts; if (index < 0 || index >= items.Length) @@ -204,10 +204,13 @@ namespace Pixiview.Illust } var item = items[index]; - if (item.Loading || (index > 0 && item.Image != null)) + if (!force) { - App.DebugPrint($"skipped, loading or already loaded, index: {index}, loading: {item.Loading}"); - return; + if (item.Loading || (index > 0 && item.Image != null)) + { + App.DebugPrint($"skipped, loading or already loaded, index: {index}, loading: {item.Loading}"); + return; + } } item.Loading = true; var image = Stores.LoadPreviewImage(item.PreviewUrl); diff --git a/Pixiview/UI/SegmentedControl.cs b/Pixiview/UI/SegmentedControl.cs new file mode 100644 index 0000000..c472618 --- /dev/null +++ b/Pixiview/UI/SegmentedControl.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using Xamarin.Forms; + +namespace Pixiview.UI +{ + public class SegmentedControl : View, IViewContainer + { + public IList Children { get; set; } + + public SegmentedControl() + { + Children = new List(); + } + + public static readonly BindableProperty TintColorProperty = BindableProperty.Create( + nameof(TintColor), typeof(Color), typeof(SegmentedControl)); + public static readonly BindableProperty DisabledColorProperty = BindableProperty.Create( + nameof(DisabledColor), typeof(Color), typeof(SegmentedControl)); + public static readonly BindableProperty SelectedTextColorProperty = BindableProperty.Create( + nameof(SelectedTextColor), typeof(Color), typeof(SegmentedControl)); + public static readonly BindableProperty SelectedSegmentIndexProperty = BindableProperty.Create( + nameof(SelectedSegmentIndex), typeof(int), typeof(SegmentedControl)); + + public Color TintColor + { + get => (Color)GetValue(TintColorProperty); + set => SetValue(TintColorProperty, value); + } + public Color DisabledColor + { + get => (Color)GetValue(DisabledColorProperty); + set => SetValue(DisabledColorProperty, value); + } + public Color SelectedTextColor + { + get => (Color)GetValue(SelectedTextColorProperty); + set => SetValue(SelectedTextColorProperty, value); + } + public int SelectedSegmentIndex + { + get => (int)GetValue(SelectedSegmentIndexProperty); + set => SetValue(SelectedSegmentIndexProperty, value); + } + + public SegmentedControlOption SelectedSegment => Children[SelectedSegmentIndex]; + + public event EventHandler ValueChanged; + + [EditorBrowsable(EditorBrowsableState.Never)] + public void SendValueChanged() + { + ValueChanged?.Invoke(this, new ValueChangedEventArgs { NewValue = SelectedSegmentIndex }); + } + } + + public class SegmentedControlOption : View + { + public static readonly BindableProperty TextProperty = BindableProperty.Create( + nameof(Text), typeof(string), typeof(SegmentedControlOption)); + + public string Text + { + get => (string)GetValue(TextProperty); + set => SetValue(TextProperty, value); + } + + public object Value { get; set; } + } + + public class ValueChangedEventArgs : EventArgs + { + public int NewValue { get; set; } + } +}