mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
Merge branch 'mkirk/first-responder'
This commit is contained in:
commit
445c10a7ea
|
@ -44,6 +44,24 @@ public class ContactsPicker: OWSViewController, UITableViewDelegate, UITableView
|
|||
return Environment.current().contactsManager
|
||||
}
|
||||
|
||||
// HACK: Though we don't have an input accessory view, the VC we are presented above (ConversationVC) does.
|
||||
// If the app is backgrounded and then foregrounded, when OWSWindowManager calls mainWindow.makeKeyAndVisible
|
||||
// the ConversationVC's inputAccessoryView will appear *above* us unless we'd previously become first responder.
|
||||
override public var canBecomeFirstResponder: Bool {
|
||||
Logger.debug("\(self.logTag) in \(#function)")
|
||||
return true
|
||||
}
|
||||
|
||||
override public func becomeFirstResponder() -> Bool {
|
||||
Logger.debug("\(self.logTag) in \(#function)")
|
||||
return super.becomeFirstResponder()
|
||||
}
|
||||
|
||||
override public func resignFirstResponder() -> Bool {
|
||||
Logger.debug("\(self.logTag) in \(#function)")
|
||||
return super.resignFirstResponder()
|
||||
}
|
||||
|
||||
private let collation = UILocalizedIndexedCollation.current()
|
||||
private let contactStore = CNContactStore()
|
||||
|
||||
|
|
|
@ -552,15 +552,7 @@ typedef enum : NSUInteger {
|
|||
|
||||
- (BOOL)canBecomeFirstResponder
|
||||
{
|
||||
// Normally there'd be no mechanism for us to become first responder while presenting
|
||||
// another view controller, however due to our screen lock window juggling, a side effect of
|
||||
// calling `makeKeyAndVisible` is that "last known" first responder is sent "becomeFirstResponder",
|
||||
// regardless of if it is no longer the top most VC.
|
||||
if (self.presentedViewController) {
|
||||
return NO;
|
||||
} else {
|
||||
return YES;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (nullable UIView *)inputAccessoryView
|
||||
|
@ -604,6 +596,28 @@ typedef enum : NSUInteger {
|
|||
[self markVisibleMessagesAsRead];
|
||||
[self.cellMediaCache removeAllObjects];
|
||||
[self cancelReadTimer];
|
||||
[self dismissPresentedViewControllerIfNecessary];
|
||||
}
|
||||
|
||||
- (void)dismissPresentedViewControllerIfNecessary
|
||||
{
|
||||
UIViewController *_Nullable presentedViewController = self.presentedViewController;
|
||||
if (!presentedViewController) {
|
||||
DDLogDebug(@"%@ presentedViewController was nil", self.logTag);
|
||||
return;
|
||||
}
|
||||
|
||||
if ([presentedViewController isKindOfClass:[UIAlertController class]]) {
|
||||
DDLogDebug(@"%@ dismissing presentedViewController: %@", self.logTag, presentedViewController);
|
||||
[self dismissViewControllerAnimated:NO completion:nil];
|
||||
return;
|
||||
}
|
||||
|
||||
if ([presentedViewController isKindOfClass:[UIImagePickerController class]]) {
|
||||
DDLogDebug(@"%@ dismissing presentedViewController: %@", self.logTag, presentedViewController);
|
||||
[self dismissViewControllerAnimated:NO completion:nil];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
|
|
|
@ -220,6 +220,24 @@ class MediaGalleryViewController: UINavigationController, MediaGalleryDataSource
|
|||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
// HACK: Though we don't have an input accessory view, the VC we are presented above (ConversationVC) does.
|
||||
// If the app is backgrounded and then foregrounded, when OWSWindowManager calls mainWindow.makeKeyAndVisible
|
||||
// the ConversationVC's inputAccessoryView will appear *above* us unless we'd previously become first responder.
|
||||
override public var canBecomeFirstResponder: Bool {
|
||||
Logger.debug("\(self.logTag) in \(#function)")
|
||||
return true
|
||||
}
|
||||
|
||||
override public func becomeFirstResponder() -> Bool {
|
||||
Logger.debug("\(self.logTag) in \(#function)")
|
||||
return super.becomeFirstResponder()
|
||||
}
|
||||
|
||||
override public func resignFirstResponder() -> Bool {
|
||||
Logger.debug("\(self.logTag) in \(#function)")
|
||||
return super.resignFirstResponder()
|
||||
}
|
||||
|
||||
// MARK: View Lifecyle
|
||||
|
||||
override func viewDidLoad() {
|
||||
|
@ -296,7 +314,7 @@ class MediaGalleryViewController: UINavigationController, MediaGalleryDataSource
|
|||
self.applyInitialMediaViewConstraints()
|
||||
|
||||
// Restore presentationView.alpha in case a previous dismiss left us in a bad state.
|
||||
pageViewController.navigationController?.setNavigationBarHidden(false, animated: false)
|
||||
self.setNavigationBarHidden(false, animated: false)
|
||||
self.presentationView.alpha = 1
|
||||
|
||||
// We want to animate the tapped media from it's position in the previous VC
|
||||
|
@ -362,6 +380,15 @@ class MediaGalleryViewController: UINavigationController, MediaGalleryDataSource
|
|||
self.view.isUserInteractionEnabled = true
|
||||
|
||||
pageViewController.wasPresented()
|
||||
|
||||
// Since we're presenting *over* the ConversationVC, we need to `becomeFirstResponder`.
|
||||
//
|
||||
// Otherwise, the `ConversationVC.inputAccessoryView` will appear over top of us whenever
|
||||
// OWSWindowManager window juggling calls `[rootWindow makeKeyAndVisible]`.
|
||||
//
|
||||
// We don't need to do this when pushing VCs onto the SignalsNavigationController - only when
|
||||
// presenting directly from ConversationVC.
|
||||
_ = self.becomeFirstResponder()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,9 +67,6 @@ const UIWindowLevel UIWindowLevel_ScreenBlocking(void)
|
|||
|
||||
@property (nonatomic, nullable) UIViewController *callViewController;
|
||||
|
||||
@property (nonatomic, nullable) UIResponder *rootWindowResponder;
|
||||
@property (nonatomic, nullable, weak) UIViewController *rootFrontmostViewController;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
@ -348,37 +345,11 @@ const UIWindowLevel UIWindowLevel_ScreenBlocking(void)
|
|||
DDLogInfo(@"%@ showing root window.", self.logTag);
|
||||
}
|
||||
|
||||
BOOL shouldTryToRestoreFirstResponder = self.rootWindow.hidden;
|
||||
|
||||
// By calling makeKeyAndVisible we ensure the rootViewController becomes firt responder.
|
||||
// In the normal case, that means the SignalViewController will call `becomeFirstResponder`
|
||||
// on the vc on top of its navigation stack.
|
||||
[self.rootWindow makeKeyAndVisible];
|
||||
|
||||
// When we hide the block window, try to restore the first
|
||||
// responder of the root window.
|
||||
//
|
||||
// It's important we restore first responder status once the user completes
|
||||
// In some cases, (RegistrationLock Reminder) it just puts the keyboard back where
|
||||
// the user needs it, saving them a tap.
|
||||
// But in the case of an inputAccessoryView, like the ConversationViewController,
|
||||
// failing to restore firstResponder could hide the input toolbar.
|
||||
if (shouldTryToRestoreFirstResponder) {
|
||||
UIViewController *rootFrontmostViewController =
|
||||
[UIApplication.sharedApplication frontmostViewControllerIgnoringAlerts];
|
||||
|
||||
DDLogInfo(@"%@ trying to restore self.rootWindowResponder: %@ (%@ ? %@ == %d)",
|
||||
self.logTag,
|
||||
self.rootWindowResponder,
|
||||
[self.rootFrontmostViewController class],
|
||||
rootFrontmostViewController,
|
||||
self.rootFrontmostViewController == rootFrontmostViewController);
|
||||
if (self.rootFrontmostViewController == rootFrontmostViewController) {
|
||||
[self.rootWindowResponder becomeFirstResponder];
|
||||
} else {
|
||||
[rootFrontmostViewController becomeFirstResponder];
|
||||
}
|
||||
}
|
||||
|
||||
self.rootWindowResponder = nil;
|
||||
self.rootFrontmostViewController = nil;
|
||||
}
|
||||
|
||||
- (void)ensureRootWindowHidden
|
||||
|
@ -389,17 +360,6 @@ const UIWindowLevel UIWindowLevel_ScreenBlocking(void)
|
|||
DDLogInfo(@"%@ hiding root window.", self.logTag);
|
||||
}
|
||||
|
||||
// When we hide the root window, try to capture its first responder and
|
||||
// current vc before it is hidden.
|
||||
if (!self.rootWindow.hidden) {
|
||||
self.rootWindowResponder = [UIResponder currentFirstResponder];
|
||||
self.rootFrontmostViewController = [UIApplication.sharedApplication frontmostViewControllerIgnoringAlerts];
|
||||
DDLogInfo(@"%@ trying to capture self.rootWindowResponder: %@ (%@)",
|
||||
self.logTag,
|
||||
self.rootWindowResponder,
|
||||
[self.rootFrontmostViewController class]);
|
||||
}
|
||||
|
||||
self.rootWindow.hidden = YES;
|
||||
}
|
||||
|
||||
|
@ -450,7 +410,6 @@ const UIWindowLevel UIWindowLevel_ScreenBlocking(void)
|
|||
}
|
||||
|
||||
[self.callViewWindow makeKeyAndVisible];
|
||||
[self.callViewWindow.rootViewController becomeFirstResponder];
|
||||
}
|
||||
|
||||
- (void)ensureCallViewWindowHidden
|
||||
|
@ -473,7 +432,7 @@ const UIWindowLevel UIWindowLevel_ScreenBlocking(void)
|
|||
}
|
||||
|
||||
self.screenBlockingWindow.windowLevel = UIWindowLevel_ScreenBlocking();
|
||||
[self.screenBlockingWindow.rootViewController becomeFirstResponder];
|
||||
[self.screenBlockingWindow makeKeyAndVisible];
|
||||
}
|
||||
|
||||
- (void)ensureScreenBlockWindowHidden
|
||||
|
@ -488,7 +447,6 @@ const UIWindowLevel UIWindowLevel_ScreenBlocking(void)
|
|||
// Instead, manipulate its window level to move it in front of
|
||||
// or behind the root window.
|
||||
self.screenBlockingWindow.windowLevel = UIWindowLevel_Background;
|
||||
[self.screenBlockingWindow resignFirstResponder];
|
||||
}
|
||||
|
||||
#pragma mark - Events
|
||||
|
|
|
@ -33,9 +33,6 @@ import LocalAuthentication
|
|||
private let OWSScreenLock_Key_IsScreenLockEnabled = "OWSScreenLock_Key_IsScreenLockEnabled"
|
||||
private let OWSScreenLock_Key_ScreenLockTimeoutSeconds = "OWSScreenLock_Key_ScreenLockTimeoutSeconds"
|
||||
|
||||
// We temporarily resign any first responder while the Screen Lock is presented.
|
||||
weak var firstResponderBeforeLockscreen: UIResponder?
|
||||
|
||||
// MARK - Singleton class
|
||||
|
||||
@objc(sharedManager)
|
||||
|
|
Loading…
Reference in a new issue