Как обрабатывать анимацию больших заголовков iOS 11 при использовании нескольких представлений контейнера?

Я делаю приложение в тот момент, когда 1 экран имеет сегментированный элемент управления с 3 сегментами. Изначально у меня было 1 табличное представление, и когда вы меняете сегмент, я просто меняю источник данных / ячейку и т. Д. И перезагружаю таблицу. Хотя это прекрасно работает, всегда есть проблема, что при изменении сегментов он не запомнит вашу последнюю позицию прокрутки, потому что табличное представление перезагружается.

Я пытался обойти это с сохранением положения смещения, строк и т. Д., Но я никогда не мог заставить его работать так, как я хотел. Это особенно раздражает, когда у вас есть разные типы ячеек для сегментов, и они также сами меняют размеры.

Затем я решил иметь главный контроллер представления с сегментированным управлением и 3 представления контейнера с их собственным VC и табличным представлением для каждого сегмента. Я просто скрываю / показываю правильный вид контейнера при смене сегментов. Это также прекрасно работает, но у меня есть 1 проблема с большими заголовками в стиле iOS 11. Только 1-й контейнерный вид, добавленный как подпредставление к представлению ViewControllers, управляет свертыванием / развертыванием заголовка при прокрутке.

Поэтому, когда я переключаюсь на 2-й или 3-й вид контейнера и начинаю прокручивать, я не получаю анимацию свертывания большого заголовка. Как я могу обойти это?

Я попробовал следующее

1) Изменить представление контейнера zPosition при изменении сегментов

2) Переместите вид контейнера вперед, вызвав view.bringSubview(toFront: ...)

3) Перебирая подпредставления и звоня view.exchangeSubview(at: 0, withSubviewAt: ...)

Я считаю, что могу удалить все виды контейнеров и добавить тот, который мне нужен, и задать им ограничения, но мне интересно, есть ли более простое решение.

Или, если у кого-то есть хорошее решение запомнить позицию прокрутки tableViews перед ее перезагрузкой, я был бы также признателен за это.

2 ответа

Решение

Так что я нашел ответ, который, кажется, работает для меня, благодаря этой замечательной статье. https://cocoacasts.com/managing-view-controllers-with-container-view-controllers/

По сути, то, что я сделал, это

1) Удалите виды контейнера и сегменты из раскадровки MasterViewController.

2) Добавьте ленивое свойство для каждого VC сегментированного элемента управления в MasterViewController. Они ленивы, поэтому инициализируются только тогда, когда они вам действительно нужны

 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 с помощью следующих 2 методов. Я добавил их в расширение исключительно для организации кода, так как они могут быть использованы в других ViewControllers.

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. Приятно то, что удаление / добавление их фактически не освобождает их.

func didPressSegmentedControl() {

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

5) Убедитесь, что вы вызываете метод в точке 4 в ViewDidLoad так что правильный VC добавляется при первой загрузке VC.

func viewDidLoad() {
    super.viewDidLoad()

    didPressSegmentedControl()
}

Таким образом, когда мы удаляем ChildViewController и добавляем еще один, он всегда будет верхним VC в массиве subviews, и я получаю хорошую анимацию свертывания заголовка.

Еще одним дополнительным преимуществом этого подхода является то, что если вы никогда не перейдете к определенному сегменту, этот конкретный VC никогда не будет инициализирован, потому что это ленивые свойства, которые должны способствовать эффективности.

Надеюсь, это поможет кому-то сделать то же самое.

Это ужасная проблема, которая, я надеюсь, будет решена в ближайшее время, но есть и другое решение - хотя я свободно признаю, что это неприятный хак.

По сути, эта проблема относится только к представлению контейнера FIRST в иерархии. Добавьте другой контейнерный вид в вашу иерархию. Установите его как скрытый, затем удалите его segue и целевой контроллер вида, чтобы быть аккуратным. Наконец, убедитесь, что это первое контейнерное представление в иерархии, перетащив его в верхнюю часть списка.

Другие вопросы по тегам