【问题标题】:Present view controller modally at app launch在应用启动时模态显示视图控制器
【发布时间】:2015-03-30 15:02:04
【问题描述】:

我的应用程序有一个设置屏幕,如果满足某些条件,它应该以模态方式显示在根视图控制器上。

我在 SO 和互联网上环顾四周,到目前为止,关于如何执行此操作的最接近的答案在这里:

AppDelegate, rootViewController and presentViewController

这种方法有两个问题:

  1. 在 iOS 8 中,这样做会使控制台中出现日志,这似乎不是错误,但可能还是不好:

Unbalanced calls to begin/end appearance transitions for UITabBarController: 0x7fe20058d570.

  1. 根视图控制器实际上在应用启动时非常短暂地显示,然后淡入呈现的视图控制器(尽管我在 presentViewController 方法上明确调用了 animated:NO)。

我知道我可以在applicationDidFinishLaunchingWithOptions: 中动态设置我的根控制器,但我特别想以模态方式显示设置屏幕,这样当用户完成它时,它就会关闭并显示应用程序的真实第一个视图.也就是说,我不想将我的根视图控制器动态更改为我的设置屏幕,并在用户完成设置后以模态方式呈现我的应用体验。

在我的根视图控制器 viewDidLoad 方法上显示视图控制器也会导致应用程序首次启动时 UI 明显闪烁。

是否可以在应用程序呈现任何内容之前以编程方式以模态方式呈现视图控制器,以便第一个视图是模态视图控制器?

更新:感谢 cmets,按照建议添加我当前的代码:

在我的AppDelegate.m

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];

    [self.window makeKeyAndVisible];
    [self.window.rootViewController presentViewController:[storyboard instantiateViewControllerWithIdentifier:@"setupViewController"] animated:NO completion:NULL];

    return YES;
}

这可以满足我的需要,除了它会在应用程序启动时短暂显示窗口的根视图控制器一秒钟,然后淡化 setupViewController,我觉得这很奇怪,因为我在没有动画的情况下呈现它并且淡化不是无论如何呈现模态视图控制器。

唯一让我接近的事情是在根视图控制器的视图中手动添加视图确实加载方法如下:

- (void)viewDidLoad
{
    [self.view addSubview:setupViewController.view];
    [self addChildViewController:setupViewController];
}

这种方法的问题是我不能再“本机”关闭 setupViewController,现在需要自己处理视图层次结构并为其设置动画,如果这是唯一的解决方案,那很好,但我希望在根视图控制器显示之前,有一种认可的方式可以在没有动画的情况下以模态方式添加视图控制器。

更新 2:在尝试了很多事情并等待了 2 个月的答案之后,这个问题提出了最有创意的解决方案:

iOS Present modal view controller on startup without flash

我想是时候接受不可能在根视图控制器出现之前以模态方式呈现没有动画的视图了。然而,该线程中的建议是创建一个启动屏幕的实例,并将其保持比默认设置更长的时间,直到模态视图控制器有机会呈现自己。

【问题讨论】:

  • 用相关代码更新您的问题,显示您目前正在做什么。
  • 我认为子视图控制器方法实际上是最好的,但我给了你两个选择 - 看看你的想法。
  • 我已经在另一个问题中提供了这个问题的答案:stackoverflow.com/q/26355847/1652710 希望这就是你要找的。​​span>
  • @Stakenborg 谢谢!这一直处于不确定状态,但启动屏幕覆盖是我见过的最具创意的解决方案。我将更新我的问题以指向该线程。

标签: ios objective-c


【解决方案1】:

我想是时候接受不可能在根视图控制器出现之前以模态方式呈现没有动画的视图了。

它出现之前,不,你不能呈现。但是有多种有效的方法可以直观地解决这个问题。为了简单起见,我推荐下面的解决方案 A。

A.将launchScreen添加为子视图,然后呈现,然后删除launchscreen

Solution is presented here by ullstrm 并且不会受到对开始/结束外观转换的不平衡调用

let launchScreenView = UIStoryboard(name: "LaunchScreen", bundle: nil).instantiateInitialViewController()!.view!
launchScreenView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
launchScreenView.frame = window!.rootViewController!.view.bounds
window?.rootViewController?.view.addSubview(launchScreenView)
window?.makeKeyAndVisible()
// avoiding: Unbalanced calls to begin/end appearance transitions.
DispatchQueue.global().async {
    DispatchQueue.main.async {
        self.window?.rootViewController?.present(myViewControllerToPresent, animated: false, completion: {
            launchScreenView.removeFromSuperview()
        })
    }
}

B.先addChildViewController,再remove,再present

Solution is presented here by Benedict Cohen.

【讨论】:

    【解决方案2】:

    我和你在同一条船上,找到了相同的答案。我了解到,您可以通过将 setupViewController 的 modalPresentationStyle 设置为 .OverCurrentContext.OverFullScreen 来解决第一个问题(不平衡呼叫警告)。问题解决了——我想。

    直到后来我才注意到第二个问题,那是我无法忍受的……回到第一个问题。

    和你一样,我想要一个具有正常视图层次结构的解决方案,我不想“伪造”某些东西。我认为最优雅的解决方案是在第一次关闭 setupViewController 时切换 Windows rootViewController。

    因此,在启动时,您将 setupViewController 设置为 rootViewController(如果需要):

    var window: UIWindow?
    var tabBarController: UITabBarController!
    
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        tabBarController = window!.rootViewController as! UITabBarController
        if needsToShowSetup() {
            let setupViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("SetupViewController") as! SetupViewController
            window?.rootViewController = setupViewController
        }
        return true
    }
    

    设置完成后,您在 appDelegate 中调用一个方法来切换到“真正的”rootViewController:

    func switchToTabBarController() {
        let setupUpViewController = window!.rootViewController!
    
        tabBarController.view.frame = window!.bounds
        window!.insertSubview(tabBarController.view, atIndex: 0)
    
        let height = setupUpViewController.view.bounds.size.height
        UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1, options: .allZeros, animations: { () -> Void in
            setupUpViewController.view.transform = CGAffineTransformMakeTranslation(0, height)
            }) { (completed) -> Void in
                self.window!.rootViewController = self.tabBarController
        }
    }
    

    我在“垂直封面”关闭动画之后。对于淡入淡出和其他,您可以使用UIView.transitionFromView(fromView: UIView, toView: UIView...)。此后,您可以正常方式展示/关闭您的 setupController,因此您的 doneButton 操作可能是这样的:

    @IBAction func doneButtonSelected(sender: UIButton) {
        if presentingViewController != nil {
            presentingViewController!.dismissViewControllerAnimated(true, completion: nil)
        } else {
            let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
            appDelegate.switchToTabBarController()
        }
    }
    

    实际上,我第一次通过委托来实现这一点,其中 appDelegate 是委托。

    【讨论】:

    • 在为此苦苦挣扎了几个星期后,我遇到了这个答案,它对我有用。它完美地伪造了标准的模态解雇动画。令人惊讶的是,这很难实现。
    【解决方案3】:

    我知道我可以在 applicationDidFinishLaunchingWithOptions: 中动态设置我的根控制器,但我特别想以模态方式呈现设置屏幕,这样当用户完成它时,它就会关闭并显示应用程序的真实第一个视图。

    我有两个建议。一种是尝试在viewDidAppear: 中执行此操作。我试过了,虽然如果你仔细看你确实看到了根视图控制器的视图,但你几乎看不到它,有时如果你眨眼你根本看不到它:

    -(void)viewDidAppear:(BOOL)animated {
        [super viewDidAppear:animated];
        [self presentViewController:[self.storyboard instantiateViewControllerWithIdentifier:@"setupViewController"] animated:NO completion:NULL];
    }
    

    当然,您需要添加一个标志,这样您就不会在每次调用 viewDidAppear: 时都这样做 - 否则您将永远无法回到这个视图控制器!但这是微不足道的,我把它留给读者作为练习。

    我的另一个建议——你已经清楚地考虑过这样做——是改用自定义的嵌入式(子)视图控制器。这可以解决整个“演示”事物的局限性。

    正如您所说,如果需要,我会动态启动和设置事物,并在启动过程中配置所有子视图控制器。子视图控制器的视图只会覆盖根视图控制器的视图。这就是用户在应用启动时看到的内容。

    然后当用户的设置过程结束并且用户“关闭”此视图时,您会使用动画拆除该视图,并移除子视图控制器 - 显示下面的根视图控制器的视图。动画将使这一切与解除呈现的视图无法区分,即使它不是真正的视图。

    【讨论】:

      猜你喜欢
      • 2015-03-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-20
      • 1970-01-01
      相关资源
      最近更新 更多