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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

enter image description here

Ответ 2

iOS 8.0, Xcode 6.0.1, ARC включен

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

Можно ли изменить контроллер корневого представления несколько раз в приложении?

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

Чтобы сделать это...

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

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

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

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

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

  1. 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;

    }];

Надеюсь, это поможет кому-то! Приветствия.

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

Контроллер корневого представления предоставляет представление содержимого окна. Назначение контроллера вида для этого свойства (либо программно или с помощью Interface Builder) устанавливает представление контроллеров представления как просмотр содержимого окна. Если окно имеет существующее представление иерархии, старые представления удаляются до того, как новые установлен. Значение по умолчанию этого свойства равно 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;

                }];

Ответ 3

Из комментариев по запросу 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 (и его можно увидеть, например, во время анимации перехода Flip Horizontal или Cross Dissolve) и старая иерархия контроллера просмотра остается выделенной (что может вызвать серьезные проблемы с памятью, если замена происходит несколько раз).

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

Ответ 4

Вы можете изменить окно 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];
      }
    }
}

Ответ 5

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

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;

        }];
    }];
}