From 888f44366c7db46f268b65954ce4872d2d4f91ab Mon Sep 17 00:00:00 2001 From: Shadowfacts Date: Sat, 8 Jun 2024 10:32:32 -0700 Subject: [PATCH] Fix multi-column nav not animating scroll position when replacing subsequent columns Closes #500 --- .../MultiColumnNavigationController.swift | 57 ++++++++++--------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/Tusker/Screens/Utilities/MultiColumnNavigationController.swift b/Tusker/Screens/Utilities/MultiColumnNavigationController.swift index 48bf7e08..96576a61 100644 --- a/Tusker/Screens/Utilities/MultiColumnNavigationController.swift +++ b/Tusker/Screens/Utilities/MultiColumnNavigationController.swift @@ -11,14 +11,18 @@ import UIKit class MultiColumnNavigationController: UIViewController { private var isManuallyUpdating = false - var viewControllers: [UIViewController] = [] { - didSet { - guard isViewLoaded, - !isManuallyUpdating else { - return + private var _viewControllers: [UIViewController] = [] + var viewControllers: [UIViewController] { + get { + _viewControllers + } + set { + _viewControllers = newValue + if isViewLoaded, + !isManuallyUpdating { + updateViews() + scrollToEnd(animated: false) } - updateViews() - scrollToEnd(animated: false) } } @@ -68,13 +72,13 @@ class MultiColumnNavigationController: UIViewController { private func updateViews() { var i = 0 - while i < viewControllers.count { + while i < _viewControllers.count { let needsCloseButton = i > 0 if i <= stackView.arrangedSubviews.count - 1 { let existing = stackView.arrangedSubviews[i] as! ColumnView - existing.setContent(viewControllers[i], needsCloseButton: needsCloseButton) + existing.setContent(_viewControllers[i], needsCloseButton: needsCloseButton) } else { - let new = ColumnView(owner: self, contentViewController: viewControllers[i], needsCloseButton: needsCloseButton) + let new = ColumnView(owner: self, contentViewController: _viewControllers[i], needsCloseButton: needsCloseButton) stackView.addArrangedSubview(new) } i += 1 @@ -92,7 +96,7 @@ class MultiColumnNavigationController: UIViewController { var index: Int? = nil var current: UIViewController? = sender while let c = current { - index = viewControllers.firstIndex(of: c) + index = _viewControllers.firstIndex(of: c) if index != nil { break } else { @@ -112,19 +116,20 @@ class MultiColumnNavigationController: UIViewController { } func replaceViewControllers(_ vcs: [UIViewController], after afterIndex: Int, animated: Bool) { - if afterIndex == viewControllers.count - 1 && vcs.count == 1 { + if afterIndex == _viewControllers.count - 1 && vcs.count == 1 { pushViewController(vcs[0], animated: animated) } else { - viewControllers = Array(viewControllers[...afterIndex]) + vcs + _viewControllers = Array(_viewControllers[...afterIndex]) + vcs + updateViews() scrollToEnd(animated: animated) } } private func scrollToEnd(animated: Bool) { - if viewControllers.isEmpty { + if _viewControllers.isEmpty { scrollView.setContentOffset(.init(x: -scrollView.adjustedLeadingContentInset, y: -scrollView.adjustedContentInset.top), animated: false) } else { - scrollColumnToEnd(columnIndex: viewControllers.count - 1, animated: animated) + scrollColumnToEnd(columnIndex: _viewControllers.count - 1, animated: animated) } } @@ -142,14 +147,12 @@ class MultiColumnNavigationController: UIViewController { } fileprivate func closeColumn(_ vc: UIViewController) { - guard let index = viewControllers.firstIndex(of: vc), + guard let index = _viewControllers.firstIndex(of: vc), index > 0 else { // Can't close the last column return } - isManuallyUpdating = true - defer { isManuallyUpdating = false } - viewControllers.removeSubrange(index...) + _viewControllers.removeSubrange(index...) animateChanges { for column in self.stackView.arrangedSubviews[index...] { column.layer.opacity = 0 @@ -158,7 +161,6 @@ class MultiColumnNavigationController: UIViewController { } completion: { self.updateViews() } - } private func animateChanges(_ animations: @escaping () -> Void, completion: (() -> Void)? = nil) { @@ -173,19 +175,22 @@ class MultiColumnNavigationController: UIViewController { extension MultiColumnNavigationController: NavigationControllerProtocol { var topViewController: UIViewController? { - viewControllers.last + _viewControllers.last } func popToRootViewController(animated: Bool) -> [UIViewController]? { - let removed = Array(viewControllers.dropFirst()) - viewControllers = [viewControllers.first!] + guard !_viewControllers.isEmpty else { + return nil + } + let removed = Array(_viewControllers.dropFirst()) + _viewControllers = [_viewControllers.first!] + updateViews() + scrollToEnd(animated: animated) return removed } func pushViewController(_ vc: UIViewController, animated: Bool) { - isManuallyUpdating = true - defer { isManuallyUpdating = false } - viewControllers.append(vc) + _viewControllers.append(vc) updateViews() scrollToEnd(animated: animated) if animated {