From fd963cc8e1150722c0a889042358cafc52984a14 Mon Sep 17 00:00:00 2001 From: Chris Brind Date: Tue, 10 Oct 2023 19:53:47 +0100 Subject: [PATCH] nicer animation for bottom bar, some code tidy up --- DuckDuckGo/BrowserChromeManager.swift | 2 +- .../BrowsingMenuViewController.swift | 4 +- DuckDuckGo/MainView.swift | 32 +++-- .../MainViewController+CookiesManaged.swift | 2 +- .../MainViewController+KeyCommands.swift | 4 +- DuckDuckGo/MainViewController.swift | 111 +++++++++--------- DuckDuckGo/OmniBar.swift | 5 + DuckDuckGo/TabViewController.swift | 2 +- 8 files changed, 89 insertions(+), 73 deletions(-) diff --git a/DuckDuckGo/BrowserChromeManager.swift b/DuckDuckGo/BrowserChromeManager.swift index 183f4b87d0..3f458dd74f 100644 --- a/DuckDuckGo/BrowserChromeManager.swift +++ b/DuckDuckGo/BrowserChromeManager.swift @@ -31,7 +31,7 @@ protocol BrowserChromeDelegate: AnyObject { var toolbarHeight: CGFloat { get } var barsMaxHeight: CGFloat { get } - var omniBar: OmniBar! { get } + var omniBar: OmniBar { get } var tabBarContainer: UIView { get } } diff --git a/DuckDuckGo/BrowsingMenu/BrowsingMenuViewController.swift b/DuckDuckGo/BrowsingMenu/BrowsingMenuViewController.swift index afe4c01c17..bc04ec8e79 100644 --- a/DuckDuckGo/BrowsingMenu/BrowsingMenuViewController.swift +++ b/DuckDuckGo/BrowsingMenu/BrowsingMenuViewController.swift @@ -61,7 +61,7 @@ final class BrowsingMenuViewController: UIViewController { var searchBarRect: () -> CGRect - var isAddressBarBottom: Bool { + var isAddressBarAtBottom: Bool { searchBarRect().minY > view.frame.midY } @@ -227,7 +227,7 @@ final class BrowsingMenuViewController: UIViewController { topConstraint.constant = frame.minY + (isIPhoneLandscape ? -10 : 5) // Move menu up in Landscape, as bottom toolbar shrinks - let barPositionOffset: CGFloat = isAddressBarBottom ? 52 : 0 + let barPositionOffset: CGFloat = isAddressBarAtBottom ? 52 : 0 bottomConstraint.constant = windowBounds.maxY - frame.maxY - (isIPhoneLandscape ? 2 : 10) - barPositionOffset rightConstraint.constant = isIPad ? 67 : 10 diff --git a/DuckDuckGo/MainView.swift b/DuckDuckGo/MainView.swift index f20b5029f7..7cf161de30 100644 --- a/DuckDuckGo/MainView.swift +++ b/DuckDuckGo/MainView.swift @@ -72,7 +72,10 @@ extension MainViewFactory { class NavigationBarContainer: UIView { } private func createNavigationBarContainer() { + coordinator.omniBar = OmniBar.loadFromXib() + coordinator.omniBar.translatesAutoresizingMaskIntoConstraints = false coordinator.navigationBarContainer = NavigationBarContainer() + coordinator.navigationBarContainer.addSubview(coordinator.omniBar) superview.addSubview(coordinator.navigationBarContainer) } @@ -182,15 +185,22 @@ 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), - coordinator.constraints.navigationBarContainerTop, - navigationBarContainer.constrainAttribute(.height, to: 52), + 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, ]) } @@ -306,22 +316,23 @@ class MainViewCoordinator { let superview: UIView - var logoContainer: UIView! + var contentContainer: UIView! + var lastToolbarButton: UIBarButtonItem! var logo: UIImageView! + var logoContainer: UIView! var logoText: UIImageView! - var toolbar: UIToolbar! - var suggestionTrayContainer: UIView! - var contentContainer: UIView! + var navigationBarContainer: UIView! var notificationBarContainer: UIView! + var omniBar: OmniBar! + var progress: ProgressView! var statusBackground: UIView! + var suggestionTrayContainer: UIView! var tabBarContainer: UIView! - var navigationBarContainer: UIView! - var progress: ProgressView! + var toolbar: UIToolbar! var toolbarBackButton: UIBarButtonItem! - var toolbarForwardButton: UIBarButtonItem! var toolbarFireButton: UIBarButtonItem! + var toolbarForwardButton: UIBarButtonItem! var toolbarTabSwitcherButton: UIBarButtonItem! - var lastToolbarButton: UIBarButtonItem! let constraints = Constraints() @@ -358,6 +369,7 @@ 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+CookiesManaged.swift b/DuckDuckGo/MainViewController+CookiesManaged.swift index 53b4c7f465..27c1422bad 100644 --- a/DuckDuckGo/MainViewController+CookiesManaged.swift +++ b/DuckDuckGo/MainViewController+CookiesManaged.swift @@ -37,7 +37,7 @@ extension MainViewController { topURL == tabManager.current?.url else { return } - omniBar.showOrScheduleCookiesManagedNotification(isCosmetic: isCosmetic) + viewCoordinator.omniBar.showOrScheduleCookiesManagedNotification(isCosmetic: isCosmetic) } } diff --git a/DuckDuckGo/MainViewController+KeyCommands.swift b/DuckDuckGo/MainViewController+KeyCommands.swift index 4aa1cb4b89..e1229209d2 100644 --- a/DuckDuckGo/MainViewController+KeyCommands.swift +++ b/DuckDuckGo/MainViewController+KeyCommands.swift @@ -71,7 +71,7 @@ extension MainViewController { } var arrowKeys = [UIKeyCommand]() - if omniBar.textField.isFirstResponder { + if viewCoordinator.omniBar.textField.isFirstResponder { arrowKeys = [ UIKeyCommand(title: "", action: #selector(keyboardMoveSelectionUp), input: UIKeyCommand.inputUpArrow, modifierFlags: []), UIKeyCommand(title: "", action: #selector(keyboardMoveSelectionDown), input: UIKeyCommand.inputDownArrow, modifierFlags: []) @@ -140,7 +140,7 @@ extension MainViewController { controller.launchNewSearch() } else { showBars() - omniBar.becomeFirstResponder() + viewCoordinator.omniBar.becomeFirstResponder() } } diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 1069aa527f..1b3c06dbfc 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -23,7 +23,6 @@ import Combine import Common import Core import DDGSync -import Lottie import Kingfisher import BrowserServicesKit import Bookmarks @@ -52,7 +51,6 @@ class MainViewController: UIViewController { weak var notificationView: NotificationView? - var omniBar: OmniBar! var chromeManager: BrowserChromeManager! var allowContentUnderflow = false { @@ -99,7 +97,7 @@ class MainViewController: UIViewController { /// Do not reference directly, use `presentedMenuButton` let menuButton = MenuButton() var presentedMenuButton: MenuButton { - AppWidthObserver.shared.isLargeWidth ? omniBar.menuButtonContent : menuButton + AppWidthObserver.shared.isLargeWidth ? viewCoordinator.omniBar.menuButtonContent : menuButton } let gestureBookmarksButton = GestureToolbarButton() @@ -115,7 +113,11 @@ class MainViewController: UIViewController { var searchBarRect: CGRect { let view = UIApplication.shared.windows.filter({ $0.isKeyWindow }).first?.rootViewController?.view - return omniBar.searchContainer.convert(omniBar.searchContainer.bounds, to: view) + return viewCoordinator.omniBar.searchContainer.convert(viewCoordinator.omniBar.searchContainer.bounds, to: view) + } + + var isAddressBarAtBottom: Bool { + searchBarRect.minY > view.frame.midY } var keyModifierFlags: UIKeyModifierFlags? @@ -337,11 +339,11 @@ class MainViewController: UIViewController { func refreshViewsBasedOnAddressBarPosition() { switch appSettings.currentAddressBarPosition { case .top: - omniBar.moveSeparatorToBottom() + viewCoordinator.omniBar.moveSeparatorToBottom() viewCoordinator.showToolbarSeparator() case .bottom: - omniBar.moveSeparatorToTop() + viewCoordinator.omniBar.moveSeparatorToTop() viewCoordinator.hideToolbarSeparator() } } @@ -380,11 +382,11 @@ class MainViewController: UIViewController { } } - let navBarOffset = max(0, intersection.height - toolbarHeight) - self.viewCoordinator.constraints.navigationBarContainerBottom.constant = -navBarOffset + let navBarOffset = min(0, toolbarHeight - intersection.height) + self.viewCoordinator.constraints.omniBarBottom.constant = isAddressBarAtBottom ? navBarOffset : 0 self.animateForKeyboard(userInfo: userInfo, y: self.view.frame.height - height) } - + private func animateForKeyboard(userInfo: [AnyHashable: Any], y: CGFloat) { let duration: TimeInterval = (userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0 let animationCurveRawNSN = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber @@ -396,7 +398,6 @@ class MainViewController: UIViewController { self.findInPageView.frame = CGRect(x: 0, y: y - frame.height, width: frame.width, height: frame.height) self.view.layoutSubviews() }, completion: nil) - } private func initTabButton() { @@ -415,7 +416,7 @@ class MainViewController: UIViewController { } private func initBookmarksButton() { - omniBar.bookmarksButton.addGestureRecognizer(UILongPressGestureRecognizer(target: self, + viewCoordinator.omniBar.bookmarksButton.addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: #selector(quickSaveBookmarkLongPress(gesture:)))) gestureBookmarksButton.delegate = self gestureBookmarksButton.image = UIImage(named: "Bookmarks") @@ -537,11 +538,8 @@ class MainViewController: UIViewController { } private func attachOmniBar() { - omniBar = OmniBar.loadFromXib() - omniBar.omniDelegate = self - omniBar.menuButtonContent.delegate = self - omniBar.frame = viewCoordinator.navigationBarContainer.bounds - viewCoordinator.navigationBarContainer.addSubview(omniBar) + viewCoordinator.omniBar.omniDelegate = self + viewCoordinator.omniBar.menuButtonContent.delegate = self } fileprivate func attachHomeScreen() { @@ -655,7 +653,7 @@ class MainViewController: UIViewController { func enterSearch() { if presentedViewController == nil { showBars() - omniBar.becomeFirstResponder() + viewCoordinator.omniBar.becomeFirstResponder() } } @@ -747,7 +745,7 @@ class MainViewController: UIViewController { fileprivate func updateCurrentTab() { if let currentTab = currentTab { select(tab: currentTab) - omniBar.resignFirstResponder() + viewCoordinator.omniBar.resignFirstResponder() } else { attachHomeScreen() } @@ -769,25 +767,25 @@ class MainViewController: UIViewController { private func refreshOmniBar() { guard let tab = currentTab, tab.link != nil else { - omniBar.stopBrowsing() + viewCoordinator.omniBar.stopBrowsing() return } - omniBar.refreshText(forUrl: tab.url) + viewCoordinator.omniBar.refreshText(forUrl: tab.url) if tab.isError { - omniBar.hidePrivacyIcon() + viewCoordinator.omniBar.hidePrivacyIcon() } else if let privacyInfo = tab.privacyInfo, privacyInfo.url.host == tab.url?.host { - omniBar.updatePrivacyIcon(for: privacyInfo) + viewCoordinator.omniBar.updatePrivacyIcon(for: privacyInfo) } else { - omniBar.resetPrivacyIcon(for: tab.url) + viewCoordinator.omniBar.resetPrivacyIcon(for: tab.url) } - omniBar.startBrowsing() + viewCoordinator.omniBar.startBrowsing() } func dismissOmniBar() { - omniBar.resignFirstResponder() + viewCoordinator.omniBar.resignFirstResponder() hideSuggestionTray() refreshOmniBar() } @@ -796,8 +794,8 @@ class MainViewController: UIViewController { viewCoordinator.toolbarBackButton.isEnabled = currentTab?.canGoBack ?? false viewCoordinator.toolbarForwardButton.isEnabled = currentTab?.canGoForward ?? false - omniBar.backButton.isEnabled = viewCoordinator.toolbarBackButton.isEnabled - omniBar.forwardButton.isEnabled = viewCoordinator.toolbarForwardButton.isEnabled + viewCoordinator.omniBar.backButton.isEnabled = viewCoordinator.toolbarBackButton.isEnabled + viewCoordinator.omniBar.forwardButton.isEnabled = viewCoordinator.toolbarForwardButton.isEnabled } override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { @@ -844,7 +842,7 @@ class MainViewController: UIViewController { if homeController != nil { expectedState = .bookmarksImage viewCoordinator.lastToolbarButton.accessibilityLabel = UserText.bookmarksButtonHint - omniBar.menuButton.accessibilityLabel = UserText.bookmarksButtonHint + viewCoordinator.omniBar.menuButton.accessibilityLabel = UserText.bookmarksButtonHint } else { if presentedViewController is BrowsingMenuViewController { @@ -853,7 +851,7 @@ class MainViewController: UIViewController { expectedState = .menuImage } viewCoordinator.lastToolbarButton.accessibilityLabel = UserText.menuButtonHint - omniBar.menuButton.accessibilityLabel = UserText.menuButtonHint + viewCoordinator.omniBar.menuButton.accessibilityLabel = UserText.menuButtonHint } presentedMenuButton.decorate(with: ThemeManager.shared.currentTheme) @@ -862,7 +860,7 @@ class MainViewController: UIViewController { private func applyWidthToTrayController() { if AppWidthObserver.shared.isLargeWidth { - self.suggestionTrayController?.float(withWidth: self.omniBar.searchStackContainer.frame.width + 24) + self.suggestionTrayController?.float(withWidth: self.viewCoordinator.omniBar.searchStackContainer.frame.width + 24) } else { self.suggestionTrayController?.fill() } @@ -871,13 +869,13 @@ class MainViewController: UIViewController { private func applyLargeWidth() { viewCoordinator.tabBarContainer.isHidden = false viewCoordinator.toolbar.isHidden = true - omniBar.enterPadState() + viewCoordinator.omniBar.enterPadState() } private func applySmallWidth() { viewCoordinator.tabBarContainer.isHidden = true viewCoordinator.toolbar.isHidden = false - omniBar.enterPhoneState() + viewCoordinator.omniBar.enterPhoneState() } @discardableResult @@ -897,7 +895,7 @@ class MainViewController: UIViewController { ViewHighlighter.hideAll() } if type.hideOmnibarSeparator() && appSettings.currentAddressBarPosition == .top { - omniBar.hideSeparator() + viewCoordinator.omniBar.hideSeparator() } } viewCoordinator.suggestionTrayContainer.isHidden = false @@ -905,7 +903,7 @@ class MainViewController: UIViewController { } func hideSuggestionTray() { - omniBar.showSeparator() + viewCoordinator.omniBar.showSeparator() viewCoordinator.suggestionTrayContainer.isHidden = true currentTab?.webView.accessibilityElementsHidden = false suggestionTrayController?.didHide() @@ -1044,7 +1042,7 @@ class MainViewController: UIViewController { private func showVoiceSearch() { // https://app.asana.com/0/0/1201408131067987 UIMenuController.shared.hideMenu() - omniBar.removeTextSelection() + viewCoordinator.omniBar.removeTextSelection() Pixel.fire(pixel: .openVoiceSearch) let voiceSearchController = VoiceSearchViewController() @@ -1136,6 +1134,10 @@ extension MainViewController: BrowserChromeDelegate { viewCoordinator.tabBarContainer } + var omniBar: OmniBar { + viewCoordinator.omniBar + } + private func hideKeyboard() { dismissOmniBar() _ = findInPageView.resignFirstResponder() @@ -1161,7 +1163,7 @@ extension MainViewController: BrowserChromeDelegate { self.view.layoutIfNeeded() - self.omniBar.alpha = percent + self.viewCoordinator.omniBar.alpha = percent self.viewCoordinator.tabBarContainer.alpha = percent self.viewCoordinator.toolbar.alpha = percent } @@ -1177,7 +1179,7 @@ extension MainViewController: BrowserChromeDelegate { if hidden { hideKeyboard() } updateNavBarConstant(hidden ? 0 : 1.0) - omniBar.alpha = hidden ? 0 : 1 + viewCoordinator.omniBar.alpha = hidden ? 0 : 1 viewCoordinator.tabBarContainer.alpha = hidden ? 0 : 1 viewCoordinator.statusBackground.alpha = hidden ? 0 : 1 } @@ -1195,7 +1197,7 @@ extension MainViewController: BrowserChromeDelegate { } var barsMaxHeight: CGFloat { - return max(toolbarHeight, omniBar.frame.size.height) + return max(toolbarHeight, viewCoordinator.omniBar.frame.size.height) } // 1.0 - full size, 0.0 - hidden @@ -1314,7 +1316,7 @@ extension MainViewController: OmniBarDelegate { func onEnterPressed() { guard !viewCoordinator.suggestionTrayContainer.isHidden else { return } - suggestionTrayController?.willDismiss(with: omniBar.textField.text ?? "") + suggestionTrayController?.willDismiss(with: viewCoordinator.omniBar.textField.text ?? "") } func onDismissed() { @@ -1372,7 +1374,7 @@ extension MainViewController: OmniBarDelegate { func onSharePressed() { hideSuggestionTray() guard let link = currentTab?.link else { return } - currentTab?.onShareAction(forLink: link, fromView: omniBar.shareButton, orginatedFromMenu: false) + currentTab?.onShareAction(forLink: link, fromView: viewCoordinator.omniBar.shareButton, orginatedFromMenu: false) } func onVoiceSearchPressed() { @@ -1426,26 +1428,23 @@ extension MainViewController: AutocompleteViewControllerDelegate { func autocomplete(pressedPlusButtonForSuggestion suggestion: Suggestion) { if let url = suggestion.url { if url.isDuckDuckGoSearch { - omniBar.textField.text = suggestion.suggestion + viewCoordinator.omniBar.textField.text = suggestion.suggestion } else if !url.isBookmarklet() { - omniBar.textField.text = url.absoluteString + viewCoordinator.omniBar.textField.text = url.absoluteString } } else { - omniBar.textField.text = suggestion.suggestion + viewCoordinator.omniBar.textField.text = suggestion.suggestion } - omniBar.textDidChange() + viewCoordinator.omniBar.textDidChange() } func autocomplete(highlighted suggestion: Suggestion, for query: String) { if let url = suggestion.url { - omniBar.textField.text = url.absoluteString + viewCoordinator.omniBar.textField.text = url.absoluteString } else { - omniBar.textField.text = suggestion.suggestion - - if suggestion.suggestion.hasPrefix(query), - let fromPosition = omniBar.textField.position(from: omniBar.textField.beginningOfDocument, offset: query.count) { - omniBar.textField.selectedTextRange = omniBar.textField.textRange(from: fromPosition, - to: omniBar.textField.endOfDocument) + viewCoordinator.omniBar.textField.text = suggestion.suggestion + if suggestion.suggestion.hasPrefix(query) { + viewCoordinator.omniBar.selectTextToEnd(query.count) } } } @@ -1590,7 +1589,7 @@ extension MainViewController: TabDelegate { func tab(_ tab: TabViewController, didChangePrivacyInfo privacyInfo: PrivacyInfo?) { if currentTab == tab { - omniBar.updatePrivacyIcon(for: privacyInfo) + viewCoordinator.omniBar.updatePrivacyIcon(for: privacyInfo) } } @@ -1664,7 +1663,7 @@ extension MainViewController: TabDelegate { didRequestPresentingTrackerAnimation privacyInfo: PrivacyInfo, isCollapsing: Bool) { guard tabManager.current === tab else { return } - omniBar?.startTrackersAnimation(privacyInfo, forDaxDialog: !isCollapsing) + viewCoordinator.omniBar?.startTrackersAnimation(privacyInfo, forDaxDialog: !isCollapsing) } func tabDidRequestShowingMenuHighlighter(tab: TabViewController) { @@ -1933,7 +1932,7 @@ extension MainViewController: Themable { viewCoordinator.navigationBarContainer.backgroundColor = theme.barBackgroundColor viewCoordinator.navigationBarContainer.tintColor = theme.barTintColor - omniBar.decorate(with: theme) + viewCoordinator.omniBar.decorate(with: theme) viewCoordinator.progress.decorate(with: theme) @@ -2026,11 +2025,11 @@ extension MainViewController { } let backMenu = historyMenu(with: currentTab.webView.backForwardList.backList.reversed()) - omniBar.backButton.menu = backMenu + viewCoordinator.omniBar.backButton.menu = backMenu viewCoordinator.toolbarBackButton.menu = backMenu let forwardMenu = historyMenu(with: currentTab.webView.backForwardList.forwardList) - omniBar.forwardButton.menu = forwardMenu + viewCoordinator.omniBar.forwardButton.menu = forwardMenu viewCoordinator.toolbarForwardButton.menu = forwardMenu } diff --git a/DuckDuckGo/OmniBar.swift b/DuckDuckGo/OmniBar.swift index 72075d4824..fbc26fa94f 100644 --- a/DuckDuckGo/OmniBar.swift +++ b/DuckDuckGo/OmniBar.swift @@ -281,6 +281,11 @@ class OmniBar: UIView { } } + func selectTextToEnd(_ offset: Int) { + guard let fromPosition = textField.position(from: textField.beginningOfDocument, offset: offset) else { return } + textField.selectedTextRange = textField.textRange(from: fromPosition, to: textField.endOfDocument) + } + fileprivate func refreshState(_ newState: OmniBarState) { if state.name != newState.name { os_log("OmniBar entering %s from %s", log: .generalLog, type: .debug, newState.name, state.name) diff --git a/DuckDuckGo/TabViewController.swift b/DuckDuckGo/TabViewController.swift index 92ce803499..67e544522a 100644 --- a/DuckDuckGo/TabViewController.swift +++ b/DuckDuckGo/TabViewController.swift @@ -2336,7 +2336,7 @@ extension TabViewController: SecureVaultManagerDelegate { } // if user is interacting with the searchBar, don't show the autofill prompt since it will overlay the keyboard - if let parent = parent as? MainViewController, parent.omniBar.textField.isFirstResponder { + if let parent = parent as? MainViewController, parent.viewCoordinator.omniBar.textField.isFirstResponder { completionHandler(nil) return }