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