From 9456f5dc31335f13cccf45041f0829f4c3ffa249 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Wed, 3 Jan 2018 12:18:15 -0500 Subject: [PATCH] redraw stack layouts on ios tableviews --- src/App/Controls/ExtendedTableView.cs | 3 +- src/App/Controls/RedrawableStackLayout.cs | 27 ++++++++++++ src/App/Pages/EnvironmentPage.cs | 23 ++++------ src/App/Pages/Lock/LockPasswordPage.cs | 4 +- src/App/Pages/LoginPage.cs | 9 ++-- src/App/Pages/LoginTwoFactorPage.cs | 11 +++-- src/App/Pages/PasswordHintPage.cs | 3 +- src/App/Pages/RegisterPage.cs | 19 +++----- src/App/Pages/Settings/SettingsAboutPage.cs | 4 +- .../Pages/Settings/SettingsEditFolderPage.cs | 2 - src/App/Pages/Settings/SettingsHelpPage.cs | 22 +++------- src/App/Pages/Settings/SettingsOptionsPage.cs | 44 +++++-------------- .../Pages/Tools/ToolsPasswordGeneratorPage.cs | 4 +- src/App/Pages/Vault/VaultAttachmentsPage.cs | 5 ++- src/App/Pages/Vault/VaultCustomFieldsPage.cs | 20 ++++++--- src/iOS/Controls/ExtendedTableViewRenderer.cs | 33 +++++++++++++- 16 files changed, 130 insertions(+), 103 deletions(-) create mode 100644 src/App/Controls/RedrawableStackLayout.cs diff --git a/src/App/Controls/ExtendedTableView.cs b/src/App/Controls/ExtendedTableView.cs index 0c646f739..f5d07e271 100644 --- a/src/App/Controls/ExtendedTableView.cs +++ b/src/App/Controls/ExtendedTableView.cs @@ -1,6 +1,4 @@ using Xamarin.Forms; -using XLabs.Ioc; -using Bit.App.Abstractions; using System; using System.Reflection; @@ -42,6 +40,7 @@ namespace Bit.App.Controls public bool NoHeader { get; set; } public bool NoFooter { get; set; } public int BottomPadding { get; set; } + public Func WrappingStackLayout { get; set; } protected override SizeRequest OnSizeRequest(double widthConstraint, double heightConstraint) { diff --git a/src/App/Controls/RedrawableStackLayout.cs b/src/App/Controls/RedrawableStackLayout.cs new file mode 100644 index 000000000..1be2b1d6d --- /dev/null +++ b/src/App/Controls/RedrawableStackLayout.cs @@ -0,0 +1,27 @@ +using System; +using System.Threading.Tasks; +using Xamarin.Forms; + +namespace Bit.App.Controls +{ + public class RedrawableStackLayout : StackLayout + { + private DateTime _lastRedraw = DateTime.MinValue; + private TimeSpan _redrawThreshold = TimeSpan.FromMilliseconds(1000); + + public async Task RedrawIfNeededAsync(int delay = 0, bool force = false) + { + var now = DateTime.UtcNow; + if(Device.RuntimePlatform == Device.iOS && (force || (now - _lastRedraw) > _redrawThreshold)) + { + _lastRedraw = now; + if(delay > 0) + { + await Task.Delay(delay); + } + + InvalidateLayout(); + } + } + } +} diff --git a/src/App/Pages/EnvironmentPage.cs b/src/App/Pages/EnvironmentPage.cs index 1db08c0dc..fe1ecae4d 100644 --- a/src/App/Pages/EnvironmentPage.cs +++ b/src/App/Pages/EnvironmentPage.cs @@ -30,7 +30,7 @@ namespace Bit.App.Pages public FormEntryCell ApiUrlCell { get; set; } public FormEntryCell IdentityUrlCell { get; set; } public FormEntryCell IconsUrlCell { get; set; } - public StackLayout StackLayout { get; set; } + public RedrawableStackLayout StackLayout { get; set; } public Label SelfHostLabel { get; set; } public Label CustomLabel { get; set; } @@ -57,7 +57,7 @@ namespace Bit.App.Pages entryKeyboard: Keyboard.Url); BaseUrlCell.Entry.Text = _appSettings.BaseUrl; - var table = new FormTableView + var table = new FormTableView(this) { Root = new TableRoot { @@ -74,10 +74,10 @@ namespace Bit.App.Pages LineBreakMode = LineBreakMode.WordWrap, FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label)), Style = (Style)Application.Current.Resources["text-muted"], - Margin = new Thickness(15, (this.IsLandscape() ? 5 : 0), 15, 25) + Margin = new Thickness(15, (this.IsLandscape() ? 5 : 0), 15, 5) }; - var table2 = new FormTableView + var table2 = new FormTableView(this) { Root = new TableRoot { @@ -97,10 +97,10 @@ namespace Bit.App.Pages LineBreakMode = LineBreakMode.WordWrap, FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label)), Style = (Style)Application.Current.Resources["text-muted"], - Margin = new Thickness(15, (this.IsLandscape() ? 5 : 0), 15, 25) + Margin = new Thickness(15, (this.IsLandscape() ? 5 : 0), 15, 5) }; - StackLayout = new StackLayout + StackLayout = new RedrawableStackLayout { Children = { table, SelfHostLabel, table2, CustomLabel }, Spacing = 0 @@ -138,7 +138,6 @@ namespace Bit.App.Pages IdentityUrlCell.InitEvents(); ApiUrlCell.InitEvents(); WebVaultUrlCell.InitEvents(); - StackLayout.LayoutChanged += Layout_LayoutChanged; BaseUrlCell.Entry.FocusWithDelay(); } protected override void OnDisappearing() @@ -149,13 +148,6 @@ namespace Bit.App.Pages IdentityUrlCell.Dispose(); ApiUrlCell.Dispose(); WebVaultUrlCell.Dispose(); - StackLayout.LayoutChanged -= Layout_LayoutChanged; - } - - private void Layout_LayoutChanged(object sender, EventArgs e) - { - SelfHostLabel.WidthRequest = StackLayout.Bounds.Width - SelfHostLabel.Bounds.Left * 2; - CustomLabel.WidthRequest = StackLayout.Bounds.Width - CustomLabel.Bounds.Left * 2; } private async Task SaveAsync() @@ -259,7 +251,7 @@ namespace Bit.App.Pages private class FormTableView : ExtendedTableView { - public FormTableView() + public FormTableView(EnvironmentPage page) { Intent = TableIntent.Settings; EnableScrolling = false; @@ -267,6 +259,7 @@ namespace Bit.App.Pages EnableSelection = true; VerticalOptions = LayoutOptions.Start; NoFooter = true; + WrappingStackLayout = () => page.StackLayout; } } } diff --git a/src/App/Pages/Lock/LockPasswordPage.cs b/src/App/Pages/Lock/LockPasswordPage.cs index 2f3d5963b..b04ff0796 100644 --- a/src/App/Pages/Lock/LockPasswordPage.cs +++ b/src/App/Pages/Lock/LockPasswordPage.cs @@ -67,12 +67,14 @@ namespace Bit.App.Pages Uppercase = false }; - var stackLayout = new StackLayout + var stackLayout = new RedrawableStackLayout { Spacing = 10, Children = { table, logoutButton } }; + table.WrappingStackLayout = () => stackLayout; + var scrollView = new ScrollView { Content = stackLayout }; if(Device.RuntimePlatform == Device.iOS) diff --git a/src/App/Pages/LoginPage.cs b/src/App/Pages/LoginPage.cs index db9666a93..5228fd6d1 100644 --- a/src/App/Pages/LoginPage.cs +++ b/src/App/Pages/LoginPage.cs @@ -71,7 +71,6 @@ namespace Bit.App.Pages HasUnevenRows = true, EnableSelection = true, NoFooter = true, - //NoHeader = true, VerticalOptions = LayoutOptions.Start, Root = new TableRoot { @@ -87,18 +86,20 @@ namespace Bit.App.Pages { Text = AppResources.GetPasswordHint, Style = (Style)Application.Current.Resources["btn-primaryAccent"], - Margin = new Thickness(15, 0, 15, 25), Command = new Command(async () => await ForgotPasswordAsync()), + VerticalOptions = LayoutOptions.End, Uppercase = false, BackgroundColor = Color.Transparent }; - var layout = new StackLayout + var layout = new RedrawableStackLayout { Children = { table, forgotPasswordButton }, - Spacing = Helpers.OnPlatform(iOS: 0, Android: 10, Windows: 0) + Spacing = 10 }; + table.WrappingStackLayout = () => layout; + var scrollView = new ScrollView { Content = layout }; if(Device.RuntimePlatform == Device.iOS) diff --git a/src/App/Pages/LoginTwoFactorPage.cs b/src/App/Pages/LoginTwoFactorPage.cs index e8787e4f6..b6d2f1835 100644 --- a/src/App/Pages/LoginTwoFactorPage.cs +++ b/src/App/Pages/LoginTwoFactorPage.cs @@ -135,12 +135,13 @@ namespace Bit.App.Pages RememberCell }); - var layout = new StackLayout + var layout = new RedrawableStackLayout { Children = { instruction, table }, Spacing = 0 }; + table.WrappingStackLayout = () => layout; scrollView.Content = layout; switch(_providerType.Value) @@ -213,12 +214,13 @@ namespace Bit.App.Pages RememberCell }); - var layout = new StackLayout + var layout = new RedrawableStackLayout { Children = { webView, table, anotherMethodButton }, Spacing = 0 }; + table.WrappingStackLayout = () => layout; scrollView.Content = layout; Title = "Duo"; @@ -244,12 +246,13 @@ namespace Bit.App.Pages RememberCell }); - var layout = new StackLayout + var layout = new RedrawableStackLayout { Children = { instruction, image, table, anotherMethodButton }, Spacing = 0 }; + table.WrappingStackLayout = () => layout; scrollView.Content = layout; Title = AppResources.YubiKeyTitle; @@ -524,7 +527,7 @@ namespace Bit.App.Pages NoFooter = true; NoHeader = true; VerticalOptions = LayoutOptions.Start; - Root = Root = new TableRoot + Root = new TableRoot { section }; diff --git a/src/App/Pages/PasswordHintPage.cs b/src/App/Pages/PasswordHintPage.cs index 9b7ee38dd..cae77ef51 100644 --- a/src/App/Pages/PasswordHintPage.cs +++ b/src/App/Pages/PasswordHintPage.cs @@ -64,12 +64,13 @@ namespace Bit.App.Pages Margin = new Thickness(15, (this.IsLandscape() ? 5 : 0), 15, 25) }; - var layout = new StackLayout + var layout = new RedrawableStackLayout { Children = { table, hintLabel }, Spacing = 0 }; + table.WrappingStackLayout = () => layout; var scrollView = new ScrollView { Content = layout }; if(Device.RuntimePlatform == Device.iOS) diff --git a/src/App/Pages/RegisterPage.cs b/src/App/Pages/RegisterPage.cs index a56df15c6..3e0fc5d52 100644 --- a/src/App/Pages/RegisterPage.cs +++ b/src/App/Pages/RegisterPage.cs @@ -35,7 +35,7 @@ namespace Bit.App.Pages public FormEntryCell PasswordCell { get; set; } public FormEntryCell ConfirmPasswordCell { get; set; } public FormEntryCell PasswordHintCell { get; set; } - public StackLayout StackLayout { get; set; } + public RedrawableStackLayout StackLayout { get; set; } public Label PasswordLabel { get; set; } public Label HintLabel { get; set; } @@ -62,7 +62,7 @@ namespace Bit.App.Pages PasswordHintCell.Entry.ReturnType = Enums.ReturnType.Done; - var table = new FormTableView + var table = new FormTableView(this) { Root = new TableRoot { @@ -83,7 +83,7 @@ namespace Bit.App.Pages Margin = new Thickness(15, (this.IsLandscape() ? 5 : 0), 15, 25) }; - var table2 = new FormTableView + var table2 = new FormTableView(this) { NoHeader = true, Root = new TableRoot @@ -105,7 +105,7 @@ namespace Bit.App.Pages Margin = new Thickness(15, (this.IsLandscape() ? 5 : 0), 15, 25) }; - StackLayout = new StackLayout + StackLayout = new RedrawableStackLayout { Children = { table, PasswordLabel, table2, HintLabel }, Spacing = 0 @@ -145,7 +145,6 @@ namespace Bit.App.Pages PasswordHintCell.InitEvents(); ConfirmPasswordCell.InitEvents(); PasswordHintCell.Entry.Completed += Entry_Completed; - StackLayout.LayoutChanged += Layout_LayoutChanged; EmailCell.Entry.FocusWithDelay(); } protected override void OnDisappearing() @@ -156,13 +155,6 @@ namespace Bit.App.Pages PasswordHintCell.Dispose(); ConfirmPasswordCell.Dispose(); PasswordHintCell.Entry.Completed -= Entry_Completed; - StackLayout.LayoutChanged -= Layout_LayoutChanged; - } - - private void Layout_LayoutChanged(object sender, EventArgs e) - { - PasswordLabel.WidthRequest = StackLayout.Bounds.Width - PasswordLabel.Bounds.Left * 2; - HintLabel.WidthRequest = StackLayout.Bounds.Width - HintLabel.Bounds.Left * 2; } private async void Entry_Completed(object sender, EventArgs e) @@ -229,7 +221,7 @@ namespace Bit.App.Pages private class FormTableView : ExtendedTableView { - public FormTableView() + public FormTableView(RegisterPage page) { Intent = TableIntent.Settings; EnableScrolling = false; @@ -237,6 +229,7 @@ namespace Bit.App.Pages EnableSelection = true; VerticalOptions = LayoutOptions.Start; NoFooter = true; + WrappingStackLayout = () => page.StackLayout; } } } diff --git a/src/App/Pages/Settings/SettingsAboutPage.cs b/src/App/Pages/Settings/SettingsAboutPage.cs index ff41bba1f..b574da1d7 100644 --- a/src/App/Pages/Settings/SettingsAboutPage.cs +++ b/src/App/Pages/Settings/SettingsAboutPage.cs @@ -73,12 +73,14 @@ namespace Bit.App.Pages table.EstimatedRowHeight = 44; } - var stackLayout = new StackLayout + var stackLayout = new RedrawableStackLayout { Children = { logoVersionStackLayout, table }, Spacing = 0 }; + table.WrappingStackLayout = () => stackLayout; + if(Device.RuntimePlatform == Device.iOS || Device.RuntimePlatform == Device.UWP) { ToolbarItems.Add(new DismissModalToolBarItem(this, AppResources.Close)); diff --git a/src/App/Pages/Settings/SettingsEditFolderPage.cs b/src/App/Pages/Settings/SettingsEditFolderPage.cs index d441ffb8a..b69413460 100644 --- a/src/App/Pages/Settings/SettingsEditFolderPage.cs +++ b/src/App/Pages/Settings/SettingsEditFolderPage.cs @@ -50,9 +50,7 @@ namespace Bit.App.Pages var mainTable = new ExtendedTableView { Intent = TableIntent.Settings, - EnableScrolling = false, HasUnevenRows = true, - VerticalOptions = LayoutOptions.Start, Root = new TableRoot { new TableSection(Helpers.GetEmptyTableSectionTitle()) diff --git a/src/App/Pages/Settings/SettingsHelpPage.cs b/src/App/Pages/Settings/SettingsHelpPage.cs index 4961aace1..8f2fc5da8 100644 --- a/src/App/Pages/Settings/SettingsHelpPage.cs +++ b/src/App/Pages/Settings/SettingsHelpPage.cs @@ -22,7 +22,7 @@ namespace Bit.App.Pages public ExtendedTextCell EmailCell { get; set; } public ExtendedTextCell WebsiteCell { get; set; } public ExtendedTextCell BugCell { get; set; } - public StackLayout StackLayout { get; set; } + public RedrawableStackLayout StackLayout { get; set; } private CustomLabel EmailLabel { get; set; } private CustomLabel WebsiteLabel { get; set; } private CustomLabel BugLabel { get; set; } @@ -35,7 +35,7 @@ namespace Bit.App.Pages ShowDisclousure = true }; - var emailTable = new CustomTableView + var emailTable = new CustomTableView(this) { Root = new TableRoot { @@ -57,7 +57,7 @@ namespace Bit.App.Pages ShowDisclousure = true }; - var websiteTable = new CustomTableView + var websiteTable = new CustomTableView(this) { NoHeader = true, Root = new TableRoot @@ -80,7 +80,7 @@ namespace Bit.App.Pages ShowDisclousure = true }; - var bugTable = new CustomTableView + var bugTable = new CustomTableView(this) { NoHeader = true, Root = new TableRoot @@ -97,7 +97,7 @@ namespace Bit.App.Pages Text = AppResources.FileBugReportDescription }; - StackLayout = new StackLayout + StackLayout = new RedrawableStackLayout { Children = { emailTable, EmailLabel, websiteTable, WebsiteLabel, bugTable, BugLabel }, Spacing = 0 @@ -118,7 +118,6 @@ namespace Bit.App.Pages EmailCell.Tapped += EmailCell_Tapped; WebsiteCell.Tapped += WebsiteCell_Tapped; BugCell.Tapped += BugCell_Tapped; - StackLayout.LayoutChanged += StackLayout_LayoutChanged; } protected override void OnDisappearing() @@ -127,14 +126,6 @@ namespace Bit.App.Pages EmailCell.Tapped -= EmailCell_Tapped; WebsiteCell.Tapped -= WebsiteCell_Tapped; BugCell.Tapped -= BugCell_Tapped; - StackLayout.LayoutChanged -= StackLayout_LayoutChanged; - } - - private void StackLayout_LayoutChanged(object sender, EventArgs e) - { - WebsiteLabel.WidthRequest = StackLayout.Bounds.Width - WebsiteLabel.Bounds.Left * 2; - EmailLabel.WidthRequest = StackLayout.Bounds.Width - EmailLabel.Bounds.Left * 2; - BugLabel.WidthRequest = StackLayout.Bounds.Width - BugLabel.Bounds.Left * 2; } private void EmailCell_Tapped(object sender, EventArgs e) @@ -157,7 +148,7 @@ namespace Bit.App.Pages private class CustomTableView : ExtendedTableView { - public CustomTableView() + public CustomTableView(SettingsHelpPage page) { Intent = TableIntent.Settings; EnableScrolling = false; @@ -165,6 +156,7 @@ namespace Bit.App.Pages EnableSelection = true; VerticalOptions = LayoutOptions.Start; NoFooter = true; + WrappingStackLayout = () => page.StackLayout; if(Device.RuntimePlatform == Device.iOS) { diff --git a/src/App/Pages/Settings/SettingsOptionsPage.cs b/src/App/Pages/Settings/SettingsOptionsPage.cs index d7517dd15..bf8ed9e3d 100644 --- a/src/App/Pages/Settings/SettingsOptionsPage.cs +++ b/src/App/Pages/Settings/SettingsOptionsPage.cs @@ -24,7 +24,7 @@ namespace Bit.App.Pages Init(); } - private StackLayout StackLayout { get; set; } + private RedrawableStackLayout StackLayout { get; set; } private ExtendedSwitchCell CopyTotpCell { get; set; } private Label CopyTotpLabel { get; set; } private ExtendedSwitchCell AnalyticsCell { get; set; } @@ -46,7 +46,7 @@ namespace Bit.App.Pages On = _appSettings.DisableWebsiteIcons }; - var websiteIconsTable = new FormTableView(true) + var websiteIconsTable = new FormTableView(this, true) { Root = new TableRoot { @@ -63,7 +63,7 @@ namespace Bit.App.Pages On = _settings.GetValueOrDefault(Constants.SettingDisableTotpCopy, false) }; - var totpTable = new FormTableView + var totpTable = new FormTableView(this) { Root = new TableRoot { @@ -80,7 +80,7 @@ namespace Bit.App.Pages On = _settings.GetValueOrDefault(Constants.SettingGaOptOut, false) }; - var analyticsTable = new FormTableView + var analyticsTable = new FormTableView(this) { Root = new TableRoot { @@ -106,7 +106,7 @@ namespace Bit.App.Pages Text = AppResources.DisableWebsiteIconsDescription }; - StackLayout = new StackLayout + StackLayout = new RedrawableStackLayout { Children = { @@ -125,7 +125,7 @@ namespace Bit.App.Pages On = !_appSettings.AutofillPersistNotification && !_appSettings.AutofillPasswordField }; - var autofillAlwaysTable = new FormTableView(true) + var autofillAlwaysTable = new FormTableView(this, true) { Root = new TableRoot { @@ -147,7 +147,7 @@ namespace Bit.App.Pages On = _appSettings.AutofillPersistNotification }; - var autofillPersistNotificationTable = new FormTableView + var autofillPersistNotificationTable = new FormTableView(this) { Root = new TableRoot { @@ -169,7 +169,7 @@ namespace Bit.App.Pages On = _appSettings.AutofillPasswordField }; - var autofillPasswordFieldTable = new FormTableView + var autofillPasswordFieldTable = new FormTableView(this) { Root = new TableRoot { @@ -217,7 +217,6 @@ namespace Bit.App.Pages AnalyticsCell.OnChanged += AnalyticsCell_Changed; WebsiteIconsCell.OnChanged += WebsiteIconsCell_Changed; CopyTotpCell.OnChanged += CopyTotpCell_OnChanged; - StackLayout.LayoutChanged += Layout_LayoutChanged; if(Device.RuntimePlatform == Device.Android) { @@ -234,7 +233,6 @@ namespace Bit.App.Pages AnalyticsCell.OnChanged -= AnalyticsCell_Changed; WebsiteIconsCell.OnChanged -= WebsiteIconsCell_Changed; CopyTotpCell.OnChanged -= CopyTotpCell_OnChanged; - StackLayout.LayoutChanged -= Layout_LayoutChanged; if(Device.RuntimePlatform == Device.Android) { @@ -244,29 +242,6 @@ namespace Bit.App.Pages } } - private void Layout_LayoutChanged(object sender, EventArgs e) - { - AnalyticsLabel.WidthRequest = StackLayout.Bounds.Width - AnalyticsLabel.Bounds.Left * 2; - WebsiteIconsLabel.WidthRequest = StackLayout.Bounds.Width - WebsiteIconsLabel.Bounds.Left * 2; - CopyTotpLabel.WidthRequest = StackLayout.Bounds.Width - CopyTotpLabel.Bounds.Left * 2; - - if(AutofillAlwaysLabel != null) - { - AutofillAlwaysLabel.WidthRequest = StackLayout.Bounds.Width - AutofillAlwaysLabel.Bounds.Left * 2; - } - - if(AutofillPasswordFieldLabel != null) - { - AutofillPasswordFieldLabel.WidthRequest = StackLayout.Bounds.Width - AutofillPasswordFieldLabel.Bounds.Left * 2; - } - - if(AutofillPersistNotificationLabel != null) - { - AutofillPersistNotificationLabel.WidthRequest = - StackLayout.Bounds.Width - AutofillPersistNotificationLabel.Bounds.Left * 2; - } - } - private void WebsiteIconsCell_Changed(object sender, ToggledEventArgs e) { var cell = sender as ExtendedSwitchCell; @@ -352,7 +327,7 @@ namespace Bit.App.Pages private class FormTableView : ExtendedTableView { - public FormTableView(bool header = false) + public FormTableView(SettingsOptionsPage page, bool header = false) { Intent = TableIntent.Settings; EnableScrolling = false; @@ -361,6 +336,7 @@ namespace Bit.App.Pages VerticalOptions = LayoutOptions.Start; NoFooter = true; NoHeader = !header; + WrappingStackLayout = () => page.StackLayout; } } diff --git a/src/App/Pages/Tools/ToolsPasswordGeneratorPage.cs b/src/App/Pages/Tools/ToolsPasswordGeneratorPage.cs index 7e7eeb686..b3c74e988 100644 --- a/src/App/Pages/Tools/ToolsPasswordGeneratorPage.cs +++ b/src/App/Pages/Tools/ToolsPasswordGeneratorPage.cs @@ -163,7 +163,7 @@ namespace Bit.App.Pages table.BottomPadding = 50; } - var stackLayout = new StackLayout + var stackLayout = new RedrawableStackLayout { Orientation = StackOrientation.Vertical, Children = { Password, table }, @@ -171,6 +171,8 @@ namespace Bit.App.Pages Spacing = 0 }; + table.WrappingStackLayout = () => stackLayout; + var scrollView = new ScrollView { Content = stackLayout, diff --git a/src/App/Pages/Vault/VaultAttachmentsPage.cs b/src/App/Pages/Vault/VaultAttachmentsPage.cs index 628bbcc36..2b0acde30 100644 --- a/src/App/Pages/Vault/VaultAttachmentsPage.cs +++ b/src/App/Pages/Vault/VaultAttachmentsPage.cs @@ -44,7 +44,7 @@ namespace Bit.App.Pages public ExtendedObservableCollection PresentationAttchments { get; private set; } = new ExtendedObservableCollection(); public ExtendedListView ListView { get; set; } - public StackLayout NoDataStackLayout { get; set; } + public RedrawableStackLayout NoDataStackLayout { get; set; } public StackLayout AddNewStackLayout { get; set; } public Label FileLabel { get; set; } public ExtendedTableView NewTable { get; set; } @@ -88,6 +88,7 @@ namespace Bit.App.Pages EnableSelection = false, VerticalOptions = LayoutOptions.Start, Margin = new Thickness(0, Helpers.OnPlatform(iOS: 10, Android: 30), 0, 0), + WrappingStackLayout = () => NoDataStackLayout, Root = new TableRoot { new TableSection(AppResources.AddNewAttachment) @@ -122,7 +123,7 @@ namespace Bit.App.Pages Style = (Style)Application.Current.Resources["text-muted"] }; - NoDataStackLayout = new StackLayout + NoDataStackLayout = new RedrawableStackLayout { VerticalOptions = LayoutOptions.Start, Spacing = 0, diff --git a/src/App/Pages/Vault/VaultCustomFieldsPage.cs b/src/App/Pages/Vault/VaultCustomFieldsPage.cs index 91896e35a..b0fe9cf47 100644 --- a/src/App/Pages/Vault/VaultCustomFieldsPage.cs +++ b/src/App/Pages/Vault/VaultCustomFieldsPage.cs @@ -36,6 +36,7 @@ namespace Bit.App.Pages } public ToolbarItem SaveToolbarItem { get; set; } + public ToolbarItem CloseToolbarItem { get; set; } public Label NoDataLabel { get; set; } public TableSection FieldsSection { get; set; } public ExtendedTableView Table { get; set; } @@ -131,12 +132,16 @@ namespace Bit.App.Pages await DisplayAlert(null, AppResources.AnErrorHasOccurred, AppResources.Ok); } }, ToolbarItemOrder.Default, 0); + ToolbarItems.Add(SaveToolbarItem); Title = AppResources.CustomFields; Content = Table; if(Device.RuntimePlatform == Device.iOS) { + CloseToolbarItem = new DismissModalToolBarItem(this, AppResources.Close); + ToolbarItems.Add(CloseToolbarItem); + Table.RowHeight = -1; Table.EstimatedRowHeight = 44; } @@ -157,13 +162,14 @@ namespace Bit.App.Pages return; } - if(_cipher.Fields != null && _cipher.Fields.Any()) + var hasFields = _cipher.Fields?.Any() ?? false; + + if(hasFields) { Content = Table; - ToolbarItems.Add(SaveToolbarItem); - if(Device.RuntimePlatform == Device.iOS) + if(CloseToolbarItem != null) { - ToolbarItems.Add(new DismissModalToolBarItem(this, AppResources.Cancel)); + CloseToolbarItem.Text = AppResources.Cancel; } foreach(var field in _cipher.Fields) @@ -189,7 +195,7 @@ namespace Bit.App.Pages textFieldCell.Button.Command = new Command(() => { textFieldCell.Entry.InvokeToggleIsPassword(); - textFieldCell.Button.Image = + textFieldCell.Button.Image = "eye" + (!textFieldCell.Entry.IsPasswordFromToggled ? "_slash" : string.Empty) + ".png"; }); } @@ -213,9 +219,9 @@ namespace Bit.App.Pages else { Content = NoDataLabel; - if(Device.RuntimePlatform == Device.iOS) + if(ToolbarItems.Count > 0) { - ToolbarItems.Add(new DismissModalToolBarItem(this, AppResources.Close)); + ToolbarItems.RemoveAt(0); } } } diff --git a/src/iOS/Controls/ExtendedTableViewRenderer.cs b/src/iOS/Controls/ExtendedTableViewRenderer.cs index a791c2990..d40927d64 100644 --- a/src/iOS/Controls/ExtendedTableViewRenderer.cs +++ b/src/iOS/Controls/ExtendedTableViewRenderer.cs @@ -63,7 +63,7 @@ namespace Bit.iOS.Controls private void SetSource() { var view = (ExtendedTableView)Element; - if(view.NoFooter || view.NoHeader) + if(true || view.NoFooter || view.NoHeader) { Control.Source = new CustomTableViewModelRenderer(view); } @@ -116,6 +116,7 @@ namespace Bit.iOS.Controls public class CustomTableViewModelRenderer : UnEvenTableViewModelRenderer { private readonly ExtendedTableView _view; + private bool _didRedraw = false; public CustomTableViewModelRenderer(ExtendedTableView model) : base(model) @@ -123,6 +124,16 @@ namespace Bit.iOS.Controls _view = model; } + public override async void WillDisplay(UITableView tableView, UITableViewCell cell, NSIndexPath indexPath) + { + // ref https://stackoverflow.com/a/48076188/1090359 + if(!_didRedraw && _view.WrappingStackLayout != null) + { + _didRedraw = true; + await _view.WrappingStackLayout()?.RedrawIfNeededAsync(10, false); + } + } + public override nfloat GetHeightForRow(UITableView tableView, NSIndexPath indexPath) { if(_view.HasUnevenRows) @@ -143,6 +154,16 @@ namespace Bit.iOS.Controls return base.GetHeightForHeader(tableView, section); } + public override UIView GetViewForHeader(UITableView tableView, nint section) + { + if(_view.NoHeader && section == 0) + { + return new UIView(new CGRect(0, 0, 0, 0)); + } + + return null; + } + public override nfloat GetHeightForFooter(UITableView tableView, nint section) { if(_view.NoFooter && (section + 1) == NumberOfSections(tableView)) @@ -152,6 +173,16 @@ namespace Bit.iOS.Controls return 10f; } + + public override UIView GetViewForFooter(UITableView tableView, nint section) + { + if(_view.NoFooter && (section + 1) == NumberOfSections(tableView)) + { + return new UIView(new CGRect(0, 0, 0, 0)); + } + + return null; + } } } }