diff --git a/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/DuckDuckGo.xcscheme b/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/DuckDuckGo.xcscheme index 7c553f4089..e564636928 100644 --- a/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/DuckDuckGo.xcscheme +++ b/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/DuckDuckGo.xcscheme @@ -1,7 +1,7 @@ + version = "1.7"> diff --git a/DuckDuckGo/BarsAnimator.swift b/DuckDuckGo/BarsAnimator.swift index d2f4a8d758..01cae59724 100644 --- a/DuckDuckGo/BarsAnimator.swift +++ b/DuckDuckGo/BarsAnimator.swift @@ -84,10 +84,6 @@ class BarsAnimator { let ratio = calculateTransitionRatio(for: scrollView.contentOffset.y) delegate?.setBarsVisibility(1.0 - ratio, animated: false) transitionProgress = ratio - - var offset = scrollView.contentOffset - offset.y = transitionStartPosY - scrollView.contentOffset = offset } private func transitioningAndScrolling(in scrollView: UIScrollView) { @@ -103,10 +99,6 @@ class BarsAnimator { delegate?.setBarsVisibility(1.0 - ratio, animated: false) transitionProgress = ratio - - var offset = scrollView.contentOffset - offset.y = transitionStartPosY - scrollView.contentOffset = offset } private func hiddenAndScrolling(in scrollView: UIScrollView) { @@ -139,7 +131,12 @@ class BarsAnimator { let cumulativeDistance = (barsHeight * transitionProgress) + distance let normalizedDistance = max(cumulativeDistance, 0) - return min(normalizedDistance / barsHeight, 1.0) + // We used to fix the scroll position in place as the transition happened + // but now the bars disappear too. This adjusts for that. + let transitionSpeed = 0.5 + + let ratio = min(normalizedDistance / barsHeight * transitionSpeed, 1.0) + return ratio } func didFinishScrolling(in scrollView: UIScrollView, velocity: CGFloat) { diff --git a/DuckDuckGo/BrowserChromeManager.swift b/DuckDuckGo/BrowserChromeManager.swift index 3f458dd74f..c59afa3bd0 100644 --- a/DuckDuckGo/BrowserChromeManager.swift +++ b/DuckDuckGo/BrowserChromeManager.swift @@ -38,7 +38,6 @@ protocol BrowserChromeDelegate: AnyObject { class BrowserChromeManager: NSObject, UIScrollViewDelegate { struct Constants { - static let dragThreshold: CGFloat = 30 static let zoomThreshold: CGFloat = 0.1 static let contentSizeKVOKey = "contentSize" diff --git a/DuckDuckGo/BrowsingMenu/BrowsingMenuAnimator.swift b/DuckDuckGo/BrowsingMenu/BrowsingMenuAnimator.swift index f5b5fbc572..6f1a53d398 100644 --- a/DuckDuckGo/BrowsingMenu/BrowsingMenuAnimator.swift +++ b/DuckDuckGo/BrowsingMenu/BrowsingMenuAnimator.swift @@ -61,7 +61,7 @@ final class BrowsingMenuAnimator: NSObject, UIViewControllerAnimatedTransitionin transitionContext.containerView.addSubview(fromSnapshot) } - toViewController.bindConstraints(to: fromViewController.currentTab?.webView) + toViewController.bindConstraints(to: fromViewController.viewCoordinator.contentContainer) toViewController.view.layoutIfNeeded() let snapshot = toViewController.menuView.snapshotView(afterScreenUpdates: true) diff --git a/DuckDuckGo/BrowsingMenu/BrowsingMenuViewController.swift b/DuckDuckGo/BrowsingMenu/BrowsingMenuViewController.swift index dec954d795..867f7cc79a 100644 --- a/DuckDuckGo/BrowsingMenu/BrowsingMenuViewController.swift +++ b/DuckDuckGo/BrowsingMenu/BrowsingMenuViewController.swift @@ -157,7 +157,7 @@ final class BrowsingMenuViewController: UIViewController { recalculatePreferredWidthConstraint() recalculateHeightConstraints() - webView.map(recalculateMenuConstraints(with:)) + guideView.map(recalculateMenuConstraints(with:)) if tableView.bounds.height < tableView.contentSize.height + tableView.contentInset.top + tableView.contentInset.bottom { tableView.isScrollEnabled = true @@ -166,12 +166,12 @@ final class BrowsingMenuViewController: UIViewController { } } - private weak var webView: UIView? - private var webViewFrameObserver: NSKeyValueObservation? - func bindConstraints(to webView: UIView?) { - self.webView = webView - self.webViewFrameObserver = webView?.observe(\.frame, options: [.initial]) { [weak self] webView, _ in - self?.recalculateMenuConstraints(with: webView) + private weak var guideView: UIView? + private var guideViewFrameObserver: NSKeyValueObservation? + func bindConstraints(to guideView: UIView?) { + self.guideView = guideView + self.guideViewFrameObserver = guideView?.observe(\.frame, options: [.initial]) { [weak self] guideView, _ in + self?.recalculateMenuConstraints(with: guideView) } } @@ -205,9 +205,9 @@ final class BrowsingMenuViewController: UIViewController { } } - private func recalculateMenuConstraints(with webView: UIView) { - guard let frame = webView.superview?.convert(webView.frame, to: webView.window), - let windowBounds = webView.window?.bounds + private func recalculateMenuConstraints(with guideView: UIView) { + guard let frame = guideView.superview?.convert(guideView.frame, to: guideView.window), + let windowBounds = guideView.window?.bounds else { return } let isIPad = AppWidthObserver.shared.isLargeWidth diff --git a/DuckDuckGo/MainView.swift b/DuckDuckGo/MainView.swift index e78b925d59..d998f53826 100644 --- a/DuckDuckGo/MainView.swift +++ b/DuckDuckGo/MainView.swift @@ -54,6 +54,7 @@ class MainViewFactory { extension MainViewFactory { private func createViews() { + createWebViewContainer() createLogoBackground() createContentContainer() createSuggestionTrayContainer() @@ -64,13 +65,19 @@ extension MainViewFactory { createProgressView() createToolbar() } + + final class WebViewContainerView: UIView { } + private func createWebViewContainer() { + coordinator.webViewContainer = WebViewContainerView() + superview.addSubview(coordinator.webViewContainer) + } private func createProgressView() { coordinator.progress = ProgressView() superview.addSubview(coordinator.progress) } - class NavigationBarContainer: UIView { } + final class NavigationBarContainer: UIView { } private func createNavigationBarContainer() { coordinator.omniBar = OmniBar.loadFromXib() coordinator.omniBar.translatesAutoresizingMaskIntoConstraints = false @@ -79,31 +86,31 @@ extension MainViewFactory { superview.addSubview(coordinator.navigationBarContainer) } - class NotificationBarContainer: UIView { } + final class NotificationBarContainer: UIView { } private func createNotificationBarContainer() { coordinator.notificationBarContainer = NotificationBarContainer() superview.addSubview(coordinator.notificationBarContainer) } - class ContentContainer: UIView { } + final class ContentContainer: UIView { } private func createContentContainer() { coordinator.contentContainer = ContentContainer() superview.addSubview(coordinator.contentContainer) } - class StatusBackgroundView: UIView { } + final class StatusBackgroundView: UIView { } private func createStatusBackground() { coordinator.statusBackground = StatusBackgroundView() superview.addSubview(coordinator.statusBackground) } - class TabBarContainer: UIView { } + final class TabBarContainer: UIView { } private func createTabBarContainer() { coordinator.tabBarContainer = TabBarContainer() superview.addSubview(coordinator.tabBarContainer) } - class SuggestionTrayContainer: UIView { } + final class SuggestionTrayContainer: UIView { } private func createSuggestionTrayContainer() { coordinator.suggestionTrayContainer = SuggestionTrayContainer() coordinator.suggestionTrayContainer.isHidden = true @@ -136,7 +143,7 @@ extension MainViewFactory { ], animated: true) } - class LogoBackgroundView: UIView { } + final class LogoBackgroundView: UIView { } private func createLogoBackground() { coordinator.logoContainer = LogoBackgroundView() coordinator.logo = UIImageView(image: UIImage(named: "Logo")) @@ -156,6 +163,7 @@ extension MainViewFactory { extension MainViewFactory { private func constrainViews() { + constrainWebViewContainer() constrainLogoBackground() constrainContentContainer() constrainSuggestionTrayContainer() @@ -166,6 +174,16 @@ extension MainViewFactory { constrainProgress() constrainToolbar() } + + private func constrainWebViewContainer() { + let webViewContainer = coordinator.webViewContainer! + NSLayoutConstraint.activate([ + webViewContainer.constrainView(superview, by: .width), + webViewContainer.constrainView(superview, by: .height), + webViewContainer.constrainView(superview, by: .centerX), + webViewContainer.constrainView(superview, by: .centerY), + ]) + } private func constrainProgress() { let progress = coordinator.progress! @@ -333,6 +351,7 @@ class MainViewCoordinator { var toolbarFireButton: UIBarButtonItem! var toolbarForwardButton: UIBarButtonItem! var toolbarTabSwitcherButton: UIBarButtonItem! + var webViewContainer: UIView! let constraints = Constraints() diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index f591b60c62..920c674066 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -137,6 +137,8 @@ class MainViewController: UIViewController { // 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:") @@ -430,6 +432,7 @@ class MainViewController: UIViewController { @objc func onAddressBarPositionChanged() { viewCoordinator.moveAddressBarToPosition(appSettings.currentAddressBarPosition) refreshViewsBasedOnAddressBarPosition(appSettings.currentAddressBarPosition) + refreshWebViewContentInsets() } func refreshViewsBasedOnAddressBarPosition(_ position: AddressBarPosition) { @@ -475,7 +478,8 @@ class MainViewController: UIViewController { height = intersection.height findInPageBottomLayoutConstraint.constant = height - currentTab?.webView.scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: height, right: 0) + keyboardHeight = height + refreshWebViewContentInsets() if let suggestionsTray = suggestionTrayController { let suggestionsFrameInView = suggestionsTray.view.convert(suggestionsTray.contentFrame, to: view) @@ -640,7 +644,7 @@ class MainViewController: UIViewController { guard let tab = tabManager.current(createIfNeeded: true) else { fatalError("Unable to create tab") } - addToView(tab: tab) + addToWebViewContainer(tab: tab) refreshControls() } else { attachHomeScreen() @@ -700,7 +704,7 @@ class MainViewController: UIViewController { controller.chromeDelegate = self controller.delegate = self - addToView(controller: controller) + addToContentContainer(controller: controller) refreshControls() syncService.scheduler.requestSyncImmediately() @@ -849,7 +853,7 @@ class MainViewController: UIViewController { private func addTab(url: URL?, inheritedAttribution: AdClickAttributionLogic.State?) { let tab = tabManager.add(url: url, inheritedAttribution: inheritedAttribution) dismissOmniBar() - addToView(tab: tab) + addToWebViewContainer(tab: tab) } func select(tabAt index: Int) { @@ -863,7 +867,7 @@ class MainViewController: UIViewController { if tab.link == nil { attachHomeScreen() } else { - addToView(tab: tab) + addToWebViewContainer(tab: tab) refreshControls() } tabsBarController?.refresh(tabsModel: tabManager.model, scrollToSelected: true) @@ -872,18 +876,29 @@ class MainViewController: UIViewController { } } - private func addToView(tab: TabViewController) { + private func addToWebViewContainer(tab: TabViewController) { removeHomeScreen() updateFindInPage() currentTab?.progressWorker.progressBar = nil currentTab?.chromeDelegate = nil - addToView(controller: tab) + currentTab?.webView.scrollView.contentInsetAdjustmentBehavior = .never + + addChild(tab) + viewCoordinator.webViewContainer.subviews.forEach { $0.removeFromSuperview() } + viewCoordinator.webViewContainer.addSubview(tab.view) + tab.view.frame = self.viewCoordinator.webViewContainer.bounds + tab.didMove(toParent: self) + + viewCoordinator.logoContainer.isHidden = true + viewCoordinator.contentContainer.isHidden = true + tab.progressWorker.progressBar = viewCoordinator.progress chromeManager.attach(to: tab.webView.scrollView) tab.chromeDelegate = self } - private func addToView(controller: UIViewController) { + private func addToContentContainer(controller: UIViewController) { + viewCoordinator.contentContainer.isHidden = false addChild(controller) viewCoordinator.contentContainer.subviews.forEach { $0.removeFromSuperview() } viewCoordinator.contentContainer.addSubview(controller.view) @@ -1313,21 +1328,45 @@ extension MainViewController: BrowserChromeDelegate { let updateBlock = { self.updateToolbarConstant(percent) self.updateNavBarConstant(percent) - + self.view.layoutIfNeeded() - self.viewCoordinator.omniBar.alpha = percent + self.viewCoordinator.navigationBarContainer.alpha = percent self.viewCoordinator.tabBarContainer.alpha = percent self.viewCoordinator.toolbar.alpha = percent } - + if animated { - UIView.animate(withDuration: ChromeAnimationConstants.duration, animations: updateBlock) + UIView.animate(withDuration: ChromeAnimationConstants.duration, animations: updateBlock) { _ in + self.refreshWebViewContentInsets() + } } else { updateBlock() + self.refreshWebViewContentInsets() } } + func refreshWebViewContentInsets() { + guard let webView = currentTab?.webView else { return } + + let top = viewCoordinator.statusBackground.frame.height + let bottom: CGFloat + if isToolbarHidden { + bottom = 0 + } else if appSettings.currentAddressBarPosition.isBottom { + bottom = viewCoordinator.toolbar.frame.height + + viewCoordinator.navigationBarContainer.frame.height + + view.safeAreaInsets.bottom + additionalSafeAreaInsets.bottom + + keyboardHeight + } else { + bottom = viewCoordinator.toolbar.frame.height + + view.safeAreaInsets.bottom + additionalSafeAreaInsets.bottom + + keyboardHeight + } + + webView.scrollView.contentInset = .init(top: top, left: 0, bottom: bottom, right: 0) + } + func setNavigationBarHidden(_ hidden: Bool) { if hidden { hideKeyboard() } @@ -1335,6 +1374,7 @@ extension MainViewController: BrowserChromeDelegate { viewCoordinator.omniBar.alpha = hidden ? 0 : 1 viewCoordinator.tabBarContainer.alpha = hidden ? 0 : 1 viewCoordinator.statusBackground.alpha = hidden ? 0 : 1 + } var canHideBars: Bool { @@ -1692,7 +1732,7 @@ extension MainViewController: TabDelegate { guard self.tabManager.model.tabs.contains(newTab.tabModel) else { return } self.dismissOmniBar() - self.addToView(tab: newTab) + self.addToWebViewContainer(tab: newTab) self.refreshOmniBar() } @@ -1796,6 +1836,7 @@ extension MainViewController: TabDelegate { func showBars() { chromeManager.reset() + refreshWebViewContentInsets() } func tabDidRequestFindInPage(tab: TabViewController) { diff --git a/DuckDuckGo/TabViewController.swift b/DuckDuckGo/TabViewController.swift index 2a970476fc..22593be12e 100644 --- a/DuckDuckGo/TabViewController.swift +++ b/DuckDuckGo/TabViewController.swift @@ -1147,7 +1147,12 @@ extension TabViewController: WKNavigationDelegate { DispatchQueue.main.async { [weak self] in guard let webView = self?.webView, webView.bounds.height > 0 && webView.bounds.width > 0 else { completion(nil); return } - UIGraphicsBeginImageContextWithOptions(webView.bounds.size, false, UIScreen.main.scale) + + let size = CGSize(width: webView.frame.size.width, + height: webView.frame.size.height - webView.scrollView.contentInset.top - webView.scrollView.contentInset.bottom) + + UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale) + UIGraphicsGetCurrentContext()?.translateBy(x: 0, y: -webView.scrollView.contentInset.top) webView.drawHierarchy(in: webView.bounds, afterScreenUpdates: true) if let jsAlertController = self?.jsAlertController { jsAlertController.view.drawHierarchy(in: jsAlertController.view.bounds, diff --git a/DuckDuckGo/WebViewTransition.swift b/DuckDuckGo/WebViewTransition.swift index 8e123343ab..af7ffbc8fe 100644 --- a/DuckDuckGo/WebViewTransition.swift +++ b/DuckDuckGo/WebViewTransition.swift @@ -90,7 +90,7 @@ class FromWebViewTransition: WebViewTransition { solidBackground.backgroundColor = theme.backgroundColor solidBackground.frame = webViewFrame - imageContainer.frame = webViewFrame + imageContainer.frame = mainViewController.viewCoordinator.contentContainer.frame imageView.frame = imageContainer.bounds imageView.image = preview @@ -152,7 +152,7 @@ class ToWebViewTransition: WebViewTransition { let preview = tabSwitcherViewController.previewsSource.preview(for: tab) if let preview = preview { imageView.frame = previewFrame(for: imageContainer.bounds.size, - preview: preview) + preview: preview) } else { imageView.frame = CGRect(origin: .zero, size: imageContainer.bounds.size) } @@ -165,8 +165,10 @@ class ToWebViewTransition: WebViewTransition { scrollIfOutsideViewport(collectionView: tabSwitcherViewController.collectionView, rowIndex: rowIndex, attributes: layoutAttr) UIView.animate(withDuration: TabSwitcherTransition.Constants.duration, animations: { - self.imageContainer.frame = webViewFrame + 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 + self.imageView.frame = self.destinationImageFrame(for: webViewFrame.size, preview: preview) self.imageView.alpha = 1