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.
This commit is contained in:
Brian Sanders 2020-05-15 17:56:14 -04:00
parent 62d04e8881
commit b058f27064
4 changed files with 42 additions and 41 deletions

View File

@ -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 {

View File

@ -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 {

View File

@ -16,19 +16,16 @@
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="h1Q-FS-jlg" customClass="ArticleSearchBar" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="0.0" y="759" width="414" height="54"/>
<view hidden="YES" contentMode="scaleToFill" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="h1Q-FS-jlg" customClass="ArticleSearchBar" customModule="NetNewsWire" customModuleProvider="target">
<rect key="frame" x="0.0" y="777" width="414" height="36"/>
<color key="backgroundColor" name="barBackgroundColor"/>
<constraints>
<constraint firstAttribute="height" constant="54" id="FQw-KK-lT7"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints>
<constraint firstItem="h1Q-FS-jlg" firstAttribute="leading" secondItem="VUw-jc-0yf" secondAttribute="leading" id="2DV-em-ArG"/>
<constraint firstItem="VUw-jc-0yf" firstAttribute="trailing" secondItem="h1Q-FS-jlg" secondAttribute="trailing" id="5aF-IN-1ff"/>
<constraint firstItem="VUw-jc-0yf" firstAttribute="bottom" secondItem="h1Q-FS-jlg" secondAttribute="bottom" id="Kmv-Hg-0wL"/>
<constraint firstItem="VUw-jc-0yf" firstAttribute="trailing" secondItem="h1Q-FS-jlg" secondAttribute="trailing" id="2Nt-fa-LhC"/>
<constraint firstItem="h1Q-FS-jlg" firstAttribute="leading" secondItem="VUw-jc-0yf" secondAttribute="leading" id="Vgz-hA-Zrp"/>
<constraint firstItem="VUw-jc-0yf" firstAttribute="bottom" secondItem="h1Q-FS-jlg" secondAttribute="bottom" id="XyH-A7-Trj"/>
</constraints>
<viewLayoutGuide key="safeArea" id="VUw-jc-0yf"/>
</view>
@ -103,6 +100,7 @@
<outlet property="prevArticleBarButtonItem" destination="v4j-fq-23N" id="Gny-Oh-cQa"/>
<outlet property="readBarButtonItem" destination="hy0-LS-MzE" id="BzM-x9-tuj"/>
<outlet property="searchBar" destination="h1Q-FS-jlg" id="IQA-Wt-BB8"/>
<outlet property="searchBarBottomConstraint" destination="XyH-A7-Trj" id="5gH-az-8vg"/>
<outlet property="starBarButtonItem" destination="wU4-eH-wC9" id="Z8Q-Lt-dKk"/>
</connections>
</viewController>

View File

@ -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