From b108f284bdf2b82ed2ed0d8f4881603f32ba1770 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Sat, 24 Nov 2018 10:37:47 -0600 Subject: [PATCH] WIP: hide caption keyboard It's tricky because we're hopping from one first responder to another. Specifically, from the CaptionView.textView, which shows the keyboard, to making the AttachmentApprovalViewController first responder, which shows the BottomToolbar message text field, so in short order, we're getting multiple notifications. User hit's "Done" with caption - Point A - CaptionView is positioned at the top of the keyboard - Hide keyboard (frame change details must be calculated by y offset, since willChanage notification doesn't "shrink" the keyboard frame, it just offsets it to be non-visible. - Point B - caption view is positioned at the bottom of the screen, input accessory view not visible - Show Keyboard (not actually showing the *keyboard* here, but rather the VC's input accessory view) - Point C - caption view is positioned atop the input accessory view We want to animated smoothly from A->C, skipping B. But how do we do that robustly? We could track something like "last known input accessory view height" and never present the captionView below that. But I'm worried it won't be very robust since the input accessory view can change height, e.g. text view grows with text content or dynamic text changes. --- .../AttachmentApprovalViewController.swift | 138 +++++++++++++++++- 1 file changed, 131 insertions(+), 7 deletions(-) diff --git a/SignalMessaging/ViewControllers/AttachmentApprovalViewController.swift b/SignalMessaging/ViewControllers/AttachmentApprovalViewController.swift index 4c2e6490f..0975eeb40 100644 --- a/SignalMessaging/ViewControllers/AttachmentApprovalViewController.swift +++ b/SignalMessaging/ViewControllers/AttachmentApprovalViewController.swift @@ -198,12 +198,98 @@ public class AttachmentApprovalViewController: UIPageViewController, UIPageViewC self.setCurrentItem(firstItem, direction: .forward, animated: false) - NotificationCenter.default.addObserver(self, - selector: #selector(keyboardWillChangeFrame(notification:)), - name: .UIKeyboardWillChangeFrame, - object: nil) +// NotificationCenter.default.addObserver(self, +// selector: #selector(keyboardWillChangeFrame(notification:)), +// name: .UIKeyboardWillChangeFrame, +// object: nil) + + observers = [ + NotificationCenter.default.addObserver(forName: .UIKeyboardWillShow, object: nil, queue: nil) { [weak self] notification in + guard let strongSelf = self else { return } + + guard let userInfo = notification.userInfo else { + owsFailDebug("userInfo was unexpectedly nil") + return + } + + guard let keyboardStartFrame = userInfo[UIKeyboardFrameBeginUserInfoKey] as? CGRect else { + owsFailDebug("keyboardEndFrame was unexpectedly nil") + return + } + + guard let keyboardEndFrame = userInfo[UIKeyboardFrameEndUserInfoKey] as? CGRect else { + owsFailDebug("keyboardEndFrame was unexpectedly nil") + return + } + + Logger.debug("UIKeyboardWillShow frame: \(keyboardStartFrame) -> \(keyboardEndFrame)") + strongSelf.keyboardWillShow(notification: notification) + }, + +// NotificationCenter.default.addObserver(forName: .UIKeyboardDidShow, object: nil, queue: nil) { [weak self] notification in +// guard let userInfo = notification.userInfo else { +// owsFailDebug("userInfo was unexpectedly nil") +// return +// } +// +// guard let keyboardStartFrame = userInfo[UIKeyboardFrameBeginUserInfoKey] as? CGRect else { +// owsFailDebug("keyboardEndFrame was unexpectedly nil") +// return +// } +// +// guard let keyboardEndFrame = userInfo[UIKeyboardFrameEndUserInfoKey] as? CGRect else { +// owsFailDebug("keyboardEndFrame was unexpectedly nil") +// return +// } +// +// Logger.debug("UIKeyboardDidShow frame: \(keyboardStartFrame) -> \(keyboardEndFrame)") +// }, + + NotificationCenter.default.addObserver(forName: .UIKeyboardWillHide, object: nil, queue: nil) { [weak self] notification in + guard let strongSelf = self else { return } + + guard let userInfo = notification.userInfo else { + owsFailDebug("userInfo was unexpectedly nil") + return + } + + guard let keyboardStartFrame = userInfo[UIKeyboardFrameBeginUserInfoKey] as? CGRect else { + owsFailDebug("keyboardEndFrame was unexpectedly nil") + return + } + + guard let keyboardEndFrame = userInfo[UIKeyboardFrameEndUserInfoKey] as? CGRect else { + owsFailDebug("keyboardEndFrame was unexpectedly nil") + return + } + + Logger.debug("UIKeyboardWillHide frame: \(keyboardStartFrame) -> \(keyboardEndFrame)") + strongSelf.keyboardWillHide(notification: notification) + } + +// NotificationCenter.default.addObserver(forName: .UIKeyboardDidHide, object: nil, queue: nil) { [weak self] notification in +// guard let userInfo = notification.userInfo else { +// owsFailDebug("userInfo was unexpectedly nil") +// return +// } +// +// guard let keyboardStartFrame = userInfo[UIKeyboardFrameBeginUserInfoKey] as? CGRect else { +// owsFailDebug("keyboardEndFrame was unexpectedly nil") +// return +// } +// +// guard let keyboardEndFrame = userInfo[UIKeyboardFrameEndUserInfoKey] as? CGRect else { +// owsFailDebug("keyboardEndFrame was unexpectedly nil") +// return +// } +// +// Logger.debug("UIKeyboardDidHide frame: \(keyboardStartFrame) -> \(keyboardEndFrame)") +// }, + ] } + var observers: [NSObjectProtocol] = [] + override public func viewWillAppear(_ animated: Bool) { Logger.debug("") super.viewWillAppear(animated) @@ -238,21 +324,59 @@ public class AttachmentApprovalViewController: UIPageViewController, UIPageViewC var lastObservedKeyboardHeight: CGFloat = 0 @objc - func keyboardWillChangeFrame(notification: Notification) { - Logger.debug("") + func keyboardWillShow(notification: Notification) { + guard let userInfo = notification.userInfo else { + owsFailDebug("userInfo was unexpectedly nil") + return + } - // NSDictionary *userInfo = [notification userInfo]; + guard let keyboardStartFrame = userInfo[UIKeyboardFrameBeginUserInfoKey] as? CGRect else { + owsFailDebug("keyboardEndFrame was unexpectedly nil") + return + } + + guard let keyboardEndFrame = userInfo[UIKeyboardFrameEndUserInfoKey] as? CGRect else { + owsFailDebug("keyboardEndFrame was unexpectedly nil") + return + } + + Logger.debug("\(keyboardStartFrame) -> \(keyboardEndFrame)") + + lastObservedKeyboardHeight = keyboardEndFrame.size.height + + viewControllers?.forEach { viewController in + guard let prepViewController = viewController as? AttachmentPrepViewController else { + owsFailDebug("unexpected prepViewController: \(viewController)") + return + } + + prepViewController.updateCaptionViewBottomInset() + } + } + + @objc + func keyboardWillHide(notification: Notification) { guard let userInfo = notification.userInfo else { owsFailDebug("userInfo was unexpectedly nil") return } + guard let keyboardStartFrame = userInfo[UIKeyboardFrameBeginUserInfoKey] as? CGRect else { + owsFailDebug("keyboardEndFrame was unexpectedly nil") + return + } + guard let keyboardEndFrame = userInfo[UIKeyboardFrameEndUserInfoKey] as? CGRect else { owsFailDebug("keyboardEndFrame was unexpectedly nil") return } + Logger.debug("\(keyboardStartFrame) -> \(keyboardEndFrame)") + lastObservedKeyboardHeight = keyboardEndFrame.size.height + if keyboardStartFrame.height == keyboardEndFrame.height { + lastObservedKeyboardHeight -= keyboardEndFrame.maxY - keyboardStartFrame.maxY + } viewControllers?.forEach { viewController in guard let prepViewController = viewController as? AttachmentPrepViewController else {