From bfe1eb5503d5abf868d2db470b34ea875020f468 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Mon, 30 Jul 2018 18:31:08 -0600 Subject: [PATCH 1/3] Move reminder views into scrollable content // FREEBIE --- .../HomeView/HomeViewController.m | 259 +++++++++++------- 1 file changed, 160 insertions(+), 99 deletions(-) diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index ba50c92be..4249fe394 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -40,6 +40,23 @@ typedef NS_ENUM(NSInteger, HomeViewMode) { HomeViewMode_Inbox, }; +// The bulk of the content in this view is driven by a YapDB view/mapping. +// However, we also want to optionally include ReminderView's at the top +// and an "Archived Conversations" button at the bottom. Rather than introduce +// index-offsets into the Mapping calculation, we introduce two pseudo groups +// to add a top and bottom section to the content, and create cells for those +// sections without consulting the YapMapping. +// This is a bit of a hack, but it consolidates the hacks into the Reminder/Archive section +// and allows us to leaves the bulk of the content logic on the happy path. +NSString *const kReminderViewPseudoGroup = @"kReminderViewPseudoGroup"; +NSString *const kArchiveButtonPseudoGroup = @"kArchiveButtonPseudoGroup"; + +typedef NS_ENUM(NSInteger, HomeViewControllerSection) { + HomeViewControllerSectionReminders, + HomeViewControllerSectionConversations, + HomeViewControllerSectionArchiveButton, +}; + NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversationsReuseIdentifier"; @interface HomeViewController () )previewingContext @@ -542,7 +556,7 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations BOOL isShowingSearchResults = !self.searchResultsController.view.hidden; if (isShowingSearchResults) { OWSAssert(self.searchBar.text.ows_stripped.length > 0); - self.tableView.contentOffset = CGPointZero; + [self scrollSearchBarToTopAnimated:NO]; } else if (self.lastThread) { OWSAssert(self.searchBar.text.ows_stripped.length == 0); @@ -734,31 +748,30 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations return (NSInteger)[self.threadMappings numberOfSections]; } -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)aSection { - NSInteger result = (NSInteger)[self.threadMappings numberOfItemsInSection:(NSUInteger)section]; - if (self.hasArchivedThreadsRow) { - // Add the "archived conversations" row. - result++; + HomeViewControllerSection section = (HomeViewControllerSection)aSection; + switch (section) { + case HomeViewControllerSectionReminders: { + return self.hasVisibleReminders ? 1 : 0; + } + case HomeViewControllerSectionConversations: { + NSInteger result = (NSInteger)[self.threadMappings numberOfItemsInSection:(NSUInteger)section]; + return result; + } + case HomeViewControllerSectionArchiveButton: { + return self.hasArchivedThreadsRow ? 1 : 0; + } } - return result; -} -- (BOOL)isIndexPathForArchivedConversations:(NSIndexPath *)indexPath -{ - if (self.homeViewMode != HomeViewMode_Inbox) { - return NO; - } - if (indexPath.section != 0) { - return NO; - } - NSInteger cellCount = (NSInteger)[self.threadMappings numberOfItemsInSection:(NSUInteger)0]; - return indexPath.row == cellCount; + OWSFail(@"%@ failure: unexpected section: %lu", self.logTag, (unsigned long)section); + return 0; } - (ThreadViewModel *)threadViewModelForIndexPath:(NSIndexPath *)indexPath { TSThread *threadRecord = [self threadForIndexPath:indexPath]; + OWSAssert(threadRecord); ThreadViewModel *_Nullable cachedThreadViewModel = [self.threadViewModelCache objectForKey:threadRecord.uniqueId]; if (cachedThreadViewModel) { @@ -775,11 +788,21 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - if ([self isIndexPathForArchivedConversations:indexPath]) { - return [self cellForArchivedConversationsRow:tableView]; - } else { - return [self tableView:tableView cellForConversationAtIndexPath:indexPath]; + HomeViewControllerSection section = (HomeViewControllerSection)indexPath.section; + switch (section) { + case HomeViewControllerSectionReminders: { + return self.reminderViewCell; + } + case HomeViewControllerSectionConversations: { + return [self tableView:tableView cellForConversationAtIndexPath:indexPath]; + } + case HomeViewControllerSectionArchiveButton: { + return [self cellForArchivedConversationsRow:tableView]; + } } + + OWSFail(@"%@ failure: unexpected section: %lu", self.logTag, (unsigned long)section); + return [UITableViewCell new]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForConversationAtIndexPath:(NSIndexPath *)indexPath @@ -875,56 +898,70 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations - (nullable NSArray *)tableView:(UITableView *)tableView editActionsForRowAtIndexPath:(NSIndexPath *)indexPath { - if ([self isIndexPathForArchivedConversations:indexPath]) { - return @[]; - } - - UITableViewRowAction *deleteAction = - [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault - title:NSLocalizedString(@"TXT_DELETE_TITLE", nil) - handler:^(UITableViewRowAction *action, NSIndexPath *swipedIndexPath) { - [self tableViewCellTappedDelete:swipedIndexPath]; - }]; - - UITableViewRowAction *archiveAction; - if (self.homeViewMode == HomeViewMode_Inbox) { - archiveAction = [UITableViewRowAction - rowActionWithStyle:UITableViewRowActionStyleNormal - title:NSLocalizedString(@"ARCHIVE_ACTION", - @"Pressing this button moves a thread from the inbox to the archive") - handler:^(UITableViewRowAction *_Nonnull action, NSIndexPath *_Nonnull tappedIndexPath) { - [self archiveIndexPath:tappedIndexPath]; - [Environment.preferences setHasArchivedAMessage:YES]; - }]; + HomeViewControllerSection section = (HomeViewControllerSection)indexPath.section; + switch (section) { + case HomeViewControllerSectionReminders: { + return @[]; + } + case HomeViewControllerSectionConversations: { + UITableViewRowAction *deleteAction = + [UITableViewRowAction rowActionWithStyle:UITableViewRowActionStyleDefault + title:NSLocalizedString(@"TXT_DELETE_TITLE", nil) + handler:^(UITableViewRowAction *action, NSIndexPath *swipedIndexPath) { + [self tableViewCellTappedDelete:swipedIndexPath]; + }]; + + UITableViewRowAction *archiveAction; + if (self.homeViewMode == HomeViewMode_Inbox) { + archiveAction = [UITableViewRowAction + rowActionWithStyle:UITableViewRowActionStyleNormal + title:NSLocalizedString(@"ARCHIVE_ACTION", + @"Pressing this button moves a thread from the inbox to the archive") + handler:^(UITableViewRowAction *_Nonnull action, NSIndexPath *_Nonnull tappedIndexPath) { + [self archiveIndexPath:tappedIndexPath]; + [Environment.preferences setHasArchivedAMessage:YES]; + }]; + + } else { + archiveAction = [UITableViewRowAction + rowActionWithStyle:UITableViewRowActionStyleNormal + title:NSLocalizedString(@"UNARCHIVE_ACTION", + @"Pressing this button moves an archived thread from the archive back to " + @"the inbox") + handler:^(UITableViewRowAction *_Nonnull action, NSIndexPath *_Nonnull tappedIndexPath) { + [self archiveIndexPath:tappedIndexPath]; + }]; + } - } else { - archiveAction = [UITableViewRowAction - rowActionWithStyle:UITableViewRowActionStyleNormal - title:NSLocalizedString(@"UNARCHIVE_ACTION", - @"Pressing this button moves an archived thread from the archive back to the inbox") - handler:^(UITableViewRowAction *_Nonnull action, NSIndexPath *_Nonnull tappedIndexPath) { - [self archiveIndexPath:tappedIndexPath]; - }]; + return @[ deleteAction, archiveAction ]; + } + case HomeViewControllerSectionArchiveButton: { + return @[]; + } } - - - return @[ deleteAction, archiveAction ]; } - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { - if ([self isIndexPathForArchivedConversations:indexPath]) { - return NO; + HomeViewControllerSection section = (HomeViewControllerSection)indexPath.section; + switch (section) { + case HomeViewControllerSectionReminders: { + return NO; + } + case HomeViewControllerSectionConversations: { + return YES; + } + case HomeViewControllerSectionArchiveButton: { + return NO; + } } - - return YES; } #pragma mark - UISearchBarDelegate - (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar { - [self.tableView setContentOffset:CGPointZero animated:NO]; + [self scrollSearchBarToTopAnimated:NO]; [self updateSearchResultsVisibility]; @@ -977,13 +1014,19 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations self.searchResultsController.view.hidden = !isSearching; if (isSearching) { - [self.tableView setContentOffset:CGPointZero animated:NO]; + [self scrollSearchBarToTopAnimated:NO]; self.tableView.scrollEnabled = NO; } else { self.tableView.scrollEnabled = YES; } } +- (void)scrollSearchBarToTopAnimated:(BOOL)isAnimated +{ + CGFloat topInset = self.topLayoutGuide.length; + [self.tableView setContentOffset:CGPointMake(0, -topInset) animated:isAnimated]; +} + #pragma mark - UIScrollViewDelegate - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView @@ -1004,6 +1047,11 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations - (void)tableViewCellTappedDelete:(NSIndexPath *)indexPath { + if (indexPath.section != HomeViewControllerSectionConversations) { + OWSFail(@"%@ failure: unexpected section: %lu", self.logTag, (unsigned long)indexPath.section); + return; + } + TSThread *thread = [self threadForIndexPath:indexPath]; if ([thread isKindOfClass:[TSGroupThread class]]) { @@ -1055,6 +1103,11 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations - (void)archiveIndexPath:(NSIndexPath *)indexPath { + if (indexPath.section != HomeViewControllerSectionConversations) { + OWSFail(@"%@ failure: unexpected section: %lu", self.logTag, (unsigned long)indexPath.section); + return; + } + TSThread *thread = [self threadForIndexPath:indexPath]; [self.editingDbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { @@ -1075,15 +1128,22 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations DDLogInfo(@"%@ %s %ld %ld", self.logTag, __PRETTY_FUNCTION__, (long)indexPath.row, (long)indexPath.section); [self.searchBar resignFirstResponder]; - - if ([self isIndexPathForArchivedConversations:indexPath]) { - [self showArchivedConversations]; - return; + HomeViewControllerSection section = (HomeViewControllerSection)indexPath.section; + switch (section) { + case HomeViewControllerSectionReminders: { + break; + } + case HomeViewControllerSectionConversations: { + TSThread *thread = [self threadForIndexPath:indexPath]; + [self presentThread:thread action:ConversationViewActionNone]; + [tableView deselectRowAtIndexPath:indexPath animated:YES]; + break; + } + case HomeViewControllerSectionArchiveButton: { + [self showArchivedConversations]; + break; + } } - - TSThread *thread = [self threadForIndexPath:indexPath]; - [self presentThread:thread action:ConversationViewActionNone]; - [tableView deselectRowAtIndexPath:indexPath animated:YES]; } - (void)presentThread:(TSThread *)thread action:(ConversationViewAction)action @@ -1218,8 +1278,9 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations { OWSAssertIsOnMainThread(); - self.threadMappings = [[YapDatabaseViewMappings alloc] initWithGroups:@[ self.currentGrouping ] - view:TSThreadDatabaseViewExtensionName]; + self.threadMappings = [[YapDatabaseViewMappings alloc] + initWithGroups:@[ kReminderViewPseudoGroup, self.currentGrouping, kArchiveButtonPseudoGroup ] + view:TSThreadDatabaseViewExtensionName]; [self.threadMappings setIsReversed:YES forGroup:self.currentGrouping]; [self resetMappings]; From 78b4df95a5d93078650e65ce5dfa9640a77a3488 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Mon, 30 Jul 2018 19:25:18 -0600 Subject: [PATCH 2/3] fixup call banner offsets --- .../ViewControllers/OWSNavigationController.m | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/SignalMessaging/ViewControllers/OWSNavigationController.m b/SignalMessaging/ViewControllers/OWSNavigationController.m index bd38547ee..4fa9f5853 100644 --- a/SignalMessaging/ViewControllers/OWSNavigationController.m +++ b/SignalMessaging/ViewControllers/OWSNavigationController.m @@ -133,18 +133,11 @@ NS_ASSUME_NONNULL_BEGIN if (@available(iOS 11.0, *)) { if (OWSWindowManager.sharedManager.hasCall) { - if (UIDevice.currentDevice.isIPhoneX) { - // iPhoneX computes status bar height differently. - // IOS_DEVICE_CONSTANT - self.additionalSafeAreaInsets = UIEdgeInsetsMake(navbar.navbarWithoutStatusHeight + 20, 0, 0, 0); - - } else { - self.additionalSafeAreaInsets - = UIEdgeInsetsMake(navbar.navbarWithoutStatusHeight + CurrentAppContext().statusBarHeight, 0, 0, 0); - } + self.additionalSafeAreaInsets = UIEdgeInsetsMake(20, 0, 0, 0); } else { self.additionalSafeAreaInsets = UIEdgeInsetsZero; } + // in iOS11 we have to ensure the navbar frame *in* layoutSubviews. [navbar layoutSubviews]; } else { From def8b43daa181a5edc6e82fbaa2ac98960fa70da Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Tue, 31 Jul 2018 14:28:41 -0600 Subject: [PATCH 3/3] iOS9/10 fixups --- .../HomeView/HomeViewController.m | 6 +++++- .../ViewControllers/OWSNavigationController.m | 15 ++------------ SignalMessaging/Views/OWSNavigationBar.swift | 20 +++++++++++++------ 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/Signal/src/ViewControllers/HomeView/HomeViewController.m b/Signal/src/ViewControllers/HomeView/HomeViewController.m index 4249fe394..49e95f7bd 100644 --- a/Signal/src/ViewControllers/HomeView/HomeViewController.m +++ b/Signal/src/ViewControllers/HomeView/HomeViewController.m @@ -418,7 +418,11 @@ NSString *const kArchivedConversationsReuseIdentifier = @"kArchivedConversations [self addChildViewController:searchResultsController]; [self.view addSubview:searchResultsController.view]; [searchResultsController.view autoPinEdgesToSuperviewEdgesWithInsets:UIEdgeInsetsZero excludingEdge:ALEdgeTop]; - [searchResultsController.view autoPinTopToSuperviewMarginWithInset:56]; + if (@available(iOS 11, *)) { + [searchResultsController.view autoPinTopToSuperviewMarginWithInset:56]; + } else { + [searchResultsController.view autoPinToTopLayoutGuideOfViewController:self withInset:40]; + } searchResultsController.view.hidden = YES; [self updateBarButtonItems]; diff --git a/SignalMessaging/ViewControllers/OWSNavigationController.m b/SignalMessaging/ViewControllers/OWSNavigationController.m index 4fa9f5853..7a9d3e17e 100644 --- a/SignalMessaging/ViewControllers/OWSNavigationController.m +++ b/SignalMessaging/ViewControllers/OWSNavigationController.m @@ -141,20 +141,9 @@ NS_ASSUME_NONNULL_BEGIN // in iOS11 we have to ensure the navbar frame *in* layoutSubviews. [navbar layoutSubviews]; } else { - // Pre iOS11 we size the navbar, and position it vertically once. + // in iOS9/10 we only need to size the navbar once [navbar sizeToFit]; - - if (OWSWindowManager.sharedManager.hasCall) { - CGRect oldFrame = navbar.frame; - CGRect newFrame = oldFrame; - newFrame.size.height = navbar.callBannerHeight; - navbar.frame = newFrame; - } else { - CGRect oldFrame = navbar.frame; - CGRect newFrame - = CGRectMake(oldFrame.origin.x, navbar.statusBarHeight, oldFrame.size.width, oldFrame.size.height); - navbar.frame = newFrame; - } + [navbar layoutIfNeeded]; // Since the navbar's frame was updated, we need to be sure our child VC's // container view is updated. diff --git a/SignalMessaging/Views/OWSNavigationBar.swift b/SignalMessaging/Views/OWSNavigationBar.swift index 04014a96d..c05a63ded 100644 --- a/SignalMessaging/Views/OWSNavigationBar.swift +++ b/SignalMessaging/Views/OWSNavigationBar.swift @@ -94,11 +94,18 @@ public class OWSNavigationBar: UINavigationBar { if #available(iOS 11, *) { return super.sizeThatFits(size) - } else { - // pre iOS11, sizeThatFits is repeatedly called to determine how much space to reserve for that navbar. + } else if #available(iOS 10, *) { + // iOS10 + // sizeThatFits is repeatedly called to determine how much space to reserve for that navbar. // That is, increasing this causes the child view controller to be pushed down. // (as of iOS11, this is not used and instead we use additionalSafeAreaInsets) return CGSize(width: fullWidth, height: navbarWithoutStatusHeight + statusBarHeight) + } else { + // iOS9 + // sizeThatFits is repeatedly called to determine how much space to reserve for that navbar. + // That is, increasing this causes the child view controller to be pushed down. + // (as of iOS11, this is not used and instead we use additionalSafeAreaInsets) + return CGSize(width: fullWidth, height: navbarWithoutStatusHeight + callBannerHeight + 20) } } @@ -108,15 +115,16 @@ public class OWSNavigationBar: UINavigationBar { return } + guard #available(iOS 11, *) else { + super.layoutSubviews() + return + } + self.frame = CGRect(x: 0, y: callBannerHeight, width: fullWidth, height: navbarWithoutStatusHeight) self.bounds = CGRect(x: 0, y: 0, width: fullWidth, height: navbarWithoutStatusHeight) super.layoutSubviews() - guard #available(iOS 11, *) else { - return - } - // This is only necessary on iOS11, which has some private views within that lay outside of the navbar. // They aren't actually visible behind the call status bar, but they looks strange during present/dismiss // animations for modal VC's