mirror of
https://github.com/Ranchero-Software/NetNewsWire
synced 2025-08-12 06:26:36 +00:00
Articles
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:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user