From 08347478a2b2f0a62513b76f1a1e29b84169d049 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Thu, 17 Aug 2017 12:37:21 -0400 Subject: [PATCH] Implement alternative approach to veto-able back buttons. // FREEBIE --- Signal.xcodeproj/project.pbxproj | 6 +++ Signal/src/AppDelegate.m | 5 ++- .../ViewControllers/NewGroupViewController.m | 15 ++++++-- .../OWSConversationSettingsViewController.m | 5 +-- .../ViewControllers/OWSNavigationController.h | 20 ++++++++++ .../ViewControllers/OWSNavigationController.m | 37 +++++++++++++++++++ .../ViewControllers/ProfileViewController.m | 16 ++++++-- .../SignalsNavigationController.h | 4 +- .../ViewControllers/SignalsViewController.m | 7 ++-- .../UpdateGroupViewController.m | 19 +++++++--- Signal/src/util/UIViewController+OWS.h | 2 - 11 files changed, 109 insertions(+), 27 deletions(-) create mode 100644 Signal/src/ViewControllers/OWSNavigationController.h create mode 100644 Signal/src/ViewControllers/OWSNavigationController.m diff --git a/Signal.xcodeproj/project.pbxproj b/Signal.xcodeproj/project.pbxproj index e10cb3b11..85a903bd3 100644 --- a/Signal.xcodeproj/project.pbxproj +++ b/Signal.xcodeproj/project.pbxproj @@ -74,6 +74,7 @@ 34B3F89C1E8DF3270035BE1A /* BlockListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F89B1E8DF3270035BE1A /* BlockListViewController.m */; }; 34B3F89F1E8DF5490035BE1A /* OWSTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F89E1E8DF5490035BE1A /* OWSTableViewController.m */; }; 34B3F8A21E8EA6040035BE1A /* ViewControllerUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 34B3F8A11E8EA6040035BE1A /* ViewControllerUtils.m */; }; + 34C42D5B1F45F7A80072EC04 /* OWSNavigationController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34C42D5A1F45F7A80072EC04 /* OWSNavigationController.m */; }; 34CCAF381F0C0599004084F4 /* AppUpdateNag.m in Sources */ = {isa = PBXBuildFile; fileRef = 34CCAF371F0C0599004084F4 /* AppUpdateNag.m */; }; 34CCAF3B1F0C2748004084F4 /* OWSAddToContactViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34CCAF3A1F0C2748004084F4 /* OWSAddToContactViewController.m */; }; 34CE88E71F2FB9A10098030F /* ProfileViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34CE88E61F2FB9A10098030F /* ProfileViewController.m */; }; @@ -505,6 +506,8 @@ 34B3F89E1E8DF5490035BE1A /* OWSTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSTableViewController.m; sourceTree = ""; }; 34B3F8A01E8EA6040035BE1A /* ViewControllerUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewControllerUtils.h; sourceTree = ""; }; 34B3F8A11E8EA6040035BE1A /* ViewControllerUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewControllerUtils.m; sourceTree = ""; }; + 34C42D591F45F7A80072EC04 /* OWSNavigationController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSNavigationController.h; sourceTree = ""; }; + 34C42D5A1F45F7A80072EC04 /* OWSNavigationController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSNavigationController.m; sourceTree = ""; }; 34CCAF361F0C0599004084F4 /* AppUpdateNag.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppUpdateNag.h; sourceTree = ""; }; 34CCAF371F0C0599004084F4 /* AppUpdateNag.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppUpdateNag.m; sourceTree = ""; }; 34CCAF391F0C2748004084F4 /* OWSAddToContactViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSAddToContactViewController.h; sourceTree = ""; }; @@ -1026,6 +1029,8 @@ 34B3F85F1E8DF1700035BE1A /* OWSLinkedDevicesTableViewController.h */, 34B3F8601E8DF1700035BE1A /* OWSLinkedDevicesTableViewController.m */, 34B3F8611E8DF1700035BE1A /* OWSMessagesToolbarContentView.xib */, + 34C42D591F45F7A80072EC04 /* OWSNavigationController.h */, + 34C42D5A1F45F7A80072EC04 /* OWSNavigationController.m */, 34B3F8621E8DF1700035BE1A /* OWSQRCodeScanningViewController.h */, 34B3F8631E8DF1700035BE1A /* OWSQRCodeScanningViewController.m */, 34B3F89D1E8DF5490035BE1A /* OWSTableViewController.h */, @@ -2307,6 +2312,7 @@ 341BB7491DB727EE001E2975 /* JSQMediaItem+OWS.m in Sources */, 34B3F89C1E8DF3270035BE1A /* BlockListViewController.m in Sources */, 45F2B1941D9C9F48000D2C69 /* OWSOutgoingMessageCollectionViewCell.m in Sources */, + 34C42D5B1F45F7A80072EC04 /* OWSNavigationController.m in Sources */, BFB074C919A5611000F2947C /* ObservableValue.m in Sources */, B68EF9BA1C0B1EBD009C3DCD /* FLAnimatedImage.m in Sources */, B68112EA1A4D9EC400BA82FF /* UIImage+normalizeImage.m in Sources */, diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index b97adf95b..c38553cdf 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -11,6 +11,7 @@ #import "NotificationsManager.h" #import "OWSContactsManager.h" #import "OWSContactsSyncing.h" +#import "OWSNavigationController.h" #import "OWSProfileManager.h" #import "OWSStaleNotificationObserver.h" #import "Pastelog.h" @@ -802,8 +803,8 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; self.window.rootViewController = navigationController; } else { RegistrationViewController *viewController = [RegistrationViewController new]; - UINavigationController *navigationController = - [[UINavigationController alloc] initWithRootViewController:viewController]; + OWSNavigationController *navigationController = + [[OWSNavigationController alloc] initWithRootViewController:viewController]; navigationController.navigationBarHidden = YES; self.window.rootViewController = navigationController; } diff --git a/Signal/src/ViewControllers/NewGroupViewController.m b/Signal/src/ViewControllers/NewGroupViewController.m index 11f350826..03ef92704 100644 --- a/Signal/src/ViewControllers/NewGroupViewController.m +++ b/Signal/src/ViewControllers/NewGroupViewController.m @@ -10,6 +10,7 @@ #import "ContactsViewHelper.h" #import "Environment.h" #import "OWSContactsManager.h" +#import "OWSNavigationController.h" #import "OWSTableViewController.h" #import "Signal-Swift.h" #import "SignalKeyingStorage.h" @@ -34,7 +35,8 @@ const NSUInteger kNewGroupViewControllerAvatarWidth = 68; AvatarViewHelperDelegate, AddToGroupViewControllerDelegate, OWSTableViewControllerDelegate, - UINavigationControllerDelegate> + UINavigationControllerDelegate, + OWSNavigationView> @property (nonatomic, readonly) OWSMessageSender *messageSender; @property (nonatomic, readonly) ContactsViewHelper *contactsViewHelper; @@ -97,8 +99,6 @@ const NSUInteger kNewGroupViewControllerAvatarWidth = 68; [super loadView]; self.title = NSLocalizedString(@"NEW_GROUP_DEFAULT_TITLE", @"The navbar title for the 'new group' view."); - self.navigationItem.leftBarButtonItem = - [self createOWSBackButtonWithTarget:self selector:@selector(backButtonPressed:)]; self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"NEW_GROUP_CREATE_BUTTON", @"The title for the 'create group' button.") @@ -547,7 +547,7 @@ const NSUInteger kNewGroupViewControllerAvatarWidth = 68; #pragma mark - Event Handling -- (void)backButtonPressed:(id)sender +- (void)backButtonPressed { [self.groupNameTextField resignFirstResponder]; @@ -649,6 +649,13 @@ const NSUInteger kNewGroupViewControllerAvatarWidth = 68; return [self.memberRecipientIds containsObject:recipientId]; } +#pragma mark - OWSNavigationView + +- (void)navBackButtonPressed +{ + [self backButtonPressed]; +} + @end NS_ASSUME_NONNULL_END diff --git a/Signal/src/ViewControllers/OWSConversationSettingsViewController.m b/Signal/src/ViewControllers/OWSConversationSettingsViewController.m index de045a625..a72897566 100644 --- a/Signal/src/ViewControllers/OWSConversationSettingsViewController.m +++ b/Signal/src/ViewControllers/OWSConversationSettingsViewController.m @@ -827,10 +827,7 @@ NS_ASSUME_NONNULL_BEGIN updateGroupViewController.conversationSettingsViewDelegate = self.conversationSettingsViewDelegate; updateGroupViewController.thread = (TSGroupThread *)self.thread; updateGroupViewController.mode = mode; - - UINavigationController *navigationController = - [[UINavigationController alloc] initWithRootViewController:updateGroupViewController]; - [self presentViewController:navigationController animated:YES completion:nil]; + [self.navigationController pushViewController:updateGroupViewController animated:YES]; } - (void)presentContactViewController diff --git a/Signal/src/ViewControllers/OWSNavigationController.h b/Signal/src/ViewControllers/OWSNavigationController.h new file mode 100644 index 000000000..d8279dacb --- /dev/null +++ b/Signal/src/ViewControllers/OWSNavigationController.h @@ -0,0 +1,20 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +#import + +@protocol OWSNavigationView + +- (void)navBackButtonPressed; + +@end + +#pragma mark - + +// This navigation controller subclass should be used anywhere we might +// want to cancel back button presses or back gestures due to, for example, +// unsaved changes. +@interface OWSNavigationController : UINavigationController + +@end diff --git a/Signal/src/ViewControllers/OWSNavigationController.m b/Signal/src/ViewControllers/OWSNavigationController.m new file mode 100644 index 000000000..e475f48a4 --- /dev/null +++ b/Signal/src/ViewControllers/OWSNavigationController.m @@ -0,0 +1,37 @@ +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +#import "OWSNavigationController.h" + +@interface OWSNavigationController () + +@end + +#pragma mark - + +@implementation OWSNavigationController + +- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item +{ + + UIViewController *topViewController = self.topViewController; + BOOL wasBackButtonClicked = topViewController.navigationItem == item; + + if (wasBackButtonClicked) { + if ([topViewController respondsToSelector:@selector(navBackButtonPressed)]) { + // if user did press back on the view controller where you handle the navBackButtonPressed + [topViewController performSelector:@selector(navBackButtonPressed)]; + return NO; + } else { + // if user did press back but you are not on the view controller that can handle the navBackButtonPressed + [self popViewControllerAnimated:YES]; + return YES; + } + } else { + // when you call popViewController programmatically you do not want to pop it twice + return YES; + } +} + +@end diff --git a/Signal/src/ViewControllers/ProfileViewController.m b/Signal/src/ViewControllers/ProfileViewController.m index 0230df82d..1c2c20f16 100644 --- a/Signal/src/ViewControllers/ProfileViewController.m +++ b/Signal/src/ViewControllers/ProfileViewController.m @@ -5,6 +5,7 @@ #import "ProfileViewController.h" #import "AppDelegate.h" #import "AvatarViewHelper.h" +#import "OWSNavigationController.h" #import "OWSProfileManager.h" #import "Signal-Swift.h" #import "SignalsNavigationController.h" @@ -27,7 +28,7 @@ typedef NS_ENUM(NSInteger, ProfileViewMode) { NSString *const kProfileView_Collection = @"kProfileView_Collection"; NSString *const kProfileView_LastPresentedDate = @"kProfileView_LastPresentedDate"; -@interface ProfileViewController () +@interface ProfileViewController () @property (nonatomic, readonly) AvatarViewHelper *avatarViewHelper; @@ -259,8 +260,6 @@ NSString *const kProfileView_LastPresentedDate = @"kProfileView_LastPresentedDat // context. switch (self.profileViewMode) { case ProfileViewMode_AppSettings: - self.navigationItem.leftBarButtonItem = - [self createOWSBackButtonWithTarget:self selector:@selector(backOrSkipButtonPressed)]; break; case ProfileViewMode_UpgradeOrNag: self.navigationItem.leftBarButtonItem = @@ -432,6 +431,7 @@ NSString *const kProfileView_LastPresentedDate = @"kProfileView_LastPresentedDat + (void)presentForAppSettings:(UINavigationController *)navigationController { OWSAssert(navigationController); + OWSAssert([navigationController isKindOfClass:[OWSNavigationController class]]); ProfileViewController *vc = [[ProfileViewController alloc] initWithMode:ProfileViewMode_AppSettings]; [navigationController pushViewController:vc animated:YES]; @@ -440,6 +440,7 @@ NSString *const kProfileView_LastPresentedDate = @"kProfileView_LastPresentedDat + (void)presentForRegistration:(UINavigationController *)navigationController { OWSAssert(navigationController); + OWSAssert([navigationController isKindOfClass:[OWSNavigationController class]]); ProfileViewController *vc = [[ProfileViewController alloc] initWithMode:ProfileViewMode_Registration]; [navigationController pushViewController:vc animated:YES]; @@ -450,7 +451,7 @@ NSString *const kProfileView_LastPresentedDate = @"kProfileView_LastPresentedDat OWSAssert(presentingController); ProfileViewController *vc = [[ProfileViewController alloc] initWithMode:ProfileViewMode_UpgradeOrNag]; - UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:vc]; + OWSNavigationController *navigationController = [[OWSNavigationController alloc] initWithRootViewController:vc]; [presentingController presentTopLevelModalViewController:navigationController animateDismissal:YES animatePresentation:YES]; @@ -492,6 +493,13 @@ NSString *const kProfileView_LastPresentedDate = @"kProfileView_LastPresentedDat self.avatar = nil; } +#pragma mark - OWSNavigationView + +- (void)navBackButtonPressed +{ + [self backOrSkipButtonPressed]; +} + #pragma mark - Logging + (NSString *)tag diff --git a/Signal/src/ViewControllers/SignalsNavigationController.h b/Signal/src/ViewControllers/SignalsNavigationController.h index 88d3f97ff..7d8b6a041 100644 --- a/Signal/src/ViewControllers/SignalsNavigationController.h +++ b/Signal/src/ViewControllers/SignalsNavigationController.h @@ -2,8 +2,8 @@ // Copyright (c) 2017 Open Whisper Systems. All rights reserved. // -#import +#import "OWSNavigationController.h" -@interface SignalsNavigationController : UINavigationController +@interface SignalsNavigationController : OWSNavigationController @end diff --git a/Signal/src/ViewControllers/SignalsViewController.m b/Signal/src/ViewControllers/SignalsViewController.m index 9db269e52..86d63a7be 100644 --- a/Signal/src/ViewControllers/SignalsViewController.m +++ b/Signal/src/ViewControllers/SignalsViewController.m @@ -10,6 +10,7 @@ #import "MessagesViewController.h" #import "NSDate+millisecondTimeStamp.h" #import "OWSContactsManager.h" +#import "OWSNavigationController.h" #import "ProfileViewController.h" #import "PropertyListPreferences.h" #import "PushManager.h" @@ -309,7 +310,7 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; - (void)settingsButtonPressed:(id)sender { AppSettingsViewController *vc = [AppSettingsViewController new]; - UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:vc]; + OWSNavigationController *navigationController = [[OWSNavigationController alloc] initWithRootViewController:vc]; [self presentViewController:navigationController animated:YES completion:nil]; } @@ -353,8 +354,8 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState }; // // We just want to make sure contact access is *complete* before showing the compose // screen to avoid flicker. - UINavigationController *navigationController = - [[UINavigationController alloc] initWithRootViewController:viewController]; + OWSNavigationController *navigationController = + [[OWSNavigationController alloc] initWithRootViewController:viewController]; [self presentTopLevelModalViewController:navigationController animateDismissal:YES animatePresentation:YES]; }]; } diff --git a/Signal/src/ViewControllers/UpdateGroupViewController.m b/Signal/src/ViewControllers/UpdateGroupViewController.m index 0bf85eafc..3ebb1edf2 100644 --- a/Signal/src/ViewControllers/UpdateGroupViewController.m +++ b/Signal/src/ViewControllers/UpdateGroupViewController.m @@ -10,6 +10,7 @@ #import "ContactsViewHelper.h" #import "Environment.h" #import "OWSContactsManager.h" +#import "OWSNavigationController.h" #import "OWSTableViewController.h" #import "Signal-Swift.h" #import "SignalKeyingStorage.h" @@ -33,7 +34,8 @@ NS_ASSUME_NONNULL_BEGIN AvatarViewHelperDelegate, AddToGroupViewControllerDelegate, OWSTableViewControllerDelegate, - UINavigationControllerDelegate> + UINavigationControllerDelegate, + OWSNavigationView> @property (nonatomic, readonly) OWSMessageSender *messageSender; @property (nonatomic, readonly) ContactsViewHelper *contactsViewHelper; @@ -103,8 +105,6 @@ NS_ASSUME_NONNULL_BEGIN self.previousMemberRecipientIds = [NSSet setWithArray:self.thread.groupModel.groupMemberIds]; self.title = NSLocalizedString(@"EDIT_GROUP_DEFAULT_TITLE", @"The navbar title for the 'update group' view."); - self.navigationItem.leftBarButtonItem = - [self createOWSBackButtonWithTarget:self selector:@selector(backButtonPressed:)]; // First section. @@ -409,13 +409,13 @@ NS_ASSUME_NONNULL_BEGIN #pragma mark - Event Handling -- (void)backButtonPressed:(id)sender +- (void)backButtonPressed { [self.groupNameTextField resignFirstResponder]; if (!self.hasUnsavedChanges) { // If user made no changes, return to conversation settings view. - [self dismissViewControllerAnimated:YES completion:nil]; + [self.navigationController popViewControllerAnimated:YES]; return; } @@ -441,7 +441,7 @@ NS_ASSUME_NONNULL_BEGIN @"The label for the 'don't save' button in action sheets.") style:UIAlertActionStyleDestructive handler:^(UIAlertAction *action) { - [self dismissViewControllerAnimated:YES completion:nil]; + [self.navigationController popViewControllerAnimated:YES]; }]]; [self presentViewController:controller animated:YES completion:nil]; } @@ -528,6 +528,13 @@ NS_ASSUME_NONNULL_BEGIN return [self.memberRecipientIds containsObject:recipientId]; } +#pragma mark - OWSNavigationView + +- (void)navBackButtonPressed +{ + [self backButtonPressed]; +} + @end NS_ASSUME_NONNULL_END diff --git a/Signal/src/util/UIViewController+OWS.h b/Signal/src/util/UIViewController+OWS.h index 5bbd8a077..ac00b42e9 100644 --- a/Signal/src/util/UIViewController+OWS.h +++ b/Signal/src/util/UIViewController+OWS.h @@ -17,8 +17,6 @@ NS_ASSUME_NONNULL_BEGIN */ - (UIBarButtonItem *)createOWSBackButton; -- (UIBarButtonItem *)createOWSBackButtonWithTarget:(id)target selector:(SEL)selector; - @end NS_ASSUME_NONNULL_END