it-swarm.com.de

iOS Present Modal View Controller beim Start ohne Flash

Ich möchte dem Benutzer zunächst beim ersten Start einen Tutorial-Assistenten vorstellen.

Gibt es eine Möglichkeit, eine modale UIViewController beim Start der Anwendung darzustellen, ohne zumindest für eine Millisekunde die rootViewController dahinter zu sehen?

Jetzt mache ich so etwas (die Überprüfungen beim ersten Start werden zur Klarheit weggelassen):

- (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];
}

ohne Glück. Ich habe versucht, [self.window makeKeyAndVisible]; vor die [... presentViewController:tutorialViewController ...]-Anweisung zu verschieben, aber dann erscheint der Modal nicht einmal.

32
sonxurxo

Alle presentViewController-Methoden setzen voraus, dass der Präsentationsansicht-Controller zuerst angezeigt wurde. Um die Wurzel VC auszublenden, muss eine Überlagerung angezeigt werden. Der Startbildschirm kann weiterhin im Fenster angezeigt werden, bis die Präsentation abgeschlossen ist und die Überlagerung ausgeblendet wird.

    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];
    }];
}];
30
Bruce Burnham

kann sein, dass Sie den "childViewController" verwenden können

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

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

[self.window makeKeyAndVisible];

Wenn Sie Ihren Tutor entlassen müssen, können Sie die Ansicht aus dem Überblick entfernen. Sie können auch eine Animation in die Ansicht einfügen, indem Sie die Eigenschaft alpha festlegen. Hoffnung hilfreich :)

8
Pandara

Bruce's Antwort in 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()
                    })
                })
            }
        }
    }
7
ullstrm

Dieses Problem besteht immer noch in iOS 10. Mein Fix war:

  1. fügen Sie in viewWillAppear die modale VC als childVC zur rootVC hinzu
  2. in viewDidAppear:
    1. Entfernen Sie die modalVC als untergeordnetes Element der rootVC
    2. Die childVC modal ohne Animation präsentieren

Code:

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)
        }

        //....
    }
}
6
Benedict Cohen

Die Antwort von Bruce wies mich in die richtige Richtung, aber da mein Modal öfter als nur beim Start angezeigt werden kann (es handelt sich um einen Anmeldebildschirm, der angezeigt werden muss, wenn er sich abmeldet), wollte ich mein Overlay nicht direkt an den Präsentation des View Controllers.

Hier ist die Logik, die ich mir ausgedacht habe:

    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];
                     }];

In einem anderen Abschnitt führe ich die Logik aus, mein Login VC im typischen self.window.rootViewController presentViewController:...-Format darzustellen, das ich verwenden kann, unabhängig davon, ob es sich um einen App-Start handelt oder nicht.

Wenn es jemanden interessiert, so habe ich meine Overlay-Ansicht erstellt:

@implementation WSILaunchImageView

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

Und hier ist die Logik für das Startbild selbst:

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;
}

Aaaaund nur der Vollständigkeit halber sehen diese Methoden von EnvironmentDevice so aus:

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;
}
0
Stakenborg

Es kann zu spät sein, aber in Ihrer AppDelegate können Sie Folgendes tun:

//Set your rootViewController
self.window.rootViewController=myRootViewController;
//Hide the rootViewController to avoid the flash
self.window.rootViewController.view.hidden=YES;
//Display the window
[self.window makeKeyAndVisible];

if(shouldPresentModal){

    //Present your modal controller
    UIViewController *lc_viewController = [UIViewController new];
    UINavigationController *lc_navigationController = [[UINavigationController alloc] initWithRootViewController:lc_viewController];
    [self.window.rootViewController presentViewController:lc_navigationController animated:NO completion:^{

        //Display the rootViewController to show your modal
        self.window.rootViewController.view.hidden=NO;
    }];
}
else{

    //Otherwise display the rootViewController
    self.window.rootViewController.view.hidden=NO;
}
0
Bejil
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
    }
}
0
Azon

Ist möglicherweise eine schlechte Lösung, aber Sie könnten einen ViewController mit zwei Containern darin erstellen, wobei beide Container jeweils mit einem VC verknüpft sind. Dann können Sie steuern, welcher Container im Code sichtbar sein soll, das ist eine Idee

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;
}
0
Erik