From f0b3e1fde9c95967604338684807c2628666c2c1 Mon Sep 17 00:00:00 2001 From: Jorge Bernal Date: Thu, 1 Dec 2022 15:07:30 +0100 Subject: [PATCH 1/8] Initial approach to animating the JITM message --- .../Dashboard/DashboardViewController.swift | 69 +++++++++++++++---- 1 file changed, 54 insertions(+), 15 deletions(-) diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift b/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift index e5a4704e76c..8bc9ea59973 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift @@ -55,6 +55,10 @@ final class DashboardViewController: UIViewController { return view }() + /// Constraint to attach the content view's top to the bottom of the header + /// When we hide the header, we disable this constraint so the content view can grow to fill the screen + private var contentTopToHeaderConstraint: NSLayoutConstraint? + // Used to trick the navigation bar for large title (ref: issue 3 in p91TBi-45c-p2). private let hiddenScrollView = UIScrollView() @@ -145,16 +149,42 @@ final class DashboardViewController: UIViewController { return true } - /// Hide the announcement card when the navigation bar is compact - /// - func updateAnnouncementCardVisibility() { - announcementView?.isHidden = navigationBarIsShort + func updateHeaderVisibility() { + headerIsHidden = navigationBarIsShort } - /// Hide the store name when the navigation bar is compact - /// - func updateStoreNameLabelVisibility() { - storeNameLabel.isHidden = !shouldShowStoreNameAsSubtitle || navigationBarIsShort + var headerIsHidden: Bool = false { + didSet { + switch (oldValue, headerIsHidden) { + case (false, true): + hideHeaderWithAnimation() + case (true, false): + showHeaderWithAnimation() + default: + // Ignore calls if the value hasn't changed + break + } + } + } + + func hideHeaderWithAnimation() { + contentTopToHeaderConstraint?.isActive = false + UIView.animate(withDuration: Constants.animationDurationSeconds, animations: { [headerStackView] in + headerStackView.alpha = 0 + self.view.layoutIfNeeded() + }, completion: { [headerStackView] _ in + headerStackView.isHidden = true + }) + + } + + func showHeaderWithAnimation() { + headerStackView.isHidden = false + contentTopToHeaderConstraint?.isActive = true + UIView.animate(withDuration: Constants.animationDurationSeconds, animations: { [headerStackView] in + self.view.layoutIfNeeded() + headerStackView.alpha = 1 + }) } } @@ -208,8 +238,19 @@ private extension DashboardViewController { func addViewBelowHeaderStackView(contentView: UIView) { contentView.translatesAutoresizingMaskIntoConstraints = false + + // This constraint will pin the bottom of the header to the top of the content + // We want this to be active when the header is visible + contentTopToHeaderConstraint = contentView.topAnchor.constraint(equalTo: headerStackView.bottomAnchor) + contentTopToHeaderConstraint?.isActive = true + + // This constraint has a lower priority and will pin the top of the content view to its superview + // This way, it has a defined height when contentTopToHeaderConstraint is disabled + let contentTopToContainerConstraint = contentView.topAnchor.constraint(equalTo: containerView.safeTopAnchor) + contentTopToContainerConstraint.priority = .defaultLow + NSLayoutConstraint.activate([ - contentView.topAnchor.constraint(equalTo: headerStackView.bottomAnchor), + contentTopToContainerConstraint, contentView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor), contentView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor), ]) @@ -377,8 +418,6 @@ private extension DashboardViewController { let indexAfterHeader = (headerStackView.arrangedSubviews.firstIndex(of: innerStackView) ?? -1) + 1 headerStackView.insertArrangedSubview(uiView, at: indexAfterHeader) - updateAnnouncementCardVisibility() - hostingController.didMove(toParent: self) hostingController.view.layoutIfNeeded() } @@ -402,11 +441,12 @@ private extension DashboardViewController { guard siteName.isNotEmpty else { shouldShowStoreNameAsSubtitle = false storeNameLabel.text = nil + storeNameLabel.isHidden = true return } shouldShowStoreNameAsSubtitle = true + storeNameLabel.isHidden = false storeNameLabel.text = siteName - updateStoreNameLabelVisibility() } } @@ -589,9 +629,7 @@ private extension DashboardViewController { navigationController?.navigationBar.publisher(for: \.frame, options: [.initial, .new]) .removeDuplicates() .sink(receiveValue: { [weak self] _ in - guard let self else { return } - self.updateStoreNameLabelVisibility() - self.updateAnnouncementCardVisibility() + self?.updateHeaderVisibility() }) .store(in: &subscriptions) } @@ -623,6 +661,7 @@ private extension DashboardViewController { } enum Constants { + static let animationDurationSeconds = CGFloat(0.3) static let bannerBottomMargin = CGFloat(8) static let horizontalMargin = CGFloat(16) static let storeNameTextColor: UIColor = .secondaryLabel From 196d3afce16a6d5ae3963d997c9cb1f9fa6c0248 Mon Sep 17 00:00:00 2001 From: Jorge Bernal Date: Thu, 1 Dec 2022 16:29:41 +0100 Subject: [PATCH 2/8] Simplify header hiding logic --- .../Dashboard/DashboardViewController.swift | 47 ++++++------------- 1 file changed, 14 insertions(+), 33 deletions(-) diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift b/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift index 8bc9ea59973..5a92f071236 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift @@ -123,7 +123,7 @@ final class DashboardViewController: UIViewController { configureBottomJetpackBenefitsBanner() observeSiteForUIUpdates() observeBottomJetpackBenefitsBannerVisibilityUpdates() - observeNavigationBarHeightForHeaderExtrasVisibility() + observeNavigationBarHeightForHeaderVisibility() observeStatsVersionForDashboardUIUpdates() observeAnnouncements() observeShowWebViewSheet() @@ -149,24 +149,6 @@ final class DashboardViewController: UIViewController { return true } - func updateHeaderVisibility() { - headerIsHidden = navigationBarIsShort - } - - var headerIsHidden: Bool = false { - didSet { - switch (oldValue, headerIsHidden) { - case (false, true): - hideHeaderWithAnimation() - case (true, false): - showHeaderWithAnimation() - default: - // Ignore calls if the value hasn't changed - break - } - } - } - func hideHeaderWithAnimation() { contentTopToHeaderConstraint?.isActive = false UIView.animate(withDuration: Constants.animationDurationSeconds, animations: { [headerStackView] in @@ -625,29 +607,28 @@ private extension DashboardViewController { }.store(in: &subscriptions) } - func observeNavigationBarHeightForHeaderExtrasVisibility() { + func observeNavigationBarHeightForHeaderVisibility() { navigationController?.navigationBar.publisher(for: \.frame, options: [.initial, .new]) + .map({ [collapsedNavigationBarHeight] rect in + rect.height <= collapsedNavigationBarHeight + }) // true if navigation bar is collapsed .removeDuplicates() - .sink(receiveValue: { [weak self] _ in - self?.updateHeaderVisibility() + .sink(receiveValue: { [weak self] navigationBarIsShort in + if navigationBarIsShort { + self?.hideHeaderWithAnimation() + } else { + self?.showHeaderWithAnimation() + } }) .store(in: &subscriptions) } - /// Returns true if the navigation bar has a compact height as opposed to showing a large title - /// - var navigationBarIsShort: Bool { - guard let navigationBarHeight = navigationController?.navigationBar.frame.height else { - return false - } - - let collapsedNavigationBarHeight: CGFloat + var collapsedNavigationBarHeight: CGFloat { if self.traitCollection.userInterfaceIdiom == .pad { - collapsedNavigationBarHeight = Constants.iPadCollapsedNavigationBarHeight + return Constants.iPadCollapsedNavigationBarHeight } else { - collapsedNavigationBarHeight = Constants.iPhoneCollapsedNavigationBarHeight + return Constants.iPhoneCollapsedNavigationBarHeight } - return navigationBarHeight <= collapsedNavigationBarHeight } } From e03d673b6c1fdff67c9b33b1d9c4b1a258e1214b Mon Sep 17 00:00:00 2001 From: Jorge Bernal Date: Thu, 1 Dec 2022 18:11:59 +0100 Subject: [PATCH 3/8] Switch to UIViewPropertyAnimator for animating the JITM --- .../Dashboard/DashboardViewController.swift | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift b/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift index 5a92f071236..9ff6020f4f4 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift @@ -59,6 +59,17 @@ final class DashboardViewController: UIViewController { /// When we hide the header, we disable this constraint so the content view can grow to fill the screen private var contentTopToHeaderConstraint: NSLayoutConstraint? + private lazy var hideHeaderAnimator = { + let animator = UIViewPropertyAnimator(duration: Constants.animationDurationSeconds, curve: .easeOut) + animator.pausesOnCompletion = true + animator.addAnimations { [weak self] in + self?.contentTopToHeaderConstraint?.isActive = false + self?.headerStackView.alpha = 0 + self?.view.layoutIfNeeded() + } + return animator + }() + // Used to trick the navigation bar for large title (ref: issue 3 in p91TBi-45c-p2). private let hiddenScrollView = UIScrollView() @@ -123,7 +134,6 @@ final class DashboardViewController: UIViewController { configureBottomJetpackBenefitsBanner() observeSiteForUIUpdates() observeBottomJetpackBenefitsBannerVisibilityUpdates() - observeNavigationBarHeightForHeaderVisibility() observeStatsVersionForDashboardUIUpdates() observeAnnouncements() observeShowWebViewSheet() @@ -140,6 +150,11 @@ final class DashboardViewController: UIViewController { configureTitle() } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + observeNavigationBarHeightForHeaderVisibility() + } + override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() dashboardUI?.view.frame = containerView.bounds @@ -149,24 +164,22 @@ final class DashboardViewController: UIViewController { return true } - func hideHeaderWithAnimation() { - contentTopToHeaderConstraint?.isActive = false - UIView.animate(withDuration: Constants.animationDurationSeconds, animations: { [headerStackView] in - headerStackView.alpha = 0 - self.view.layoutIfNeeded() - }, completion: { [headerStackView] _ in - headerStackView.isHidden = true - }) + func startHeaderAnimation() { + hideHeaderAnimator.startAnimation() + } + func hideHeaderWithAnimation() { + hideHeaderAnimator.isReversed = false + hideHeaderAnimator.startAnimation() } func showHeaderWithAnimation() { - headerStackView.isHidden = false - contentTopToHeaderConstraint?.isActive = true - UIView.animate(withDuration: Constants.animationDurationSeconds, animations: { [headerStackView] in - self.view.layoutIfNeeded() - headerStackView.alpha = 1 - }) + // If the view hasn't been hidden yet, don't bother trying to animate showing it + guard hideHeaderAnimator.state == .active else { + return + } + hideHeaderAnimator.isReversed = true + hideHeaderAnimator.startAnimation() } } From ef31cf2d140467dfcab071106f32a190f670e25b Mon Sep 17 00:00:00 2001 From: Jorge Bernal Date: Fri, 2 Dec 2022 07:28:50 +0100 Subject: [PATCH 4/8] Don't reuse animator and cancel any existing animations instead --- .../Dashboard/DashboardViewController.swift | 53 +++++++++++-------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift b/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift index 9ff6020f4f4..6c18383efc5 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift @@ -59,16 +59,7 @@ final class DashboardViewController: UIViewController { /// When we hide the header, we disable this constraint so the content view can grow to fill the screen private var contentTopToHeaderConstraint: NSLayoutConstraint? - private lazy var hideHeaderAnimator = { - let animator = UIViewPropertyAnimator(duration: Constants.animationDurationSeconds, curve: .easeOut) - animator.pausesOnCompletion = true - animator.addAnimations { [weak self] in - self?.contentTopToHeaderConstraint?.isActive = false - self?.headerStackView.alpha = 0 - self?.view.layoutIfNeeded() - } - return animator - }() + private var headerAnimator: UIViewPropertyAnimator? // Used to trick the navigation bar for large title (ref: issue 3 in p91TBi-45c-p2). private let hiddenScrollView = UIScrollView() @@ -164,22 +155,42 @@ final class DashboardViewController: UIViewController { return true } - func startHeaderAnimation() { - hideHeaderAnimator.startAnimation() + func showHeader() { + contentTopToHeaderConstraint?.isActive = true + headerStackView.alpha = 1 + view.layoutIfNeeded() } - func hideHeaderWithAnimation() { - hideHeaderAnimator.isReversed = false - hideHeaderAnimator.startAnimation() + func hideHeader() { + contentTopToHeaderConstraint?.isActive = false + headerStackView.alpha = 0 + view.layoutIfNeeded() } func showHeaderWithAnimation() { - // If the view hasn't been hidden yet, don't bother trying to animate showing it - guard hideHeaderAnimator.state == .active else { - return - } - hideHeaderAnimator.isReversed = true - hideHeaderAnimator.startAnimation() + headerAnimator?.stopAnimation(true) + headerAnimator = UIViewPropertyAnimator.runningPropertyAnimator( + withDuration: Constants.animationDurationSeconds, + delay: 0, + animations: { [weak self] in + self?.showHeader() + }, + completion: { [weak self] position in + self?.headerAnimator = nil + }) + } + + func hideHeaderWithAnimation() { + headerAnimator?.stopAnimation(true) + headerAnimator = UIViewPropertyAnimator.runningPropertyAnimator( + withDuration: Constants.animationDurationSeconds, + delay: 0, + animations: { [weak self] in + self?.hideHeader() + }, + completion: { [weak self] position in + self?.headerAnimator = nil + }) } } From c22901daa62ec4a56b2d0ecd7c9d3f925701f4d6 Mon Sep 17 00:00:00 2001 From: Jorge Bernal Date: Fri, 2 Dec 2022 11:28:36 +0100 Subject: [PATCH 5/8] Document headerAnimator --- .../Classes/ViewRelated/Dashboard/DashboardViewController.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift b/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift index 6c18383efc5..4cdd90fb60c 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift @@ -59,6 +59,8 @@ final class DashboardViewController: UIViewController { /// When we hide the header, we disable this constraint so the content view can grow to fill the screen private var contentTopToHeaderConstraint: NSLayoutConstraint? + /// Stores an animator for showing/hiding the header view while there is an animation in progress + /// so we can interrupt and reverse if needed private var headerAnimator: UIViewPropertyAnimator? // Used to trick the navigation bar for large title (ref: issue 3 in p91TBi-45c-p2). From 3b1833035b1394693a5694a621723adb7d25a1fe Mon Sep 17 00:00:00 2001 From: Jorge Bernal Date: Fri, 2 Dec 2022 16:07:53 +0100 Subject: [PATCH 6/8] Pause navbar observer when view is not visible --- .../Dashboard/DashboardViewController.swift | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift b/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift index 4cdd90fb60c..7f1afdb6a0e 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift @@ -106,6 +106,7 @@ final class DashboardViewController: UIViewController { private let viewModel: DashboardViewModel = .init() private var subscriptions = Set() + private var navbarObserverSubscription: AnyCancellable? // MARK: View Lifecycle @@ -148,6 +149,11 @@ final class DashboardViewController: UIViewController { observeNavigationBarHeightForHeaderVisibility() } + override func viewWillDisappear(_ animated: Bool) { + stopObservingNavigationBarHeightForHeaderVisibility() + super.viewWillDisappear(animated) + } + override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() dashboardUI?.view.frame = containerView.bounds @@ -170,7 +176,9 @@ final class DashboardViewController: UIViewController { } func showHeaderWithAnimation() { - headerAnimator?.stopAnimation(true) + if headerAnimator?.isRunning == true { + headerAnimator?.stopAnimation(true) + } headerAnimator = UIViewPropertyAnimator.runningPropertyAnimator( withDuration: Constants.animationDurationSeconds, delay: 0, @@ -183,7 +191,9 @@ final class DashboardViewController: UIViewController { } func hideHeaderWithAnimation() { - headerAnimator?.stopAnimation(true) + if headerAnimator?.isRunning == true { + headerAnimator?.stopAnimation(true) + } headerAnimator = UIViewPropertyAnimator.runningPropertyAnimator( withDuration: Constants.animationDurationSeconds, delay: 0, @@ -634,19 +644,25 @@ private extension DashboardViewController { } func observeNavigationBarHeightForHeaderVisibility() { - navigationController?.navigationBar.publisher(for: \.frame, options: [.initial, .new]) + navbarObserverSubscription = navigationController?.navigationBar.publisher(for: \.frame, options: [.initial, .new]) .map({ [collapsedNavigationBarHeight] rect in rect.height <= collapsedNavigationBarHeight }) // true if navigation bar is collapsed .removeDuplicates() .sink(receiveValue: { [weak self] navigationBarIsShort in + guard let self else { return } + if navigationBarIsShort { - self?.hideHeaderWithAnimation() + self.hideHeaderWithAnimation() } else { - self?.showHeaderWithAnimation() + self.showHeaderWithAnimation() } }) - .store(in: &subscriptions) + } + + func stopObservingNavigationBarHeightForHeaderVisibility() { + navbarObserverSubscription?.cancel() + navbarObserverSubscription = nil } var collapsedNavigationBarHeight: CGFloat { From 15795b1759f75e2ff70350a8f47f8de51d41d4fe Mon Sep 17 00:00:00 2001 From: Jorge Bernal Date: Fri, 2 Dec 2022 16:31:20 +0100 Subject: [PATCH 7/8] Consolidate header animation code and allow updates without animation --- .../Dashboard/DashboardViewController.swift | 96 +++++++++++-------- 1 file changed, 58 insertions(+), 38 deletions(-) diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift b/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift index 7f1afdb6a0e..2e471b5b21d 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift @@ -146,6 +146,7 @@ final class DashboardViewController: UIViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) + updateHeaderVisibility(animated: false) observeNavigationBarHeightForHeaderVisibility() } @@ -162,48 +163,78 @@ final class DashboardViewController: UIViewController { override var shouldShowOfflineBanner: Bool { return true } +} - func showHeader() { +// MARK: - Header animation +private extension DashboardViewController { + func showHeaderWithoutAnimation() { contentTopToHeaderConstraint?.isActive = true headerStackView.alpha = 1 view.layoutIfNeeded() } - func hideHeader() { + func hideHeaderWithoutAnimation() { contentTopToHeaderConstraint?.isActive = false headerStackView.alpha = 0 view.layoutIfNeeded() } - func showHeaderWithAnimation() { - if headerAnimator?.isRunning == true { - headerAnimator?.stopAnimation(true) + func updateHeaderVisibility(animated: Bool) { + if navigationBarIsCollapsed() { + hideHeader(animated: animated) + } else { + showHeader(animated: animated) } - headerAnimator = UIViewPropertyAnimator.runningPropertyAnimator( - withDuration: Constants.animationDurationSeconds, - delay: 0, - animations: { [weak self] in - self?.showHeader() - }, - completion: { [weak self] position in - self?.headerAnimator = nil - }) } - func hideHeaderWithAnimation() { + func showHeader(animated: Bool) { + if animated { + animateHeaderVisibility { + self.showHeaderWithoutAnimation() + } + } else { + showHeaderWithoutAnimation() + } + } + + func hideHeader(animated: Bool) { + if animated { + animateHeaderVisibility { + self.hideHeaderWithoutAnimation() + } + } else { + hideHeaderWithoutAnimation() + } + } + + func animateHeaderVisibility(animations: @escaping () -> Void) { if headerAnimator?.isRunning == true { headerAnimator?.stopAnimation(true) } headerAnimator = UIViewPropertyAnimator.runningPropertyAnimator( withDuration: Constants.animationDurationSeconds, delay: 0, - animations: { [weak self] in - self?.hideHeader() - }, + animations: animations, completion: { [weak self] position in self?.headerAnimator = nil }) } + + func navigationBarIsCollapsed() -> Bool { + guard let frame = navigationController?.navigationBar.frame else { + return false + } + + return frame.height <= collapsedNavigationBarHeight + } + + var collapsedNavigationBarHeight: CGFloat { + if self.traitCollection.userInterfaceIdiom == .pad { + return Constants.iPadCollapsedNavigationBarHeight + } else { + return Constants.iPhoneCollapsedNavigationBarHeight + } + } } // MARK: - Configuration @@ -644,19 +675,16 @@ private extension DashboardViewController { } func observeNavigationBarHeightForHeaderVisibility() { - navbarObserverSubscription = navigationController?.navigationBar.publisher(for: \.frame, options: [.initial, .new]) - .map({ [collapsedNavigationBarHeight] rect in - rect.height <= collapsedNavigationBarHeight - }) // true if navigation bar is collapsed + navbarObserverSubscription = navigationController?.navigationBar.publisher(for: \.frame, options: [.new]) + .map({ [weak self] rect in + // This seems useless given that we're discarding the value later + // and recalculating within updateHeaderVisibility, but this is an easy + // way to avoid constant updates with the `removeDuplicates` that follows + self?.navigationBarIsCollapsed() ?? false + }) .removeDuplicates() - .sink(receiveValue: { [weak self] navigationBarIsShort in - guard let self else { return } - - if navigationBarIsShort { - self.hideHeaderWithAnimation() - } else { - self.showHeaderWithAnimation() - } + .sink(receiveValue: { [weak self] _ in + self?.updateHeaderVisibility(animated: true) }) } @@ -664,14 +692,6 @@ private extension DashboardViewController { navbarObserverSubscription?.cancel() navbarObserverSubscription = nil } - - var collapsedNavigationBarHeight: CGFloat { - if self.traitCollection.userInterfaceIdiom == .pad { - return Constants.iPadCollapsedNavigationBarHeight - } else { - return Constants.iPhoneCollapsedNavigationBarHeight - } - } } // MARK: Constants From 1f169d7a3c585893ce6ca9060e66a68ea0c9fe20 Mon Sep 17 00:00:00 2001 From: Jorge Bernal Date: Fri, 2 Dec 2022 16:37:03 +0100 Subject: [PATCH 8/8] Avoid retain cycles in animations --- .../ViewRelated/Dashboard/DashboardViewController.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift b/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift index 2e471b5b21d..74bae413d2e 100644 --- a/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift +++ b/WooCommerce/Classes/ViewRelated/Dashboard/DashboardViewController.swift @@ -189,8 +189,8 @@ private extension DashboardViewController { func showHeader(animated: Bool) { if animated { - animateHeaderVisibility { - self.showHeaderWithoutAnimation() + animateHeaderVisibility { [weak self] in + self?.showHeaderWithoutAnimation() } } else { showHeaderWithoutAnimation() @@ -199,8 +199,8 @@ private extension DashboardViewController { func hideHeader(animated: Bool) { if animated { - animateHeaderVisibility { - self.hideHeaderWithoutAnimation() + animateHeaderVisibility { [weak self] in + self?.hideHeaderWithoutAnimation() } } else { hideHeaderWithoutAnimation()