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

IOS Представлен контроллер модального просмотра при запуске без вспышки

Я хотел бы представить модально, при первом запуске, мастер-мастер для пользователя.

Есть ли способ представить модальный UIViewController при запуске приложения, не видя, по крайней мере, за миллисекунду, rootViewController за ним?

Теперь я делаю что-то вроде этого (исключая проверки первого запуска для ясности):

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    // ...

    UIStoryboard *storyboard = self.window.rootViewController.storyboard;
    TutorialViewController* tutorialViewController = [storyboard instantiateViewControllerWithIdentifier:@"tutorial"];
    tutorialViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
    [self.window makeKeyAndVisible];
    [self.window.rootViewController presentViewController:tutorialViewController animated:NO completion:NULL];
}

не повезло. Я попытался переместить [self.window makeKeyAndVisible]; до инструкции [... presentViewController:tutorialViewController ...], но модальность даже не появляется.

4b9b3361

Ответ 1

Все методы presentViewController требуют, чтобы предстающий контроллер представления появился первым. Чтобы скрыть корень VC, должен быть представлен оверлей. Экран запуска можно продолжать отображать в окне до тех пор, пока презентация не завершится, а затем исчезнет наложение.

    UIView* overlayView = [[[UINib nibWithNibName:@"LaunchScreen" bundle:nil] instantiateWithOwner:nil options:nil] firstObject];
overlayView.frame = self.window.rootViewController.view.bounds;
overlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

UIStoryboard *storyboard = self.window.rootViewController.storyboard;
TutorialViewController* tutorialViewController = [storyboard instantiateViewControllerWithIdentifier:@"tutorial"];
tutorialViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
[self.window makeKeyAndVisible];
[self.window addSubview:overlayView];
[self.window.rootViewController presentViewController:tutorialViewController animated:NO completion:^{
    NSLog(@"displaying");
    [UIView animateWithDuration:0.5 animations:^{
        overlayView.alpha = 0;
    } completion:^(BOOL finished) {
        [overlayView removeFromSuperview];
    }];
}];

Ответ 2

может быть, вы можете использовать "childViewController"

UIStoryboard *storyboard = self.window.rootViewController.storyboard;
TutorialViewController* tutorialViewController = [storyboard instantiateViewControllerWithIdentifier:@"tutorial"];

[self.window addSubview: tutorialViewController.view];
[self.window.rootViewController addChildViewController: tutorialViewController];

[self.window makeKeyAndVisible];

Когда вам нужно уволить своего наставника, вы можете удалить его представление из супервизора. Также вы можете добавить анимацию в представление, установив альфа-свойство. Полезно:)

Ответ 3

Брюс поддержал ответ в Swift 3:

if let vc = window?.rootViewController?.storyboard?.instantiateViewController(withIdentifier: "LOGIN")
    {
        let launch = UIStoryboard(name: "LaunchScreen", bundle: nil).instantiateInitialViewController()!
        launch.view.frame = vc.view.bounds
        launch.view.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]
        window?.makeKeyAndVisible()
        window?.addSubview(launch.view)

        //Using DispatchQueue to prevent "Unbalanced calls to begin/end appearance transitions"
        DispatchQueue.global().async {
            // Bounce back to the main thread to update the UI
            DispatchQueue.main.async {
                self.window?.rootViewController?.present(vc, animated: false, completion: {

                    UIView.animate(withDuration: 0.5, animations: {
                        launch.view.alpha = 0
                    }, completion: { (_) in
                        launch.view.removeFromSuperview()
                    })
                })
            }
        }
    }

Ответ 4

Эта проблема все еще существует в iOS 10. Мое исправление:

  • в viewWillAppear добавить модальный VC в качестве childVC в rootVC
  • в viewDidAppear:
    • Удалить modalVC как дочерний элемент rootVC
    • Модно представить childVC без анимации

код:

extension UIViewController {

    func embed(childViewController: UIViewController) {
        childViewController.willMove(toParentViewController: self)

        view.addSubview(childViewController.view)
        childViewController.view.frame = view.bounds
        childViewController.view.autoresizingMask = [.flexibleHeight, .flexibleWidth]

        addChildViewController(childViewController)
    }


    func unembed(childViewController: UIViewController) {
        assert(childViewController.parent == self)

        childViewController.willMove(toParentViewController: nil)
        childViewController.view.removeFromSuperview()
        childViewController.removeFromParentViewController()
    }
}


class ViewController: UIViewController {

    let modalViewController = UIViewController()

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        //BUG FIX: We have to embed the VC rather than modally presenting it because:
        // - Modal presentation within viewWillAppear(animated: false) is not allowed
        // - Modal presentation within viewDidAppear(animated: false) is not visually glitchy
        //The VC is presented modally in viewDidAppear:
        if self.shouldPresentModalVC {
            embed(childViewController: modalViewController)
        }
        //...
    }


    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        //BUG FIX: Move the embedded VC to be a modal VC as is expected. See viewWillAppear
        if modalViewController.parent == self {
            unembed(childViewController: modalViewController)
            present(modalViewController, animated: false, completion: nil)
        }

        //....
    }
}

Ответ 5

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

if (!firstRun) {
    // Show normal page
    normalContainer.hidden = NO;
    firstRunContainer.hidden = YES;
} else if (firstRun) {
    // Show first run page or something similar
    normalContainer.hidden = YES;
    firstRunContainer.hidden = NO;
}

Ответ 6

Ответ Брюса указал мне в правильном направлении, но потому, что мой модальный может появляться чаще, чем просто при запуске (это экран входа в систему, поэтому он должен появиться, если они выходят из системы), я не хотел привязывать мой накладной непосредственно к представлению контроллера вида.

Вот логика, с которой я столкнулся:

    self.window.rootViewController = _tabBarController;
    [self.window makeKeyAndVisible];

    WSILaunchImageView *launchImage = [WSILaunchImageView new];
    [self.window addSubview:launchImage];

    [UIView animateWithDuration:0.1f
                          delay:0.5f
                        options:0
                     animations:^{
                         launchImage.alpha = 0.0f;
                     } completion:^(BOOL finished) {
                         [launchImage removeFromSuperview];
                     }];

В другом разделе я выполняю логику представления моего входа в систему VC в типичном формате self.window.rootViewController presentViewController:..., который я могу использовать независимо от того, запускается он или нет.

Если кто-то заботится, вот как я создал свой оверлейный вид:

@implementation WSILaunchImageView

- (instancetype)init
{
    self = [super initWithFrame:[UIScreen mainScreen].bounds];
    if (self) {
        self.image = WSILaunchImage();
    }
    return self;
}

И вот логика самого запуска:

UIImage * WSILaunchImage()
{
    static UIImage *launchImage = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (WSIEnvironmentDeviceHas480hScreen()) launchImage = [UIImage imageNamed:@"LaunchImage-700"];
        else if (WSIEnvironmentDeviceHas568hScreen()) launchImage = [UIImage imageNamed:@"LaunchImage-700-568h"];
        else if (WSIEnvironmentDeviceHas667hScreen()) launchImage = [UIImage imageNamed:@"LaunchImage-800-667h"];
        else if (WSIEnvironmentDeviceHas736hScreen()) launchImage = [UIImage imageNamed:@"LaunchImage-800-Portrait-736h"];
    });
    return launchImage;
}

Aaaaand только ради завершения, вот как выглядят эти методы EnvironmentDevice:

static CGSize const kIPhone4Size = (CGSize){.width = 320.0f, .height = 480.0f};

BOOL WSIEnvironmentDeviceHas480hScreen(void)
{
    static BOOL result = NO;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        result = CGSizeEqualToSize([UIScreen mainScreen].bounds.size, kIPhone4Size);
    });
    return result;
}

Ответ 7

let vc = UIViewController()
vc.modalPresentationStyle = .custom
vc.transitioningDelegate = noFlashTransitionDelegate
present(vc, animated: false, completion: nil)

class NoFlashTransitionDelegate: NSObject, UIViewControllerTransitioningDelegate {

    public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
        if source.view.window == nil,
            let overlayViewController = UIStoryboard(name: "LaunchScreen", bundle: nil).instantiateInitialViewController(),
            let overlay = overlayViewController.view {
                source.view.addSubview(overlay)
                UIView.animate(withDuration: 0, animations: {}) { (finished) in
                    overlay.removeFromSuperview()
            }
        }
        return nil
    }
}

Ответ 8

Вот как я делаю это с раскадными досками, и он работает с несколькими модалами. Этот пример имеет 3. Нижний, средний и верхний.

Просто убедитесь, что сценарий storyboardID каждого контроллера viewController правильно установлен в конструкторе интерфейса.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    UIStoryboard * storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
    BottomViewController *bottomViewController = [storyboard instantiateViewControllerWithIdentifier:@"BottomViewController"];
    UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    [window setRootViewController:bottomViewController];
    [window makeKeyAndVisible];

    if (!_loggedIn) {
        MiddleViewController *middleViewController = [storyboard instantiateViewControllerWithIdentifier:@"middleViewController"];
        TopViewController *topViewController = [storyboard instantiateViewControllerWithIdentifier:@"topViewController"];

        [bottomViewController presentViewController:middleViewController animated:NO completion:nil];
        [middleViewController presentViewController:topViewController animated:NO completion:nil];

    }
    else {
        // setup as you normally would.
    }

    self.window = window;

    return YES;
}