From 16db9cb005cb6b3fad990f87b4786a5141dd66af Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Fri, 19 Jan 2024 14:16:06 +0000 Subject: [PATCH 01/39] use uicollection view for the navigation bar --- DuckDuckGo/MainView.swift | 30 ++++++++++++-------- DuckDuckGo/MainViewController.swift | 44 +++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 12 deletions(-) diff --git a/DuckDuckGo/MainView.swift b/DuckDuckGo/MainView.swift index 4d5171167e..7c03c01f09 100644 --- a/DuckDuckGo/MainView.swift +++ b/DuckDuckGo/MainView.swift @@ -60,6 +60,7 @@ extension MainViewFactory { createNotificationBarContainer() createStatusBackground() createTabBarContainer() + createOmniBar() createNavigationBarContainer() createProgressView() createToolbar() @@ -70,12 +71,17 @@ extension MainViewFactory { superview.addSubview(coordinator.progress) } - final class NavigationBarContainer: UIView { } - private func createNavigationBarContainer() { + private func createOmniBar() { coordinator.omniBar = OmniBar.loadFromXib() coordinator.omniBar.translatesAutoresizingMaskIntoConstraints = false - coordinator.navigationBarContainer = NavigationBarContainer() - coordinator.navigationBarContainer.addSubview(coordinator.omniBar) + } + + final class NavigationBarContainer: UICollectionView { } + private func createNavigationBarContainer() { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .horizontal + layout.itemSize = CGSize(width: superview.frame.size.width, height: 52) + coordinator.navigationBarContainer = NavigationBarContainer(frame: .zero, collectionViewLayout: layout) superview.addSubview(coordinator.navigationBarContainer) } @@ -189,18 +195,18 @@ extension MainViewFactory { coordinator.constraints.navigationBarContainerTop = navigationBarContainer.constrainView(superview.safeAreaLayoutGuide, by: .top) coordinator.constraints.navigationBarContainerBottom = navigationBarContainer.constrainView(toolbar, by: .bottom, to: .top) - coordinator.constraints.omniBarBottom = omniBar.constrainView(navigationBarContainer, by: .bottom, relatedBy: .greaterThanOrEqual) +// coordinator.constraints.omniBarBottom = omniBar.constrainView(navigationBarContainer, by: .bottom, relatedBy: .greaterThanOrEqual) NSLayoutConstraint.activate([ coordinator.constraints.navigationBarContainerTop, navigationBarContainer.constrainView(superview, by: .centerX), navigationBarContainer.constrainView(superview, by: .width), - navigationBarContainer.constrainAttribute(.height, to: 52, relatedBy: .greaterThanOrEqual), - omniBar.constrainAttribute(.height, to: 52), - omniBar.constrainView(navigationBarContainer, by: .top), - omniBar.constrainView(navigationBarContainer, by: .leading), - omniBar.constrainView(navigationBarContainer, by: .trailing), - coordinator.constraints.omniBarBottom, + navigationBarContainer.constrainAttribute(.height, to: 52), // , relatedBy: .greaterThanOrEqual), +// omniBar.constrainAttribute(.height, to: 52), +// omniBar.constrainView(navigationBarContainer, by: .top), +// omniBar.constrainView(navigationBarContainer, by: .leading), +// omniBar.constrainView(navigationBarContainer, by: .trailing), +// coordinator.constraints.omniBarBottom, ]) } @@ -321,7 +327,7 @@ class MainViewCoordinator { var logo: UIImageView! var logoContainer: UIView! var logoText: UIImageView! - var navigationBarContainer: UIView! + var navigationBarContainer: UICollectionView! var notificationBarContainer: UIView! var omniBar: OmniBar! var progress: ProgressView! diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 8a825a9d73..1bc7be7f50 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -215,6 +215,47 @@ class MainViewController: UIViewController { findInPageBottomLayoutConstraint = bottom findInPageHeightLayoutConstraint = height } + + class OmniBarCell: UICollectionViewCell { + + weak var omniBar: OmniBar? { + didSet { + subviews.forEach { $0.removeFromSuperview() } + if let omniBar { + addSubview(omniBar) + NSLayoutConstraint.activate([ + constrainView(omniBar, by: .leading), + constrainView(omniBar, by: .trailing), + constrainView(omniBar, by: .top), + constrainView(omniBar, by: .bottom), + ]) + } + } + } + + } + + var swipeTabsDataSource: SwipeTabsDataSource! + class SwipeTabsDataSource: NSObject, UICollectionViewDataSource { + + weak var coordinator: MainViewCoordinator! + + init(coordinator: MainViewCoordinator) { + self.coordinator = coordinator + coordinator.navigationBarContainer.register(OmniBarCell.self, forCellWithReuseIdentifier: "omnibar") + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + 1 + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "omnibar", for: indexPath) as! OmniBarCell + cell.omniBar = coordinator.omniBar + return cell + } + + } override func viewDidLoad() { super.viewDidLoad() @@ -226,6 +267,9 @@ class MainViewController: UIViewController { viewCoordinator.toolbarForwardButton.action = #selector(onForwardPressed) viewCoordinator.toolbarFireButton.action = #selector(onFirePressed) + swipeTabsDataSource = SwipeTabsDataSource(coordinator: viewCoordinator) + viewCoordinator.navigationBarContainer.dataSource = swipeTabsDataSource + loadSuggestionTray() loadTabsBarIfNeeded() loadFindInPage() From 234faeac57b88f6274735742f31f6fec06c479f5 Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Fri, 19 Jan 2024 17:38:54 +0000 Subject: [PATCH 02/39] handle swiping and snapping the view --- DuckDuckGo/MainView.swift | 29 ++++- DuckDuckGo/MainViewController.swift | 163 ++++++++++++++++++++-------- 2 files changed, 148 insertions(+), 44 deletions(-) diff --git a/DuckDuckGo/MainView.swift b/DuckDuckGo/MainView.swift index 7c03c01f09..7229676267 100644 --- a/DuckDuckGo/MainView.swift +++ b/DuckDuckGo/MainView.swift @@ -76,12 +76,39 @@ extension MainViewFactory { coordinator.omniBar.translatesAutoresizingMaskIntoConstraints = false } + final class NavigationBarLayout: UICollectionViewFlowLayout { + override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint { + guard let collectionView = self.collectionView else { + return super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity) + } + + let pageWidth = itemSize.width + minimumLineSpacing + let currentPage = collectionView.contentOffset.x / pageWidth + let nextPage = velocity.x.sign == .minus ? floor(currentPage) : ceil(currentPage) + let point = CGPoint(x: nextPage * pageWidth, y: proposedContentOffset.y) + + print("***", #function, currentPage, nextPage, point) + return point + } + + override func prepare() { + super.prepare() + guard let collectionView = self.collectionView else { return } + + itemSize = CGSize(width: collectionView.bounds.width, height: collectionView.bounds.height) + minimumLineSpacing = 0 + minimumInteritemSpacing = 0 + scrollDirection = .horizontal + } + } + final class NavigationBarContainer: UICollectionView { } private func createNavigationBarContainer() { - let layout = UICollectionViewFlowLayout() + let layout = NavigationBarLayout() layout.scrollDirection = .horizontal layout.itemSize = CGSize(width: superview.frame.size.width, height: 52) coordinator.navigationBarContainer = NavigationBarContainer(frame: .zero, collectionViewLayout: layout) + coordinator.navigationBarContainer.decelerationRate = .fast superview.addSubview(coordinator.navigationBarContainer) } diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 1bc7be7f50..144142cb7c 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -34,8 +34,8 @@ import Networking // swiftlint:disable file_length // swiftlint:disable type_body_length class MainViewController: UIViewController { -// swiftlint:enable type_body_length - + // swiftlint:enable type_body_length + override var preferredStatusBarStyle: UIStatusBarStyle { return ThemeManager.shared.currentTheme.statusBarStyle } @@ -45,15 +45,15 @@ class MainViewController: UIViewController { return isIPad ? [.left, .right] : [] } - + weak var findInPageView: FindInPageView! weak var findInPageHeightLayoutConstraint: NSLayoutConstraint! weak var findInPageBottomLayoutConstraint: NSLayoutConstraint! - + weak var notificationView: NotificationView? - + var chromeManager: BrowserChromeManager! - + var allowContentUnderflow = false { didSet { viewCoordinator.constraints.contentContainerTop.constant = allowContentUnderflow ? contentUnderflow : 0 @@ -63,50 +63,50 @@ class MainViewController: UIViewController { var contentUnderflow: CGFloat { return 3 + (allowContentUnderflow ? -viewCoordinator.navigationBarContainer.frame.size.height : 0) } - + lazy var emailManager: EmailManager = { let emailManager = EmailManager() emailManager.aliasPermissionDelegate = self emailManager.requestDelegate = self return emailManager }() - + var homeController: HomeViewController? var tabsBarController: TabsBarViewController? var suggestionTrayController: SuggestionTrayViewController? - + var tabManager: TabManager! let previewsSource = TabPreviewsSource() let appSettings: AppSettings private var launchTabObserver: LaunchTabNotification.Observer? - + #if APP_TRACKING_PROTECTION private let appTrackingProtectionDatabase: CoreDataDatabase #endif - + let bookmarksDatabase: CoreDataDatabase private weak var bookmarksDatabaseCleaner: BookmarkDatabaseCleaner? private var favoritesViewModel: FavoritesListInteracting let syncService: DDGSyncing let syncDataProviders: SyncDataProviders - + @UserDefaultsWrapper(key: .syncDidShowSyncPausedByFeatureFlagAlert, defaultValue: false) private var syncDidShowSyncPausedByFeatureFlagAlert: Bool - + private var localUpdatesCancellable: AnyCancellable? private var syncUpdatesCancellable: AnyCancellable? private var syncFeatureFlagsCancellable: AnyCancellable? private var favoritesDisplayModeCancellable: AnyCancellable? private var emailCancellables = Set() - + private lazy var featureFlagger = AppDependencyProvider.shared.featureFlagger - + lazy var menuBookmarksViewModel: MenuBookmarksInteracting = { let viewModel = MenuBookmarksViewModel(bookmarksDatabase: bookmarksDatabase, syncService: syncService) viewModel.favoritesDisplayMode = appSettings.favoritesDisplayMode return viewModel }() - + weak var tabSwitcherController: TabSwitcherViewController? let tabSwitcherButton = TabSwitcherButton() @@ -121,31 +121,31 @@ class MainViewController: UIViewController { private lazy var fireButtonAnimator: FireButtonAnimator = FireButtonAnimator(appSettings: appSettings) let bookmarksCachingSearch: BookmarksCachingSearch - + lazy var tabSwitcherTransition = TabSwitcherTransitionDelegate() var currentTab: TabViewController? { return tabManager?.current(createIfNeeded: false) } - + var searchBarRect: CGRect { let view = UIApplication.shared.windows.filter({ $0.isKeyWindow }).first?.rootViewController?.view return viewCoordinator.omniBar.searchContainer.convert(viewCoordinator.omniBar.searchContainer.bounds, to: view) } - + var keyModifierFlags: UIKeyModifierFlags? var showKeyboardAfterFireButton: DispatchWorkItem? // Skip SERP flow (focusing on autocomplete logic) and prepare for new navigation when selecting search bar private var skipSERPFlow = true - + private var keyboardHeight: CGFloat = 0.0 - + required init?(coder: NSCoder) { fatalError("Use init?(code:") } - + var viewCoordinator: MainViewCoordinator! - + #if APP_TRACKING_PROTECTION init( bookmarksDatabase: CoreDataDatabase, @@ -163,9 +163,9 @@ class MainViewController: UIViewController { self.favoritesViewModel = FavoritesListViewModel(bookmarksDatabase: bookmarksDatabase, favoritesDisplayMode: appSettings.favoritesDisplayMode) self.bookmarksCachingSearch = BookmarksCachingSearch(bookmarksStore: CoreDataBookmarksSearchStore(bookmarksStore: bookmarksDatabase)) self.appSettings = appSettings - + super.init(nibName: nil, bundle: nil) - + bindFavoritesDisplayMode() bindSyncService() } @@ -186,38 +186,38 @@ class MainViewController: UIViewController { self.appSettings = appSettings super.init(nibName: nil, bundle: nil) - + bindSyncService() } #endif - + fileprivate var tabCountInfo: TabCountInfo? - + func loadFindInPage() { - + let view = FindInPageView.loadFromXib() self.view.addSubview(view) - + // Avoids coercion swiftlint warnings let superview = self.view! - + let height = view.constrainAttribute(.height, to: view.frame.height) let bottom = superview.constrainView(view, by: .bottom, to: .bottom) - + NSLayoutConstraint.activate([ bottom, superview.constrainView(view, by: .width, to: .width), height, superview.constrainView(view, by: .centerX, to: .centerX) ]) - + findInPageView = view findInPageBottomLayoutConstraint = bottom findInPageHeightLayoutConstraint = height } class OmniBarCell: UICollectionViewCell { - + weak var omniBar: OmniBar? { didSet { subviews.forEach { $0.removeFromSuperview() } @@ -235,26 +235,102 @@ class MainViewController: UIViewController { } - var swipeTabsDataSource: SwipeTabsDataSource! - class SwipeTabsDataSource: NSObject, UICollectionViewDataSource { + class NewTabCell: UICollectionViewCell { + static let identifier = "AddCell" + + private let addButton: UIButton = { + let button = UIButton() + button.setTitle("+ Add", for: .normal) + button.backgroundColor = .systemBlue + button.layer.cornerRadius = 5 + button.translatesAutoresizingMaskIntoConstraints = false + return button + }() + + override init(frame: CGRect) { + super.init(frame: frame) + setupButton() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupButton() { + contentView.addSubview(addButton) + addButton.addTarget(self, action: #selector(addButtonTapped), for: .touchUpInside) + + // Auto Layout Constraints + let padding: CGFloat = 10 + NSLayoutConstraint.activate([ + addButton.topAnchor.constraint(equalTo: contentView.topAnchor, constant: padding), + addButton.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -padding), + addButton.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: padding), + addButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -padding) + ]) + } + + @objc private func addButtonTapped() { + // Handle the add button tap event + print("Add button tapped") + } + } + + var swipeTabsCoordinator: SwipeTabsCoordinator! + class SwipeTabsCoordinator: NSObject, UICollectionViewDataSource, UICollectionViewDelegate { weak var coordinator: MainViewCoordinator! init(coordinator: MainViewCoordinator) { self.coordinator = coordinator coordinator.navigationBarContainer.register(OmniBarCell.self, forCellWithReuseIdentifier: "omnibar") + coordinator.navigationBarContainer.register(NewTabCell.self, forCellWithReuseIdentifier: "newtab") } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - 1 + 2 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "omnibar", for: indexPath) as! OmniBarCell - cell.omniBar = coordinator.omniBar - return cell + if indexPath.row > 0 { + return collectionView.dequeueReusableCell(withReuseIdentifier: "newtab", for: indexPath) + } else { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "omnibar", for: indexPath) as! OmniBarCell + cell.omniBar = coordinator.omniBar + return cell + } + } + + var startOffsetX: CGFloat = 0.0 + var startingIndexPath: IndexPath? + func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + startOffsetX = scrollView.contentOffset.x + startingIndexPath = coordinator.navigationBarContainer.indexPathsForVisibleItems[0] + } + + var targetIndexPath: IndexPath? + func scrollViewDidScroll(_ scrollView: UIScrollView) { + guard let startingIndexPath else { return } + let distance = scrollView.contentOffset.x - startOffsetX + print("***", #function, startingIndexPath, distance) + if abs(distance) > coordinator.superview.frame.width * 0.3 { + var targetIndexPath = startingIndexPath + if distance < 0 { + targetIndexPath.row -= 1 + } else { + targetIndexPath.row += 1 + } + self.targetIndexPath = targetIndexPath + } else { + targetIndexPath = nil + } + } + + func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + guard let targetIndexPath else { return } + print("***", #function, targetIndexPath) + coordinator.navigationBarContainer.scrollToItem(at: targetIndexPath, at: .centeredHorizontally, animated: true) } - } override func viewDidLoad() { @@ -267,8 +343,9 @@ class MainViewController: UIViewController { viewCoordinator.toolbarForwardButton.action = #selector(onForwardPressed) viewCoordinator.toolbarFireButton.action = #selector(onFirePressed) - swipeTabsDataSource = SwipeTabsDataSource(coordinator: viewCoordinator) - viewCoordinator.navigationBarContainer.dataSource = swipeTabsDataSource + swipeTabsCoordinator = SwipeTabsCoordinator(coordinator: viewCoordinator) + viewCoordinator.navigationBarContainer.delegate = swipeTabsCoordinator + viewCoordinator.navigationBarContainer.dataSource = swipeTabsCoordinator loadSuggestionTray() loadTabsBarIfNeeded() From 27955e6bf24e17d4eadb0e46e9e20ea7244d247e Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Mon, 22 Jan 2024 09:44:33 +0000 Subject: [PATCH 03/39] use tabs model --- DuckDuckGo/MainViewController.swift | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 144142cb7c..4e010d4f40 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -280,6 +280,7 @@ class MainViewController: UIViewController { class SwipeTabsCoordinator: NSObject, UICollectionViewDataSource, UICollectionViewDelegate { weak var coordinator: MainViewCoordinator! + weak var tabsModel: TabsModel! init(coordinator: MainViewCoordinator) { self.coordinator = coordinator @@ -287,8 +288,14 @@ class MainViewController: UIViewController { coordinator.navigationBarContainer.register(NewTabCell.self, forCellWithReuseIdentifier: "newtab") } + func refresh(tabsModel: TabsModel, scrollToSelected: Bool = false) { + print("***", #function) + self.tabsModel = tabsModel + coordinator.navigationBarContainer.reloadData() + } + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - 2 + tabsModel.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { @@ -373,6 +380,7 @@ class MainViewController: UIViewController { applyTheme(ThemeManager.shared.currentTheme) tabsBarController?.refresh(tabsModel: tabManager.model, scrollToSelected: true) + swipeTabsCoordinator?.refresh(tabsModel: tabManager.model, scrollToSelected: true) _ = AppWidthObserver.shared.willResize(toWidth: view.frame.width) applyWidth() @@ -391,6 +399,7 @@ class MainViewController: UIViewController { startOnboardingFlowIfNotSeenBefore() tabsBarController?.refresh(tabsModel: tabManager.model) + swipeTabsCoordinator?.refresh(tabsModel: tabManager.model) _ = AppWidthObserver.shared.willResize(toWidth: view.frame.width) applyWidth() @@ -910,6 +919,7 @@ class MainViewController: UIViewController { refreshTabIcon() refreshControls() tabsBarController?.refresh(tabsModel: tabManager.model) + swipeTabsCoordinator?.refresh(tabsModel: tabManager.model) } func enterSearch() { @@ -990,6 +1000,7 @@ class MainViewController: UIViewController { refreshControls() } tabsBarController?.refresh(tabsModel: tabManager.model, scrollToSelected: true) + tabsBarController?.refresh(tabsModel: tabManager.model, scrollToSelected: true) if DaxDialogs.shared.shouldShowFireButtonPulse { showFireButtonPulse() } @@ -1107,6 +1118,7 @@ class MainViewController: UIViewController { } // If tabs have been udpated, do this async to make sure size calcs are current self.tabsBarController?.refresh(tabsModel: self.tabManager.model) + self.swipeTabsCoordinator?.refresh(tabsModel: self.tabManager.model) // Do this on the next UI thread pass so we definitely have the right width self.applyWidthToTrayController() @@ -1288,6 +1300,7 @@ class MainViewController: UIViewController { attachHomeScreen() homeController?.openedAsNewTab(allowingKeyboard: allowingKeyboard) tabsBarController?.refresh(tabsModel: tabManager.model) + swipeTabsCoordinator?.refresh(tabsModel: tabManager.model) } func animateLogoAppearance() { @@ -1839,6 +1852,7 @@ extension MainViewController: TabDelegate { } tabManager?.save() tabsBarController?.refresh(tabsModel: tabManager.model) + swipeTabsCoordinator?.refresh(tabsModel: tabManager.model) } func tab(_ tab: TabViewController, didUpdatePreview preview: UIImage) { @@ -2137,6 +2151,7 @@ extension MainViewController: AutoClearWorker { showBars() attachHomeScreen() tabsBarController?.refresh(tabsModel: tabManager.model) + swipeTabsCoordinator?.refresh(tabsModel: tabManager.model) Favicons.shared.clearCache(.tabs) } From 0c2029c3886551efdbb19e300061bacbee2ceb4a Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Mon, 22 Jan 2024 17:43:39 +0000 Subject: [PATCH 04/39] super basic rough swiping --- DuckDuckGo.xcodeproj/project.pbxproj | 4 + DuckDuckGo/MainViewController.swift | 127 +----------------- DuckDuckGo/SwipeTabsCoordinator.swift | 184 ++++++++++++++++++++++++++ 3 files changed, 191 insertions(+), 124 deletions(-) create mode 100644 DuckDuckGo/SwipeTabsCoordinator.swift diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index b9042fd2ea..0429f4a4d4 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -460,6 +460,7 @@ 85AE668E2097206E0014CF04 /* NotificationView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 85AE668D2097206E0014CF04 /* NotificationView.xib */; }; 85AE6690209724120014CF04 /* NotificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85AE668F209724120014CF04 /* NotificationView.swift */; }; 85AFA1212B45D14F0028A504 /* BookmarksMigrationAssertionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85AFA1202B45D14F0028A504 /* BookmarksMigrationAssertionTests.swift */; }; + 85B9814E2B5EB618009AC9A6 /* SwipeTabsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85B9814D2B5EB618009AC9A6 /* SwipeTabsCoordinator.swift */; }; 85B9CB8921AEBDD5009001F1 /* FavoriteHomeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85B9CB8821AEBDD5009001F1 /* FavoriteHomeCell.swift */; }; 85BA58551F34F49E00C6E8CA /* AppUserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85BA58541F34F49E00C6E8CA /* AppUserDefaults.swift */; }; 85BA58581F34F72F00C6E8CA /* AppUserDefaultsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85BA58561F34F61C00C6E8CA /* AppUserDefaultsTests.swift */; }; @@ -1541,6 +1542,7 @@ 85AE668D2097206E0014CF04 /* NotificationView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = NotificationView.xib; sourceTree = ""; }; 85AE668F209724120014CF04 /* NotificationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationView.swift; sourceTree = ""; }; 85AFA1202B45D14F0028A504 /* BookmarksMigrationAssertionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksMigrationAssertionTests.swift; sourceTree = ""; }; + 85B9814D2B5EB618009AC9A6 /* SwipeTabsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeTabsCoordinator.swift; sourceTree = ""; }; 85B9CB8821AEBDD5009001F1 /* FavoriteHomeCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoriteHomeCell.swift; sourceTree = ""; }; 85BA58541F34F49E00C6E8CA /* AppUserDefaults.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppUserDefaults.swift; sourceTree = ""; }; 85BA58561F34F61C00C6E8CA /* AppUserDefaultsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppUserDefaultsTests.swift; sourceTree = ""; }; @@ -5389,6 +5391,7 @@ 85864FBB24D31EF300E756FF /* SuggestionTrayViewController.swift */, 851DFD86212C39D300D95F20 /* TabSwitcherButton.swift */, CBEFB9102ADFFE7900DEDE7B /* CriticalAlerts.swift */, + 85B9814D2B5EB618009AC9A6 /* SwipeTabsCoordinator.swift */, ); name = Main; sourceTree = ""; @@ -6879,6 +6882,7 @@ 3151F0EA27357FBA00226F58 /* SpeechRecognizer.swift in Sources */, F17922E21E71CD67006E3D97 /* NoSuggestionsTableViewCell.swift in Sources */, 0290472229E723260008FE3C /* AppTPManageTrackerCell.swift in Sources */, + 85B9814E2B5EB618009AC9A6 /* SwipeTabsCoordinator.swift in Sources */, 985AAE4524899369007A43EC /* HomeScreenTransition.swift in Sources */, 85E58C2C28FDA94F006A801A /* FavoritesViewController.swift in Sources */, 1E8AD1CF27C000A000ABA377 /* CompleteDownloadRow.swift in Sources */, diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 4e010d4f40..1fc59b5f0e 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -216,129 +216,7 @@ class MainViewController: UIViewController { findInPageHeightLayoutConstraint = height } - class OmniBarCell: UICollectionViewCell { - - weak var omniBar: OmniBar? { - didSet { - subviews.forEach { $0.removeFromSuperview() } - if let omniBar { - addSubview(omniBar) - NSLayoutConstraint.activate([ - constrainView(omniBar, by: .leading), - constrainView(omniBar, by: .trailing), - constrainView(omniBar, by: .top), - constrainView(omniBar, by: .bottom), - ]) - } - } - } - - } - - class NewTabCell: UICollectionViewCell { - static let identifier = "AddCell" - - private let addButton: UIButton = { - let button = UIButton() - button.setTitle("+ Add", for: .normal) - button.backgroundColor = .systemBlue - button.layer.cornerRadius = 5 - button.translatesAutoresizingMaskIntoConstraints = false - return button - }() - - override init(frame: CGRect) { - super.init(frame: frame) - setupButton() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupButton() { - contentView.addSubview(addButton) - addButton.addTarget(self, action: #selector(addButtonTapped), for: .touchUpInside) - - // Auto Layout Constraints - let padding: CGFloat = 10 - NSLayoutConstraint.activate([ - addButton.topAnchor.constraint(equalTo: contentView.topAnchor, constant: padding), - addButton.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -padding), - addButton.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: padding), - addButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -padding) - ]) - } - - @objc private func addButtonTapped() { - // Handle the add button tap event - print("Add button tapped") - } - } - var swipeTabsCoordinator: SwipeTabsCoordinator! - class SwipeTabsCoordinator: NSObject, UICollectionViewDataSource, UICollectionViewDelegate { - - weak var coordinator: MainViewCoordinator! - weak var tabsModel: TabsModel! - - init(coordinator: MainViewCoordinator) { - self.coordinator = coordinator - coordinator.navigationBarContainer.register(OmniBarCell.self, forCellWithReuseIdentifier: "omnibar") - coordinator.navigationBarContainer.register(NewTabCell.self, forCellWithReuseIdentifier: "newtab") - } - - func refresh(tabsModel: TabsModel, scrollToSelected: Bool = false) { - print("***", #function) - self.tabsModel = tabsModel - coordinator.navigationBarContainer.reloadData() - } - - func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - tabsModel.count - } - - func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - if indexPath.row > 0 { - return collectionView.dequeueReusableCell(withReuseIdentifier: "newtab", for: indexPath) - } else { - let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "omnibar", for: indexPath) as! OmniBarCell - cell.omniBar = coordinator.omniBar - return cell - } - } - - var startOffsetX: CGFloat = 0.0 - var startingIndexPath: IndexPath? - func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { - startOffsetX = scrollView.contentOffset.x - startingIndexPath = coordinator.navigationBarContainer.indexPathsForVisibleItems[0] - } - - var targetIndexPath: IndexPath? - func scrollViewDidScroll(_ scrollView: UIScrollView) { - guard let startingIndexPath else { return } - let distance = scrollView.contentOffset.x - startOffsetX - print("***", #function, startingIndexPath, distance) - if abs(distance) > coordinator.superview.frame.width * 0.3 { - var targetIndexPath = startingIndexPath - if distance < 0 { - targetIndexPath.row -= 1 - } else { - targetIndexPath.row += 1 - } - self.targetIndexPath = targetIndexPath - } else { - targetIndexPath = nil - } - } - - func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { - guard let targetIndexPath else { return } - print("***", #function, targetIndexPath) - coordinator.navigationBarContainer.scrollToItem(at: targetIndexPath, at: .centeredHorizontally, animated: true) - } - } override func viewDidLoad() { super.viewDidLoad() @@ -350,7 +228,8 @@ class MainViewController: UIViewController { viewCoordinator.toolbarForwardButton.action = #selector(onForwardPressed) viewCoordinator.toolbarFireButton.action = #selector(onFirePressed) - swipeTabsCoordinator = SwipeTabsCoordinator(coordinator: viewCoordinator) + swipeTabsCoordinator = SwipeTabsCoordinator(coordinator: viewCoordinator, tabPreviewsSource: previewsSource, selectTab: select) + viewCoordinator.navigationBarContainer.delegate = swipeTabsCoordinator viewCoordinator.navigationBarContainer.dataSource = swipeTabsCoordinator @@ -1000,7 +879,7 @@ class MainViewController: UIViewController { refreshControls() } tabsBarController?.refresh(tabsModel: tabManager.model, scrollToSelected: true) - tabsBarController?.refresh(tabsModel: tabManager.model, scrollToSelected: true) + swipeTabsCoordinator?.refresh(tabsModel: tabManager.model, scrollToSelected: true) if DaxDialogs.shared.shouldShowFireButtonPulse { showFireButtonPulse() } diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift new file mode 100644 index 0000000000..d77ea905ea --- /dev/null +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -0,0 +1,184 @@ +// +// SwipeTabsCoordinator.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +class SwipeTabsCoordinator: NSObject, UICollectionViewDataSource, UICollectionViewDelegate { + + // Set by refresh function + weak var tabsModel: TabsModel! + + weak var coordinator: MainViewCoordinator! + weak var tabPreviewsSource: TabPreviewsSource! + + let selectTab: (Int) -> Void + + init(coordinator: MainViewCoordinator, tabPreviewsSource: TabPreviewsSource, selectTab: @escaping (Int) -> Void) { + self.coordinator = coordinator + self.tabPreviewsSource = tabPreviewsSource + self.selectTab = selectTab + coordinator.navigationBarContainer.register(OmniBarCell.self, forCellWithReuseIdentifier: "omnibar") + coordinator.navigationBarContainer.register(NewTabCell.self, forCellWithReuseIdentifier: "newtab") + } + + func refresh(tabsModel: TabsModel, scrollToSelected: Bool = false) { + print("***", #function) + self.tabsModel = tabsModel + coordinator.navigationBarContainer.reloadData() + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + tabsModel.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + if indexPath.row > 0 { + return collectionView.dequeueReusableCell(withReuseIdentifier: "newtab", for: indexPath) + } else { + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "omnibar", for: indexPath) as? OmniBarCell else { + fatalError("Not \(OmniBarCell.self)") + } + cell.omniBar = coordinator.omniBar + return cell + } + } + + var startOffsetX: CGFloat = 0.0 + var startingIndexPath: IndexPath? + weak var nextTabPreview: UIView? + + func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + startOffsetX = scrollView.contentOffset.x + startingIndexPath = coordinator.navigationBarContainer.indexPathsForVisibleItems[0] + } + + var targetIndexPath: IndexPath? + func scrollViewDidScroll(_ scrollView: UIScrollView) { + guard let startingIndexPath else { return } + + let distance = scrollView.contentOffset.x - startOffsetX + + if abs(distance) > coordinator.superview.frame.width * 0.3 { + var targetIndexPath = startingIndexPath + if distance < 0 { + targetIndexPath.row -= 1 + } else { + targetIndexPath.row += 1 + } + self.targetIndexPath = targetIndexPath + } else { + targetIndexPath = nil + } + + if nextTabPreview == nil { + let index = startingIndexPath.row + (distance < 0 ? -1 : 1) + if tabsModel.tabs.indices.contains(index) { + let tab = tabsModel.get(tabAt: index) + + let view = UIView(frame: CGRect(origin: .zero, size: coordinator.contentContainer.frame.size)) + view.backgroundColor = .clear + coordinator.contentContainer.addSubview(view) + nextTabPreview = view + + let imageView = UIImageView(image: tabPreviewsSource.preview(for: tab)) + imageView.frame = view.frame + view.addSubview(imageView) + } + } + + // Could add some 'curve' or 'parallex' to this. + coordinator.contentContainer.subviews[0].frame.origin.x = -distance + if distance > 0 { + nextTabPreview?.frame.origin.x = coordinator.contentContainer.frame.size.width - abs(distance) + } else { + nextTabPreview?.frame.origin.x = -coordinator.contentContainer.frame.size.width + abs(distance) + } + } + + func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + guard let targetIndexPath else { return } + print("***", #function, targetIndexPath) + coordinator.navigationBarContainer.scrollToItem(at: targetIndexPath, at: .centeredHorizontally, animated: true) + coordinator.contentContainer.subviews[0].frame.origin.x = 0 + selectTab(targetIndexPath.row) + self.nextTabPreview?.removeFromSuperview() + self.startingIndexPath = nil + self.targetIndexPath = nil + } +} + +class OmniBarCell: UICollectionViewCell { + + weak var omniBar: OmniBar? { + didSet { + subviews.forEach { $0.removeFromSuperview() } + if let omniBar { + addSubview(omniBar) + NSLayoutConstraint.activate([ + constrainView(omniBar, by: .leading), + constrainView(omniBar, by: .trailing), + constrainView(omniBar, by: .top), + constrainView(omniBar, by: .bottom), + ]) + } + } + } + +} + +class NewTabCell: UICollectionViewCell { + static let identifier = "AddCell" + + private let addButton: UIButton = { + let button = UIButton() + button.setTitle("+ Add", for: .normal) + button.backgroundColor = .systemBlue + button.layer.cornerRadius = 5 + button.translatesAutoresizingMaskIntoConstraints = false + return button + }() + + override init(frame: CGRect) { + super.init(frame: frame) + setupButton() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupButton() { + contentView.addSubview(addButton) + addButton.addTarget(self, action: #selector(addButtonTapped), for: .touchUpInside) + + // Auto Layout Constraints + let padding: CGFloat = 10 + NSLayoutConstraint.activate([ + addButton.topAnchor.constraint(equalTo: contentView.topAnchor, constant: padding), + addButton.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -padding), + addButton.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: padding), + addButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -padding) + ]) + } + + @objc private func addButtonTapped() { + // Handle the add button tap event + print("Add button tapped") + } +} From fb9b6573d29fd8996067bd0ba899502d0cc5fef7 Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Mon, 22 Jan 2024 18:06:54 +0000 Subject: [PATCH 05/39] use fake omnibar --- DuckDuckGo/MainView.swift | 4 ++-- DuckDuckGo/MainViewController.swift | 2 +- DuckDuckGo/SwipeTabsCoordinator.swift | 22 +++++++++++++++------- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/DuckDuckGo/MainView.swift b/DuckDuckGo/MainView.swift index 7229676267..6078619140 100644 --- a/DuckDuckGo/MainView.swift +++ b/DuckDuckGo/MainView.swift @@ -218,7 +218,7 @@ extension MainViewFactory { private func constrainNavigationBarContainer() { let navigationBarContainer = coordinator.navigationBarContainer! let toolbar = coordinator.toolbar! - let omniBar = coordinator.omniBar! +// let omniBar = coordinator.omniBar! coordinator.constraints.navigationBarContainerTop = navigationBarContainer.constrainView(superview.safeAreaLayoutGuide, by: .top) coordinator.constraints.navigationBarContainerBottom = navigationBarContainer.constrainView(toolbar, by: .bottom, to: .top) @@ -400,7 +400,7 @@ class MainViewCoordinator { var notificationContainerTopToNavigationBar: NSLayoutConstraint! var notificationContainerTopToStatusBackground: NSLayoutConstraint! var notificationContainerHeight: NSLayoutConstraint! - var omniBarBottom: NSLayoutConstraint! + // var omniBarBottom: NSLayoutConstraint! var progressBarTop: NSLayoutConstraint! var progressBarBottom: NSLayoutConstraint! var statusBackgroundToNavigationBarContainerBottom: NSLayoutConstraint! diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 1fc59b5f0e..954267ef05 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -507,7 +507,7 @@ class MainViewController: UIViewController { if self.appSettings.currentAddressBarPosition.isBottom { let navBarOffset = min(0, self.toolbarHeight - intersection.height) - self.viewCoordinator.constraints.omniBarBottom.constant = navBarOffset + self.viewCoordinator.constraints.navigationBarContainerBottom.constant = navBarOffset UIView.animate(withDuration: duration, delay: 0, options: animationCurve) { self.viewCoordinator.navigationBarContainer.superview?.layoutIfNeeded() } diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index d77ea905ea..214b970aab 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -48,15 +48,23 @@ class SwipeTabsCoordinator: NSObject, UICollectionViewDataSource, UICollectionVi } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - if indexPath.row > 0 { - return collectionView.dequeueReusableCell(withReuseIdentifier: "newtab", for: indexPath) - } else { - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "omnibar", for: indexPath) as? OmniBarCell else { - fatalError("Not \(OmniBarCell.self)") - } + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "omnibar", for: indexPath) as? OmniBarCell else { + fatalError("Not \(OmniBarCell.self)") + } + + if tabsModel.currentIndex == indexPath.row { + print("***", #function, "using real omnibar") cell.omniBar = coordinator.omniBar - return cell + } else { + let tab = tabsModel.get(tabAt: indexPath.row) + cell.omniBar = OmniBar.loadFromXib() + cell.omniBar?.startBrowsing() + cell.omniBar?.refreshText(forUrl: tab.link?.url) + cell.omniBar?.decorate(with: ThemeManager.shared.currentTheme) + cell.omniBar?.frame = coordinator.omniBar.frame } + + return cell } var startOffsetX: CGFloat = 0.0 From edf9d8e0ab3eb564e0a25f9f211c1347c3c85e65 Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Mon, 22 Jan 2024 18:10:55 +0000 Subject: [PATCH 06/39] todo items --- DuckDuckGo/SwipeTabsCoordinator.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index 214b970aab..9c18ccd660 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -19,6 +19,10 @@ import UIKit +// TODO handle new tab + +// TODO current tab being not first index + class SwipeTabsCoordinator: NSObject, UICollectionViewDataSource, UICollectionViewDelegate { // Set by refresh function From 49cffd1fcc4daa988ffd60c40c3cbfab209cbc85 Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Tue, 23 Jan 2024 12:28:15 +0000 Subject: [PATCH 07/39] move layout to other class and fix constraint warnings --- DuckDuckGo/MainView.swift | 34 +----------- DuckDuckGo/SwipeTabsCoordinator.swift | 80 +++++++++++++-------------- DuckDuckGo/WebViewTransition.swift | 1 - 3 files changed, 43 insertions(+), 72 deletions(-) diff --git a/DuckDuckGo/MainView.swift b/DuckDuckGo/MainView.swift index 6078619140..d171b8090c 100644 --- a/DuckDuckGo/MainView.swift +++ b/DuckDuckGo/MainView.swift @@ -76,38 +76,10 @@ extension MainViewFactory { coordinator.omniBar.translatesAutoresizingMaskIntoConstraints = false } - final class NavigationBarLayout: UICollectionViewFlowLayout { - override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint { - guard let collectionView = self.collectionView else { - return super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity) - } - - let pageWidth = itemSize.width + minimumLineSpacing - let currentPage = collectionView.contentOffset.x / pageWidth - let nextPage = velocity.x.sign == .minus ? floor(currentPage) : ceil(currentPage) - let point = CGPoint(x: nextPage * pageWidth, y: proposedContentOffset.y) - - print("***", #function, currentPage, nextPage, point) - return point - } - - override func prepare() { - super.prepare() - guard let collectionView = self.collectionView else { return } - - itemSize = CGSize(width: collectionView.bounds.width, height: collectionView.bounds.height) - minimumLineSpacing = 0 - minimumInteritemSpacing = 0 - scrollDirection = .horizontal - } - } - final class NavigationBarContainer: UICollectionView { } - private func createNavigationBarContainer() { - let layout = NavigationBarLayout() - layout.scrollDirection = .horizontal - layout.itemSize = CGSize(width: superview.frame.size.width, height: 52) - coordinator.navigationBarContainer = NavigationBarContainer(frame: .zero, collectionViewLayout: layout) + private func createNavigationBarContainer() { + // Layout is replaced elsewhere, but required to construct the view. + coordinator.navigationBarContainer = NavigationBarContainer(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) coordinator.navigationBarContainer.decelerationRate = .fast superview.addSubview(coordinator.navigationBarContainer) } diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index 9c18ccd660..2f7be632c2 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -21,7 +21,9 @@ import UIKit // TODO handle new tab -// TODO current tab being not first index +// TODO handle iPad + +// TODO slide the logo when in homescreen view? class SwipeTabsCoordinator: NSObject, UICollectionViewDataSource, UICollectionViewDelegate { @@ -38,13 +40,26 @@ class SwipeTabsCoordinator: NSObject, UICollectionViewDataSource, UICollectionVi self.tabPreviewsSource = tabPreviewsSource self.selectTab = selectTab coordinator.navigationBarContainer.register(OmniBarCell.self, forCellWithReuseIdentifier: "omnibar") - coordinator.navigationBarContainer.register(NewTabCell.self, forCellWithReuseIdentifier: "newtab") + + let layout = NavigationBarLayout() + layout.scrollDirection = .horizontal + layout.itemSize = CGSize(width: coordinator.superview.frame.size.width, height: coordinator.omniBar.frame.height) + coordinator.navigationBarContainer.setCollectionViewLayout(layout, animated: false) } func refresh(tabsModel: TabsModel, scrollToSelected: Bool = false) { - print("***", #function) + let scrollToItem = self.tabsModel == nil + print("***", #function, scrollToItem) + self.tabsModel = tabsModel coordinator.navigationBarContainer.reloadData() + + if scrollToItem { + DispatchQueue.main.async { + self.coordinator.navigationBarContainer.scrollToItem(at: .init(row: tabsModel.currentIndex, section: 0), + at: .centeredHorizontally, animated: false) + } + } } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { @@ -62,10 +77,10 @@ class SwipeTabsCoordinator: NSObject, UICollectionViewDataSource, UICollectionVi } else { let tab = tabsModel.get(tabAt: indexPath.row) cell.omniBar = OmniBar.loadFromXib() + cell.omniBar?.translatesAutoresizingMaskIntoConstraints = false cell.omniBar?.startBrowsing() cell.omniBar?.refreshText(forUrl: tab.link?.url) cell.omniBar?.decorate(with: ThemeManager.shared.currentTheme) - cell.omniBar?.frame = coordinator.omniBar.frame } return cell @@ -154,43 +169,28 @@ class OmniBarCell: UICollectionViewCell { } -class NewTabCell: UICollectionViewCell { - static let identifier = "AddCell" - - private let addButton: UIButton = { - let button = UIButton() - button.setTitle("+ Add", for: .normal) - button.backgroundColor = .systemBlue - button.layer.cornerRadius = 5 - button.translatesAutoresizingMaskIntoConstraints = false - return button - }() - - override init(frame: CGRect) { - super.init(frame: frame) - setupButton() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupButton() { - contentView.addSubview(addButton) - addButton.addTarget(self, action: #selector(addButtonTapped), for: .touchUpInside) +final class NavigationBarLayout: UICollectionViewFlowLayout { + override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint { + guard let collectionView = self.collectionView else { + return super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity) + } - // Auto Layout Constraints - let padding: CGFloat = 10 - NSLayoutConstraint.activate([ - addButton.topAnchor.constraint(equalTo: contentView.topAnchor, constant: padding), - addButton.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -padding), - addButton.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: padding), - addButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -padding) - ]) + let pageWidth = itemSize.width + minimumLineSpacing + let currentPage = collectionView.contentOffset.x / pageWidth + let nextPage = velocity.x.sign == .minus ? floor(currentPage) : ceil(currentPage) + let point = CGPoint(x: nextPage * pageWidth, y: proposedContentOffset.y) + + print("***", #function, currentPage, nextPage, point) + return point } - - @objc private func addButtonTapped() { - // Handle the add button tap event - print("Add button tapped") + + override func prepare() { + super.prepare() + guard let collectionView = self.collectionView else { return } + + itemSize = CGSize(width: collectionView.bounds.width, height: collectionView.bounds.height) + minimumLineSpacing = 0 + minimumInteritemSpacing = 0 + scrollDirection = .horizontal } } diff --git a/DuckDuckGo/WebViewTransition.swift b/DuckDuckGo/WebViewTransition.swift index af7ffbc8fe..0b25b7ba38 100644 --- a/DuckDuckGo/WebViewTransition.swift +++ b/DuckDuckGo/WebViewTransition.swift @@ -165,7 +165,6 @@ class ToWebViewTransition: WebViewTransition { scrollIfOutsideViewport(collectionView: tabSwitcherViewController.collectionView, rowIndex: rowIndex, attributes: layoutAttr) UIView.animate(withDuration: TabSwitcherTransition.Constants.duration, animations: { - let frame = CGRect(x: 0, y: webView.scrollView.contentInset.top, width: webViewFrame.width, height: webViewFrame.height) self.imageContainer.frame = mainViewController.viewCoordinator.contentContainer.frame self.imageContainer.layer.cornerRadius = 0 From 7af8a7442455fc5dfe024183f0a25e71bfe363b2 Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Tue, 23 Jan 2024 17:16:24 +0000 Subject: [PATCH 08/39] use paging instead of custom layout --- DuckDuckGo/SwipeTabsCoordinator.swift | 191 +++++++++++++------------- 1 file changed, 95 insertions(+), 96 deletions(-) diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index 2f7be632c2..6df208ee99 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -25,43 +25,130 @@ import UIKit // TODO slide the logo when in homescreen view? -class SwipeTabsCoordinator: NSObject, UICollectionViewDataSource, UICollectionViewDelegate { +class SwipeTabsCoordinator: NSObject { // Set by refresh function weak var tabsModel: TabsModel! - + weak var coordinator: MainViewCoordinator! weak var tabPreviewsSource: TabPreviewsSource! let selectTab: (Int) -> Void + var startOffsetX: CGFloat = 0.0 + var startingIndexPath: IndexPath? + weak var nextTabPreview: UIView? + + var targetIndexPath: IndexPath? + var isDragging = false + init(coordinator: MainViewCoordinator, tabPreviewsSource: TabPreviewsSource, selectTab: @escaping (Int) -> Void) { self.coordinator = coordinator self.tabPreviewsSource = tabPreviewsSource self.selectTab = selectTab + coordinator.navigationBarContainer.register(OmniBarCell.self, forCellWithReuseIdentifier: "omnibar") + coordinator.navigationBarContainer.isPagingEnabled = true - let layout = NavigationBarLayout() - layout.scrollDirection = .horizontal - layout.itemSize = CGSize(width: coordinator.superview.frame.size.width, height: coordinator.omniBar.frame.height) - coordinator.navigationBarContainer.setCollectionViewLayout(layout, animated: false) + let layout = coordinator.navigationBarContainer.collectionViewLayout as? UICollectionViewFlowLayout + layout?.scrollDirection = .horizontal + layout?.itemSize = CGSize(width: coordinator.superview.frame.size.width, height: coordinator.omniBar.frame.height) + layout?.minimumLineSpacing = 0 + layout?.minimumInteritemSpacing = 0 + layout?.scrollDirection = .horizontal } +} + +// MARK: UICollectionViewDelegate +extension SwipeTabsCoordinator: UICollectionViewDelegate { + + func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + startOffsetX = scrollView.contentOffset.x + startingIndexPath = coordinator.navigationBarContainer.indexPathsForVisibleItems[0] + isDragging = true + } + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + guard let startingIndexPath else { return } + print("***", #function, startingIndexPath) + + let distance = scrollView.contentOffset.x - startOffsetX + + if abs(distance) > coordinator.superview.frame.width * 0.3 { + var targetIndexPath = startingIndexPath + if distance < 0 { + targetIndexPath.row -= 1 + } else { + targetIndexPath.row += 1 + } + self.targetIndexPath = targetIndexPath + } else { + targetIndexPath = nil + } + + if nextTabPreview == nil { + let index = startingIndexPath.row + (distance < 0 ? -1 : 1) + if tabsModel.tabs.indices.contains(index) { + let tab = tabsModel.get(tabAt: index) + + let view = UIView(frame: CGRect(origin: .zero, size: coordinator.contentContainer.frame.size)) + view.backgroundColor = .clear + coordinator.contentContainer.addSubview(view) + nextTabPreview = view + + let imageView = UIImageView(image: tabPreviewsSource.preview(for: tab)) + imageView.frame = view.frame + view.addSubview(imageView) + } + } + + // Could add some 'curve' or 'parallex' to this. + coordinator.contentContainer.subviews[0].frame.origin.x = -distance + if distance > 0 { + nextTabPreview?.frame.origin.x = coordinator.contentContainer.frame.size.width - abs(distance) + } else { + nextTabPreview?.frame.origin.x = -coordinator.contentContainer.frame.size.width + abs(distance) + } + } + + func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + isDragging = false + + guard let targetIndexPath else { return } + print("***", #function, targetIndexPath) + coordinator.navigationBarContainer.scrollToItem(at: targetIndexPath, at: .centeredHorizontally, animated: true) + coordinator.contentContainer.subviews[0].frame.origin.x = 0 + selectTab(targetIndexPath.row) + self.nextTabPreview?.removeFromSuperview() + self.startingIndexPath = nil + self.targetIndexPath = nil + } +} + +// MARK: Public Interface +extension SwipeTabsCoordinator { + func refresh(tabsModel: TabsModel, scrollToSelected: Bool = false) { let scrollToItem = self.tabsModel == nil print("***", #function, scrollToItem) - + self.tabsModel = tabsModel coordinator.navigationBarContainer.reloadData() if scrollToItem { DispatchQueue.main.async { self.coordinator.navigationBarContainer.scrollToItem(at: .init(row: tabsModel.currentIndex, section: 0), - at: .centeredHorizontally, animated: false) + at: .centeredHorizontally, animated: false) } } } +} + +// MARK: UICollectionViewDataSource +extension SwipeTabsCoordinator: UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { tabsModel.count } @@ -86,68 +173,6 @@ class SwipeTabsCoordinator: NSObject, UICollectionViewDataSource, UICollectionVi return cell } - var startOffsetX: CGFloat = 0.0 - var startingIndexPath: IndexPath? - weak var nextTabPreview: UIView? - - func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { - startOffsetX = scrollView.contentOffset.x - startingIndexPath = coordinator.navigationBarContainer.indexPathsForVisibleItems[0] - } - - var targetIndexPath: IndexPath? - func scrollViewDidScroll(_ scrollView: UIScrollView) { - guard let startingIndexPath else { return } - - let distance = scrollView.contentOffset.x - startOffsetX - - if abs(distance) > coordinator.superview.frame.width * 0.3 { - var targetIndexPath = startingIndexPath - if distance < 0 { - targetIndexPath.row -= 1 - } else { - targetIndexPath.row += 1 - } - self.targetIndexPath = targetIndexPath - } else { - targetIndexPath = nil - } - - if nextTabPreview == nil { - let index = startingIndexPath.row + (distance < 0 ? -1 : 1) - if tabsModel.tabs.indices.contains(index) { - let tab = tabsModel.get(tabAt: index) - - let view = UIView(frame: CGRect(origin: .zero, size: coordinator.contentContainer.frame.size)) - view.backgroundColor = .clear - coordinator.contentContainer.addSubview(view) - nextTabPreview = view - - let imageView = UIImageView(image: tabPreviewsSource.preview(for: tab)) - imageView.frame = view.frame - view.addSubview(imageView) - } - } - - // Could add some 'curve' or 'parallex' to this. - coordinator.contentContainer.subviews[0].frame.origin.x = -distance - if distance > 0 { - nextTabPreview?.frame.origin.x = coordinator.contentContainer.frame.size.width - abs(distance) - } else { - nextTabPreview?.frame.origin.x = -coordinator.contentContainer.frame.size.width + abs(distance) - } - } - - func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { - guard let targetIndexPath else { return } - print("***", #function, targetIndexPath) - coordinator.navigationBarContainer.scrollToItem(at: targetIndexPath, at: .centeredHorizontally, animated: true) - coordinator.contentContainer.subviews[0].frame.origin.x = 0 - selectTab(targetIndexPath.row) - self.nextTabPreview?.removeFromSuperview() - self.startingIndexPath = nil - self.targetIndexPath = nil - } } class OmniBarCell: UICollectionViewCell { @@ -168,29 +193,3 @@ class OmniBarCell: UICollectionViewCell { } } - -final class NavigationBarLayout: UICollectionViewFlowLayout { - override func targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint) -> CGPoint { - guard let collectionView = self.collectionView else { - return super.targetContentOffset(forProposedContentOffset: proposedContentOffset, withScrollingVelocity: velocity) - } - - let pageWidth = itemSize.width + minimumLineSpacing - let currentPage = collectionView.contentOffset.x / pageWidth - let nextPage = velocity.x.sign == .minus ? floor(currentPage) : ceil(currentPage) - let point = CGPoint(x: nextPage * pageWidth, y: proposedContentOffset.y) - - print("***", #function, currentPage, nextPage, point) - return point - } - - override func prepare() { - super.prepare() - guard let collectionView = self.collectionView else { return } - - itemSize = CGSize(width: collectionView.bounds.width, height: collectionView.bounds.height) - minimumLineSpacing = 0 - minimumInteritemSpacing = 0 - scrollDirection = .horizontal - } -} From e7d834890f14491d54d4141db4a8c641071a678a Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Tue, 23 Jan 2024 17:35:06 +0000 Subject: [PATCH 09/39] back to basics for scrolling logic --- DuckDuckGo/SwipeTabsCoordinator.swift | 100 +++++++++----------------- 1 file changed, 32 insertions(+), 68 deletions(-) diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index 6df208ee99..9ed8753455 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -34,13 +34,6 @@ class SwipeTabsCoordinator: NSObject { weak var tabPreviewsSource: TabPreviewsSource! let selectTab: (Int) -> Void - - var startOffsetX: CGFloat = 0.0 - var startingIndexPath: IndexPath? - weak var nextTabPreview: UIView? - - var targetIndexPath: IndexPath? - var isDragging = false init(coordinator: MainViewCoordinator, tabPreviewsSource: TabPreviewsSource, selectTab: @escaping (Int) -> Void) { self.coordinator = coordinator @@ -62,68 +55,41 @@ class SwipeTabsCoordinator: NSObject { // MARK: UICollectionViewDelegate extension SwipeTabsCoordinator: UICollectionViewDelegate { - - func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { - startOffsetX = scrollView.contentOffset.x - startingIndexPath = coordinator.navigationBarContainer.indexPathsForVisibleItems[0] - isDragging = true - } - - func scrollViewDidScroll(_ scrollView: UIScrollView) { - guard let startingIndexPath else { return } - print("***", #function, startingIndexPath) - - let distance = scrollView.contentOffset.x - startOffsetX - - if abs(distance) > coordinator.superview.frame.width * 0.3 { - var targetIndexPath = startingIndexPath - if distance < 0 { - targetIndexPath.row -= 1 - } else { - targetIndexPath.row += 1 - } - self.targetIndexPath = targetIndexPath - } else { - targetIndexPath = nil - } - - if nextTabPreview == nil { - let index = startingIndexPath.row + (distance < 0 ? -1 : 1) - if tabsModel.tabs.indices.contains(index) { - let tab = tabsModel.get(tabAt: index) + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + print("***", #function) + } + + func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + print("***", #function) + } - let view = UIView(frame: CGRect(origin: .zero, size: coordinator.contentContainer.frame.size)) - view.backgroundColor = .clear - coordinator.contentContainer.addSubview(view) - nextTabPreview = view + func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { + let index = coordinator.navigationBarContainer.indexPathsForVisibleItems[0].row + print("***", #function, index) + selectTab(index) + } - let imageView = UIImageView(image: tabPreviewsSource.preview(for: tab)) - imageView.frame = view.frame - view.addSubview(imageView) - } - } - - // Could add some 'curve' or 'parallex' to this. - coordinator.contentContainer.subviews[0].frame.origin.x = -distance - if distance > 0 { - nextTabPreview?.frame.origin.x = coordinator.contentContainer.frame.size.width - abs(distance) - } else { - nextTabPreview?.frame.origin.x = -coordinator.contentContainer.frame.size.width + abs(distance) - } - } + func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) { + print("***", #function) + } + + func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { + print("***", #function) + } + + func scrollViewDidChangeAdjustedContentInset(_ scrollView: UIScrollView) { + print("***", #function) + } + + func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + print("***", #function) + } + + func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { + print("***", #function) + } - func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { - isDragging = false - - guard let targetIndexPath else { return } - print("***", #function, targetIndexPath) - coordinator.navigationBarContainer.scrollToItem(at: targetIndexPath, at: .centeredHorizontally, animated: true) - coordinator.contentContainer.subviews[0].frame.origin.x = 0 - selectTab(targetIndexPath.row) - self.nextTabPreview?.removeFromSuperview() - self.startingIndexPath = nil - self.targetIndexPath = nil - } } // MARK: Public Interface @@ -131,7 +97,6 @@ extension SwipeTabsCoordinator { func refresh(tabsModel: TabsModel, scrollToSelected: Bool = false) { let scrollToItem = self.tabsModel == nil - print("***", #function, scrollToItem) self.tabsModel = tabsModel coordinator.navigationBarContainer.reloadData() @@ -159,7 +124,6 @@ extension SwipeTabsCoordinator: UICollectionViewDataSource { } if tabsModel.currentIndex == indexPath.row { - print("***", #function, "using real omnibar") cell.omniBar = coordinator.omniBar } else { let tab = tabsModel.get(tabAt: indexPath.row) From 0a011a67def1a1d52b060ccc605daedda4dc2658 Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Tue, 23 Jan 2024 18:14:04 +0000 Subject: [PATCH 10/39] get correct item when the collection view finishes decelerating --- DuckDuckGo/SwipeTabsCoordinator.swift | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index 9ed8753455..1643889682 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -25,6 +25,8 @@ import UIKit // TODO slide the logo when in homescreen view? +// TOD save preview when start dragging + class SwipeTabsCoordinator: NSObject { // Set by refresh function @@ -56,6 +58,10 @@ class SwipeTabsCoordinator: NSObject { // MARK: UICollectionViewDelegate extension SwipeTabsCoordinator: UICollectionViewDelegate { + func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { + print("***", #function, indexPath) + } + func scrollViewDidScroll(_ scrollView: UIScrollView) { print("***", #function) } @@ -65,9 +71,13 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { } func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { - let index = coordinator.navigationBarContainer.indexPathsForVisibleItems[0].row - print("***", #function, index) - selectTab(index) + print("***", #function, coordinator.navigationBarContainer.indexPathsForVisibleItems) + + let index = coordinator.navigationBarContainer.indexPathForItem(at: .init(x: coordinator.navigationBarContainer.bounds.midX, + y: coordinator.navigationBarContainer.bounds.midY))?.row + assert(index != nil) + selectTab(index ?? coordinator.navigationBarContainer.indexPathsForVisibleItems[0].row) + } func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) { From c575714037427fd9c5ab87157c286fd0beab2e32 Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Fri, 26 Jan 2024 23:01:25 +0000 Subject: [PATCH 11/39] swiping with previews --- DuckDuckGo/MainView.swift | 2 +- DuckDuckGo/SwipeTabsCoordinator.swift | 90 ++++++++++++++++++++++++--- 2 files changed, 83 insertions(+), 9 deletions(-) diff --git a/DuckDuckGo/MainView.swift b/DuckDuckGo/MainView.swift index d171b8090c..4054124778 100644 --- a/DuckDuckGo/MainView.swift +++ b/DuckDuckGo/MainView.swift @@ -77,7 +77,7 @@ extension MainViewFactory { } final class NavigationBarContainer: UICollectionView { } - private func createNavigationBarContainer() { + private func createNavigationBarContainer() { // Layout is replaced elsewhere, but required to construct the view. coordinator.navigationBarContainer = NavigationBarContainer(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) coordinator.navigationBarContainer.decelerationRate = .fast diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index 1643889682..ccb4e912f3 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -36,7 +36,7 @@ class SwipeTabsCoordinator: NSObject { weak var tabPreviewsSource: TabPreviewsSource! let selectTab: (Int) -> Void - + init(coordinator: MainViewCoordinator, tabPreviewsSource: TabPreviewsSource, selectTab: @escaping (Int) -> Void) { self.coordinator = coordinator self.tabPreviewsSource = tabPreviewsSource @@ -53,6 +53,23 @@ class SwipeTabsCoordinator: NSObject { layout?.scrollDirection = .horizontal } + enum State { + + case idle + case starting(CGPoint) + case swiping(CGPoint, FloatingPointSign) + case finishing + + } + + var state: State = .idle { + didSet { + print("***", #function, state) + } + } + + weak var preview: UIView? + } // MARK: UICollectionViewDelegate @@ -63,21 +80,76 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { } func scrollViewDidScroll(_ scrollView: UIScrollView) { - print("***", #function) + + switch state { + case .idle: break + + case .starting(let startPosition): + let offset = startPosition.x - scrollView.contentOffset.x + // coordinator.contentContainer.transform.tx = offset + preview?.transform.tx = offset + createPreview(offset) + state = .swiping(startPosition, offset.sign) + + case .swiping(let startPosition, let sign): + let offset = startPosition.x - scrollView.contentOffset.x + if offset.sign == sign { + // coordinator.contentContainer.transform.tx = offset + preview?.transform.tx = offset + } else { + state = .finishing + } + + case .finishing: break + } + } + + private func createPreview(_ offset: CGFloat) { + let modifier = (offset > 0 ? -1 : 1) + let nextIndex = tabsModel.currentIndex + modifier + print("***", #function, "nextIndex", nextIndex) + guard tabsModel.tabs.indices.contains(nextIndex) else { + print("***", #function, "invalid index", nextIndex) + return + } + let tab = tabsModel.get(tabAt: nextIndex) + guard let image = tabPreviewsSource.preview(for: tab) else { + print("***", #function, "no preview for tab at index", nextIndex) + return + } + + let imageView = UIImageView(image: image) + + imageView.layer.shadowOpacity = 0.5 + imageView.layer.shadowRadius = 10 + imageView.layer.shadowOffset = CGSize(width: 5, height: 5) + // imageView.transform = CGAffineTransform(scaleX: 1.05, y: 1.05) + + self.preview = imageView + imageView.frame = CGRect(origin: .zero, size: coordinator.contentContainer.frame.size) + let gap = CGFloat(10 * modifier) + imageView.frame.origin.x = (coordinator.contentContainer.frame.width * CGFloat(modifier)) + print("***", #function, "offset", imageView.frame.origin.x) + coordinator.contentContainer.addSubview(imageView) } func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { print("***", #function) + state = .starting(scrollView.contentOffset) } func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { print("***", #function, coordinator.navigationBarContainer.indexPathsForVisibleItems) - - let index = coordinator.navigationBarContainer.indexPathForItem(at: .init(x: coordinator.navigationBarContainer.bounds.midX, - y: coordinator.navigationBarContainer.bounds.midY))?.row + + let point = CGPoint(x: coordinator.navigationBarContainer.bounds.midX, + y: coordinator.navigationBarContainer.bounds.midY) + let index = coordinator.navigationBarContainer.indexPathForItem(at: point)?.row assert(index != nil) selectTab(index ?? coordinator.navigationBarContainer.indexPathsForVisibleItems[0].row) - + + preview?.removeFromSuperview() + + state = .idle } func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) { @@ -113,8 +185,10 @@ extension SwipeTabsCoordinator { if scrollToItem { DispatchQueue.main.async { - self.coordinator.navigationBarContainer.scrollToItem(at: .init(row: tabsModel.currentIndex, section: 0), - at: .centeredHorizontally, animated: false) + let indexPath = IndexPath(row: tabsModel.currentIndex, section: 0) + self.coordinator.navigationBarContainer.scrollToItem(at: indexPath, + at: .centeredHorizontally, + animated: false) } } } From 9b569ddfb38c8dc0c6a7864df56bcac03f14bfca Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Mon, 29 Jan 2024 13:36:50 +0000 Subject: [PATCH 12/39] clean up code, add feature flag, update preview on swipe --- Core/FeatureFlag.swift | 3 ++- DuckDuckGo/MainView.swift | 12 +++------ DuckDuckGo/MainViewController.swift | 38 +++++++++++++++++++++++---- DuckDuckGo/SwipeTabsCoordinator.swift | 28 +++++++++++++++----- 4 files changed, 61 insertions(+), 20 deletions(-) diff --git a/Core/FeatureFlag.swift b/Core/FeatureFlag.swift index f5c96b04ad..112e400eab 100644 --- a/Core/FeatureFlag.swift +++ b/Core/FeatureFlag.swift @@ -35,12 +35,13 @@ public enum FeatureFlag: String { case networkProtectionWaitlistAccess case networkProtectionWaitlistActive case subscription + case swipeTabs } extension FeatureFlag: FeatureFlagSourceProviding { public var source: FeatureFlagSource { switch self { - case .debugMenu, .appTrackingProtection, .subscription: + case .debugMenu, .appTrackingProtection, .subscription, .swipeTabs: return .internalOnly case .sync: return .remoteReleasable(.subfeature(SyncSubfeature.level0ShowSync)) diff --git a/DuckDuckGo/MainView.swift b/DuckDuckGo/MainView.swift index 4054124778..25f126eaca 100644 --- a/DuckDuckGo/MainView.swift +++ b/DuckDuckGo/MainView.swift @@ -81,6 +81,10 @@ extension MainViewFactory { // Layout is replaced elsewhere, but required to construct the view. coordinator.navigationBarContainer = NavigationBarContainer(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) coordinator.navigationBarContainer.decelerationRate = .fast + + // scrollview subclasses change the default to true, but we need this for the separator on the omnibar + coordinator.navigationBarContainer.clipsToBounds = false + superview.addSubview(coordinator.navigationBarContainer) } @@ -190,22 +194,15 @@ extension MainViewFactory { private func constrainNavigationBarContainer() { let navigationBarContainer = coordinator.navigationBarContainer! let toolbar = coordinator.toolbar! -// let omniBar = coordinator.omniBar! coordinator.constraints.navigationBarContainerTop = navigationBarContainer.constrainView(superview.safeAreaLayoutGuide, by: .top) coordinator.constraints.navigationBarContainerBottom = navigationBarContainer.constrainView(toolbar, by: .bottom, to: .top) -// coordinator.constraints.omniBarBottom = omniBar.constrainView(navigationBarContainer, by: .bottom, relatedBy: .greaterThanOrEqual) NSLayoutConstraint.activate([ coordinator.constraints.navigationBarContainerTop, navigationBarContainer.constrainView(superview, by: .centerX), navigationBarContainer.constrainView(superview, by: .width), navigationBarContainer.constrainAttribute(.height, to: 52), // , relatedBy: .greaterThanOrEqual), -// omniBar.constrainAttribute(.height, to: 52), -// omniBar.constrainView(navigationBarContainer, by: .top), -// omniBar.constrainView(navigationBarContainer, by: .leading), -// omniBar.constrainView(navigationBarContainer, by: .trailing), -// coordinator.constraints.omniBarBottom, ]) } @@ -372,7 +369,6 @@ class MainViewCoordinator { var notificationContainerTopToNavigationBar: NSLayoutConstraint! var notificationContainerTopToStatusBackground: NSLayoutConstraint! var notificationContainerHeight: NSLayoutConstraint! - // var omniBarBottom: NSLayoutConstraint! var progressBarTop: NSLayoutConstraint! var progressBarBottom: NSLayoutConstraint! var statusBackgroundToNavigationBarContainerBottom: NSLayoutConstraint! diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index ea4c59a127..096770f01b 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -224,7 +224,7 @@ class MainViewController: UIViewController { findInPageHeightLayoutConstraint = height } - var swipeTabsCoordinator: SwipeTabsCoordinator! + var swipeTabsCoordinator: SwipeTabsCoordinator? override func viewDidLoad() { super.viewDidLoad() @@ -236,10 +236,7 @@ class MainViewController: UIViewController { viewCoordinator.toolbarForwardButton.action = #selector(onForwardPressed) viewCoordinator.toolbarFireButton.action = #selector(onFirePressed) - swipeTabsCoordinator = SwipeTabsCoordinator(coordinator: viewCoordinator, tabPreviewsSource: previewsSource, selectTab: select) - - viewCoordinator.navigationBarContainer.delegate = swipeTabsCoordinator - viewCoordinator.navigationBarContainer.dataSource = swipeTabsCoordinator + configureSwipeTabsIfEnabled() loadSuggestionTray() loadTabsBarIfNeeded() @@ -304,6 +301,37 @@ class MainViewController: UIViewController { assertionFailure() super.performSegue(withIdentifier: identifier, sender: sender) } + + private func configureSwipeTabsIfEnabled() { + // Swipe tabs will be the default eventually, so the hierarchy is constructed that way. + if featureFlagger.isFeatureOn(.swipeTabs) { + swipeTabsCoordinator = SwipeTabsCoordinator(coordinator: viewCoordinator, + tabPreviewsSource: previewsSource, + appSettings: appSettings) { [weak self] in + self?.select(tabAt: $0) + } onSwipeStarted: { [weak self] in + guard let self, let currentTab = self.tabManager.current() else { return } + currentTab.preparePreview(completion: { image in + guard let image else { return } + self.previewsSource.update(preview: image, + forTab: currentTab.tabModel) + }) + } + + viewCoordinator.navigationBarContainer.dataSource = swipeTabsCoordinator + viewCoordinator.navigationBarContainer.delegate = swipeTabsCoordinator + } else { + // Readjust the hierarchy. + viewCoordinator.omniBar.translatesAutoresizingMaskIntoConstraints = true + viewCoordinator.omniBar.frame = viewCoordinator.navigationBarContainer.frame + viewCoordinator.navigationBarContainer.addSubview(viewCoordinator.omniBar) + + if !self.appSettings.currentAddressBarPosition.isBottom { + viewCoordinator.omniBar.showSeparator() + viewCoordinator.omniBar.moveSeparatorToBottom() + } + } + } func loadSuggestionTray() { let storyboard = UIStoryboard(name: "SuggestionTray", bundle: nil) diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index ccb4e912f3..6f253400f2 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -23,9 +23,11 @@ import UIKit // TODO handle iPad +// TODO handle orientation change + // TODO slide the logo when in homescreen view? -// TOD save preview when start dragging +// TODO fix gap behind when keyboard shown class SwipeTabsCoordinator: NSObject { @@ -34,13 +36,23 @@ class SwipeTabsCoordinator: NSObject { weak var coordinator: MainViewCoordinator! weak var tabPreviewsSource: TabPreviewsSource! + weak var appSettings: AppSettings! let selectTab: (Int) -> Void + let onSwipeStarted: () -> Void - init(coordinator: MainViewCoordinator, tabPreviewsSource: TabPreviewsSource, selectTab: @escaping (Int) -> Void) { + init(coordinator: MainViewCoordinator, + tabPreviewsSource: TabPreviewsSource, + appSettings: AppSettings, + selectTab: @escaping (Int) -> Void, + onSwipeStarted: @escaping () -> Void) { + self.coordinator = coordinator self.tabPreviewsSource = tabPreviewsSource + self.appSettings = appSettings + self.selectTab = selectTab + self.onSwipeStarted = onSwipeStarted coordinator.navigationBarContainer.register(OmniBarCell.self, forCellWithReuseIdentifier: "omnibar") coordinator.navigationBarContainer.isPagingEnabled = true @@ -86,15 +98,14 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { case .starting(let startPosition): let offset = startPosition.x - scrollView.contentOffset.x - // coordinator.contentContainer.transform.tx = offset preview?.transform.tx = offset createPreview(offset) state = .swiping(startPosition, offset.sign) + onSwipeStarted() case .swiping(let startPosition, let sign): let offset = startPosition.x - scrollView.contentOffset.x if offset.sign == sign { - // coordinator.contentContainer.transform.tx = offset preview?.transform.tx = offset } else { state = .finishing @@ -123,11 +134,9 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { imageView.layer.shadowOpacity = 0.5 imageView.layer.shadowRadius = 10 imageView.layer.shadowOffset = CGSize(width: 5, height: 5) - // imageView.transform = CGAffineTransform(scaleX: 1.05, y: 1.05) self.preview = imageView imageView.frame = CGRect(origin: .zero, size: coordinator.contentContainer.frame.size) - let gap = CGFloat(10 * modifier) imageView.frame.origin.x = (coordinator.contentContainer.frame.width * CGFloat(modifier)) print("***", #function, "offset", imageView.frame.origin.x) coordinator.contentContainer.addSubview(imageView) @@ -216,6 +225,13 @@ extension SwipeTabsCoordinator: UICollectionViewDataSource { cell.omniBar?.startBrowsing() cell.omniBar?.refreshText(forUrl: tab.link?.url) cell.omniBar?.decorate(with: ThemeManager.shared.currentTheme) + + cell.omniBar?.showSeparator() + if self.appSettings.currentAddressBarPosition.isBottom { + cell.omniBar?.moveSeparatorToTop() + } else { + cell.omniBar?.moveSeparatorToBottom() + } } return cell From 3ec72c19a0aa6835f40fb7edd9c7313c106ff325 Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Mon, 29 Jan 2024 14:37:57 +0000 Subject: [PATCH 13/39] support orientation change --- DuckDuckGo/MainViewController.swift | 6 +++-- DuckDuckGo/SwipeTabsCoordinator.swift | 38 ++++++++++++++++++--------- 2 files changed, 29 insertions(+), 15 deletions(-) diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 096770f01b..f0375c04fe 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -642,7 +642,7 @@ class MainViewController: UIViewController { override var shouldAutorotate: Bool { return true } - + @objc func dismissSuggestionTray() { dismissOmniBar() } @@ -1020,7 +1020,9 @@ class MainViewController: UIViewController { self.showMenuHighlighterIfNeeded() - coordinator.animate(alongsideTransition: nil) { _ in + coordinator.animate { _ in + self.swipeTabsCoordinator?.refresh(tabsModel: self.tabManager.model, scrollToSelected: true) + } completion: { _ in ViewHighlighter.updatePositions() } } diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index 6f253400f2..f812c3c82f 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -56,13 +56,10 @@ class SwipeTabsCoordinator: NSObject { coordinator.navigationBarContainer.register(OmniBarCell.self, forCellWithReuseIdentifier: "omnibar") coordinator.navigationBarContainer.isPagingEnabled = true + + super.init() - let layout = coordinator.navigationBarContainer.collectionViewLayout as? UICollectionViewFlowLayout - layout?.scrollDirection = .horizontal - layout?.itemSize = CGSize(width: coordinator.superview.frame.size.width, height: coordinator.omniBar.frame.height) - layout?.minimumLineSpacing = 0 - layout?.minimumInteritemSpacing = 0 - layout?.scrollDirection = .horizontal + updateLayout() } enum State { @@ -82,13 +79,32 @@ class SwipeTabsCoordinator: NSObject { weak var preview: UIView? + private func updateLayout() { + let layout = coordinator.navigationBarContainer.collectionViewLayout as? UICollectionViewFlowLayout + layout?.scrollDirection = .horizontal + layout?.itemSize = CGSize(width: coordinator.superview.frame.size.width, height: coordinator.omniBar.frame.height) + layout?.minimumLineSpacing = 0 + layout?.minimumInteritemSpacing = 0 + layout?.scrollDirection = .horizontal + } + + private func scrollToCurrent(animated: Bool = false) { + print("***", #function, animated) + DispatchQueue.main.async { + let indexPath = IndexPath(row: self.tabsModel.currentIndex, section: 0) + self.coordinator.navigationBarContainer.scrollToItem(at: indexPath, + at: .centeredHorizontally, + animated: animated) + } + } } // MARK: UICollectionViewDelegate extension SwipeTabsCoordinator: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { - print("***", #function, indexPath) + print("***", #function, "row:", indexPath.row, "currentIndex:", self.tabsModel.currentIndex) + scrollToCurrent() } func scrollViewDidScroll(_ scrollView: UIScrollView) { @@ -191,14 +207,10 @@ extension SwipeTabsCoordinator { self.tabsModel = tabsModel coordinator.navigationBarContainer.reloadData() + updateLayout() if scrollToItem { - DispatchQueue.main.async { - let indexPath = IndexPath(row: tabsModel.currentIndex, section: 0) - self.coordinator.navigationBarContainer.scrollToItem(at: indexPath, - at: .centeredHorizontally, - animated: false) - } + scrollToCurrent() } } From 5239257648c813af1c85029e624f63e49a2a5590 Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Mon, 29 Jan 2024 15:44:26 +0000 Subject: [PATCH 14/39] handle orientation change --- DuckDuckGo/MainView.swift | 6 ++--- DuckDuckGo/MainViewController.swift | 1 + DuckDuckGo/SwipeTabsCoordinator.swift | 32 +++++++++++++++++++++++---- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/DuckDuckGo/MainView.swift b/DuckDuckGo/MainView.swift index 25f126eaca..b41932a46b 100644 --- a/DuckDuckGo/MainView.swift +++ b/DuckDuckGo/MainView.swift @@ -200,9 +200,9 @@ extension MainViewFactory { NSLayoutConstraint.activate([ coordinator.constraints.navigationBarContainerTop, - navigationBarContainer.constrainView(superview, by: .centerX), - navigationBarContainer.constrainView(superview, by: .width), - navigationBarContainer.constrainAttribute(.height, to: 52), // , relatedBy: .greaterThanOrEqual), + navigationBarContainer.constrainView(superview, by: .leading), + navigationBarContainer.constrainView(superview, by: .trailing), + navigationBarContainer.constrainAttribute(.height, to: 52), ]) } diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index f0375c04fe..bfa45f0b09 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -311,6 +311,7 @@ class MainViewController: UIViewController { self?.select(tabAt: $0) } onSwipeStarted: { [weak self] in guard let self, let currentTab = self.tabManager.current() else { return } + hideKeyboard() currentTab.preparePreview(completion: { image in guard let image else { return } self.previewsSource.update(preview: image, diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index f812c3c82f..a237c5f549 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -56,7 +56,7 @@ class SwipeTabsCoordinator: NSObject { coordinator.navigationBarContainer.register(OmniBarCell.self, forCellWithReuseIdentifier: "omnibar") coordinator.navigationBarContainer.isPagingEnabled = true - + super.init() updateLayout() @@ -227,11 +227,14 @@ extension SwipeTabsCoordinator: UICollectionViewDataSource { guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "omnibar", for: indexPath) as? OmniBarCell else { fatalError("Not \(OmniBarCell.self)") } - + if tabsModel.currentIndex == indexPath.row { cell.omniBar = coordinator.omniBar } else { + cell.insetsLayoutMarginsFromSafeArea = true + let tab = tabsModel.get(tabAt: indexPath.row) + cell.omniBar = OmniBar.loadFromXib() cell.omniBar?.translatesAutoresizingMaskIntoConstraints = false cell.omniBar?.startBrowsing() @@ -253,19 +256,40 @@ extension SwipeTabsCoordinator: UICollectionViewDataSource { class OmniBarCell: UICollectionViewCell { + weak var leadingConstraint: NSLayoutConstraint? + weak var trailingConstraint: NSLayoutConstraint? + weak var omniBar: OmniBar? { didSet { subviews.forEach { $0.removeFromSuperview() } if let omniBar { addSubview(omniBar) + + let leadingConstraint = constrainView(omniBar, by: .leadingMargin) + let trailingConstraint = constrainView(omniBar, by: .trailingMargin) + NSLayoutConstraint.activate([ - constrainView(omniBar, by: .leading), - constrainView(omniBar, by: .trailing), + leadingConstraint, + trailingConstraint, constrainView(omniBar, by: .top), constrainView(omniBar, by: .bottom), ]) + + self.leadingConstraint = leadingConstraint + self.trailingConstraint = trailingConstraint } } } + override func updateConstraints() { + super.updateConstraints() + print("***", #function) + + let left = superview?.safeAreaInsets.left ?? 0 + let right = superview?.safeAreaInsets.right ?? 0 + + leadingConstraint?.constant = -left + trailingConstraint?.constant = right + } + } From c08d73d20577ad12d7c09fe66f724d082fe53955 Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Mon, 29 Jan 2024 18:37:27 +0000 Subject: [PATCH 15/39] handle ipad --- DuckDuckGo/MainViewController.swift | 77 ++++++++++++++++----------- DuckDuckGo/SwipeTabsCoordinator.swift | 8 +-- 2 files changed, 49 insertions(+), 36 deletions(-) diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index bfa45f0b09..7ffcec8fa8 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -236,7 +236,8 @@ class MainViewController: UIViewController { viewCoordinator.toolbarForwardButton.action = #selector(onForwardPressed) viewCoordinator.toolbarFireButton.action = #selector(onFirePressed) - configureSwipeTabsIfEnabled() + // configureSwipeTabsIfEnabled() + disableSwipeTabs() loadSuggestionTray() loadTabsBarIfNeeded() @@ -302,35 +303,47 @@ class MainViewController: UIViewController { super.performSegue(withIdentifier: identifier, sender: sender) } - private func configureSwipeTabsIfEnabled() { - // Swipe tabs will be the default eventually, so the hierarchy is constructed that way. - if featureFlagger.isFeatureOn(.swipeTabs) { - swipeTabsCoordinator = SwipeTabsCoordinator(coordinator: viewCoordinator, - tabPreviewsSource: previewsSource, - appSettings: appSettings) { [weak self] in - self?.select(tabAt: $0) - } onSwipeStarted: { [weak self] in - guard let self, let currentTab = self.tabManager.current() else { return } - hideKeyboard() - currentTab.preparePreview(completion: { image in - guard let image else { return } - self.previewsSource.update(preview: image, - forTab: currentTab.tabModel) - }) - } - - viewCoordinator.navigationBarContainer.dataSource = swipeTabsCoordinator - viewCoordinator.navigationBarContainer.delegate = swipeTabsCoordinator - } else { - // Readjust the hierarchy. - viewCoordinator.omniBar.translatesAutoresizingMaskIntoConstraints = true - viewCoordinator.omniBar.frame = viewCoordinator.navigationBarContainer.frame - viewCoordinator.navigationBarContainer.addSubview(viewCoordinator.omniBar) - - if !self.appSettings.currentAddressBarPosition.isBottom { - viewCoordinator.omniBar.showSeparator() - viewCoordinator.omniBar.moveSeparatorToBottom() - } + private func enableSwipeTabs() { + // This guard can be removed once the feature is enabled permanently. + guard featureFlagger.isFeatureOn(.swipeTabs) else { + disableSwipeTabs() + return + } + + guard swipeTabsCoordinator == nil else { + return + } + + swipeTabsCoordinator = SwipeTabsCoordinator(coordinator: viewCoordinator, + tabPreviewsSource: previewsSource, + appSettings: appSettings) { [weak self] in + self?.select(tabAt: $0) + } onSwipeStarted: { [weak self] in + guard let self, let currentTab = self.tabManager.current() else { return } + hideKeyboard() + currentTab.preparePreview(completion: { image in + guard let image else { return } + self.previewsSource.update(preview: image, + forTab: currentTab.tabModel) + }) + } + + viewCoordinator.navigationBarContainer.dataSource = swipeTabsCoordinator + viewCoordinator.navigationBarContainer.delegate = swipeTabsCoordinator + swipeTabsCoordinator?.refresh(tabsModel: self.tabManager.model, scrollToSelected: true) + } + + private func disableSwipeTabs() { + swipeTabsCoordinator = nil + + viewCoordinator.omniBar.removeFromSuperview() + viewCoordinator.omniBar.translatesAutoresizingMaskIntoConstraints = true + viewCoordinator.omniBar.frame = CGRect(origin: .zero, size: viewCoordinator.navigationBarContainer.frame.size) + viewCoordinator.navigationBarContainer.addSubview(viewCoordinator.omniBar) + + if !self.appSettings.currentAddressBarPosition.isBottom { + viewCoordinator.omniBar.showSeparator() + viewCoordinator.omniBar.moveSeparatorToBottom() } } @@ -1087,12 +1100,16 @@ class MainViewController: UIViewController { viewCoordinator.tabBarContainer.isHidden = false viewCoordinator.toolbar.isHidden = true viewCoordinator.omniBar.enterPadState() + + disableSwipeTabs() } private func applySmallWidth() { viewCoordinator.tabBarContainer.isHidden = true viewCoordinator.toolbar.isHidden = false viewCoordinator.omniBar.enterPhoneState() + + enableSwipeTabs() } @discardableResult diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index a237c5f549..6eb117af08 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -19,11 +19,7 @@ import UIKit -// TODO handle new tab - -// TODO handle iPad - -// TODO handle orientation change +// TODO handle launching on home screen tab // TODO slide the logo when in homescreen view? @@ -220,7 +216,7 @@ extension SwipeTabsCoordinator { extension SwipeTabsCoordinator: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - tabsModel.count + return tabsModel?.count ?? 0 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { From c5707448190fc9eca705cfa8a17aa7923fbde997 Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Mon, 29 Jan 2024 20:50:11 +0000 Subject: [PATCH 16/39] be more explicit about the disabling the swipe coordinator --- DuckDuckGo/MainViewController.swift | 64 +++++++++++++++++++-------- DuckDuckGo/SwipeTabsCoordinator.swift | 12 ++++- DuckDuckGo/TabViewController.swift | 6 +++ 3 files changed, 61 insertions(+), 21 deletions(-) diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 7ffcec8fa8..1ca1275cd2 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -236,9 +236,8 @@ class MainViewController: UIViewController { viewCoordinator.toolbarForwardButton.action = #selector(onForwardPressed) viewCoordinator.toolbarFireButton.action = #selector(onFirePressed) - // configureSwipeTabsIfEnabled() - disableSwipeTabs() - + installSwipeTabs() + loadSuggestionTray() loadTabsBarIfNeeded() loadFindInPage() @@ -303,10 +302,31 @@ class MainViewController: UIViewController { super.performSegue(withIdentifier: identifier, sender: sender) } + private func installSwipeTabs() { + guard swipeTabsCoordinator == nil else { return } + + swipeTabsCoordinator = SwipeTabsCoordinator(coordinator: viewCoordinator, + tabPreviewsSource: previewsSource, + appSettings: appSettings) { [weak self] in + self?.select(tabAt: $0) + } onSwipeStarted: { [weak self] in + guard let self, let currentTab = self.tabManager.current() else { return } + hideKeyboard() + currentTab.preparePreview(completion: { image in + guard let image else { return } + self.previewsSource.update(preview: image, + forTab: currentTab.tabModel) + }) + } + + viewCoordinator.navigationBarContainer.dataSource = swipeTabsCoordinator + viewCoordinator.navigationBarContainer.delegate = swipeTabsCoordinator + } + private func enableSwipeTabs() { // This guard can be removed once the feature is enabled permanently. guard featureFlagger.isFeatureOn(.swipeTabs) else { - disableSwipeTabs() + // disableSwipeTabs() return } @@ -330,22 +350,25 @@ class MainViewController: UIViewController { viewCoordinator.navigationBarContainer.dataSource = swipeTabsCoordinator viewCoordinator.navigationBarContainer.delegate = swipeTabsCoordinator - swipeTabsCoordinator?.refresh(tabsModel: self.tabManager.model, scrollToSelected: true) - } - - private func disableSwipeTabs() { - swipeTabsCoordinator = nil - - viewCoordinator.omniBar.removeFromSuperview() - viewCoordinator.omniBar.translatesAutoresizingMaskIntoConstraints = true - viewCoordinator.omniBar.frame = CGRect(origin: .zero, size: viewCoordinator.navigationBarContainer.frame.size) - viewCoordinator.navigationBarContainer.addSubview(viewCoordinator.omniBar) - if !self.appSettings.currentAddressBarPosition.isBottom { - viewCoordinator.omniBar.showSeparator() - viewCoordinator.omniBar.moveSeparatorToBottom() + if let model = self.tabManager?.model { + swipeTabsCoordinator?.refresh(tabsModel: model, scrollToSelected: true) } } + +// private func disableSwipeTabs() { +// swipeTabsCoordinator = nil +// +// viewCoordinator.omniBar.removeFromSuperview() +// viewCoordinator.omniBar.translatesAutoresizingMaskIntoConstraints = true +// viewCoordinator.omniBar.frame = CGRect(origin: .zero, size: viewCoordinator.navigationBarContainer.frame.size) +// viewCoordinator.navigationBarContainer.addSubview(viewCoordinator.omniBar) +// +// if !self.appSettings.currentAddressBarPosition.isBottom { +// viewCoordinator.omniBar.showSeparator() +// viewCoordinator.omniBar.moveSeparatorToBottom() +// } +// } func loadSuggestionTray() { let storyboard = UIStoryboard(name: "SuggestionTray", bundle: nil) @@ -1101,7 +1124,8 @@ class MainViewController: UIViewController { viewCoordinator.toolbar.isHidden = true viewCoordinator.omniBar.enterPadState() - disableSwipeTabs() + swipeTabsCoordinator?.isEnabled = false + // disableSwipeTabs() } private func applySmallWidth() { @@ -1109,7 +1133,9 @@ class MainViewController: UIViewController { viewCoordinator.toolbar.isHidden = false viewCoordinator.omniBar.enterPhoneState() - enableSwipeTabs() + swipeTabsCoordinator?.isEnabled = featureFlagger.isFeatureOn(.swipeTabs) + + // enableSwipeTabs() } @discardableResult diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index 6eb117af08..c33a5b0796 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -37,6 +37,12 @@ class SwipeTabsCoordinator: NSObject { let selectTab: (Int) -> Void let onSwipeStarted: () -> Void + var isEnabled = false { + didSet { + coordinator.navigationBarContainer.reloadData() + } + } + init(coordinator: MainViewCoordinator, tabPreviewsSource: TabPreviewsSource, appSettings: AppSettings, @@ -85,6 +91,8 @@ class SwipeTabsCoordinator: NSObject { } private func scrollToCurrent(animated: Bool = false) { + guard isEnabled else { return } + print("***", #function, animated) DispatchQueue.main.async { let indexPath = IndexPath(row: self.tabsModel.currentIndex, section: 0) @@ -216,7 +224,7 @@ extension SwipeTabsCoordinator { extension SwipeTabsCoordinator: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return tabsModel?.count ?? 0 + return isEnabled ? tabsModel?.count ?? 0 : 1 } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { @@ -224,7 +232,7 @@ extension SwipeTabsCoordinator: UICollectionViewDataSource { fatalError("Not \(OmniBarCell.self)") } - if tabsModel.currentIndex == indexPath.row { + if !isEnabled || tabsModel.currentIndex == indexPath.row { cell.omniBar = coordinator.omniBar } else { cell.insetsLayoutMarginsFromSafeArea = true diff --git a/DuckDuckGo/TabViewController.swift b/DuckDuckGo/TabViewController.swift index f54d55883b..71cf615886 100644 --- a/DuckDuckGo/TabViewController.swift +++ b/DuckDuckGo/TabViewController.swift @@ -1453,17 +1453,23 @@ extension TabViewController: WKNavigationDelegate { || !ContentBlocking.shared.privacyConfigurationManager.privacyConfig.isEnabled(featureKey: .contentBlocking) { rulesCompilationMonitor.reportNavigationDidNotWaitForRules() + + Swift.print("***", #function, "returning false") return false } Task { + Swift.print("***", #function, "Task started") rulesCompilationMonitor.tabWillWaitForRulesCompilation(tabModel.uid) showProgressIndicator() await userContentController.awaitContentBlockingAssetsInstalled() rulesCompilationMonitor.reportTabFinishedWaitingForRules(tabModel.uid) await MainActor.run(body: completion) + Swift.print("***", #function, "Task finished") } + + Swift.print("***", #function, "returning true") return true } From 23080fd7f7ae68a1f7c1feee01afff21eedd8e66 Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Tue, 30 Jan 2024 14:15:53 +0000 Subject: [PATCH 17/39] add some haptic feedback --- DuckDuckGo/SwipeTabsCoordinator.swift | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index c33a5b0796..b476e0a83c 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -37,6 +37,12 @@ class SwipeTabsCoordinator: NSObject { let selectTab: (Int) -> Void let onSwipeStarted: () -> Void + let feedbackGenerator: UISelectionFeedbackGenerator = { + let generator = UISelectionFeedbackGenerator() + generator.prepare() + return generator + }() + var isEnabled = false { didSet { coordinator.navigationBarContainer.reloadData() @@ -174,6 +180,7 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { y: coordinator.navigationBarContainer.bounds.midY) let index = coordinator.navigationBarContainer.indexPathForItem(at: point)?.row assert(index != nil) + feedbackGenerator.selectionChanged() selectTab(index ?? coordinator.navigationBarContainer.indexPathsForVisibleItems[0].row) preview?.removeFromSuperview() From 921cf1633ca09f8a36a6c8b34836045b2767e483 Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Tue, 30 Jan 2024 18:21:45 +0000 Subject: [PATCH 18/39] trying to get 10px gap --- DuckDuckGo/SwipeTabsCoordinator.swift | 30 +++++++++++++++++++-------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index b476e0a83c..e99cb17a6f 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -27,6 +27,8 @@ import UIKit class SwipeTabsCoordinator: NSObject { + static let tabGap: CGFloat = 10 + // Set by refresh function weak var tabsModel: TabsModel! @@ -86,6 +88,7 @@ class SwipeTabsCoordinator: NSObject { } weak var preview: UIView? + weak var currentView: UIView? private func updateLayout() { let layout = coordinator.navigationBarContainer.collectionViewLayout as? UICollectionViewFlowLayout @@ -124,8 +127,8 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { case .starting(let startPosition): let offset = startPosition.x - scrollView.contentOffset.x - preview?.transform.tx = offset - createPreview(offset) + prepareCurrentView() + preparePreview(offset) state = .swiping(startPosition, offset.sign) onSwipeStarted() @@ -133,6 +136,7 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { let offset = startPosition.x - scrollView.contentOffset.x if offset.sign == sign { preview?.transform.tx = offset + currentView?.transform.tx = offset } else { state = .finishing } @@ -141,7 +145,13 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { } } - private func createPreview(_ offset: CGFloat) { + private func prepareCurrentView() { + if coordinator.contentContainer.subviews.indices.contains(0) { + currentView = coordinator.contentContainer.subviews[0] + } + } + + private func preparePreview(_ offset: CGFloat) { let modifier = (offset > 0 ? -1 : 1) let nextIndex = tabsModel.currentIndex + modifier print("***", #function, "nextIndex", nextIndex) @@ -157,14 +167,15 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { let imageView = UIImageView(image: image) - imageView.layer.shadowOpacity = 0.5 - imageView.layer.shadowRadius = 10 - imageView.layer.shadowOffset = CGSize(width: 5, height: 5) +// imageView.layer.shadowOpacity = 0.5 +// imageView.layer.shadowRadius = 10 +// imageView.layer.shadowOffset = CGSize(width: 5, height: 5) self.preview = imageView imageView.frame = CGRect(origin: .zero, size: coordinator.contentContainer.frame.size) - imageView.frame.origin.x = (coordinator.contentContainer.frame.width * CGFloat(modifier)) - print("***", #function, "offset", imageView.frame.origin.x) + imageView.frame.origin.x = coordinator.contentContainer.frame.width * CGFloat(modifier) + + print("***", #function, "offset:", offset, "modified:", imageView.frame.origin.x) coordinator.contentContainer.addSubview(imageView) } @@ -182,7 +193,8 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { assert(index != nil) feedbackGenerator.selectionChanged() selectTab(index ?? coordinator.navigationBarContainer.indexPathsForVisibleItems[0].row) - + + currentView?.transform.tx = 0 preview?.removeFromSuperview() state = .idle From 6423ed421f45a63b221d6585b621811bbc7b2111 Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Tue, 30 Jan 2024 19:53:07 +0000 Subject: [PATCH 19/39] move views proportionally --- DuckDuckGo/SwipeTabsCoordinator.swift | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index e99cb17a6f..577921e366 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -135,7 +135,9 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { case .swiping(let startPosition, let sign): let offset = startPosition.x - scrollView.contentOffset.x if offset.sign == sign { - preview?.transform.tx = offset + let modifier = sign == .plus ? -1.0 : 1.0 + swipePreviewProportionally(offset: offset, modifier: modifier) + swipeCurrentViewProportionally(offset: offset) currentView?.transform.tx = offset } else { state = .finishing @@ -145,6 +147,20 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { } } + private func swipeCurrentViewProportionally(offset: CGFloat) { + currentView?.transform.tx = offset + } + + private func swipePreviewProportionally(offset: CGFloat, modifier: CGFloat) { + let width = coordinator.contentContainer.frame.width + let percent = offset / width + let swipeWidth = width + Self.tabGap + let x = (swipeWidth * percent) + (Self.tabGap * modifier) + + print("***", #function, width, percent, swipeWidth, x, offset) + preview?.transform.tx = x + } + private func prepareCurrentView() { if coordinator.contentContainer.subviews.indices.contains(0) { currentView = coordinator.contentContainer.subviews[0] @@ -167,10 +183,6 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { let imageView = UIImageView(image: image) -// imageView.layer.shadowOpacity = 0.5 -// imageView.layer.shadowRadius = 10 -// imageView.layer.shadowOffset = CGSize(width: 5, height: 5) - self.preview = imageView imageView.frame = CGRect(origin: .zero, size: coordinator.contentContainer.frame.size) imageView.frame.origin.x = coordinator.contentContainer.frame.width * CGFloat(modifier) @@ -194,7 +206,8 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { feedbackGenerator.selectionChanged() selectTab(index ?? coordinator.navigationBarContainer.indexPathsForVisibleItems[0].row) - currentView?.transform.tx = 0 + currentView?.transform = .identity + currentView = nil preview?.removeFromSuperview() state = .idle From 6d9c12c99e03b57182dee813fd7fcc661d778e8b Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Tue, 30 Jan 2024 22:21:28 +0000 Subject: [PATCH 20/39] handle swipe to/from home screen --- DuckDuckGo/MainViewController.swift | 8 +++-- DuckDuckGo/SwipeTabsCoordinator.swift | 49 +++++++++++++++++++++------ 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index e84767db3a..23ab166f9c 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -313,8 +313,12 @@ class MainViewController: UIViewController { appSettings: appSettings) { [weak self] in self?.select(tabAt: $0) } onSwipeStarted: { [weak self] in - guard let self, let currentTab = self.tabManager.current() else { return } - hideKeyboard() + self?.hideKeyboard() + + guard let self, + let currentTab = self.tabManager.current(), + currentTab.link != nil else { return } + currentTab.preparePreview(completion: { image in guard let image else { return } self.previewsSource.update(preview: image, diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index 577921e366..11072b4c48 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -162,8 +162,11 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { } private func prepareCurrentView() { - if coordinator.contentContainer.subviews.indices.contains(0) { - currentView = coordinator.contentContainer.subviews[0] + + if tabsModel.currentTab?.link == nil { + currentView = coordinator.logoContainer + } else { + currentView = coordinator.contentContainer.subviews.last } } @@ -175,20 +178,44 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { print("***", #function, "invalid index", nextIndex) return } + + let targetFrame = CGRect(origin: .zero, size: coordinator.contentContainer.frame.size) + let tab = tabsModel.get(tabAt: nextIndex) - guard let image = tabPreviewsSource.preview(for: tab) else { - print("***", #function, "no preview for tab at index", nextIndex) - return + if let image = tabPreviewsSource.preview(for: tab) { + print("***", #function, "image preview") + createPreviewFromImage(image) + } else if tab.link == nil { + print("***", #function, "home screen preview") + createPreviewFromLogoContainerWithSize(targetFrame.size) + } else { + print("***", #function, "no preview found") } - let imageView = UIImageView(image: image) - - self.preview = imageView - imageView.frame = CGRect(origin: .zero, size: coordinator.contentContainer.frame.size) - imageView.frame.origin.x = coordinator.contentContainer.frame.width * CGFloat(modifier) + preview?.frame = targetFrame + preview?.frame.origin.x = coordinator.contentContainer.frame.width * CGFloat(modifier) - print("***", #function, "offset:", offset, "modified:", imageView.frame.origin.x) + print("***", #function, "offset:", offset, "modified:", preview?.frame.origin.x ?? .nan) + } + + private func createPreviewFromImage(_ image: UIImage) { + let imageView = UIImageView(image: image) coordinator.contentContainer.addSubview(imageView) + preview = imageView + } + + private func createPreviewFromLogoContainerWithSize(_ size: CGSize) { + let origin = coordinator.contentContainer.convert(CGPoint.zero, to: coordinator.logoContainer) + let snapshotFrame = CGRect(origin: origin, size: size) + let isHidden = coordinator.logoContainer.isHidden + coordinator.logoContainer.isHidden = false + if let snapshotView = coordinator.logoContainer.resizableSnapshotView(from: snapshotFrame, + afterScreenUpdates: true, + withCapInsets: .zero) { + coordinator.contentContainer.addSubview(snapshotView) + preview = snapshotView + } + coordinator.logoContainer.isHidden = isHidden } func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { From fda4b56e51ab2217abe3303f61804089c3da9db0 Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Tue, 30 Jan 2024 23:42:19 +0000 Subject: [PATCH 21/39] allow swipe area of navigation bar to be increased, and adjust scrollbar depending on location --- DuckDuckGo/MainView.swift | 15 ++++++++++++--- DuckDuckGo/MainViewController.swift | 2 ++ DuckDuckGo/SwipeTabsCoordinator.swift | 13 +++++++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/DuckDuckGo/MainView.swift b/DuckDuckGo/MainView.swift index b41932a46b..98c1eeb464 100644 --- a/DuckDuckGo/MainView.swift +++ b/DuckDuckGo/MainView.swift @@ -61,9 +61,9 @@ extension MainViewFactory { createStatusBackground() createTabBarContainer() createOmniBar() + createToolbar() createNavigationBarContainer() createProgressView() - createToolbar() } private func createProgressView() { @@ -76,7 +76,16 @@ extension MainViewFactory { coordinator.omniBar.translatesAutoresizingMaskIntoConstraints = false } - final class NavigationBarContainer: UICollectionView { } + final class NavigationBarContainer: UICollectionView { + + var hitTestInsets = UIEdgeInsets.zero + + override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { + var extendedBounds = bounds.inset(by: hitTestInsets) + return extendedBounds.contains(point) + } + } + private func createNavigationBarContainer() { // Layout is replaced elsewhere, but required to construct the view. coordinator.navigationBarContainer = NavigationBarContainer(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) @@ -323,7 +332,7 @@ class MainViewCoordinator { var logo: UIImageView! var logoContainer: UIView! var logoText: UIImageView! - var navigationBarContainer: UICollectionView! + var navigationBarContainer: MainViewFactory.NavigationBarContainer! var notificationBarContainer: UIView! var omniBar: OmniBar! var progress: ProgressView! diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 23ab166f9c..d853fb8f33 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -530,10 +530,12 @@ class MainViewController: UIViewController { func refreshViewsBasedOnAddressBarPosition(_ position: AddressBarPosition) { switch position { case .top: + swipeTabsCoordinator?.addressBarPositionChanged(isTop: true) viewCoordinator.omniBar.moveSeparatorToBottom() viewCoordinator.showToolbarSeparator() case .bottom: + swipeTabsCoordinator?.addressBarPositionChanged(isTop: false) viewCoordinator.omniBar.moveSeparatorToTop() // If this is called before the toolbar has shown it will not re-add the separator when moving to the top position DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index 11072b4c48..959cff95a7 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -266,6 +266,7 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { extension SwipeTabsCoordinator { func refresh(tabsModel: TabsModel, scrollToSelected: Bool = false) { + print("***", #function) let scrollToItem = self.tabsModel == nil self.tabsModel = tabsModel @@ -277,6 +278,18 @@ extension SwipeTabsCoordinator { } } + func addressBarPositionChanged(isTop: Bool) { + if isTop { + coordinator.navigationBarContainer.horizontalScrollIndicatorInsets.bottom = 50 + coordinator.navigationBarContainer.hitTestInsets.top = -12 + coordinator.navigationBarContainer.hitTestInsets.bottom = 0 + } else { + coordinator.navigationBarContainer.horizontalScrollIndicatorInsets.bottom = -8 + coordinator.navigationBarContainer.hitTestInsets.top = 0 + coordinator.navigationBarContainer.hitTestInsets.bottom = -12 + } + } + } // MARK: UICollectionViewDataSource From 0f454334c7ce92ffe4cfbbd2f7c144a39978e39b Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Wed, 31 Jan 2024 12:52:06 +0000 Subject: [PATCH 22/39] handle change swipe direction mid swipe (Jackson's comment/video) --- DuckDuckGo/SwipeTabsCoordinator.swift | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index 959cff95a7..dd25b0137c 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -77,7 +77,6 @@ class SwipeTabsCoordinator: NSObject { case idle case starting(CGPoint) case swiping(CGPoint, FloatingPointSign) - case finishing } @@ -140,10 +139,9 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { swipeCurrentViewProportionally(offset: offset) currentView?.transform.tx = offset } else { - state = .finishing + cleanUpViews() + state = .starting(startPosition) } - - case .finishing: break } } @@ -156,8 +154,6 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { let percent = offset / width let swipeWidth = width + Self.tabGap let x = (swipeWidth * percent) + (Self.tabGap * modifier) - - print("***", #function, width, percent, swipeWidth, x, offset) preview?.transform.tx = x } @@ -232,14 +228,18 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { assert(index != nil) feedbackGenerator.selectionChanged() selectTab(index ?? coordinator.navigationBarContainer.indexPathsForVisibleItems[0].row) - - currentView?.transform = .identity - currentView = nil - preview?.removeFromSuperview() + + cleanUpViews() state = .idle } + private func cleanUpViews() { + currentView?.transform = .identity + currentView = nil + preview?.removeFromSuperview() + } + func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) { print("***", #function) } From f66b3820e22a9731b621fa4ddc6618502613f7b1 Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Wed, 31 Jan 2024 13:37:41 +0000 Subject: [PATCH 23/39] clean up --- DuckDuckGo/MainViewController.swift | 52 +-------------------------- DuckDuckGo/SwipeTabsCoordinator.swift | 12 +++---- 2 files changed, 7 insertions(+), 57 deletions(-) diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index d853fb8f33..786f047c61 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -315,7 +315,7 @@ class MainViewController: UIViewController { } onSwipeStarted: { [weak self] in self?.hideKeyboard() - guard let self, + guard let self, let currentTab = self.tabManager.current(), currentTab.link != nil else { return } @@ -325,57 +325,7 @@ class MainViewController: UIViewController { forTab: currentTab.tabModel) }) } - - viewCoordinator.navigationBarContainer.dataSource = swipeTabsCoordinator - viewCoordinator.navigationBarContainer.delegate = swipeTabsCoordinator } - - private func enableSwipeTabs() { - // This guard can be removed once the feature is enabled permanently. - guard featureFlagger.isFeatureOn(.swipeTabs) else { - // disableSwipeTabs() - return - } - - guard swipeTabsCoordinator == nil else { - return - } - - swipeTabsCoordinator = SwipeTabsCoordinator(coordinator: viewCoordinator, - tabPreviewsSource: previewsSource, - appSettings: appSettings) { [weak self] in - self?.select(tabAt: $0) - } onSwipeStarted: { [weak self] in - guard let self, let currentTab = self.tabManager.current() else { return } - hideKeyboard() - currentTab.preparePreview(completion: { image in - guard let image else { return } - self.previewsSource.update(preview: image, - forTab: currentTab.tabModel) - }) - } - - viewCoordinator.navigationBarContainer.dataSource = swipeTabsCoordinator - viewCoordinator.navigationBarContainer.delegate = swipeTabsCoordinator - - if let model = self.tabManager?.model { - swipeTabsCoordinator?.refresh(tabsModel: model, scrollToSelected: true) - } - } - -// private func disableSwipeTabs() { -// swipeTabsCoordinator = nil -// -// viewCoordinator.omniBar.removeFromSuperview() -// viewCoordinator.omniBar.translatesAutoresizingMaskIntoConstraints = true -// viewCoordinator.omniBar.frame = CGRect(origin: .zero, size: viewCoordinator.navigationBarContainer.frame.size) -// viewCoordinator.navigationBarContainer.addSubview(viewCoordinator.omniBar) -// -// if !self.appSettings.currentAddressBarPosition.isBottom { -// viewCoordinator.omniBar.showSeparator() -// viewCoordinator.omniBar.moveSeparatorToBottom() -// } -// } func loadSuggestionTray() { let storyboard = UIStoryboard(name: "SuggestionTray", bundle: nil) diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index dd25b0137c..3deb4832f1 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -280,13 +280,13 @@ extension SwipeTabsCoordinator { func addressBarPositionChanged(isTop: Bool) { if isTop { - coordinator.navigationBarContainer.horizontalScrollIndicatorInsets.bottom = 50 - coordinator.navigationBarContainer.hitTestInsets.top = -12 - coordinator.navigationBarContainer.hitTestInsets.bottom = 0 + collectionView.horizontalScrollIndicatorInsets.bottom = 50 + collectionView.hitTestInsets.top = -12 + collectionView.hitTestInsets.bottom = 0 } else { - coordinator.navigationBarContainer.horizontalScrollIndicatorInsets.bottom = -8 - coordinator.navigationBarContainer.hitTestInsets.top = 0 - coordinator.navigationBarContainer.hitTestInsets.bottom = -12 + collectionView.horizontalScrollIndicatorInsets.bottom = -8 + collectionView.hitTestInsets.top = 0 + collectionView.hitTestInsets.bottom = -12 } } From c04ff1cc24ef27757b9f2b4e960756bac06b204d Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Wed, 31 Jan 2024 14:14:15 +0000 Subject: [PATCH 24/39] prevent a gap appearing behind the keyboard when it goes away and refactor the code a little --- DuckDuckGo.xcodeproj/project.pbxproj | 4 + DuckDuckGo/MainView.swift | 168 +++++--------------------- DuckDuckGo/MainViewController.swift | 6 +- DuckDuckGo/MainViewCoordinator.swift | 147 ++++++++++++++++++++++ DuckDuckGo/SwipeTabsCoordinator.swift | 43 +++---- 5 files changed, 207 insertions(+), 161 deletions(-) create mode 100644 DuckDuckGo/MainViewCoordinator.swift diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 6731e36728..ebf64e9d50 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -497,6 +497,7 @@ 85DB12EB2A1FE2A4000A4A72 /* LockScreenWidgets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85DB12EA2A1FE2A4000A4A72 /* LockScreenWidgets.swift */; }; 85DB12ED2A1FED0C000A4A72 /* AppDelegate+AppDeepLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85DB12EC2A1FED0C000A4A72 /* AppDelegate+AppDeepLinks.swift */; }; 85DDE0402AC6FF65006ABCA2 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85DDE03F2AC6FF65006ABCA2 /* MainView.swift */; }; + 85DE681A2B6A8BB000DED4FE /* MainViewCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85DE68192B6A8BB000DED4FE /* MainViewCoordinator.swift */; }; 85DF714624F7FE6100C89288 /* Core.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F143C2E41E4A4CD400CFDE3A /* Core.framework */; }; 85DFEDED24C7CCA500973FE7 /* AppWidthObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85DFEDEC24C7CCA500973FE7 /* AppWidthObserver.swift */; }; 85DFEDEF24C7EA3B00973FE7 /* SmallOmniBarState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85DFEDEE24C7EA3B00973FE7 /* SmallOmniBarState.swift */; }; @@ -1590,6 +1591,7 @@ 85DB12EA2A1FE2A4000A4A72 /* LockScreenWidgets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockScreenWidgets.swift; sourceTree = ""; }; 85DB12EC2A1FED0C000A4A72 /* AppDelegate+AppDeepLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+AppDeepLinks.swift"; sourceTree = ""; }; 85DDE03F2AC6FF65006ABCA2 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = ""; }; + 85DE68192B6A8BB000DED4FE /* MainViewCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewCoordinator.swift; sourceTree = ""; }; 85DFEDEC24C7CCA500973FE7 /* AppWidthObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppWidthObserver.swift; sourceTree = ""; }; 85DFEDEE24C7EA3B00973FE7 /* SmallOmniBarState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SmallOmniBarState.swift; sourceTree = ""; }; 85DFEDF024C7EEA400973FE7 /* LargeOmniBarState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LargeOmniBarState.swift; sourceTree = ""; }; @@ -5432,6 +5434,7 @@ 851DFD86212C39D300D95F20 /* TabSwitcherButton.swift */, CBEFB9102ADFFE7900DEDE7B /* CriticalAlerts.swift */, 85B9814D2B5EB618009AC9A6 /* SwipeTabsCoordinator.swift */, + 85DE68192B6A8BB000DED4FE /* MainViewCoordinator.swift */, ); name = Main; sourceTree = ""; @@ -6708,6 +6711,7 @@ 3151F0EE2735800800226F58 /* VoiceSearchFeedbackView.swift in Sources */, 857EEB752095FFAC008A005C /* HomeRowInstructionsViewController.swift in Sources */, 311BD1AF2836BB4200AEF6C1 /* AutofillItemsLockedView.swift in Sources */, + 85DE681A2B6A8BB000DED4FE /* MainViewCoordinator.swift in Sources */, 0290472A29E867800008FE3C /* AppTPTrackerDetailView.swift in Sources */, F1617C151E57336D00DEDCAF /* TabManager.swift in Sources */, 85449EF523FDA02800512AAF /* KeyboardSettingsViewController.swift in Sources */, diff --git a/DuckDuckGo/MainView.swift b/DuckDuckGo/MainView.swift index 98c1eeb464..25881d444b 100644 --- a/DuckDuckGo/MainView.swift +++ b/DuckDuckGo/MainView.swift @@ -19,9 +19,6 @@ import UIKit -// swiftlint:disable file_length -// swiftlint:disable line_length - class MainViewFactory { private let coordinator: MainViewCoordinator @@ -63,6 +60,7 @@ extension MainViewFactory { createOmniBar() createToolbar() createNavigationBarContainer() + createNavigationBarCollectionView() createProgressView() } @@ -76,24 +74,38 @@ extension MainViewFactory { coordinator.omniBar.translatesAutoresizingMaskIntoConstraints = false } - final class NavigationBarContainer: UICollectionView { + final class NavigationBarCollectionView: UICollectionView { var hitTestInsets = UIEdgeInsets.zero override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { - var extendedBounds = bounds.inset(by: hitTestInsets) - return extendedBounds.contains(point) + return bounds.inset(by: hitTestInsets).contains(point) + } + + // Don't allow the use to drag the scrollbar or the UI will glitch. + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + let view = super.hitTest(point, with: event) + if view == self.subviews.first(where: { $0 is UIImageView }) { + return nil + } + return view } } - private func createNavigationBarContainer() { + private func createNavigationBarCollectionView() { // Layout is replaced elsewhere, but required to construct the view. - coordinator.navigationBarContainer = NavigationBarContainer(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) - coordinator.navigationBarContainer.decelerationRate = .fast + coordinator.navigationBarCollectionView = NavigationBarCollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) // scrollview subclasses change the default to true, but we need this for the separator on the omnibar - coordinator.navigationBarContainer.clipsToBounds = false + coordinator.navigationBarCollectionView.clipsToBounds = false + coordinator.navigationBarCollectionView.translatesAutoresizingMaskIntoConstraints = false + coordinator.navigationBarContainer.addSubview(coordinator.navigationBarCollectionView) + } + + final class NavigationBarContainer: UIView { } + private func createNavigationBarContainer() { + coordinator.navigationBarContainer = NavigationBarContainer() superview.addSubview(coordinator.navigationBarContainer) } @@ -199,19 +211,26 @@ extension MainViewFactory { coordinator.constraints.progressBarTop, ]) } - + private func constrainNavigationBarContainer() { let navigationBarContainer = coordinator.navigationBarContainer! let toolbar = coordinator.toolbar! + let navigationBarCollectionView = coordinator.navigationBarCollectionView! coordinator.constraints.navigationBarContainerTop = navigationBarContainer.constrainView(superview.safeAreaLayoutGuide, by: .top) coordinator.constraints.navigationBarContainerBottom = navigationBarContainer.constrainView(toolbar, by: .bottom, to: .top) - + coordinator.constraints.navigationBarCollectionViewBottom = navigationBarCollectionView.constrainView(navigationBarContainer, by: .bottom, relatedBy: .greaterThanOrEqual) + NSLayoutConstraint.activate([ coordinator.constraints.navigationBarContainerTop, navigationBarContainer.constrainView(superview, by: .leading), navigationBarContainer.constrainView(superview, by: .trailing), - navigationBarContainer.constrainAttribute(.height, to: 52), + navigationBarContainer.constrainAttribute(.height, to: 52, relatedBy: .greaterThanOrEqual), + navigationBarCollectionView.constrainAttribute(.height, to: 52), + navigationBarCollectionView.constrainView(navigationBarContainer, by: .top), + navigationBarCollectionView.constrainView(navigationBarContainer, by: .leading), + navigationBarCollectionView.constrainView(navigationBarContainer, by: .trailing), + coordinator.constraints.navigationBarCollectionViewBottom ]) } @@ -322,126 +341,3 @@ extension MainViewFactory { } } - -class MainViewCoordinator { - - let superview: UIView - - var contentContainer: UIView! - var lastToolbarButton: UIBarButtonItem! - var logo: UIImageView! - var logoContainer: UIView! - var logoText: UIImageView! - var navigationBarContainer: MainViewFactory.NavigationBarContainer! - var notificationBarContainer: UIView! - var omniBar: OmniBar! - var progress: ProgressView! - var statusBackground: UIView! - var suggestionTrayContainer: UIView! - var tabBarContainer: UIView! - var toolbar: UIToolbar! - var toolbarBackButton: UIBarButtonItem! - var toolbarFireButton: UIBarButtonItem! - var toolbarForwardButton: UIBarButtonItem! - var toolbarTabSwitcherButton: UIBarButtonItem! - - let constraints = Constraints() - - // The default after creating the hiearchy is top - var addressBarPosition: AddressBarPosition = .top - - fileprivate init(superview: UIView) { - self.superview = superview - } - - func decorateWithTheme(_ theme: Theme) { - superview.backgroundColor = theme.mainViewBackgroundColor - logoText.tintColor = theme.ddgTextTintColor - omniBar.decorate(with: theme) - } - - func showToolbarSeparator() { - toolbar.setShadowImage(nil, forToolbarPosition: .any) - } - - func hideToolbarSeparator() { - self.toolbar.setShadowImage(UIImage(), forToolbarPosition: .any) - } - - class Constraints { - - var navigationBarContainerTop: NSLayoutConstraint! - var navigationBarContainerBottom: NSLayoutConstraint! - var toolbarBottom: NSLayoutConstraint! - var contentContainerTop: NSLayoutConstraint! - var tabBarContainerTop: NSLayoutConstraint! - var notificationContainerTopToNavigationBar: NSLayoutConstraint! - var notificationContainerTopToStatusBackground: NSLayoutConstraint! - var notificationContainerHeight: NSLayoutConstraint! - var progressBarTop: NSLayoutConstraint! - var progressBarBottom: NSLayoutConstraint! - var statusBackgroundToNavigationBarContainerBottom: NSLayoutConstraint! - var statusBackgroundBottomToSafeAreaTop: NSLayoutConstraint! - var contentContainerBottomToToolbarTop: NSLayoutConstraint! - var contentContainerBottomToNavigationBarContainerTop: NSLayoutConstraint! - - } - - func moveAddressBarToPosition(_ position: AddressBarPosition) { - guard position != addressBarPosition else { return } - switch position { - case .top: - setAddressBarBottomActive(false) - setAddressBarTopActive(true) - - case .bottom: - setAddressBarTopActive(false) - setAddressBarBottomActive(true) - } - - addressBarPosition = position - } - - func hideNavigationBarWithBottomPosition() { - guard addressBarPosition.isBottom else { - return - } - - // Hiding the container won't suffice as it still defines the contentContainer.bottomY through constraints - navigationBarContainer.isHidden = true - - constraints.contentContainerBottomToNavigationBarContainerTop.isActive = false - constraints.contentContainerBottomToToolbarTop.isActive = true - } - - func showNavigationBarWithBottomPosition() { - guard addressBarPosition.isBottom else { - return - } - - constraints.contentContainerBottomToToolbarTop.isActive = false - constraints.contentContainerBottomToNavigationBarContainerTop.isActive = true - - navigationBarContainer.isHidden = false - } - - func setAddressBarTopActive(_ active: Bool) { - constraints.contentContainerBottomToToolbarTop.isActive = active - constraints.navigationBarContainerTop.isActive = active - constraints.progressBarTop.isActive = active - constraints.notificationContainerTopToNavigationBar.isActive = active - constraints.statusBackgroundToNavigationBarContainerBottom.isActive = active - } - - func setAddressBarBottomActive(_ active: Bool) { - constraints.contentContainerBottomToNavigationBarContainerTop.isActive = active - constraints.progressBarBottom.isActive = active - constraints.navigationBarContainerBottom.isActive = active - constraints.notificationContainerTopToStatusBackground.isActive = active - constraints.statusBackgroundBottomToSafeAreaTop.isActive = active - } - -} - -// swiftlint:enable line_length -// swiftlint:enable file_length diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 786f047c61..3b12916955 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -483,6 +483,7 @@ class MainViewController: UIViewController { swipeTabsCoordinator?.addressBarPositionChanged(isTop: true) viewCoordinator.omniBar.moveSeparatorToBottom() viewCoordinator.showToolbarSeparator() + viewCoordinator.constraints.navigationBarContainerBottom.isActive = false case .bottom: swipeTabsCoordinator?.addressBarPositionChanged(isTop: false) @@ -543,7 +544,7 @@ class MainViewController: UIViewController { if self.appSettings.currentAddressBarPosition.isBottom { let navBarOffset = min(0, self.toolbarHeight - intersection.height) - self.viewCoordinator.constraints.navigationBarContainerBottom.constant = navBarOffset + self.viewCoordinator.constraints.navigationBarCollectionViewBottom.constant = navBarOffset UIView.animate(withDuration: duration, delay: 0, options: animationCurve) { self.viewCoordinator.navigationBarContainer.superview?.layoutIfNeeded() } @@ -1089,7 +1090,6 @@ class MainViewController: UIViewController { viewCoordinator.omniBar.enterPadState() swipeTabsCoordinator?.isEnabled = false - // disableSwipeTabs() } private func applySmallWidth() { @@ -1098,8 +1098,6 @@ class MainViewController: UIViewController { viewCoordinator.omniBar.enterPhoneState() swipeTabsCoordinator?.isEnabled = featureFlagger.isFeatureOn(.swipeTabs) - - // enableSwipeTabs() } @discardableResult diff --git a/DuckDuckGo/MainViewCoordinator.swift b/DuckDuckGo/MainViewCoordinator.swift new file mode 100644 index 0000000000..9906ef7767 --- /dev/null +++ b/DuckDuckGo/MainViewCoordinator.swift @@ -0,0 +1,147 @@ +// +// MainViewCoordinator.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +class MainViewCoordinator { + + let superview: UIView + + var contentContainer: UIView! + var lastToolbarButton: UIBarButtonItem! + var logo: UIImageView! + var logoContainer: UIView! + var logoText: UIImageView! + var navigationBarContainer: UIView! + var navigationBarCollectionView: MainViewFactory.NavigationBarCollectionView! + var notificationBarContainer: UIView! + var omniBar: OmniBar! + var progress: ProgressView! + var statusBackground: UIView! + var suggestionTrayContainer: UIView! + var tabBarContainer: UIView! + var toolbar: UIToolbar! + var toolbarBackButton: UIBarButtonItem! + var toolbarFireButton: UIBarButtonItem! + var toolbarForwardButton: UIBarButtonItem! + var toolbarTabSwitcherButton: UIBarButtonItem! + + let constraints = Constraints() + + // The default after creating the hiearchy is top + var addressBarPosition: AddressBarPosition = .top + + /// STOP - why are you instanciating this? + init(superview: UIView) { + self.superview = superview + } + + func showToolbarSeparator() { + toolbar.setShadowImage(nil, forToolbarPosition: .any) + } + + func hideToolbarSeparator() { + self.toolbar.setShadowImage(UIImage(), forToolbarPosition: .any) + } + + class Constraints { + + var navigationBarContainerTop: NSLayoutConstraint! + var navigationBarContainerBottom: NSLayoutConstraint! + var navigationBarCollectionViewBottom: NSLayoutConstraint! + var toolbarBottom: NSLayoutConstraint! + var contentContainerTop: NSLayoutConstraint! + var tabBarContainerTop: NSLayoutConstraint! + var notificationContainerTopToNavigationBar: NSLayoutConstraint! + var notificationContainerTopToStatusBackground: NSLayoutConstraint! + var notificationContainerHeight: NSLayoutConstraint! + var progressBarTop: NSLayoutConstraint! + var progressBarBottom: NSLayoutConstraint! + var statusBackgroundToNavigationBarContainerBottom: NSLayoutConstraint! + var statusBackgroundBottomToSafeAreaTop: NSLayoutConstraint! + var contentContainerBottomToToolbarTop: NSLayoutConstraint! + var contentContainerBottomToNavigationBarContainerTop: NSLayoutConstraint! + + } + + func moveAddressBarToPosition(_ position: AddressBarPosition) { + guard position != addressBarPosition else { return } + switch position { + case .top: + setAddressBarBottomActive(false) + setAddressBarTopActive(true) + + case .bottom: + setAddressBarTopActive(false) + setAddressBarBottomActive(true) + } + + addressBarPosition = position + } + + func hideNavigationBarWithBottomPosition() { + guard addressBarPosition.isBottom else { + return + } + + // Hiding the container won't suffice as it still defines the contentContainer.bottomY through constraints + navigationBarContainer.isHidden = true + + constraints.contentContainerBottomToNavigationBarContainerTop.isActive = false + constraints.contentContainerBottomToToolbarTop.isActive = true + } + + func showNavigationBarWithBottomPosition() { + guard addressBarPosition.isBottom else { + return + } + + constraints.contentContainerBottomToToolbarTop.isActive = false + constraints.contentContainerBottomToNavigationBarContainerTop.isActive = true + + navigationBarContainer.isHidden = false + } + + func setAddressBarTopActive(_ active: Bool) { + constraints.contentContainerBottomToToolbarTop.isActive = active + constraints.navigationBarContainerTop.isActive = active + constraints.progressBarTop.isActive = active + constraints.notificationContainerTopToNavigationBar.isActive = active + constraints.statusBackgroundToNavigationBarContainerBottom.isActive = active + } + + func setAddressBarBottomActive(_ active: Bool) { + constraints.contentContainerBottomToNavigationBarContainerTop.isActive = active + constraints.progressBarBottom.isActive = active + constraints.navigationBarContainerBottom.isActive = active + constraints.notificationContainerTopToStatusBackground.isActive = active + constraints.statusBackgroundBottomToSafeAreaTop.isActive = active + } + +} + +extension MainViewCoordinator: Themable { + + func decorate(with theme: Theme) { + superview.backgroundColor = theme.mainViewBackgroundColor + logoText.tintColor = theme.ddgTextTintColor + omniBar.decorate(with: theme) + } + +} diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index 3deb4832f1..ebe0b93c60 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -19,12 +19,6 @@ import UIKit -// TODO handle launching on home screen tab - -// TODO slide the logo when in homescreen view? - -// TODO fix gap behind when keyboard shown - class SwipeTabsCoordinator: NSObject { static let tabGap: CGFloat = 10 @@ -47,10 +41,14 @@ class SwipeTabsCoordinator: NSObject { var isEnabled = false { didSet { - coordinator.navigationBarContainer.reloadData() + collectionView.reloadData() } } + var collectionView: MainViewFactory.NavigationBarCollectionView { + coordinator.navigationBarCollectionView + } + init(coordinator: MainViewCoordinator, tabPreviewsSource: TabPreviewsSource, appSettings: AppSettings, @@ -63,12 +61,15 @@ class SwipeTabsCoordinator: NSObject { self.selectTab = selectTab self.onSwipeStarted = onSwipeStarted - - coordinator.navigationBarContainer.register(OmniBarCell.self, forCellWithReuseIdentifier: "omnibar") - coordinator.navigationBarContainer.isPagingEnabled = true - + super.init() + collectionView.register(OmniBarCell.self, forCellWithReuseIdentifier: "omnibar") + collectionView.isPagingEnabled = true + collectionView.delegate = self + collectionView.dataSource = self + collectionView.decelerationRate = .fast + updateLayout() } @@ -90,7 +91,7 @@ class SwipeTabsCoordinator: NSObject { weak var currentView: UIView? private func updateLayout() { - let layout = coordinator.navigationBarContainer.collectionViewLayout as? UICollectionViewFlowLayout + let layout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout layout?.scrollDirection = .horizontal layout?.itemSize = CGSize(width: coordinator.superview.frame.size.width, height: coordinator.omniBar.frame.height) layout?.minimumLineSpacing = 0 @@ -104,9 +105,9 @@ class SwipeTabsCoordinator: NSObject { print("***", #function, animated) DispatchQueue.main.async { let indexPath = IndexPath(row: self.tabsModel.currentIndex, section: 0) - self.coordinator.navigationBarContainer.scrollToItem(at: indexPath, - at: .centeredHorizontally, - animated: animated) + self.collectionView.scrollToItem(at: indexPath, + at: .centeredHorizontally, + animated: animated) } } } @@ -220,14 +221,14 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { } func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { - print("***", #function, coordinator.navigationBarContainer.indexPathsForVisibleItems) + print("***", #function, coordinator.navigationBarCollectionView.indexPathsForVisibleItems) - let point = CGPoint(x: coordinator.navigationBarContainer.bounds.midX, - y: coordinator.navigationBarContainer.bounds.midY) - let index = coordinator.navigationBarContainer.indexPathForItem(at: point)?.row + let point = CGPoint(x: coordinator.navigationBarCollectionView.bounds.midX, + y: coordinator.navigationBarCollectionView.bounds.midY) + let index = coordinator.navigationBarCollectionView.indexPathForItem(at: point)?.row assert(index != nil) feedbackGenerator.selectionChanged() - selectTab(index ?? coordinator.navigationBarContainer.indexPathsForVisibleItems[0].row) + selectTab(index ?? coordinator.navigationBarCollectionView.indexPathsForVisibleItems[0].row) cleanUpViews() @@ -270,7 +271,7 @@ extension SwipeTabsCoordinator { let scrollToItem = self.tabsModel == nil self.tabsModel = tabsModel - coordinator.navigationBarContainer.reloadData() + coordinator.navigationBarCollectionView.reloadData() updateLayout() if scrollToItem { From 9210a12a30853e0e35d6f23969789709b17ea18b Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Wed, 31 Jan 2024 14:25:52 +0000 Subject: [PATCH 25/39] swiftlint --- DuckDuckGo/MainView.swift | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/DuckDuckGo/MainView.swift b/DuckDuckGo/MainView.swift index 25881d444b..06564f8329 100644 --- a/DuckDuckGo/MainView.swift +++ b/DuckDuckGo/MainView.swift @@ -219,7 +219,8 @@ extension MainViewFactory { coordinator.constraints.navigationBarContainerTop = navigationBarContainer.constrainView(superview.safeAreaLayoutGuide, by: .top) coordinator.constraints.navigationBarContainerBottom = navigationBarContainer.constrainView(toolbar, by: .bottom, to: .top) - coordinator.constraints.navigationBarCollectionViewBottom = navigationBarCollectionView.constrainView(navigationBarContainer, by: .bottom, relatedBy: .greaterThanOrEqual) + coordinator.constraints.navigationBarCollectionViewBottom + = navigationBarCollectionView.constrainView(navigationBarContainer, by: .bottom, relatedBy: .greaterThanOrEqual) NSLayoutConstraint.activate([ coordinator.constraints.navigationBarContainerTop, @@ -251,9 +252,11 @@ extension MainViewFactory { let statusBackground = coordinator.statusBackground! let navigationBarContainer = coordinator.navigationBarContainer! - coordinator.constraints.statusBackgroundToNavigationBarContainerBottom = statusBackground.constrainView(navigationBarContainer, by: .bottom) + coordinator.constraints.statusBackgroundToNavigationBarContainerBottom + = statusBackground.constrainView(navigationBarContainer, by: .bottom) - coordinator.constraints.statusBackgroundBottomToSafeAreaTop = statusBackground.constrainView(coordinator.superview.safeAreaLayoutGuide, by: .bottom, to: .top) + coordinator.constraints.statusBackgroundBottomToSafeAreaTop + = statusBackground.constrainView(coordinator.superview.safeAreaLayoutGuide, by: .bottom, to: .top) NSLayoutConstraint.activate([ statusBackground.constrainView(superview, by: .width), @@ -269,8 +272,10 @@ extension MainViewFactory { let navigationBarContainer = coordinator.navigationBarContainer! let statusBackground = coordinator.statusBackground! - coordinator.constraints.notificationContainerTopToNavigationBar = notificationBarContainer.constrainView(navigationBarContainer, by: .top, to: .bottom) - coordinator.constraints.notificationContainerTopToStatusBackground = notificationBarContainer.constrainView(statusBackground, by: .top, to: .bottom) + coordinator.constraints.notificationContainerTopToNavigationBar + = notificationBarContainer.constrainView(navigationBarContainer, by: .top, to: .bottom) + coordinator.constraints.notificationContainerTopToStatusBackground + = notificationBarContainer.constrainView(statusBackground, by: .top, to: .bottom) coordinator.constraints.notificationContainerHeight = notificationBarContainer.constrainAttribute(.height, to: 0) NSLayoutConstraint.activate([ @@ -290,7 +295,8 @@ extension MainViewFactory { coordinator.constraints.contentContainerTop = contentContainer.constrainView(notificationBarContainer, by: .top, to: .bottom) coordinator.constraints.contentContainerBottomToToolbarTop = contentContainer.constrainView(toolbar, by: .bottom, to: .top) - coordinator.constraints.contentContainerBottomToNavigationBarContainerTop = contentContainer.constrainView(navigationBarContainer, by: .bottom, to: .top) + coordinator.constraints.contentContainerBottomToNavigationBarContainerTop + = contentContainer.constrainView(navigationBarContainer, by: .bottom, to: .top) NSLayoutConstraint.activate([ contentContainer.constrainView(superview, by: .leading), From 9edbfc9d200ab4832e0074c1c06392a025ec2e98 Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Wed, 31 Jan 2024 17:26:10 +0000 Subject: [PATCH 26/39] show new tab when swiping far enough to the right --- DuckDuckGo/MainViewController.swift | 2 ++ DuckDuckGo/SwipeTabsCoordinator.swift | 43 +++++++++++++++++++-------- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 3b12916955..8cb119f1d7 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -312,6 +312,8 @@ class MainViewController: UIViewController { tabPreviewsSource: previewsSource, appSettings: appSettings) { [weak self] in self?.select(tabAt: $0) + } newTab: { [weak self] in + self?.newTab() } onSwipeStarted: { [weak self] in self?.hideKeyboard() diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index ebe0b93c60..996d7f0ac0 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -31,6 +31,7 @@ class SwipeTabsCoordinator: NSObject { weak var appSettings: AppSettings! let selectTab: (Int) -> Void + let newTab: () -> Void let onSwipeStarted: () -> Void let feedbackGenerator: UISelectionFeedbackGenerator = { @@ -53,6 +54,7 @@ class SwipeTabsCoordinator: NSObject { tabPreviewsSource: TabPreviewsSource, appSettings: AppSettings, selectTab: @escaping (Int) -> Void, + newTab: @escaping () -> Void, onSwipeStarted: @escaping () -> Void) { self.coordinator = coordinator @@ -60,6 +62,7 @@ class SwipeTabsCoordinator: NSObject { self.appSettings = appSettings self.selectTab = selectTab + self.newTab = newTab self.onSwipeStarted = onSwipeStarted super.init() @@ -223,16 +226,25 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { print("***", #function, coordinator.navigationBarCollectionView.indexPathsForVisibleItems) + defer { + cleanUpViews() + state = .idle + } + let point = CGPoint(x: coordinator.navigationBarCollectionView.bounds.midX, y: coordinator.navigationBarCollectionView.bounds.midY) - let index = coordinator.navigationBarCollectionView.indexPathForItem(at: point)?.row - assert(index != nil) + + guard let index = coordinator.navigationBarCollectionView.indexPathForItem(at: point)?.row else { + assertionFailure("invalid index") + return + } feedbackGenerator.selectionChanged() - selectTab(index ?? coordinator.navigationBarCollectionView.indexPathsForVisibleItems[0].row) - - cleanUpViews() - - state = .idle + if index >= tabsModel.count { + print("***", #function, "add tab!") + newTab() + } else { + selectTab(index) + } } private func cleanUpViews() { @@ -297,7 +309,9 @@ extension SwipeTabsCoordinator { extension SwipeTabsCoordinator: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return isEnabled ? tabsModel?.count ?? 0 : 1 + guard isEnabled else { return 1 } + let extras = tabsModel.tabs.last?.link != nil ? 1 : 0 // last tab is not a home page, so let's add one + return tabsModel.count + extras } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { @@ -309,13 +323,9 @@ extension SwipeTabsCoordinator: UICollectionViewDataSource { cell.omniBar = coordinator.omniBar } else { cell.insetsLayoutMarginsFromSafeArea = true - - let tab = tabsModel.get(tabAt: indexPath.row) cell.omniBar = OmniBar.loadFromXib() cell.omniBar?.translatesAutoresizingMaskIntoConstraints = false - cell.omniBar?.startBrowsing() - cell.omniBar?.refreshText(forUrl: tab.link?.url) cell.omniBar?.decorate(with: ThemeManager.shared.currentTheme) cell.omniBar?.showSeparator() @@ -324,6 +334,15 @@ extension SwipeTabsCoordinator: UICollectionViewDataSource { } else { cell.omniBar?.moveSeparatorToBottom() } + + if tabsModel.tabs.indices.contains(indexPath.row) { + let tab = tabsModel.get(tabAt: indexPath.row) + cell.omniBar?.startBrowsing() + cell.omniBar?.refreshText(forUrl: tab.link?.url) + } else { + cell.omniBar?.stopBrowsing() + } + } return cell From 804e6dc8bbc4231b203f3044f7858a94db1b58b9 Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Wed, 31 Jan 2024 17:41:13 +0000 Subject: [PATCH 27/39] apply scroll bar tweaking --- DuckDuckGo/MainViewController.swift | 4 ++++ DuckDuckGo/SwipeTabsCoordinator.swift | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 8cb119f1d7..62888a3094 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -282,12 +282,16 @@ class MainViewController: UIViewController { tabManager.cleanupTabsFaviconCache() + // Needs to be called here to established correct view hierarchy refreshViewsBasedOnAddressBarPosition(appSettings.currentAddressBarPosition) } override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) + // Needs to be called here because sometimes the frames are not the expected size during didLoad + refreshViewsBasedOnAddressBarPosition(appSettings.currentAddressBarPosition) + startOnboardingFlowIfNotSeenBefore() tabsBarController?.refresh(tabsModel: tabManager.model) swipeTabsCoordinator?.refresh(tabsModel: tabManager.model) diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index 996d7f0ac0..cb01700d7f 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -293,11 +293,11 @@ extension SwipeTabsCoordinator { func addressBarPositionChanged(isTop: Bool) { if isTop { - collectionView.horizontalScrollIndicatorInsets.bottom = 50 + collectionView.horizontalScrollIndicatorInsets.bottom = -2 collectionView.hitTestInsets.top = -12 collectionView.hitTestInsets.bottom = 0 } else { - collectionView.horizontalScrollIndicatorInsets.bottom = -8 + collectionView.horizontalScrollIndicatorInsets.bottom = collectionView.frame.height - 8 collectionView.hitTestInsets.top = 0 collectionView.hitTestInsets.bottom = -12 } From cc424daf408358dc6525fb59d86aa9b9dd580f64 Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Wed, 31 Jan 2024 17:50:36 +0000 Subject: [PATCH 28/39] handle new tab / home screen transition --- DuckDuckGo/SwipeTabsCoordinator.swift | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index cb01700d7f..5daaf2baee 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -174,19 +174,17 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { let modifier = (offset > 0 ? -1 : 1) let nextIndex = tabsModel.currentIndex + modifier print("***", #function, "nextIndex", nextIndex) - guard tabsModel.tabs.indices.contains(nextIndex) else { - print("***", #function, "invalid index", nextIndex) - return - } let targetFrame = CGRect(origin: .zero, size: coordinator.contentContainer.frame.size) - let tab = tabsModel.get(tabAt: nextIndex) - if let image = tabPreviewsSource.preview(for: tab) { + let tab = tabsModel.safeGetTabAt(nextIndex) + if let tab, let image = tabPreviewsSource.preview(for: tab) { print("***", #function, "image preview") createPreviewFromImage(image) - } else if tab.link == nil { - print("***", #function, "home screen preview") + } else if tab?.link == nil { + // either the tab or link is nil - either way transition to the home screen + // TODO handle favorites + print("***", #function, "new tab home screen preview") createPreviewFromLogoContainerWithSize(targetFrame.size) } else { print("***", #function, "no preview found") @@ -389,3 +387,12 @@ class OmniBarCell: UICollectionViewCell { } } + +extension TabsModel { + + func safeGetTabAt(_ index: Int) -> Tab? { + guard tabs.indices.contains(index) else { return nil } + return tabs[index] + } + +} From 103dcd338796981a50951db691472f034743ba98 Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Thu, 1 Feb 2024 11:18:59 +0000 Subject: [PATCH 29/39] add pixel and only fire / change tabs when the index is different --- Core/PixelEvent.swift | 5 +++++ DuckDuckGo/MainViewController.swift | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/Core/PixelEvent.swift b/Core/PixelEvent.swift index babb0b9179..8e4938bcb2 100644 --- a/Core/PixelEvent.swift +++ b/Core/PixelEvent.swift @@ -502,6 +502,9 @@ extension Pixel { case syncDeleteAccountError case syncLoginExistingAccountError + case swipeTabsUsed + case swipeTabsUsedDaily + case bookmarksCleanupFailed case bookmarksCleanupAttemptedWhileSyncWasEnabled case favoritesCleanupFailed @@ -995,6 +998,8 @@ extension Pixel.Event { case .syncDeleteAccountError: return "m_d_sync_delete_account_error" case .syncLoginExistingAccountError: return "m_d_sync_login_existing_account_error" + case .swipeTabsUsed: return "m_swipe-tabs-used" + case .swipeTabsUsedDaily: return "m_swipe-tabs-used-daily" case .bookmarksCleanupFailed: return "m_d_bookmarks_cleanup_failed" case .bookmarksCleanupAttemptedWhileSyncWasEnabled: return "m_d_bookmarks_cleanup_attempted_while_sync_was_enabled" diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 62888a3094..3933ba0bb6 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -315,7 +315,13 @@ class MainViewController: UIViewController { swipeTabsCoordinator = SwipeTabsCoordinator(coordinator: viewCoordinator, tabPreviewsSource: previewsSource, appSettings: appSettings) { [weak self] in + + guard $0 != self?.tabManager.model.currentIndex else { return } + + DailyPixel.fire(pixel: .swipeTabsUsedDaily) + Pixel.fire(pixel: .swipeTabsUsed) self?.select(tabAt: $0) + } newTab: { [weak self] in self?.newTab() } onSwipeStarted: { [weak self] in From 229de2594878754edccfc892c691e6090428cf0b Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Thu, 1 Feb 2024 16:38:53 +0000 Subject: [PATCH 30/39] prepare / use appropriate previews for the home screen / favorites --- Core/UIViewExtension.swift | 13 +++++++++++++ DuckDuckGo/MainViewController.swift | 26 ++++++++++++++++++++++---- DuckDuckGo/SwipeTabsCoordinator.swift | 2 +- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/Core/UIViewExtension.swift b/Core/UIViewExtension.swift index eb12e52945..d1cea20ad8 100644 --- a/Core/UIViewExtension.swift +++ b/Core/UIViewExtension.swift @@ -73,4 +73,17 @@ extension UIView { view.removeFromSuperview() } } + + @MainActor + public func createImageSnapshot(inBounds bounds: CGRect? = nil) -> UIImage? { + let bounds = bounds ?? self.frame + let size = bounds.size + UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale) + UIGraphicsGetCurrentContext()?.translateBy(x: -bounds.origin.x, y: -bounds.origin.y) + drawHierarchy(in: frame, afterScreenUpdates: true) + let image = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return image + } + } diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 3933ba0bb6..ee8434f136 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -326,16 +326,34 @@ class MainViewController: UIViewController { self?.newTab() } onSwipeStarted: { [weak self] in self?.hideKeyboard() + self?.updatePreviewForCurrentTab() + } + } + + func updatePreviewForCurrentTab() { + assert(Thread.isMainThread) + + if !viewCoordinator.logoContainer.isHidden, + self.tabManager.current()?.link == nil, + let tab = self.tabManager.model.currentTab { - guard let self, - let currentTab = self.tabManager.current(), - currentTab.link != nil else { return } - + // Home screen with logo + if let image = viewCoordinator.logoContainer.createImageSnapshot(inBounds: viewCoordinator.contentContainer.frame) { + previewsSource.update(preview: image, forTab: tab) + } + + } else if let currentTab = self.tabManager.current(), currentTab.link != nil { + // Web view currentTab.preparePreview(completion: { image in guard let image else { return } self.previewsSource.update(preview: image, forTab: currentTab.tabModel) }) + } else if let tab = self.tabManager.model.currentTab { + // Favorites, etc + if let image = viewCoordinator.contentContainer.createImageSnapshot() { + previewsSource.update(preview: image, forTab: tab) + } } } diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index 5daaf2baee..ab3572b8bf 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -163,7 +163,7 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { private func prepareCurrentView() { - if tabsModel.currentTab?.link == nil { + if !coordinator.logoContainer.isHidden { currentView = coordinator.logoContainer } else { currentView = coordinator.contentContainer.subviews.last From a83f693a70e1e4b932e74c813654621eb0aca98d Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Thu, 1 Feb 2024 17:04:18 +0000 Subject: [PATCH 31/39] tweak scroll bar position --- DuckDuckGo/SwipeTabsCoordinator.swift | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index ab3572b8bf..ba35c8a3c3 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -182,8 +182,6 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { print("***", #function, "image preview") createPreviewFromImage(image) } else if tab?.link == nil { - // either the tab or link is nil - either way transition to the home screen - // TODO handle favorites print("***", #function, "new tab home screen preview") createPreviewFromLogoContainerWithSize(targetFrame.size) } else { @@ -291,11 +289,11 @@ extension SwipeTabsCoordinator { func addressBarPositionChanged(isTop: Bool) { if isTop { - collectionView.horizontalScrollIndicatorInsets.bottom = -2 + collectionView.horizontalScrollIndicatorInsets.bottom = -1.5 collectionView.hitTestInsets.top = -12 collectionView.hitTestInsets.bottom = 0 } else { - collectionView.horizontalScrollIndicatorInsets.bottom = collectionView.frame.height - 8 + collectionView.horizontalScrollIndicatorInsets.bottom = collectionView.frame.height - 7.5 collectionView.hitTestInsets.top = 0 collectionView.hitTestInsets.bottom = -12 } From a94198c431d297617cd4d769643b4f958ce26be6 Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Thu, 1 Feb 2024 18:09:30 +0000 Subject: [PATCH 32/39] control omnibar padding from the swipe controller --- DuckDuckGo/OmniBar.swift | 18 ++++-------------- DuckDuckGo/SwipeTabsCoordinator.swift | 17 +++-------------- 2 files changed, 7 insertions(+), 28 deletions(-) diff --git a/DuckDuckGo/OmniBar.swift b/DuckDuckGo/OmniBar.swift index 4a3bc54fd0..282bd81f81 100644 --- a/DuckDuckGo/OmniBar.swift +++ b/DuckDuckGo/OmniBar.swift @@ -66,7 +66,6 @@ class OmniBar: UIView { weak var omniDelegate: OmniBarDelegate? fileprivate var state: OmniBarState = SmallOmniBarState.HomeNonEditingState() - private var safeAreaInsetsObservation: NSKeyValueObservation? private var privacyIconAndTrackersAnimator = PrivacyIconAndTrackersAnimator() private var notificationAnimator = OmniBarNotificationAnimator() @@ -100,7 +99,6 @@ class OmniBar: UIView { configureEditingMenu() refreshState(state) enableInteractionsWithPointer() - observeSafeAreaInsets() privacyInfoContainer.isHidden = true } @@ -140,13 +138,7 @@ class OmniBar: UIView { name: .speechRecognizerDidChangeAvailability, object: nil) } - - private func observeSafeAreaInsets() { - safeAreaInsetsObservation = self.observe(\.safeAreaInsets, options: .new) { [weak self] (_, _) in - self?.updateOmniBarPadding() - } - } - + private func enableInteractionsWithPointer() { backButton.isPointerInteractionEnabled = true forwardButton.isPointerInteractionEnabled = true @@ -346,8 +338,6 @@ class OmniBar: UIView { if state.showVoiceSearch && state.showClear { searchStackContainer.setCustomSpacing(13, after: voiceSearchButton) } - - updateOmniBarPadding() UIView.animate(withDuration: 0.0) { self.layoutIfNeeded() @@ -355,9 +345,9 @@ class OmniBar: UIView { } - private func updateOmniBarPadding() { - omniBarLeadingConstraint.constant = (state.hasLargeWidth ? 24 : 8) + safeAreaInsets.left - omniBarTrailingConstraint.constant = (state.hasLargeWidth ? 24 : 14) + safeAreaInsets.right + func updateOmniBarPadding(left: CGFloat, right: CGFloat) { + omniBarLeadingConstraint.constant = (state.hasLargeWidth ? 24 : 8) + left + omniBarTrailingConstraint.constant = (state.hasLargeWidth ? 24 : 14) + right } /* diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index ba35c8a3c3..89bff8d4d7 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -348,27 +348,19 @@ extension SwipeTabsCoordinator: UICollectionViewDataSource { class OmniBarCell: UICollectionViewCell { - weak var leadingConstraint: NSLayoutConstraint? - weak var trailingConstraint: NSLayoutConstraint? - weak var omniBar: OmniBar? { didSet { subviews.forEach { $0.removeFromSuperview() } if let omniBar { addSubview(omniBar) - let leadingConstraint = constrainView(omniBar, by: .leadingMargin) - let trailingConstraint = constrainView(omniBar, by: .trailingMargin) - NSLayoutConstraint.activate([ - leadingConstraint, - trailingConstraint, + constrainView(omniBar, by: .leadingMargin), + constrainView(omniBar, by: .trailingMargin), constrainView(omniBar, by: .top), constrainView(omniBar, by: .bottom), ]) - self.leadingConstraint = leadingConstraint - self.trailingConstraint = trailingConstraint } } } @@ -376,12 +368,9 @@ class OmniBarCell: UICollectionViewCell { override func updateConstraints() { super.updateConstraints() print("***", #function) - let left = superview?.safeAreaInsets.left ?? 0 let right = superview?.safeAreaInsets.right ?? 0 - - leadingConstraint?.constant = -left - trailingConstraint?.constant = right + omniBar?.updateOmniBarPadding(left: left, right: right) } } From b1362db3889919c279e9bd295a8fd8c98b4771ad Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Fri, 2 Feb 2024 11:36:37 +0000 Subject: [PATCH 33/39] fix background colour when over-swiping --- DuckDuckGo/SwipeTabsCoordinator.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index 89bff8d4d7..ac5349304b 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -72,6 +72,7 @@ class SwipeTabsCoordinator: NSObject { collectionView.delegate = self collectionView.dataSource = self collectionView.decelerationRate = .fast + collectionView.backgroundColor = .clear updateLayout() } @@ -318,8 +319,6 @@ extension SwipeTabsCoordinator: UICollectionViewDataSource { if !isEnabled || tabsModel.currentIndex == indexPath.row { cell.omniBar = coordinator.omniBar } else { - cell.insetsLayoutMarginsFromSafeArea = true - cell.omniBar = OmniBar.loadFromXib() cell.omniBar?.translatesAutoresizingMaskIntoConstraints = false cell.omniBar?.decorate(with: ThemeManager.shared.currentTheme) From 6c766de140795770b3429e5e98cbc10685a173e9 Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Fri, 2 Feb 2024 12:06:18 +0000 Subject: [PATCH 34/39] fix opening tab keyboard --- DuckDuckGo/MainViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index ee8434f136..cea99fb931 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -1260,9 +1260,9 @@ class MainViewController: UIViewController { tabManager.addHomeTab() } attachHomeScreen() - homeController?.openedAsNewTab(allowingKeyboard: allowingKeyboard) tabsBarController?.refresh(tabsModel: tabManager.model) swipeTabsCoordinator?.refresh(tabsModel: tabManager.model) + homeController?.openedAsNewTab(allowingKeyboard: allowingKeyboard) } func animateLogoAppearance() { From 7cf72ca6340237213b8d74c1a4df23fdb0898891 Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Fri, 2 Feb 2024 13:06:20 +0000 Subject: [PATCH 35/39] ensure launching as new tab with keyboard happens by delaying it slightly --- DuckDuckGo/NavigationSearchHomeViewSectionRenderer.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/DuckDuckGo/NavigationSearchHomeViewSectionRenderer.swift b/DuckDuckGo/NavigationSearchHomeViewSectionRenderer.swift index 5830de7157..f39d3fdcb6 100644 --- a/DuckDuckGo/NavigationSearchHomeViewSectionRenderer.swift +++ b/DuckDuckGo/NavigationSearchHomeViewSectionRenderer.swift @@ -46,8 +46,11 @@ class NavigationSearchHomeViewSectionRenderer: HomeViewSectionRenderer { } func openedAsNewTab(allowingKeyboard: Bool) { - if allowingKeyboard && KeyboardSettings().onNewTab { - launchNewSearch() + guard allowingKeyboard && KeyboardSettings().onNewTab else { return } + // The omnibar is inside a collection view so this needs to chance to do its thing + // which might also be async. Not great. + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + self.launchNewSearch() } } @@ -66,6 +69,7 @@ class NavigationSearchHomeViewSectionRenderer: HomeViewSectionRenderer { } func launchNewSearch() { + print("***", #function) controller?.chromeDelegate?.omniBar.becomeFirstResponder() } From 48064264f7e8c116dc2b89fbf6d59422ac18a44c Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Fri, 2 Feb 2024 16:58:42 +0000 Subject: [PATCH 36/39] make swiping a bit more robust --- DuckDuckGo/SwipeTabsCoordinator.swift | 56 ++++++++++++++++++--------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index ac5349304b..3c09ddacb3 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -103,16 +103,21 @@ class SwipeTabsCoordinator: NSObject { layout?.scrollDirection = .horizontal } - private func scrollToCurrent(animated: Bool = false) { + private func scrollToCurrent() { guard isEnabled else { return } - print("***", #function, animated) - DispatchQueue.main.async { - let indexPath = IndexPath(row: self.tabsModel.currentIndex, section: 0) - self.collectionView.scrollToItem(at: indexPath, - at: .centeredHorizontally, - animated: animated) + let targetOffset = collectionView.frame.width * CGFloat(tabsModel.currentIndex) + print("***", #function, collectionView.contentOffset.x) + + guard targetOffset != collectionView.contentOffset.x else { + print("***", #function, "skipping") + return } + + let indexPath = IndexPath(row: self.tabsModel.currentIndex, section: 0) + self.collectionView.scrollToItem(at: indexPath, + at: .centeredHorizontally, + animated: false) } } @@ -120,8 +125,14 @@ class SwipeTabsCoordinator: NSObject { extension SwipeTabsCoordinator: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { - print("***", #function, "row:", indexPath.row, "currentIndex:", self.tabsModel.currentIndex) - scrollToCurrent() + print("***", #function, + "row:", indexPath.row, + "currentIndex:", self.tabsModel.currentIndex, + "count: ", collectionView.dataSource?.collectionView(collectionView, numberOfItemsInSection: 0) ?? -1) + + DispatchQueue.main.async { + self.scrollToCurrent() + } } func scrollViewDidScroll(_ scrollView: UIScrollView) { @@ -217,7 +228,12 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { print("***", #function) - state = .starting(scrollView.contentOffset) + switch state { + case .idle: + state = .starting(scrollView.contentOffset) + + default: break + } } func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { @@ -276,14 +292,16 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { extension SwipeTabsCoordinator { func refresh(tabsModel: TabsModel, scrollToSelected: Bool = false) { - print("***", #function) - let scrollToItem = self.tabsModel == nil + print("***", #function, scrollToSelected) self.tabsModel = tabsModel coordinator.navigationBarCollectionView.reloadData() + + print("***", #function, "count:", collectionView.dataSource?.collectionView(collectionView, numberOfItemsInSection: 0) ?? -1) + updateLayout() - if scrollToItem { + if scrollToSelected { scrollToCurrent() } } @@ -308,7 +326,9 @@ extension SwipeTabsCoordinator: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { guard isEnabled else { return 1 } let extras = tabsModel.tabs.last?.link != nil ? 1 : 0 // last tab is not a home page, so let's add one - return tabsModel.count + extras + let count = tabsModel.count + extras + print("***", #function, count) + return count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { @@ -321,6 +341,7 @@ extension SwipeTabsCoordinator: UICollectionViewDataSource { } else { cell.omniBar = OmniBar.loadFromXib() cell.omniBar?.translatesAutoresizingMaskIntoConstraints = false + cell.updateConstraints() cell.omniBar?.decorate(with: ThemeManager.shared.currentTheme) cell.omniBar?.showSeparator() @@ -330,12 +351,9 @@ extension SwipeTabsCoordinator: UICollectionViewDataSource { cell.omniBar?.moveSeparatorToBottom() } - if tabsModel.tabs.indices.contains(indexPath.row) { - let tab = tabsModel.get(tabAt: indexPath.row) + if let url = tabsModel.safeGetTabAt(indexPath.row)?.link?.url { cell.omniBar?.startBrowsing() - cell.omniBar?.refreshText(forUrl: tab.link?.url) - } else { - cell.omniBar?.stopBrowsing() + cell.omniBar?.refreshText(forUrl: url) } } From 181c269f5af9dd103e3990a44179b7ff5ac024eb Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Fri, 2 Feb 2024 17:42:47 +0000 Subject: [PATCH 37/39] use aspect fill for the previews to handle orientation change --- DuckDuckGo/SwipeTabsCoordinator.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index 3c09ddacb3..93f0a7ba03 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -208,6 +208,7 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { private func createPreviewFromImage(_ image: UIImage) { let imageView = UIImageView(image: image) + imageView.contentMode = .scaleAspectFill coordinator.contentContainer.addSubview(imageView) preview = imageView } From 7b4fd092710833c29db9750f3aa9bd09d792309a Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Mon, 5 Feb 2024 16:42:32 +0000 Subject: [PATCH 38/39] fix ghost logo --- DuckDuckGo/MainViewController.swift | 4 +++- DuckDuckGo/SwipeTabsCoordinator.swift | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index cea99fb931..172402ff87 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -336,13 +336,14 @@ class MainViewController: UIViewController { if !viewCoordinator.logoContainer.isHidden, self.tabManager.current()?.link == nil, let tab = self.tabManager.model.currentTab { - + print("***", #function, "home screen with logo") // Home screen with logo if let image = viewCoordinator.logoContainer.createImageSnapshot(inBounds: viewCoordinator.contentContainer.frame) { previewsSource.update(preview: image, forTab: tab) } } else if let currentTab = self.tabManager.current(), currentTab.link != nil { + print("***", #function, "web view") // Web view currentTab.preparePreview(completion: { image in guard let image else { return } @@ -350,6 +351,7 @@ class MainViewController: UIViewController { forTab: currentTab.tabModel) }) } else if let tab = self.tabManager.model.currentTab { + print("***", #function, "favorites") // Favorites, etc if let image = viewCoordinator.contentContainer.createImageSnapshot() { previewsSource.update(preview: image, forTab: tab) diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index 93f0a7ba03..6d181ed6be 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -187,6 +187,11 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { let nextIndex = tabsModel.currentIndex + modifier print("***", #function, "nextIndex", nextIndex) + guard tabsModel.tabs.indices.contains(nextIndex) || tabsModel.tabs.last?.link != nil else { + print("***", #function, "no preview required") + return + } + let targetFrame = CGRect(origin: .zero, size: coordinator.contentContainer.frame.size) let tab = tabsModel.safeGetTabAt(nextIndex) From 4dcc4424188dac4e085cfb5bf9e8d5cf817d9f25 Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Mon, 5 Feb 2024 16:52:21 +0000 Subject: [PATCH 39/39] remove print statements --- DuckDuckGo/MainViewController.swift | 3 -- ...igationSearchHomeViewSectionRenderer.swift | 1 - DuckDuckGo/SwipeTabsCoordinator.swift | 51 +------------------ DuckDuckGo/TabViewController.swift | 6 --- 4 files changed, 1 insertion(+), 60 deletions(-) diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 172402ff87..697640fce6 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -336,14 +336,12 @@ class MainViewController: UIViewController { if !viewCoordinator.logoContainer.isHidden, self.tabManager.current()?.link == nil, let tab = self.tabManager.model.currentTab { - print("***", #function, "home screen with logo") // Home screen with logo if let image = viewCoordinator.logoContainer.createImageSnapshot(inBounds: viewCoordinator.contentContainer.frame) { previewsSource.update(preview: image, forTab: tab) } } else if let currentTab = self.tabManager.current(), currentTab.link != nil { - print("***", #function, "web view") // Web view currentTab.preparePreview(completion: { image in guard let image else { return } @@ -351,7 +349,6 @@ class MainViewController: UIViewController { forTab: currentTab.tabModel) }) } else if let tab = self.tabManager.model.currentTab { - print("***", #function, "favorites") // Favorites, etc if let image = viewCoordinator.contentContainer.createImageSnapshot() { previewsSource.update(preview: image, forTab: tab) diff --git a/DuckDuckGo/NavigationSearchHomeViewSectionRenderer.swift b/DuckDuckGo/NavigationSearchHomeViewSectionRenderer.swift index f39d3fdcb6..34f23fb686 100644 --- a/DuckDuckGo/NavigationSearchHomeViewSectionRenderer.swift +++ b/DuckDuckGo/NavigationSearchHomeViewSectionRenderer.swift @@ -69,7 +69,6 @@ class NavigationSearchHomeViewSectionRenderer: HomeViewSectionRenderer { } func launchNewSearch() { - print("***", #function) controller?.chromeDelegate?.omniBar.becomeFirstResponder() } diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index 6d181ed6be..180d9a17a0 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -85,11 +85,7 @@ class SwipeTabsCoordinator: NSObject { } - var state: State = .idle { - didSet { - print("***", #function, state) - } - } + var state: State = .idle weak var preview: UIView? weak var currentView: UIView? @@ -107,10 +103,8 @@ class SwipeTabsCoordinator: NSObject { guard isEnabled else { return } let targetOffset = collectionView.frame.width * CGFloat(tabsModel.currentIndex) - print("***", #function, collectionView.contentOffset.x) guard targetOffset != collectionView.contentOffset.x else { - print("***", #function, "skipping") return } @@ -125,11 +119,6 @@ class SwipeTabsCoordinator: NSObject { extension SwipeTabsCoordinator: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { - print("***", #function, - "row:", indexPath.row, - "currentIndex:", self.tabsModel.currentIndex, - "count: ", collectionView.dataSource?.collectionView(collectionView, numberOfItemsInSection: 0) ?? -1) - DispatchQueue.main.async { self.scrollToCurrent() } @@ -185,10 +174,8 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { private func preparePreview(_ offset: CGFloat) { let modifier = (offset > 0 ? -1 : 1) let nextIndex = tabsModel.currentIndex + modifier - print("***", #function, "nextIndex", nextIndex) guard tabsModel.tabs.indices.contains(nextIndex) || tabsModel.tabs.last?.link != nil else { - print("***", #function, "no preview required") return } @@ -196,19 +183,13 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { let tab = tabsModel.safeGetTabAt(nextIndex) if let tab, let image = tabPreviewsSource.preview(for: tab) { - print("***", #function, "image preview") createPreviewFromImage(image) } else if tab?.link == nil { - print("***", #function, "new tab home screen preview") createPreviewFromLogoContainerWithSize(targetFrame.size) - } else { - print("***", #function, "no preview found") } preview?.frame = targetFrame preview?.frame.origin.x = coordinator.contentContainer.frame.width * CGFloat(modifier) - - print("***", #function, "offset:", offset, "modified:", preview?.frame.origin.x ?? .nan) } private func createPreviewFromImage(_ image: UIImage) { @@ -233,7 +214,6 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { } func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { - print("***", #function) switch state { case .idle: state = .starting(scrollView.contentOffset) @@ -243,8 +223,6 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { } func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { - print("***", #function, coordinator.navigationBarCollectionView.indexPathsForVisibleItems) - defer { cleanUpViews() state = .idle @@ -259,7 +237,6 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { } feedbackGenerator.selectionChanged() if index >= tabsModel.count { - print("***", #function, "add tab!") newTab() } else { selectTab(index) @@ -271,26 +248,6 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { currentView = nil preview?.removeFromSuperview() } - - func scrollViewWillBeginDecelerating(_ scrollView: UIScrollView) { - print("***", #function) - } - - func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { - print("***", #function) - } - - func scrollViewDidChangeAdjustedContentInset(_ scrollView: UIScrollView) { - print("***", #function) - } - - func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { - print("***", #function) - } - - func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer) { - print("***", #function) - } } @@ -298,13 +255,9 @@ extension SwipeTabsCoordinator: UICollectionViewDelegate { extension SwipeTabsCoordinator { func refresh(tabsModel: TabsModel, scrollToSelected: Bool = false) { - print("***", #function, scrollToSelected) - self.tabsModel = tabsModel coordinator.navigationBarCollectionView.reloadData() - print("***", #function, "count:", collectionView.dataSource?.collectionView(collectionView, numberOfItemsInSection: 0) ?? -1) - updateLayout() if scrollToSelected { @@ -333,7 +286,6 @@ extension SwipeTabsCoordinator: UICollectionViewDataSource { guard isEnabled else { return 1 } let extras = tabsModel.tabs.last?.link != nil ? 1 : 0 // last tab is not a home page, so let's add one let count = tabsModel.count + extras - print("***", #function, count) return count } @@ -390,7 +342,6 @@ class OmniBarCell: UICollectionViewCell { override func updateConstraints() { super.updateConstraints() - print("***", #function) let left = superview?.safeAreaInsets.left ?? 0 let right = superview?.safeAreaInsets.right ?? 0 omniBar?.updateOmniBarPadding(left: left, right: right) diff --git a/DuckDuckGo/TabViewController.swift b/DuckDuckGo/TabViewController.swift index d16c8aa182..0e58ca1c50 100644 --- a/DuckDuckGo/TabViewController.swift +++ b/DuckDuckGo/TabViewController.swift @@ -1452,23 +1452,17 @@ extension TabViewController: WKNavigationDelegate { || !ContentBlocking.shared.privacyConfigurationManager.privacyConfig.isEnabled(featureKey: .contentBlocking) { rulesCompilationMonitor.reportNavigationDidNotWaitForRules() - - Swift.print("***", #function, "returning false") return false } Task { - Swift.print("***", #function, "Task started") rulesCompilationMonitor.tabWillWaitForRulesCompilation(tabModel.uid) showProgressIndicator() await userContentController.awaitContentBlockingAssetsInstalled() rulesCompilationMonitor.reportTabFinishedWaitingForRules(tabModel.uid) await MainActor.run(body: completion) - Swift.print("***", #function, "Task finished") } - - Swift.print("***", #function, "returning true") return true }