Fix multi-column nav not animating scroll position when replacing subsequent columns
Closes #500
This commit is contained in:
parent
c88076eec0
commit
888f44366c
|
@ -11,16 +11,20 @@ import UIKit
|
||||||
class MultiColumnNavigationController: UIViewController {
|
class MultiColumnNavigationController: UIViewController {
|
||||||
|
|
||||||
private var isManuallyUpdating = false
|
private var isManuallyUpdating = false
|
||||||
var viewControllers: [UIViewController] = [] {
|
private var _viewControllers: [UIViewController] = []
|
||||||
didSet {
|
var viewControllers: [UIViewController] {
|
||||||
guard isViewLoaded,
|
get {
|
||||||
!isManuallyUpdating else {
|
_viewControllers
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
set {
|
||||||
|
_viewControllers = newValue
|
||||||
|
if isViewLoaded,
|
||||||
|
!isManuallyUpdating {
|
||||||
updateViews()
|
updateViews()
|
||||||
scrollToEnd(animated: false)
|
scrollToEnd(animated: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private var scrollView = UIScrollView()
|
private var scrollView = UIScrollView()
|
||||||
private var stackView = UIStackView()
|
private var stackView = UIStackView()
|
||||||
|
@ -68,13 +72,13 @@ class MultiColumnNavigationController: UIViewController {
|
||||||
|
|
||||||
private func updateViews() {
|
private func updateViews() {
|
||||||
var i = 0
|
var i = 0
|
||||||
while i < viewControllers.count {
|
while i < _viewControllers.count {
|
||||||
let needsCloseButton = i > 0
|
let needsCloseButton = i > 0
|
||||||
if i <= stackView.arrangedSubviews.count - 1 {
|
if i <= stackView.arrangedSubviews.count - 1 {
|
||||||
let existing = stackView.arrangedSubviews[i] as! ColumnView
|
let existing = stackView.arrangedSubviews[i] as! ColumnView
|
||||||
existing.setContent(viewControllers[i], needsCloseButton: needsCloseButton)
|
existing.setContent(_viewControllers[i], needsCloseButton: needsCloseButton)
|
||||||
} else {
|
} else {
|
||||||
let new = ColumnView(owner: self, contentViewController: viewControllers[i], needsCloseButton: needsCloseButton)
|
let new = ColumnView(owner: self, contentViewController: _viewControllers[i], needsCloseButton: needsCloseButton)
|
||||||
stackView.addArrangedSubview(new)
|
stackView.addArrangedSubview(new)
|
||||||
}
|
}
|
||||||
i += 1
|
i += 1
|
||||||
|
@ -92,7 +96,7 @@ class MultiColumnNavigationController: UIViewController {
|
||||||
var index: Int? = nil
|
var index: Int? = nil
|
||||||
var current: UIViewController? = sender
|
var current: UIViewController? = sender
|
||||||
while let c = current {
|
while let c = current {
|
||||||
index = viewControllers.firstIndex(of: c)
|
index = _viewControllers.firstIndex(of: c)
|
||||||
if index != nil {
|
if index != nil {
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
|
@ -112,19 +116,20 @@ class MultiColumnNavigationController: UIViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
func replaceViewControllers(_ vcs: [UIViewController], after afterIndex: Int, animated: Bool) {
|
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)
|
pushViewController(vcs[0], animated: animated)
|
||||||
} else {
|
} else {
|
||||||
viewControllers = Array(viewControllers[...afterIndex]) + vcs
|
_viewControllers = Array(_viewControllers[...afterIndex]) + vcs
|
||||||
|
updateViews()
|
||||||
scrollToEnd(animated: animated)
|
scrollToEnd(animated: animated)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func scrollToEnd(animated: Bool) {
|
private func scrollToEnd(animated: Bool) {
|
||||||
if viewControllers.isEmpty {
|
if _viewControllers.isEmpty {
|
||||||
scrollView.setContentOffset(.init(x: -scrollView.adjustedLeadingContentInset, y: -scrollView.adjustedContentInset.top), animated: false)
|
scrollView.setContentOffset(.init(x: -scrollView.adjustedLeadingContentInset, y: -scrollView.adjustedContentInset.top), animated: false)
|
||||||
} else {
|
} 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) {
|
fileprivate func closeColumn(_ vc: UIViewController) {
|
||||||
guard let index = viewControllers.firstIndex(of: vc),
|
guard let index = _viewControllers.firstIndex(of: vc),
|
||||||
index > 0 else {
|
index > 0 else {
|
||||||
// Can't close the last column
|
// Can't close the last column
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
isManuallyUpdating = true
|
_viewControllers.removeSubrange(index...)
|
||||||
defer { isManuallyUpdating = false }
|
|
||||||
viewControllers.removeSubrange(index...)
|
|
||||||
animateChanges {
|
animateChanges {
|
||||||
for column in self.stackView.arrangedSubviews[index...] {
|
for column in self.stackView.arrangedSubviews[index...] {
|
||||||
column.layer.opacity = 0
|
column.layer.opacity = 0
|
||||||
|
@ -158,7 +161,6 @@ class MultiColumnNavigationController: UIViewController {
|
||||||
} completion: {
|
} completion: {
|
||||||
self.updateViews()
|
self.updateViews()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func animateChanges(_ animations: @escaping () -> Void, completion: (() -> Void)? = nil) {
|
private func animateChanges(_ animations: @escaping () -> Void, completion: (() -> Void)? = nil) {
|
||||||
|
@ -173,19 +175,22 @@ class MultiColumnNavigationController: UIViewController {
|
||||||
|
|
||||||
extension MultiColumnNavigationController: NavigationControllerProtocol {
|
extension MultiColumnNavigationController: NavigationControllerProtocol {
|
||||||
var topViewController: UIViewController? {
|
var topViewController: UIViewController? {
|
||||||
viewControllers.last
|
_viewControllers.last
|
||||||
}
|
}
|
||||||
|
|
||||||
func popToRootViewController(animated: Bool) -> [UIViewController]? {
|
func popToRootViewController(animated: Bool) -> [UIViewController]? {
|
||||||
let removed = Array(viewControllers.dropFirst())
|
guard !_viewControllers.isEmpty else {
|
||||||
viewControllers = [viewControllers.first!]
|
return nil
|
||||||
|
}
|
||||||
|
let removed = Array(_viewControllers.dropFirst())
|
||||||
|
_viewControllers = [_viewControllers.first!]
|
||||||
|
updateViews()
|
||||||
|
scrollToEnd(animated: animated)
|
||||||
return removed
|
return removed
|
||||||
}
|
}
|
||||||
|
|
||||||
func pushViewController(_ vc: UIViewController, animated: Bool) {
|
func pushViewController(_ vc: UIViewController, animated: Bool) {
|
||||||
isManuallyUpdating = true
|
_viewControllers.append(vc)
|
||||||
defer { isManuallyUpdating = false }
|
|
||||||
viewControllers.append(vc)
|
|
||||||
updateViews()
|
updateViews()
|
||||||
scrollToEnd(animated: animated)
|
scrollToEnd(animated: animated)
|
||||||
if animated {
|
if animated {
|
||||||
|
|
Loading…
Reference in New Issue