iOS在没有闪存的情况下启动模态视图控制器

时间:2014-10-14 08:16:16

标签: ios objective-c startup modalviewcontroller

我想在初次启动时向用户提供一个教程向导。

有没有办法在应用程序启动时显示模态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 ...]语句之前,但之后模态甚至都没有出现。

9 个答案:

答案 0 :(得分:31)

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

答案 1 :(得分:10)

布鲁斯在斯威夫特3中提出的回答:

createRandomPerson()

答案 2 :(得分:8)

可能是您可以使用“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];

当您需要解雇您的导师时,您可以从超级视图中删除其视图。您还可以通过设置alpha属性在视图上添加一些动画。希望有用:)

答案 3 :(得分:6)

这个问题在iOS 10中仍然存在。我的修复是:

  1. viewWillAppear中将modal VC作为childVC添加到rootVC
  2. viewDidAppear中的
    1. 删除modalVC作为rootVC的子级
    2. 模态显示没有动画的childVC
  3. 代码:

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

答案 4 :(得分:0)

可能是一个糟糕的解决方案,但你可以制作一个包含2个容器的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;
}

答案 5 :(得分:0)

Bruce的回答指出了我正确的方向,但是因为我的模态可以比启动时更频繁出现(它是登录屏幕,所以如果他们退出就需要出现),我不想绑定我的叠加直接显示视图控制器。

以下是我提出的逻辑:

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

在另一部分中,我执行以典型的self.window.rootViewController presentViewController:...格式呈现我的登录VC的逻辑,无论是应用程序启动还是其他,我都可以使用。

如果有人关心,这就是我创建叠加视图的方式:

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

答案 6 :(得分:0)

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

答案 7 :(得分:0)

可能要晚了,但是在您的AppDelegate中,您可以这样做:

browser.get("https://google.com/")

def beta_check_href(self):

    elem = self.find_elements_by_xpath("//a[@href]")
    for el in elem:
        print elem.get_attribute("href")

答案 8 :(得分:-1)

这是我使用故事板的方式,它适用于多种模态。 这个例子有3.底部,中部和顶部。

确保在界面构建器中正确设置每个viewController的storyboardID。

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