Remove toolbar autohiding and add optional fullscreen mode.

This commit is contained in:
Maurice Parker
2019-11-18 19:12:24 -06:00
parent ae254c0bd6
commit fc235a029e
9 changed files with 206 additions and 42 deletions

View File

@@ -34,6 +34,7 @@ class ArticleViewController: UIViewController {
@IBOutlet private weak var starBarButtonItem: UIBarButtonItem!
@IBOutlet private weak var actionBarButtonItem: UIBarButtonItem!
@IBOutlet private weak var webViewContainer: UIView!
@IBOutlet private weak var showNavigationView: UIView!
@IBOutlet private weak var showToolbarView: UIView!
private var articleExtractorButton: ArticleExtractorButton = {
@@ -100,11 +101,13 @@ class ArticleViewController: UIViewController {
NotificationCenter.default.addObserver(self, selector: #selector(unreadCountDidChange(_:)), name: .UnreadCountDidChange, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(statusesDidChange(_:)), name: .StatusesDidChange, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(contentSizeCategoryDidChange(_:)), name: UIContentSizeCategory.didChangeNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(willEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil)
articleExtractorButton.addTarget(self, action: #selector(toggleArticleExtractor(_:)), for: .touchUpInside)
toolbarItems?.insert(UIBarButtonItem(customView: articleExtractorButton), at: 6)
showToolbarView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(showToolBar(_:))))
showNavigationView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(showBars(_:))))
showToolbarView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(showBars(_:))))
ArticleViewControllerWebViewProvider.shared.dequeueWebView() { webView in
@@ -112,7 +115,6 @@ class ArticleViewController: UIViewController {
self.webViewContainer.addChildAndPin(webView)
webView.navigationDelegate = self
webView.uiDelegate = self
webView.scrollView.delegate = self
webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasClicked)
webView.configuration.userContentController.add(WrapperScriptMessageHandler(self), name: MessageName.imageWasShown)
@@ -126,6 +128,11 @@ class ArticleViewController: UIViewController {
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
coordinator.isArticleViewControllerPending = false
}
func updateUI() {
guard let article = currentArticle else {
@@ -208,10 +215,14 @@ class ArticleViewController: UIViewController {
reloadHTML()
}
@objc func willEnterForeground(_ note: Notification) {
showBars()
}
// MARK: Actions
@objc func showToolBar(_ sender: Any) {
showToolbar()
@objc func showBars(_ sender: Any) {
showBars()
}
@IBAction func toggleArticleExtractor(_ sender: Any) {
@@ -295,23 +306,6 @@ class ArticleViewController: UIViewController {
}
extension ArticleViewController: UIScrollViewDelegate {
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
if velocity.y > 0.5 {
hideToolbar()
} else if velocity.y < -1.0 {
showToolbar()
}
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset.y < 20 {
showToolbar()
}
}
}
// MARK: WKNavigationDelegate
extension ArticleViewController: WKNavigationDelegate {
@@ -348,6 +342,14 @@ extension ArticleViewController: WKNavigationDelegate {
}
// MARK: InteractiveNavigationControllerTappable
extension ArticleViewController: InteractiveNavigationControllerTappable {
func didTapNavigationBar() {
hideBars()
}
}
// MARK: WKUIDelegate
extension ArticleViewController: WKUIDelegate {
@@ -443,14 +445,18 @@ private extension ArticleViewController {
}
}
func showToolbar() {
func showBars() {
if traitCollection.userInterfaceIdiom == .phone && coordinator.isRootSplitCollapsed {
coordinator.showStatusBar()
navigationController?.setNavigationBarHidden(false, animated: true)
navigationController?.setToolbarHidden(false, animated: true)
}
}
func hideToolbar() {
func hideBars() {
if traitCollection.userInterfaceIdiom == .phone && coordinator.isRootSplitCollapsed {
coordinator.hideStatusBar()
navigationController?.setNavigationBarHidden(true, animated: true)
navigationController?.setToolbarHidden(true, animated: true)
}
}

View File

@@ -27,16 +27,26 @@
<constraint firstAttribute="height" constant="100" id="xX2-AK-xJX"/>
</constraints>
</view>
<view opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="A7j-8T-DqE">
<rect key="frame" x="0.0" y="32" width="414" height="100"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" constant="100" id="3HX-Dm-bA6"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints>
<constraint firstItem="VUw-jc-0yf" firstAttribute="bottom" secondItem="iEi-hX-TYy" secondAttribute="top" constant="44" id="4fZ-pn-fmB"/>
<constraint firstItem="DNb-lt-KzC" firstAttribute="top" secondItem="VUw-jc-0yf" secondAttribute="top" id="Bfh-RL-m4d"/>
<constraint firstItem="A7j-8T-DqE" firstAttribute="trailing" secondItem="VUw-jc-0yf" secondAttribute="trailing" id="Feu-hj-K01"/>
<constraint firstItem="DNb-lt-KzC" firstAttribute="bottom" secondItem="VUw-jc-0yf" secondAttribute="bottom" id="FfW-6G-Bcp"/>
<constraint firstItem="VUw-jc-0yf" firstAttribute="trailing" secondItem="iEi-hX-TYy" secondAttribute="trailing" id="Ij6-ri-sBN"/>
<constraint firstItem="iEi-hX-TYy" firstAttribute="leading" secondItem="VUw-jc-0yf" secondAttribute="leading" id="Muc-gr-S7o"/>
<constraint firstItem="DNb-lt-KzC" firstAttribute="trailing" secondItem="VUw-jc-0yf" secondAttribute="trailing" id="QJ5-Ne-ndd"/>
<constraint firstItem="A7j-8T-DqE" firstAttribute="bottom" secondItem="VUw-jc-0yf" secondAttribute="top" constant="44" id="b2h-zZ-xwi"/>
<constraint firstItem="DNb-lt-KzC" firstAttribute="leading" secondItem="VUw-jc-0yf" secondAttribute="leading" id="ezE-0p-35X"/>
<constraint firstItem="A7j-8T-DqE" firstAttribute="leading" secondItem="VUw-jc-0yf" secondAttribute="leading" id="wny-M6-akA"/>
</constraints>
<viewLayoutGuide key="safeArea" id="VUw-jc-0yf"/>
</view>
@@ -110,6 +120,7 @@
<outlet property="nextUnreadBarButtonItem" destination="2w5-e9-C2V" id="xJr-5y-p1N"/>
<outlet property="prevArticleBarButtonItem" destination="v4j-fq-23N" id="Gny-Oh-cQa"/>
<outlet property="readBarButtonItem" destination="hy0-LS-MzE" id="BzM-x9-tuj"/>
<outlet property="showNavigationView" destination="A7j-8T-DqE" id="D59-3C-HmS"/>
<outlet property="showToolbarView" destination="iEi-hX-TYy" id="zoa-h3-H8b"/>
<outlet property="starBarButtonItem" destination="wU4-eH-wC9" id="Z8Q-Lt-dKk"/>
<outlet property="webViewContainer" destination="DNb-lt-KzC" id="Fc1-Ae-pWK"/>

View File

@@ -79,6 +79,11 @@ class MasterTimelineViewController: UITableViewController, UndoableCommandRunner
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
coordinator.isTimelineViewControllerPending = false
}
// MARK: Actions
@IBAction func markAllAsRead(_ sender: Any) {

View File

@@ -13,6 +13,14 @@ class RootSplitViewController: UISplitViewController {
var coordinator: SceneCoordinator!
override var prefersStatusBarHidden: Bool {
return coordinator.prefersStatusBarHidden
}
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation {
return .slide
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
if UIApplication.shared.applicationState != .background {
self.coordinator.configureThreePanelMode(for: size)

View File

@@ -65,8 +65,8 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
private var lastSearchScope: SearchScope? = nil
private var isSearching: Bool = false
private var searchArticleIds: Set<String>? = nil
private var isTimelineViewControllerPending = false
private var isArticleViewControllerPending = false
var isTimelineViewControllerPending = false
var isArticleViewControllerPending = false
private(set) var sortDirection = AppDefaults.timelineSortDirection {
didSet {
@@ -83,6 +83,8 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
}
}
var prefersStatusBarHidden = false
var displayUndoAvailableTip: Bool {
get { AppDefaults.displayUndoAvailableTip }
set { AppDefaults.displayUndoAvailableTip = newValue }
@@ -299,7 +301,7 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
rootSplitViewController = RootSplitViewController()
rootSplitViewController.coordinator = self
rootSplitViewController.preferredDisplayMode = .allVisible
rootSplitViewController.viewControllers = [ThemedNavigationController.template()]
rootSplitViewController.viewControllers = [InteractiveNavigationController.template()]
rootSplitViewController.delegate = self
masterNavigationController = (rootSplitViewController.viewControllers.first as! UINavigationController)
@@ -779,6 +781,20 @@ class SceneCoordinator: NSObject, UndoableCommandRunner, UnreadCountProvider {
}
}
func showStatusBar() {
prefersStatusBarHidden = false
UIView.animate(withDuration: 0.15) {
self.rootSplitViewController.setNeedsStatusBarAppearanceUpdate()
}
}
func hideStatusBar() {
prefersStatusBarHidden = true
UIView.animate(withDuration: 0.15) {
self.rootSplitViewController.setNeedsStatusBarAppearanceUpdate()
}
}
func showSettings() {
let settingsNavController = UIStoryboard.settings.instantiateInitialViewController() as! UINavigationController
let settingsViewController = settingsNavController.topViewController as! SettingsViewController
@@ -945,13 +961,18 @@ extension SceneCoordinator: UISplitViewControllerDelegate {
// MARK: UINavigationControllerDelegate
extension SceneCoordinator: UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
if UIApplication.shared.applicationState == .background {
return
}
// Restore any bars hidden by the previous controller
showStatusBar()
navigationController.setNavigationBarHidden(false, animated: true)
navigationController.setToolbarHidden(false, animated: true)
// If we are showing the Feeds and only the feeds start clearing stuff
if viewController === masterFeedViewController && !isThreePanelMode && !isTimelineViewControllerPending {
activityManager.invalidateCurrentActivities()
@@ -959,10 +980,6 @@ extension SceneCoordinator: UINavigationControllerDelegate {
return
}
if viewController is MasterTimelineViewController {
isTimelineViewControllerPending = false
}
// If we are using a phone and navigate away from the detail, clear up the article resources (including activity).
// Don't clear it if we have pushed an ArticleViewController, but don't yet see it on the navigation stack.
// This happens when we are going to the next unread and we need to grab another timeline to continue. The
@@ -975,10 +992,6 @@ extension SceneCoordinator: UINavigationControllerDelegate {
return
}
if viewController is ArticleViewController {
isArticleViewControllerPending = false
}
}
}
@@ -1509,13 +1522,13 @@ private extension SceneCoordinator {
func addNavControllerIfNecessary(_ controller: UIViewController, showButton: Bool) -> UIViewController {
if rootSplitViewController.isCollapsed {
if rootSplitViewController.traitCollection.horizontalSizeClass == .compact {
return controller
} else {
let navController = ThemedNavigationController.template(rootViewController: controller)
let navController = InteractiveNavigationController.template(rootViewController: controller)
navController.isToolbarHidden = false
if showButton {

View File

@@ -0,0 +1,77 @@
//
// InteractiveNavigationController.swift
// NetNewsWire
//
// Created by Maurice Parker on 8/22/19.
// Copyright © 2019 Ranchero Software. All rights reserved.
//
import UIKit
protocol InteractiveNavigationControllerTappable {
func didTapNavigationBar()
}
class InteractiveNavigationController: UINavigationController {
private let poppableDelegate = PoppableGestureRecognizerDelegate()
static func template() -> UINavigationController {
let navController = InteractiveNavigationController()
navController.configure()
return navController
}
static func template(rootViewController: UIViewController) -> UINavigationController {
let navController = InteractiveNavigationController(rootViewController: rootViewController)
navController.configure()
return navController
}
override func viewDidLoad() {
super.viewDidLoad()
poppableDelegate.originalDelegate = interactivePopGestureRecognizer?.delegate
poppableDelegate.navigationController = self
interactivePopGestureRecognizer?.delegate = poppableDelegate
navigationBar.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTapNavigationBar)))
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if traitCollection.userInterfaceStyle != previousTraitCollection?.userInterfaceStyle {
configure()
}
}
@objc func didTapNavigationBar() {
if let tappable = topViewController as? InteractiveNavigationControllerTappable {
tappable.didTapNavigationBar()
}
}
}
// MARK: Private
private extension InteractiveNavigationController {
func configure() {
isToolbarHidden = false
view.backgroundColor = AppAssets.barBackgroundColor
let navigationAppearance = UINavigationBarAppearance()
navigationAppearance.backgroundColor = AppAssets.barBackgroundColor
navigationAppearance.titleTextAttributes = [.foregroundColor: UIColor.label]
navigationAppearance.largeTitleTextAttributes = [.foregroundColor: UIColor.label]
navigationBar.standardAppearance = navigationAppearance
navigationBar.tintColor = AppAssets.primaryAccentColor
let toolbarAppearance = UIToolbarAppearance()
toolbarAppearance.backgroundColor = AppAssets.barBackgroundColor
toolbar.standardAppearance = toolbarAppearance
toolbar.compactAppearance = toolbarAppearance
toolbar.tintColor = AppAssets.primaryAccentColor
}
}

View File

@@ -0,0 +1,40 @@
//
// PoppableGestureRecognizerDelegate.swift
// NetNewsWire-iOS
//
// Created by Maurice Parker on 11/18/19.
// Copyright © 2019 Ranchero Software. All rights reserved.
//
// https://stackoverflow.com/a/38042863
import UIKit
final class PoppableGestureRecognizerDelegate: NSObject, UIGestureRecognizerDelegate {
weak var navigationController: UINavigationController?
weak var originalDelegate: UIGestureRecognizerDelegate?
override func responds(to aSelector: Selector!) -> Bool {
if aSelector == #selector(gestureRecognizer(_:shouldReceive:)) {
return true
} else if let responds = originalDelegate?.responds(to: aSelector) {
return responds
} else {
return false
}
}
override func forwardingTarget(for aSelector: Selector!) -> Any? {
return originalDelegate
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
if let nav = navigationController, nav.isNavigationBarHidden, nav.viewControllers.count > 1 {
return true
} else if let result = originalDelegate?.gestureRecognizer?(gestureRecognizer, shouldReceive: touch) {
return result
} else {
return false
}
}
}

View File

@@ -13,7 +13,7 @@ extension UISplitViewController {
static func template() -> UISplitViewController {
let splitViewController = UISplitViewController()
splitViewController.preferredDisplayMode = .automatic
splitViewController.viewControllers = [ThemedNavigationController.template()]
splitViewController.viewControllers = [InteractiveNavigationController.template()]
return splitViewController
}