From b058f270648359873530a780945cf3b64187cd09 Mon Sep 17 00:00:00 2001 From: Brian Sanders Date: Fri, 15 May 2020 17:56:14 -0400 Subject: [PATCH] Fixes bugs in article search Placement of the article search bar is now always done with constraints. Previously, I'd used inputAccessoryView when the keyboard appeared. That approach, although ostensibly permitted, causes a hierarchy inconsistency error when the device orientation changes. --- iOS/Article/ArticleSearchBar.swift | 10 +++-- iOS/Article/ArticleViewController.swift | 53 +++++++++++++------------ iOS/Base.lproj/Main.storyboard | 14 +++---- iOS/Resources/main_ios.js | 6 +-- 4 files changed, 42 insertions(+), 41 deletions(-) diff --git a/iOS/Article/ArticleSearchBar.swift b/iOS/Article/ArticleSearchBar.swift index 9bbfac79f..3169b1e11 100644 --- a/iOS/Article/ArticleSearchBar.swift +++ b/iOS/Article/ArticleSearchBar.swift @@ -73,13 +73,15 @@ import UIKit } @discardableResult override func becomeFirstResponder() -> Bool { - super.becomeFirstResponder() - return searchField.becomeFirstResponder() + searchField.becomeFirstResponder() } @discardableResult override func resignFirstResponder() -> Bool { - super.resignFirstResponder() - return searchField.resignFirstResponder() + searchField.resignFirstResponder() + } + + override var isFirstResponder: Bool { + searchField.isFirstResponder } deinit { diff --git a/iOS/Article/ArticleViewController.swift b/iOS/Article/ArticleViewController.swift index 30d5ac813..8cceea97c 100644 --- a/iOS/Article/ArticleViewController.swift +++ b/iOS/Article/ArticleViewController.swift @@ -27,6 +27,7 @@ class ArticleViewController: UIViewController { @IBOutlet private weak var actionBarButtonItem: UIBarButtonItem! @IBOutlet private var searchBar: ArticleSearchBar! + @IBOutlet private var searchBarBottomConstraint: NSLayoutConstraint! private var defaultControls: [UIBarButtonItem]? private var pageViewController: UIPageViewController! @@ -70,6 +71,10 @@ class ArticleViewController: UIViewController { private let keyboardManager = KeyboardManager(type: .detail) override var keyCommands: [UIKeyCommand]? { + if searchBar.isFirstResponder { + return nil + } + return keyboardManager.keyCommands } @@ -132,14 +137,11 @@ class ArticleViewController: UIViewController { } // Search bar - makeSearchBarConstraints() + searchBar.translatesAutoresizingMaskIntoConstraints = false NotificationCenter.default.addObserver(self, selector: #selector(beginFind(_:)), name: .FindInArticle, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(endFind(_:)), name: .EndFindInArticle, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIWindow.keyboardWillHideNotification, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(keyboardDidChangeFrame(_:)), name: UIWindow.keyboardDidChangeFrameNotification, object: nil) - -// searchBar.translatesAutoresizingMaskIntoConstraints = false -// searchBar.delegate = self + NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChangeFrame(_:)), name: UIWindow.keyboardWillChangeFrameNotification, object: nil) + searchBar.delegate = self view.bringSubviewToFront(searchBar) updateUI() @@ -151,7 +153,9 @@ class ArticleViewController: UIViewController { } override func viewWillDisappear(_ animated: Bool) { - searchBar.inputAccessoryView = nil + if searchBar != nil && !searchBar.isHidden { + endFind() + } } override func viewSafeAreaInsetsDidChange() { @@ -335,24 +339,14 @@ extension ArticleViewController: SearchBarDelegate { extension ArticleViewController { - private func makeSearchBarConstraints() { - NSLayoutConstraint.activate([ - searchBar.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), - searchBar.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor), - searchBar.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), - ]) - } - @objc func beginFind(_ _: Any? = nil) { searchBar.isHidden = false navigationController?.setToolbarHidden(true, animated: true) currentWebViewController?.additionalSafeAreaInsets.bottom = searchBar.frame.height - searchBar.delegate = self - searchBar.inputAccessoryView = searchBar searchBar.becomeFirstResponder() } - @objc func endFind(_ notification: Notification) { + @objc func endFind(_ _: Any? = nil) { searchBar.resignFirstResponder() searchBar.isHidden = true navigationController?.setToolbarHidden(false, animated: true) @@ -360,18 +354,25 @@ extension ArticleViewController { currentWebViewController?.endSearch() } - @objc func keyboardWillHide(_ _: Notification) { - view.addSubview(searchBar) - makeSearchBarConstraints() - } - - @objc func keyboardDidChangeFrame(_ notification: Notification) { - if let frame = notification.userInfo?[UIWindow.keyboardFrameEndUserInfoKey] as? CGRect { - currentWebViewController?.additionalSafeAreaInsets.bottom = frame.height + @objc func keyboardWillChangeFrame(_ notification: Notification) { + if !searchBar.isHidden, + let duration = notification.userInfo?[UIWindow.keyboardAnimationDurationUserInfoKey] as? Double, + let curveRaw = notification.userInfo?[UIWindow.keyboardAnimationCurveUserInfoKey] as? UInt, + let frame = notification.userInfo?[UIWindow.keyboardFrameEndUserInfoKey] as? CGRect { + + let curve = UIView.AnimationOptions(rawValue: curveRaw) + let newHeight = view.safeAreaLayoutGuide.layoutFrame.maxY - frame.minY + currentWebViewController?.additionalSafeAreaInsets.bottom = newHeight + searchBar.frame.height + 10 + self.searchBarBottomConstraint.constant = newHeight + UIView.animate(withDuration: duration, delay: 0, options: curve, animations: { + self.view.layoutIfNeeded() + }) } } + } + // MARK: WebViewControllerDelegate extension ArticleViewController: WebViewControllerDelegate { diff --git a/iOS/Base.lproj/Main.storyboard b/iOS/Base.lproj/Main.storyboard index 3a2feb18c..dc69376e0 100644 --- a/iOS/Base.lproj/Main.storyboard +++ b/iOS/Base.lproj/Main.storyboard @@ -16,19 +16,16 @@ - - - - + + + @@ -103,6 +100,7 @@ + diff --git a/iOS/Resources/main_ios.js b/iOS/Resources/main_ios.js index 6f02ba28f..2de36d94b 100644 --- a/iOS/Resources/main_ios.js +++ b/iOS/Resources/main_ios.js @@ -349,15 +349,15 @@ function scrollParent(node) { elt = elt.parentElement; } } - -function scrollToRect({top, height}, node, pad=0) { + +function scrollToRect({top, height}, node, pad=20, padBottom=60) { const scrollToTop = top - pad; let scrollBy = scrollToTop; if (scrollToTop >= 0) { const visible = window.visualViewport; - const scrollToBottom = top + height + pad - visible.height; + const scrollToBottom = top + height + padBottom - visible.height; // The top of the rect is already in the viewport if (scrollToBottom <= 0 || scrollToTop === 0) // Don't need to scroll up--or can't