Изменение корневого контроллера представления для iOS окна


является ли корневой контроллер представления окна iOS обычно инициализируется один раз в начале контроллера панели вкладок или навигационного контроллера? Можно ли изменить корневой контроллер представления несколько раз в приложении?

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

5 54

5 ответов:

обычно используется "представленный контроллер вида" (presentViewController:animated:completion:). Вы можете иметь столько из них, сколько вам нравится, эффективно появляясь перед (и в основном заменяя) контроллер корневого представления. Там не должно быть никакой анимации, если вы не хотите, или может быть. Вы можете отклонить представленный контроллер вида, чтобы вернуться к исходному корневому контроллеру вида, но вам это не нужно; представленный контроллер вида может просто быть там навсегда, если вам нравится.

здесь раздел о представленных контроллерах вида из моей книги:

http://www.apeth.com/iOSBook/ch19.html#_presented_view_controller

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

enter image description here

iOS 8.0, Xcode 6.0.1, ARC enabled

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

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

ответ да. Мне пришлось сделать это недавно, чтобы сбросить иерархию UIView после начальных UIViews, которые были частью приложения. запуск больше не требовался. Другими словами, вы можно сбросить "rootViewController" из любого другого UIViewController в любое время после приложения. "didFinishLoadingWithOptions".

для этого...

1) объявить ссылку на ваше приложение. делегат (приложение называется "Тест")...

TestAppDelegate *testAppDelegate = (TestAppDelegate *)[UIApplication sharedApplication].delegate;

2) Выберите UIViewController вы хотите сделать свой "rootViewController"; либо из раскадровки или определить программно...

    a) раскадровка (убедитесь, что идентификатор, т. е. раскадровка, существует в Identity Inspector для UIViewController):
UIStoryboard *mainStoryBoard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];

NewRootViewController *newRootViewController = [mainStoryBoard instantiateViewControllerWithIdentifier:@"NewRootViewController"];

    b) программно (может addSubview и др.)
UIViewController *newRootViewController = [[UIViewController alloc] init];
newRootViewController.view = [[UIView alloc] initWithFrame:CGRectMake(0, 50, 320, 430)];
newRootViewController.view.backgroundColor = [UIColor whiteColor];

3) Положить все это вместе...

 testAppDelegate.window.rootViewController = newRootViewController;
[testAppDelegate.window makeKeyAndVisible];

4) Вы даже можете бросить в анимации...

testAppDelegate.window.rootViewController = newRootViewController;
    [testAppDelegate.window makeKeyAndVisible];

newRootViewController.view.alpha = 0.0;

    [UIView animateWithDuration:2.0 animations:^{

        newRootViewController.view.alpha = 1.0;

    }];

надеюсь, это кому-то поможет! Овации.

корневой контроллер вида для окна.

контроллер корневого представления обеспечивает представление содержимого окно. Назначение контроллера вида этому свойству (либо программно или с помощью построителя интерфейса) устанавливает представление-контроллер в качестве вид содержимого окна. Если окно имеет вид иерархия, старые представления удаляются до появления новых установленный. Значение по умолчанию этого свойства равно nil.

*обновление 9/2/2015

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

[UIView transitionWithView:self.containerView
                  duration:0.50
                   options:options
                animations:^{

                    //Transition of the two views
                    [self.viewController.view removeFromSuperview];
                    [self.containerView addSubview:aViewController.view];

                }
                completion:^(BOOL finished){

                    //At completion set the new view controller.
                    self.viewController = aViewController;

                }];

из комментариев к ответу serge-k я построил рабочее решение с обходным путем странного поведения, когда есть контроллер модального представления, представленный над старым rootViewController:

extension UIView {
    func snapshot() -> UIImage {
        UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.mainScreen().scale)
        drawViewHierarchyInRect(bounds, afterScreenUpdates: true)
        let result = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return result
    }
}

extension UIWindow {
    func replaceRootViewControllerWith(_ replacementController: UIViewController, animated: Bool, completion: (() -> Void)?) {
        let snapshotImageView = UIImageView(image: self.snapshot())
        self.addSubview(snapshotImageView)

        let dismissCompletion = { () -> Void in // dismiss all modal view controllers
            self.rootViewController = replacementController
            self.bringSubview(toFront: snapshotImageView)
            if animated {
                UIView.animate(withDuration: 0.4, animations: { () -> Void in
                    snapshotImageView.alpha = 0
                }, completion: { (success) -> Void in
                    snapshotImageView.removeFromSuperview()
                    completion?()
                })
            }
            else {
                snapshotImageView.removeFromSuperview()
                completion?()
            }
        }
        if self.rootViewController!.presentedViewController != nil {
            self.rootViewController!.dismiss(animated: false, completion: dismissCompletion)
        }
        else {
            dismissCompletion()
        }
    }
}

для замены rootViewController просто используйте:

let newRootViewController = self.storyboard!.instantiateViewControllerWithIdentifier("BlackViewController")
UIApplication.sharedApplication().keyWindow!.replaceRootViewControllerWith(newRootViewController, animated: true, completion: nil)

надеюсь, это поможет :) протестировано на iOS 8.4; также протестировано для поддержки навигационных контроллеров (должны поддерживать также контроллеры вкладок и т. д., но я не проверял это)

объяснение

Если есть модальный контроллер представления, представленный над старым rootViewController, rootViewController заменяется, но старое представление все еще остается висящим ниже представления нового rootViewController (и может быть видно, например, во время анимации горизонтального или перекрестного растворения перехода), а старая иерархия контроллера представления остается выделенной (что может вызвать серьезные проблемы с памятью, если замена происходит несколько раз времена.)

вы можете изменить rootViewController окна в течение всего жизненного цикла приложения.

UIViewController *viewController = [UIViewController alloc] init];
[self.window setRootViewController:viewController];

при изменении rootViewController, вы все еще можете добавить UIImageView в качестве подвида в окне, чтобы действовать в качестве заставки изображения. Я надеюсь, что это имеет смысл, что-то вроде этого:

- (void) addSplash {
    CGRect rect = [UIScreen mainScreen].bounds;
    UIImageView *splashImage = [[UIImageView alloc] initWithFrame:rect];
    splashImage.image = [UIImage imageNamed:@"splash.png"];
    [self.window addSubview:splashImage];
}

- (void) removeSplash {
    for (UIView *view in self.window.subviews) {
      if ([view isKindOfClass:[UIImageView class]]) {
        [view removeFromSuperview];
      }
    }
}

для iOS8, мы также должны установить ниже двух параметров да.

providesPresentationContextTransitionStyle
definesPresentationContext

вот мой код для представления прозрачная модель-представление-контроллер в контроллер навигации для iOS 6 и выше.

ViewController *vcObj = [[ViewController alloc] initWithNibName:NSStringFromClass([ViewController class]) bundle:nil];
UINavigationController *navCon = [[UINavigationController alloc] initWithRootViewController:vcObj];

if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {

    navCon.providesPresentationContextTransitionStyle = YES;
    navCon.definesPresentationContext = YES;
    navCon.modalPresentationStyle = UIModalPresentationOverCurrentContext;

    [self presentViewController:navCon animated:NO completion:nil];
}
else {

    AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
    [self presentViewController:navCon animated:NO completion:^{
        [navCon dismissViewControllerAnimated:NO completion:^{
            appDelegate.window.rootViewController.modalPresentationStyle = UIModalPresentationCurrentContext;
            [self presentViewController:navCon animated:NO completion:nil];
            appDelegate.window.rootViewController.modalPresentationStyle = UIModalPresentationFullScreen;

        }];
    }];
}