使用多个容器视图时如何处理iOS11个大标题动画?

How to handle iOS 11 large title animation when using multiple container views?

我现在正在制作一个应用程序,其中 1 个屏幕具有 3 个分段的分段控件。最初我有 1 个 table 视图,当您更改段时,我只需更改数据 source/cell 等并重新加载 table。虽然这很好用,但总会有一个问题,即当您更改段时,它不会记住您上次滚动的位置,因为 table 视图会重新加载。

我试图通过存储偏移位置、行等来解决这个问题,但我永远无法让它像我想要的那样工作。当您为片段设置不同的单元格类型并且它们也在自动调整大小时,这似乎特别烦人。

然后我决定拥有一个带分段控件的主视图控制器和 3 个容器视图,每个部分都有自己的 VC 和 table 视图。更改段时,我只是 hide/show 正确的容器视图。这也很好用,但我有 1 个问题 iOS 11 样式大 headers。只有作为子视图添加到 ViewControllers 视图的第一个容器视图在您滚动时操作标题的 collasping/expanding。

因此,当我切换到第二个或第三个容器视图并开始滚动时,我没有看到大标题折叠动画。我该如何解决这个问题?

我尝试了以下

1) 更改段时更改容器视图 zPosition

2) 通过调用view.bringSubview(toFront: ...)

将容器视图移动到最前面

3) 遍历子视图并调用 view.exchangeSubview(at: 0, withSubviewAt: ...)

我相信我可以删除所有容器视图并再次添加我需要的视图并给它们约束,但我想知道是否有更直接的解决方案。

或者,如果有人有一个很好的解决方案,可以在重新加载之前记住 tableViews 滚动位置,我也将不胜感激。

多亏了这篇很棒的文章,我找到了一个似乎对我有用的答案。 https://cocoacasts.com/managing-view-controllers-with-container-view-controllers/

基本上我所做的是

1) 从 MasterViewController Storyboard 中删除 ContainerViews 和 Segues。

2) 在MasterViewController中为每个VC的分段控件添加一个lazy属性。它们很懒惰,因此只有在您真正需要它们时才会初始化它们

 lazy var viewController1: LibraryViewController = {
        let viewController = UIStoryboard.libraryViewController // convenience property to create the VC from Storyboard
        // do other set up if required.
        return viewController
    }()

lazy var viewController2: LibraryViewController = {
            let viewController = UIStoryboard.libraryViewController // convenience property to create the VC from Storyboard
            // do other set up if required.
            return viewController
        }()

3) 使用以下两种方法创建 UIViewController 的扩展。我将它们添加到扩展中纯粹是为了代码组织,因为它们可能会在其他 ViewController 上重用。

extension UIViewController {

    func add(asChildViewController viewController: UIViewController) {
        // Add Child View Controller
        addChildViewController(viewController)

        // Add Child View as Subview
        view.addSubview(viewController.view)

        // Configure Child View
        viewController.view.frame = view.bounds
        viewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]

        // Notify Child View Controller
        viewController.didMove(toParentViewController: self)
    }

    func remove(asChildViewController viewController: UIViewController) {
        // Notify Child View Controller
        viewController.willMove(toParentViewController: nil)

        // Remove Child View From Superview
        viewController.view.removeFromSuperview()

        // Notify Child View Controller
        viewController.removeFromParentViewController()
    }
}

4) 现在,在您更改段时调用的分段控制方法中,我只需添加正确的 ViewController。好的是 remove/adding 它们实际上并没有释放它们。

func didPressSegmentedControl() {

   if segmentedControl.selectedSegmentIndex == 0 {
        remove(asChildViewController: viewController2)
        add(asChildViewController: viewController1)
    } else {
        remove(asChildViewController: viewController1)
        add(asChildViewController: viewController2)
    }
}

5) 确保在 ViewDidLoad 中调用第 4 点的方法,以便在第一次加载 VC 时添加正确的 VC。

func viewDidLoad() {
    super.viewDidLoad()

    didPressSegmentedControl()
}

这样,当我们删除一个 ChildViewController 并添加另一个时,它将始终位于子视图数组的顶部 VC,并且我得到了漂亮的标题折叠动画。

这种方法的另一个额外好处是,如果您从不进入特定段,则该特定 VC 将永远不会被初始化,因为它们是惰性属性,这应该有助于提高效率。

希望这对尝试做同样事情的人有所帮助。

这是一个可怕的问题,我希望它能尽快得到解决,但还有另一个修复方法 - 尽管我承认这是一个令人讨厌的黑客攻击。

基本上,该问题仅适用于层次结构中的第一个容器视图。将另一个容器视图添加到您的层次结构中。将它设置为隐藏,然后删除它的 segue 和它的目标视图控制器只是为了整洁。最后,通过将其拖动到列表顶部来确保这是层次结构中的第一个容器视图。