Подтвердить что ты не робот

Перемещение представлений дочерних элементов в виде контейнера

Пусть ContainerView представляет собой родительский контейнерный вид с двумя дочерними объектами: NavigationView и ContentView.

Example of View Layout

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

Это также необходимо связать с NavigationView, который имеет информацию о том, какой вид в настоящее время находится в ContentView. Например: если пользователь находится на странице новостей, панель навигации в навигационном представлении покажет, что кнопка новостей в данный момент выбрана.

Вопрос A: Есть ли способ поменять контроллер в ContentView без метода делегата, вызывающего сам ContainerView? Я хотел бы сделать это программно (без раскадровки).

Вопрос B: Как я могу сменить контроллеры в ContentView с NavigationView без вызова делегата? Я хотел бы сделать это программно (без раскадровки).

4b9b3361

Ответ 1

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

Предполагая, что вы выполнили шаблон пользовательского контейнера, если вы хотите изменить контроллер дочернего представления (и связанный с ним просмотр) для "представления содержимого", вы сделали бы это программно с помощью чего-то вроде:

UIViewController *newController = ... // instantiate new controller however you want
UIViewController *oldController = ... // grab the existing controller for the current "content view"; perhaps you maintain this in your own ivar; perhaps you just look this up in self.childViewControllers

newController.view.frame = oldController.view.frame;

[oldController willMoveToParentViewController:nil];
[self addChildViewController:newController];         // incidentally, this does the `willMoveToParentViewController` for the new controller for you

[self transitionFromViewController:oldController
                  toViewController:newController
                          duration:0.5
                           options:UIViewAnimationOptionTransitionCrossDissolve
                        animations:^{
                            // no further animations required
                        }
                        completion:^(BOOL finished) {
                            [oldController removeFromParentViewController]; // incidentally, this does the `didMoveToParentViewController` for the old controller for you
                            [newController didMoveToParentViewController:self];
                        }];

Когда вы это делаете, нет необходимости в каком-либо интерфейсе делегат-протокол с контроллером представления контента (кроме того, что iOS уже предоставляет с помощью Manage Child View Controllers в пользовательских контейнерах).


Кстати, это предполагает, что начальный дочерний контроллер, связанный с этим представлением содержимого, был добавлен так:

UIViewController *childController = ... // instantiate the content view controller any way you want
[self addChildViewController:childController];
childController.view.frame = ... // set the frame any way you want
[self.view addSubview:childController.view];
[childController didMoveToParentViewController:self];

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

  • Определите протокол для этого:

    @protocol ContainerParent <NSObject>
    
    - (void)changeContentTo:(UIViewController *)controller;
    
    @end
    
  • Определите родительский контроллер для соответствия этому протоколу, например:

    #import <UIKit/UIKit.h>
    #import "ContainerParent.h"
    
    @interface ViewController : UIViewController <ContainerParent>
    
    @end
    
  • Внедрите метод changeContentTo в родительском контроллере (как описано выше):

    - (void)changeContentTo:(UIViewController *)controller
    {
        UIViewController *newController = controller;
        UIViewController *oldController = ... // grab reference of current child from `self.childViewControllers or from some property where you stored it
    
        newController.view.frame = oldController.view.frame;
    
        [oldController willMoveToParentViewController:nil];
        [self addChildViewController:newController];
    
        [self transitionFromViewController:oldController
                          toViewController:newController
                                  duration:1.0
                                   options:UIViewAnimationOptionTransitionCrossDissolve
                                animations:^{
                                    // no further animations required
                                }
                                completion:^(BOOL finished) {
                                    [oldController removeFromParentViewController];
                                    [newController didMoveToParentViewController:self];
                                }];
    }
    
  • Теперь дочерние контроллеры могут использовать этот протокол в отношении свойства self.parentViewController, которое iOS предоставляет вам:

    - (IBAction)didTouchUpInsideButton:(id)sender
    {
        id <ContainerParent> parentViewController = (id)self.parentViewController;
        NSAssert([parentViewController respondsToSelector:@selector(changeContentTo:)], @"Parent must conform to ContainerParent protocol");
    
        UIViewController *newChild = ... // instantiate the new child controller any way you want
        [parentViewController changeContentTo:newChild];
    }
    

Ответ 2

Для такого перехода вы также можете использовать анимацию UIView.animateWith....

Например, предположим, что rootContainerView - контейнер и contentViewController текущий активный контроллер в контейнере, тогда

func setContentViewController(contentViewController:UIViewController, animated:Bool = true) {
    if animated == true {
        addChildViewController(contentViewController)
        contentViewController.view.alpha = 0
        contentViewController.view.frame = rootContainerView.bounds
        rootContainerView.addSubview(contentViewController.view)
        self.contentViewController?.willMoveToParentViewController(nil)

        UIView.animateWithDuration(0.3, animations: {
            contentViewController.view.alpha = 1
            }, completion: { (_) in
                contentViewController.didMoveToParentViewController(self)

                self.contentViewController?.view.removeFromSuperview()
                self.contentViewController?.didMoveToParentViewController(nil)
                self.contentViewController?.removeFromParentViewController()
                self.contentViewController = contentViewController
        })
    } else {
        cleanUpChildControllerIfPossible()

        contentViewController.view.frame = rootContainerView.bounds
        addChildViewController(contentViewController)
        rootContainerView.addSubview(contentViewController.view)
        contentViewController.didMoveToParentViewController(self)
        self.contentViewController = contentViewController
    }
}

// MARK: - Private

private func cleanUpChildControllerIfPossible() {
    if let childController = contentViewController {
        childController.willMoveToParentViewController(nil)
        childController.view.removeFromSuperview()
        childController.removeFromParentViewController()
    }
}

это обеспечит простую анимацию выцветания, и вы также можете попробовать любые UIViewAnimationOptions, переходы и т.д.

Ответ 3

Кажется, вы сбиты с толку. ContentView (предполагая UIView) не "содержит" контроллер, который вы бы заменили. A UIViewController обрабатывает UIView. Мне кажется, что вам требуется настройка контроллера родительского-дочернего представления.

Единый родительский контроллер представления, который будет обрабатывать контроллеры дочерних представлений, каждый из которых вы можете обрабатывать, когда каждый отображается на экране, и соответствующим образом настраивайте ваши UIView и контент. См. Документацию Apple ниже.

Контейнерное программирование - Документация Apple