From 658c08010d14f671d3c478ee596f80e732221df5 Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Tue, 1 Nov 2022 20:49:07 -0400 Subject: [PATCH] Re-add undo scroll-to-top to timelines/profiles --- Tusker.xcodeproj/project.pbxproj | 26 ++++++++-- Tusker/AppDelegate.swift | 27 +++++++++- Tusker/Extensions/UIScrollView+Top.swift | 45 +++++++++++++++++ .../{ => Scenes}/AuxiliarySceneDelegate.swift | 0 .../{ => Scenes}/ComposeSceneDelegate.swift | 0 Tusker/{ => Scenes}/MainSceneDelegate.swift | 2 +- Tusker/Scenes/TuskerSceneDelegate.swift | 30 +++++++++++ ...ountSwitchingContainerViewController.swift | 6 +++ .../Main/MainSplitViewController.swift | 16 ++++++ .../Main/MainTabBarViewController.swift | 10 ++++ .../Main/TuskerRootViewController.swift | 2 +- .../ProfileStatusesViewController.swift | 13 +++++ .../Profile/ProfileViewController.swift | 12 +++++ .../Timeline/TimelineViewController.swift | 15 +++++- .../EnhancedNavigationViewController.swift | 9 ++++ .../EnhancedTableViewController.swift | 50 +++---------------- .../SegmentedPageViewController.swift | 9 ++++ .../Utilities/SplitNavigationController.swift | 23 ++++++--- .../StatusBarTappableViewController.swift | 13 +++++ 19 files changed, 251 insertions(+), 57 deletions(-) create mode 100644 Tusker/Extensions/UIScrollView+Top.swift rename Tusker/{ => Scenes}/AuxiliarySceneDelegate.swift (100%) rename Tusker/{ => Scenes}/ComposeSceneDelegate.swift (100%) rename Tusker/{ => Scenes}/MainSceneDelegate.swift (99%) create mode 100644 Tusker/Scenes/TuskerSceneDelegate.swift create mode 100644 Tusker/Screens/Utilities/StatusBarTappableViewController.swift diff --git a/Tusker.xcodeproj/project.pbxproj b/Tusker.xcodeproj/project.pbxproj index d499118e..f1732503 100644 --- a/Tusker.xcodeproj/project.pbxproj +++ b/Tusker.xcodeproj/project.pbxproj @@ -95,6 +95,9 @@ D63661C02381C144004B9E16 /* PreferencesNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63661BF2381C144004B9E16 /* PreferencesNavigationController.swift */; }; D6370B9C24421FF30092A7FF /* Tusker.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = D6370B9A24421FF30092A7FF /* Tusker.xcdatamodeld */; }; D63CC702290EC0B8000E19DE /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = D63CC701290EC0B8000E19DE /* Sentry */; }; + D63CC70C2910AADB000E19DE /* TuskerSceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63CC70B2910AADB000E19DE /* TuskerSceneDelegate.swift */; }; + D63CC7102911F1E4000E19DE /* UIScrollView+Top.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63CC70F2911F1E4000E19DE /* UIScrollView+Top.swift */; }; + D63CC7122911F57C000E19DE /* StatusBarTappableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63CC7112911F57C000E19DE /* StatusBarTappableViewController.swift */; }; D63D8DF42850FE7A008D95E1 /* ViewTags.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63D8DF32850FE7A008D95E1 /* ViewTags.swift */; }; D63F9C6E241D2D85004C03CF /* CompositionAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63F9C6D241D2D85004C03CF /* CompositionAttachment.swift */; }; D6403CC224A6B72D00E81C55 /* VisualEffectImageButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6403CC124A6B72D00E81C55 /* VisualEffectImageButton.swift */; }; @@ -447,6 +450,9 @@ D63661BF2381C144004B9E16 /* PreferencesNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesNavigationController.swift; sourceTree = ""; }; D6370B9B24421FF30092A7FF /* Tusker.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Tusker.xcdatamodel; sourceTree = ""; }; D63CC703290EC472000E19DE /* Dist.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Dist.xcconfig; sourceTree = ""; }; + D63CC70B2910AADB000E19DE /* TuskerSceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TuskerSceneDelegate.swift; sourceTree = ""; }; + D63CC70F2911F1E4000E19DE /* UIScrollView+Top.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIScrollView+Top.swift"; sourceTree = ""; }; + D63CC7112911F57C000E19DE /* StatusBarTappableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusBarTappableViewController.swift; sourceTree = ""; }; D63D8DF32850FE7A008D95E1 /* ViewTags.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewTags.swift; sourceTree = ""; }; D63F9C6D241D2D85004C03CF /* CompositionAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompositionAttachment.swift; sourceTree = ""; }; D6403CC124A6B72D00E81C55 /* VisualEffectImageButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisualEffectImageButton.swift; sourceTree = ""; }; @@ -876,6 +882,17 @@ path = CoreData; sourceTree = ""; }; + D63CC70A2910AAC6000E19DE /* Scenes */ = { + isa = PBXGroup; + children = ( + D68C2AE225869BAB00548EFF /* AuxiliarySceneDelegate.swift */, + D69693F925859A8000F4E116 /* ComposeSceneDelegate.swift */, + D6AC956623C4347E008C9946 /* MainSceneDelegate.swift */, + D63CC70B2910AADB000E19DE /* TuskerSceneDelegate.swift */, + ); + path = Scenes; + sourceTree = ""; + }; D641C780213DD7C4004B4513 /* Screens */ = { isa = PBXGroup; children = ( @@ -1140,6 +1157,7 @@ D69693F32585941A00F4E116 /* UIWindowSceneDelegate+Close.swift */, D62E9984279CA23900C26176 /* URLSession+Development.swift */, D6ADB6ED28EA74E8009924AB /* UIView+Configure.swift */, + D63CC70F2911F1E4000E19DE /* UIScrollView+Top.swift */, ); path = Extensions; sourceTree = ""; @@ -1317,6 +1335,7 @@ D6E0DC8D216EDF1E00369478 /* Previewing.swift */, D6B81F3B2560365300F6E31D /* RefreshableViewController.swift */, D68FEC4E232C5BC300C84F23 /* SegmentedPageViewController.swift */, + D63CC7112911F57C000E19DE /* StatusBarTappableViewController.swift */, D6A6C10425B6138A00298D0F /* StatusTablePrefetching.swift */, D6412B0224AFF6A600F5412E /* TabBarScrollableViewController.swift */, D6B22A0E2560D52D004D82EF /* TabbedPageViewController.swift */, @@ -1372,14 +1391,11 @@ D6D4DDDB212518A200E1C4BB /* Info.plist */, D6D4DDCF212518A000E1C4BB /* AppDelegate.swift */, D6CA6A91249FAD8900AD45C1 /* AudioSessionHelper.swift */, - D68C2AE225869BAB00548EFF /* AuxiliarySceneDelegate.swift */, - D69693F925859A8000F4E116 /* ComposeSceneDelegate.swift */, D6E4269C2532A3E100C02E1C /* FuzzyMatcher.swift */, D6B30E08254BAF63009CAEE5 /* ImageGrayscalifier.swift */, D60E2F2B24423EAD005F8713 /* LazilyDecoding.swift */, D64D0AAC2128D88B005A6F37 /* LocalData.swift */, D61DC84528F498F200B82C6E /* Logging.swift */, - D6AC956623C4347E008C9946 /* MainSceneDelegate.swift */, D6B81F432560390300F6E31D /* MenuController.swift */, D64D8CA82463B494006B0BAA /* MultiThreadDictionary.swift */, D6945C2E23AC47C3005C403C /* SavedDataManager.swift */, @@ -1397,6 +1413,7 @@ D6E57FA525C26FAB00341037 /* Localizable.stringsdict */, D61959D2241E846D00A37B8E /* Models */, D663626021360A9600C9CBA2 /* Preferences */, + D63CC70A2910AAC6000E19DE /* Scenes */, D641C780213DD7C4004B4513 /* Screens */, D62D241E217AA46B005076CC /* Shortcuts */, D67B506B250B28FF00FAECFB /* Vendor */, @@ -1782,6 +1799,7 @@ 0450531F22B0097E00100BA2 /* Timline+UI.swift in Sources */, D667E5F52135BCD50057A976 /* ConversationTableViewController.swift in Sources */, D6C7D27D22B6EBF800071952 /* AttachmentsContainerView.swift in Sources */, + D63CC7122911F57C000E19DE /* StatusBarTappableViewController.swift in Sources */, D620483823D38190008A63EF /* StatusContentTextView.swift in Sources */, D6D3FDE224F46A8D00FF50A5 /* ComposeUIState.swift in Sources */, D6B22A0F2560D52D004D82EF /* TabbedPageViewController.swift in Sources */, @@ -1812,6 +1830,7 @@ D62D2422217AA7E1005076CC /* UserActivityManager.swift in Sources */, D6CA6A92249FAD8900AD45C1 /* AudioSessionHelper.swift in Sources */, D60D2B8223844C71001B87A3 /* BaseStatusTableViewCell.swift in Sources */, + D63CC70C2910AADB000E19DE /* TuskerSceneDelegate.swift in Sources */, D6B9366F2828452F00237D0E /* SavedHashtag.swift in Sources */, D6B9366D2828445000237D0E /* SavedInstance.swift in Sources */, D60E2F272442372B005F8713 /* StatusMO.swift in Sources */, @@ -1875,6 +1894,7 @@ D6EE63FB2551F7F60065485C /* StatusCollapseButton.swift in Sources */, D6B936712829F72900237D0E /* NSManagedObjectContext+Helpers.swift in Sources */, D627944A23A6AD6100D38C68 /* BookmarksTableViewController.swift in Sources */, + D63CC7102911F1E4000E19DE /* UIScrollView+Top.swift in Sources */, D64BC19223C271D9000D0238 /* MastodonActivity.swift in Sources */, D6945C3A23AC75E2005C403C /* FindInstanceViewController.swift in Sources */, D68E6F59253C9969001A1B4C /* MultiSourceEmojiLabel.swift in Sources */, diff --git a/Tusker/AppDelegate.swift b/Tusker/AppDelegate.swift index 4c6bc69d..f6db7068 100644 --- a/Tusker/AppDelegate.swift +++ b/Tusker/AppDelegate.swift @@ -18,8 +18,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - configureSentry() + swizzleStatusBar() AppShortcutItem.createItems(for: application) @@ -129,5 +129,28 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } UIApplication.shared.requestSceneSessionDestruction(scene.session, options: nil) } + + private func swizzleStatusBar() { + let selector = Selector(("handleTapAction:")) + var originalIMP: IMP? + let imp = imp_implementationWithBlock({ (self: UIStatusBarManager, sender: AnyObject) in + let original = unsafeBitCast(originalIMP!, to: (@convention(c) (UIStatusBarManager, Selector, AnyObject) -> Void).self) + guard let windowScene = self.perform(Selector(("windowScene"))).takeUnretainedValue() as? UIWindowScene, + let xPosition = sender.value(forKey: "xPosition") as? CGFloat, + let delegate = windowScene.delegate as? TuskerSceneDelegate else { + original(self, selector, sender) + return + } + switch delegate.handleStatusBarTapped(xPosition: xPosition) { + case .stop: + return + case .continue: + original(self, selector, sender) + } + } as @convention(block) (UIStatusBarManager, AnyObject) -> Void) + originalIMP = class_replaceMethod(UIStatusBarManager.self, selector, imp, "v@:@") + if originalIMP == nil { + Logging.general.error("Unable to swizzle status bar manager") + } + } } - diff --git a/Tusker/Extensions/UIScrollView+Top.swift b/Tusker/Extensions/UIScrollView+Top.swift new file mode 100644 index 00000000..6b5d9304 --- /dev/null +++ b/Tusker/Extensions/UIScrollView+Top.swift @@ -0,0 +1,45 @@ +// +// UIScrollView+Top.swift +// Tusker +// +// Created by Shadowfacts on 11/1/22. +// Copyright © 2022 Shadowfacts. All rights reserved. +// + +import UIKit + +private var prevScrollOffsetBeforeScrollToTopKey: Void = () + +extension UIScrollView { + private var prevScrollOffsetBeforeScrollToTop: CGFloat? { + get { + if let v = (objc_getAssociatedObject(self, &prevScrollOffsetBeforeScrollToTopKey) as? NSNumber)?.doubleValue { + return CGFloat(v) + } else { + return nil + } + } + set { + if let newValue { + objc_setAssociatedObject(self, &prevScrollOffsetBeforeScrollToTopKey, NSNumber(value: newValue), .OBJC_ASSOCIATION_COPY_NONATOMIC) + } else { + objc_setAssociatedObject(self, &prevScrollOffsetBeforeScrollToTopKey, nil, .OBJC_ASSOCIATION_COPY_NONATOMIC) + } + } + } + + func scrollToTop() { + let top = -adjustedContentInset.top + // +5 to add a little bit of wiggle room + let isScrolledToTop = contentOffset.y < top + 5 + if isScrolledToTop { + if let prevScrollOffsetBeforeScrollToTop { + self.prevScrollOffsetBeforeScrollToTop = nil + setContentOffset(CGPoint(x: 0, y: prevScrollOffsetBeforeScrollToTop), animated: true) + } + } else { + prevScrollOffsetBeforeScrollToTop = contentOffset.y + setContentOffset(CGPoint(x: 0, y: top), animated: true) + } + } +} diff --git a/Tusker/AuxiliarySceneDelegate.swift b/Tusker/Scenes/AuxiliarySceneDelegate.swift similarity index 100% rename from Tusker/AuxiliarySceneDelegate.swift rename to Tusker/Scenes/AuxiliarySceneDelegate.swift diff --git a/Tusker/ComposeSceneDelegate.swift b/Tusker/Scenes/ComposeSceneDelegate.swift similarity index 100% rename from Tusker/ComposeSceneDelegate.swift rename to Tusker/Scenes/ComposeSceneDelegate.swift diff --git a/Tusker/MainSceneDelegate.swift b/Tusker/Scenes/MainSceneDelegate.swift similarity index 99% rename from Tusker/MainSceneDelegate.swift rename to Tusker/Scenes/MainSceneDelegate.swift index 5584eb80..0198d1ab 100644 --- a/Tusker/MainSceneDelegate.swift +++ b/Tusker/Scenes/MainSceneDelegate.swift @@ -11,7 +11,7 @@ import Pachyderm import MessageUI import CoreData -class MainSceneDelegate: UIResponder, UIWindowSceneDelegate { +class MainSceneDelegate: UIResponder, UIWindowSceneDelegate, TuskerSceneDelegate { var window: UIWindow? diff --git a/Tusker/Scenes/TuskerSceneDelegate.swift b/Tusker/Scenes/TuskerSceneDelegate.swift new file mode 100644 index 00000000..e12e13fb --- /dev/null +++ b/Tusker/Scenes/TuskerSceneDelegate.swift @@ -0,0 +1,30 @@ +// +// TuskerSceneDelegate.swift +// Tusker +// +// Created by Shadowfacts on 10/31/22. +// Copyright © 2022 Shadowfacts. All rights reserved. +// + +import UIKit + +protocol TuskerSceneDelegate: UISceneDelegate { + var rootViewController: TuskerRootViewController? { get } + + func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult +} + +enum StatusBarTapActionResult { + case `continue` + case stop +} + +extension TuskerSceneDelegate { + func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult { + if let rootViewController { + let converted = rootViewController.view.convert(CGPoint(x: xPosition, y: 0), from: nil) + return rootViewController.handleStatusBarTapped(xPosition: converted.x) + } + return .continue + } +} diff --git a/Tusker/Screens/Main/AccountSwitchingContainerViewController.swift b/Tusker/Screens/Main/AccountSwitchingContainerViewController.swift index 58b93249..7ad6ea50 100644 --- a/Tusker/Screens/Main/AccountSwitchingContainerViewController.swift +++ b/Tusker/Screens/Main/AccountSwitchingContainerViewController.swift @@ -111,6 +111,12 @@ extension AccountSwitchingContainerViewController: TuskerRootViewController { loadViewIfNeeded() root.presentPreferences(completion: completion) } + + func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult { + loadViewIfNeeded() + // TODO: check if fast account switcher is being presented? + return root.handleStatusBarTapped(xPosition: xPosition) + } } extension AccountSwitchingContainerViewController: BackgroundableViewController { diff --git a/Tusker/Screens/Main/MainSplitViewController.swift b/Tusker/Screens/Main/MainSplitViewController.swift index 1fc9e742..09545ddb 100644 --- a/Tusker/Screens/Main/MainSplitViewController.swift +++ b/Tusker/Screens/Main/MainSplitViewController.swift @@ -456,6 +456,22 @@ extension MainSplitViewController: TuskerRootViewController { func presentPreferences(completion: (() -> Void)?) { present(PreferencesNavigationController(mastodonController: mastodonController), animated: true, completion: completion) } + + func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult { + guard presentedViewController == nil else { + return .stop + } + if traitCollection.horizontalSizeClass == .compact { + return tabBarViewController.handleStatusBarTapped(xPosition: xPosition) + } else { + let pointInSecondary = secondaryNavController.view.convert(CGPoint(x: xPosition, y: 0), from: view) + if secondaryNavController.view.bounds.contains(pointInSecondary) { + return secondaryNavController.handleStatusBarTapped(xPosition: pointInSecondary.x) + } else { + return .continue + } + } + } } extension MainSplitViewController: BackgroundableViewController { diff --git a/Tusker/Screens/Main/MainTabBarViewController.swift b/Tusker/Screens/Main/MainTabBarViewController.swift index 02a75f9f..a508706a 100644 --- a/Tusker/Screens/Main/MainTabBarViewController.swift +++ b/Tusker/Screens/Main/MainTabBarViewController.swift @@ -285,6 +285,16 @@ extension MainTabBarViewController: TuskerRootViewController { func presentPreferences(completion: (() -> Void)?) { present(PreferencesNavigationController(mastodonController: mastodonController), animated: true, completion: completion) } + + func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult { + guard presentedViewController == nil else { + return .stop + } + guard let vc = viewController(for: selectedTab) as? StatusBarTappableViewController else { + return .continue + } + return vc.handleStatusBarTapped(xPosition: xPosition) + } } extension MainTabBarViewController: BackgroundableViewController { diff --git a/Tusker/Screens/Main/TuskerRootViewController.swift b/Tusker/Screens/Main/TuskerRootViewController.swift index d5f04bed..e0fd0a4a 100644 --- a/Tusker/Screens/Main/TuskerRootViewController.swift +++ b/Tusker/Screens/Main/TuskerRootViewController.swift @@ -8,7 +8,7 @@ import UIKit -protocol TuskerRootViewController: UIViewController { +protocol TuskerRootViewController: UIViewController, StatusBarTappableViewController { func presentCompose() func select(tab: MainTabBarViewController.Tab) func getTabController(tab: MainTabBarViewController.Tab) -> UIViewController? diff --git a/Tusker/Screens/Profile/ProfileStatusesViewController.swift b/Tusker/Screens/Profile/ProfileStatusesViewController.swift index 5603d386..9ddc9997 100644 --- a/Tusker/Screens/Profile/ProfileStatusesViewController.swift +++ b/Tusker/Screens/Profile/ProfileStatusesViewController.swift @@ -470,3 +470,16 @@ extension ProfileStatusesViewController: StatusCollectionViewCellDelegate { } } } + +extension ProfileStatusesViewController: TabBarScrollableViewController { + func tabBarScrollToTop() { + collectionView.scrollToTop() + } +} + +extension ProfileStatusesViewController: StatusBarTappableViewController { + func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult { + collectionView.scrollToTop() + return .stop + } +} diff --git a/Tusker/Screens/Profile/ProfileViewController.swift b/Tusker/Screens/Profile/ProfileViewController.swift index 2fdf2f15..52086c69 100644 --- a/Tusker/Screens/Profile/ProfileViewController.swift +++ b/Tusker/Screens/Profile/ProfileViewController.swift @@ -307,3 +307,15 @@ extension ProfileViewController: TabbedPageViewController { selectPage(at: currentIndex - 1, animated: true) } } + +extension ProfileViewController: TabBarScrollableViewController { + func tabBarScrollToTop() { + currentViewController.tabBarScrollToTop() + } +} + +extension ProfileViewController: StatusBarTappableViewController { + func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult { + return currentViewController.handleStatusBarTapped(xPosition: xPosition) + } +} diff --git a/Tusker/Screens/Timeline/TimelineViewController.swift b/Tusker/Screens/Timeline/TimelineViewController.swift index f986e394..0272a779 100644 --- a/Tusker/Screens/Timeline/TimelineViewController.swift +++ b/Tusker/Screens/Timeline/TimelineViewController.swift @@ -10,8 +10,6 @@ import UIKit import Pachyderm import Combine -// TODO: gonna need a thing to replicate all of EnhancedTableViewController - class TimelineViewController: UIViewController, TimelineLikeCollectionViewController, RefreshableViewController { let timeline: Timeline weak var mastodonController: MastodonController! @@ -413,3 +411,16 @@ extension TimelineViewController: StatusCollectionViewCellDelegate { } } } + +extension TimelineViewController: TabBarScrollableViewController { + func tabBarScrollToTop() { + collectionView.scrollToTop() + } +} + +extension TimelineViewController: StatusBarTappableViewController { + func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult { + collectionView.scrollToTop() + return .stop + } +} diff --git a/Tusker/Screens/Utilities/EnhancedNavigationViewController.swift b/Tusker/Screens/Utilities/EnhancedNavigationViewController.swift index f92b5cf4..4de68a3f 100644 --- a/Tusker/Screens/Utilities/EnhancedNavigationViewController.swift +++ b/Tusker/Screens/Utilities/EnhancedNavigationViewController.swift @@ -248,3 +248,12 @@ extension EnhancedNavigationViewController: BackgroundableViewController { } } } + +extension EnhancedNavigationViewController: StatusBarTappableViewController { + func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult { + if let topVC = topViewController as? StatusBarTappableViewController { + return topVC.handleStatusBarTapped(xPosition: xPosition) + } + return .continue + } +} diff --git a/Tusker/Screens/Utilities/EnhancedTableViewController.swift b/Tusker/Screens/Utilities/EnhancedTableViewController.swift index a7ba51fe..6ea5e358 100644 --- a/Tusker/Screens/Utilities/EnhancedTableViewController.swift +++ b/Tusker/Screens/Utilities/EnhancedTableViewController.swift @@ -11,11 +11,6 @@ import SafariServices class EnhancedTableViewController: UITableViewController { - private var prevScrollToTopOffset: CGPoint? = nil - private(set) var isCurrentlyScrollingToTop = false - private var prevScrollViewContentOffset: CGPoint? - private(set) var scrollViewDirection: CGFloat = 0 - var dragEnabled = false override func viewDidLoad() { @@ -26,38 +21,6 @@ class EnhancedTableViewController: UITableViewController { } } - // MARK: Scroll View Delegate - - override func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool { - if let offset = prevScrollToTopOffset { - tableView.setContentOffset(offset, animated: true) - prevScrollToTopOffset = nil - return false - } else { - prevScrollToTopOffset = tableView.contentOffset - isCurrentlyScrollingToTop = true - return true - } - } - - override func scrollViewDidScrollToTop(_ scrollView: UIScrollView) { - isCurrentlyScrollingToTop = false - // add one so it's not technically scrolled all the way to the top, - // otherwise there's no way of detecting a status bar press to scroll back down - tableView.contentOffset.y -= 0.5 - } - - override func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { - prevScrollToTopOffset = nil - } - - override func scrollViewDidScroll(_ scrollView: UIScrollView) { - if let prev = prevScrollViewContentOffset { - scrollViewDirection = scrollView.contentOffset.y - prev.y - } - prevScrollViewContentOffset = scrollView.contentOffset - } - // MARK: Table View Delegate override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { @@ -117,10 +80,13 @@ extension EnhancedTableViewController: UITableViewDragDelegate { extension EnhancedTableViewController: TabBarScrollableViewController { func tabBarScrollToTop() { - if scrollViewShouldScrollToTop(tableView) { - let topOffset = CGPoint(x: 0, y: -tableView.adjustedContentInset.top) - tableView.setContentOffset(topOffset, animated: true) - scrollViewDidScrollToTop(tableView) - } + tableView.scrollToTop() + } +} + +extension EnhancedTableViewController: StatusBarTappableViewController { + func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult { + tableView.scrollToTop() + return .stop } } diff --git a/Tusker/Screens/Utilities/SegmentedPageViewController.swift b/Tusker/Screens/Utilities/SegmentedPageViewController.swift index b5dd159a..0e19512e 100644 --- a/Tusker/Screens/Utilities/SegmentedPageViewController.swift +++ b/Tusker/Screens/Utilities/SegmentedPageViewController.swift @@ -105,3 +105,12 @@ extension SegmentedPageViewController: BackgroundableViewController { } } } + +extension SegmentedPageViewController: StatusBarTappableViewController { + func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult { + if let current = pageControllers[currentIndex] as? StatusBarTappableViewController { + return current.handleStatusBarTapped(xPosition: xPosition) + } + return .continue + } +} diff --git a/Tusker/Screens/Utilities/SplitNavigationController.swift b/Tusker/Screens/Utilities/SplitNavigationController.swift index 8e7e9181..adaa2e99 100644 --- a/Tusker/Screens/Utilities/SplitNavigationController.swift +++ b/Tusker/Screens/Utilities/SplitNavigationController.swift @@ -135,12 +135,6 @@ class SplitNavigationController: UIViewController { updateSecondaryNavVisibility() } - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - - - } - override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() @@ -245,7 +239,24 @@ class SplitNavigationController: UIViewController { self.updateSecondaryNavVisibility() } } +} +extension SplitNavigationController: StatusBarTappableViewController { + func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult { + let vcs = viewControllers + if !canShowSecondaryNav || vcs.count < 2 { + return (vcs.first! as? StatusBarTappableViewController)?.handleStatusBarTapped(xPosition: xPosition) ?? .continue + } else { + let positionInRoot = rootNav.view.convert(CGPoint(x: xPosition, y: 0), from: view) + let positionInSecondary = secondaryNav.view.convert(CGPoint(x: xPosition, y: 0), from: view) + if rootNav.view.bounds.contains(positionInRoot) { + return (rootNav.topViewController as? StatusBarTappableViewController)?.handleStatusBarTapped(xPosition: positionInRoot.x) ?? .continue + } else if secondaryNav.view.bounds.contains(positionInSecondary) { + return (secondaryNav.topViewController as? StatusBarTappableViewController)?.handleStatusBarTapped(xPosition: positionInRoot.x) ?? .continue + } + } + return .continue + } } private class SplitRootNavigationController: UINavigationController { diff --git a/Tusker/Screens/Utilities/StatusBarTappableViewController.swift b/Tusker/Screens/Utilities/StatusBarTappableViewController.swift new file mode 100644 index 00000000..aef7ceb5 --- /dev/null +++ b/Tusker/Screens/Utilities/StatusBarTappableViewController.swift @@ -0,0 +1,13 @@ +// +// StatusBarTappableViewController.swift +// Tusker +// +// Created by Shadowfacts on 11/1/22. +// Copyright © 2022 Shadowfacts. All rights reserved. +// + +import UIKit + +protocol StatusBarTappableViewController: UIViewController { + func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult +}