This commit focuses on the `ArticleViewController`:

- Adds an "Aa" menu to the article view that allows for theme changes and full screen article toggling on iPhone, and theme changes on iPad.
- Removes separate Settings option for full screen availability on iPhone.
- Removes tap target in nav bar to enable full screen mode.
This commit is contained in:
Stuart Breckenridge
2022-02-10 12:13:06 +08:00
parent 106ce36a21
commit 08618f5f15
7 changed files with 158 additions and 164 deletions

View File

@@ -15,16 +15,17 @@ import SafariServices
class ArticleViewController: UIViewController, MainControllerIdentifiable {
typealias State = (extractedArticle: ExtractedArticle?,
isShowingExtractedArticle: Bool,
articleExtractorButtonState: ArticleExtractorButtonState,
windowScrollY: Int)
isShowingExtractedArticle: Bool,
articleExtractorButtonState: ArticleExtractorButtonState,
windowScrollY: Int)
@IBOutlet private weak var nextUnreadBarButtonItem: UIBarButtonItem!
@IBOutlet private weak var prevArticleBarButtonItem: UIBarButtonItem!
@IBOutlet private weak var nextArticleBarButtonItem: UIBarButtonItem!
@IBOutlet private weak var readBarButtonItem: UIBarButtonItem!
@IBOutlet private weak var starBarButtonItem: UIBarButtonItem!
@IBOutlet private weak var actionBarButtonItem: UIBarButtonItem!
@IBOutlet private weak var appearanceBarButtonItem: UIBarButtonItem!
@IBOutlet private var searchBar: ArticleSearchBar!
@IBOutlet private var searchBarBottomConstraint: NSLayoutConstraint!
@@ -48,7 +49,7 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable {
weak var coordinator: SceneCoordinator!
private let poppableDelegate = PoppableGestureRecognizerDelegate()
var article: Article? {
didSet {
if let controller = currentWebViewController, controller.article != article {
@@ -88,19 +89,12 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable {
override func viewDidLoad() {
super.viewDidLoad()
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)
let fullScreenTapZone = UIView()
NSLayoutConstraint.activate([
fullScreenTapZone.widthAnchor.constraint(equalToConstant: 150),
fullScreenTapZone.heightAnchor.constraint(equalToConstant: 44)
])
fullScreenTapZone.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(didTapNavigationBar)))
navigationItem.titleView = fullScreenTapZone
NotificationCenter.default.addObserver(self, selector: #selector(reloadDueToThemeChange(_:)), name: .CurrentArticleThemeDidChangeNotification, object: nil)
articleExtractorButton.addTarget(self, action: #selector(toggleArticleExtractor(_:)), for: .touchUpInside)
toolbarItems?.insert(UIBarButtonItem(customView: articleExtractorButton), at: 6)
@@ -113,14 +107,14 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable {
pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: [:])
pageViewController.delegate = self
pageViewController.dataSource = self
// This code is to disallow paging if we scroll from the left edge. If this code is removed
// PoppableGestureRecognizerDelegate will allow us to both navigate back and page back at the
// same time. That is really weird when it happens.
let panGestureRecognizer = UIPanGestureRecognizer()
panGestureRecognizer.delegate = self
pageViewController.scrollViewInsidePageControl?.addGestureRecognizer(panGestureRecognizer)
pageViewController.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(pageViewController.view)
addChild(pageViewController!)
@@ -130,7 +124,7 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable {
view.topAnchor.constraint(equalTo: pageViewController.view.topAnchor),
view.bottomAnchor.constraint(equalTo: pageViewController.view.bottomAnchor)
])
let controller: WebViewController
if let state = restoreState {
controller = createWebViewController(article, updateView: false)
@@ -145,7 +139,7 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable {
if let rsp = restoreScrollPosition {
controller.setScrollPosition(isShowingExtractedArticle: rsp.isShowingExtractedArticle, articleWindowScrollY: rsp.articleWindowScrollY)
}
articleExtractorButton.buttonState = controller.articleExtractorButtonState
self.pageViewController.setViewControllers([controller], direction: .forward, animated: false, completion: nil)
@@ -158,6 +152,7 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable {
searchBar.delegate = self
view.bringSubviewToFront(searchBar)
configureAppearanceMenu()
updateUI()
}
@@ -166,10 +161,10 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable {
if AppDefaults.shared.articleFullscreenEnabled {
currentWebViewController?.hideBars()
}
super.viewWillAppear(animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if searchBar != nil && !searchBar.isHidden {
@@ -192,14 +187,16 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable {
readBarButtonItem.isEnabled = false
starBarButtonItem.isEnabled = false
actionBarButtonItem.isEnabled = false
appearanceBarButtonItem.isEnabled = false
return
}
nextUnreadBarButtonItem.isEnabled = coordinator.isAnyUnreadAvailable
prevArticleBarButtonItem.isEnabled = coordinator.isPrevArticleAvailable
nextArticleBarButtonItem.isEnabled = coordinator.isNextArticleAvailable
readBarButtonItem.isEnabled = true
starBarButtonItem.isEnabled = true
appearanceBarButtonItem.isEnabled = true
let permalinkPresent = article.preferredLink != nil
var isFeedProvider = false
@@ -233,6 +230,61 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable {
return currentWebViewController?.webView?.scrollView
}
@objc
func configureAppearanceMenu(_ sender: Any? = nil) {
var themeActions = [UIAction]()
for themeName in ArticleThemesManager.shared.themeNames {
let action = UIAction(title: themeName,
image: nil,
identifier: nil,
discoverabilityTitle: nil,
attributes: [],
state: ArticleThemesManager.shared.currentThemeName == themeName ? .on : .off,
handler: { action in
ArticleThemesManager.shared.currentThemeName = themeName
})
themeActions.append(action)
}
let defaultThemeAction = UIAction(title: NSLocalizedString("Default", comment: "Default"), image: nil, identifier: nil, discoverabilityTitle: nil, attributes: [], state: ArticleThemesManager.shared.currentThemeName == AppDefaults.defaultThemeName ? .on : .off) { _ in
ArticleThemesManager.shared.currentThemeName = AppDefaults.defaultThemeName
}
themeActions.append(defaultThemeAction)
let themeMenu = UIMenu(title: "Theme", image: AppAssets.themeImage, identifier: nil, options: .singleSelection, children: themeActions)
themeMenu.subtitle = NSLocalizedString("Change the look of articles.", comment: "Change theme")
var children: [UIMenuElement] = [themeMenu]
if let currentWebViewController = currentWebViewController {
if currentWebViewController.isFullScreenAvailable {
let fullScreenAction = UIAction(title: NSLocalizedString("Full Screen", comment: "Full Screen"),
image: UIImage(systemName: "arrow.up.backward.and.arrow.down.forward"),
identifier: nil,
discoverabilityTitle: nil,
attributes: [],
state: .off) { [weak self] _ in
self?.currentWebViewController?.hideBars()
}
fullScreenAction.subtitle = NSLocalizedString("Tap the top of the screen to exit Full Screen.", comment: "Exit criteria.")
children.append(fullScreenAction)
}
}
let appearanceMenu = UIMenu(title: NSLocalizedString("Article Appearance", comment: "Appearance"), image: UIImage(systemName: "textformat.size") , identifier: nil, options: .displayInline, children: children)
appearanceBarButtonItem.menu = appearanceMenu
}
@objc
func reloadDueToThemeChange(_ notification: Notification) {
currentWebViewController?.fullReload()
configureAppearanceMenu()
}
// MARK: Notifications
@@ -251,7 +303,7 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable {
updateUI()
}
}
@objc func contentSizeCategoryDidChange(_ note: Notification) {
currentWebViewController?.fullReload()
}
@@ -264,15 +316,11 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable {
}
// MARK: Actions
@objc func didTapNavigationBar() {
currentWebViewController?.hideBars()
}
@objc func showBars(_ sender: Any) {
currentWebViewController?.showBars()
}
@IBAction func toggleArticleExtractor(_ sender: Any) {
currentWebViewController?.toggleArticleExtractor()
}
@@ -300,35 +348,35 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable {
@IBAction func showActivityDialog(_ sender: Any) {
currentWebViewController?.showActivityDialog(popOverBarButtonItem: actionBarButtonItem)
}
@objc func toggleReaderView(_ sender: Any?) {
currentWebViewController?.toggleArticleExtractor()
}
// MARK: Keyboard Shortcuts
@objc func navigateToTimeline(_ sender: Any?) {
coordinator.navigateToTimeline()
}
// MARK: API
func focus() {
currentWebViewController?.focus()
}
func canScrollDown() -> Bool {
return currentWebViewController?.canScrollDown() ?? false
}
func canScrollUp() -> Bool {
return currentWebViewController?.canScrollUp() ?? false
}
func scrollPageDown() {
currentWebViewController?.scrollPageDown()
}
func scrollPageUp() {
currentWebViewController?.scrollPageUp()
}
@@ -336,7 +384,7 @@ class ArticleViewController: UIViewController, MainControllerIdentifiable {
func stopArticleExtractorIfProcessing() {
currentWebViewController?.stopArticleExtractorIfProcessing()
}
func openInAppBrowser() {
currentWebViewController?.openInAppBrowser()
}
@@ -403,9 +451,9 @@ extension ArticleViewController {
@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 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
@@ -438,19 +486,19 @@ extension ArticleViewController: UIPageViewControllerDataSource {
func pageViewController(_ pageViewController: UIPageViewController, viewControllerBefore viewController: UIViewController) -> UIViewController? {
guard let webViewController = viewController as? WebViewController,
let currentArticle = webViewController.article,
let article = coordinator.findPrevArticle(currentArticle) else {
return nil
}
let currentArticle = webViewController.article,
let article = coordinator.findPrevArticle(currentArticle) else {
return nil
}
return createWebViewController(article)
}
func pageViewController(_ pageViewController: UIPageViewController, viewControllerAfter viewController: UIViewController) -> UIViewController? {
guard let webViewController = viewController as? WebViewController,
let currentArticle = webViewController.article,
let article = coordinator.findNextArticle(currentArticle) else {
return nil
}
let currentArticle = webViewController.article,
let article = coordinator.findNextArticle(currentArticle) else {
return nil
}
return createWebViewController(article)
}
@@ -459,7 +507,7 @@ extension ArticleViewController: UIPageViewControllerDataSource {
// MARK: UIPageViewControllerDelegate
extension ArticleViewController: UIPageViewControllerDelegate {
func pageViewController(_ pageViewController: UIPageViewController, didFinishAnimating finished: Bool, previousViewControllers: [UIViewController], transitionCompleted completed: Bool) {
guard finished, completed else { return }
guard let article = currentWebViewController?.article else { return }
@@ -476,17 +524,17 @@ extension ArticleViewController: UIPageViewControllerDelegate {
extension ArticleViewController: UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
let point = gestureRecognizer.location(in: nil)
if point.x > 40 {
return true
}
return false
}
}
}