IOS:程序化 rootViewController 以不同的方式呈现视图到 Storyboard 'isInitialView'

Posted

技术标签:

【中文标题】IOS:程序化 rootViewController 以不同的方式呈现视图到 Storyboard \'isInitialView\'【英文标题】:IOS: Programmatic rootViewController renders the view differently to Storyboard 'isInitialView'IOS:程序化 rootViewController 以不同的方式呈现视图到 Storyboard 'isInitialView' 【发布时间】:2014-08-13 10:32:23 【问题描述】:

旨在实现一个仅在我修改didFinishLaunchingWithOptions 后才显示的初始屏幕,以便动态选择适当的视图控制器。逻辑似乎运行良好,我打算加载的视图是已启动的视图

但是,如果我没有更改 didFinishLaunchingWithOptions 函数,UI 似乎缺少原本会显示的元素。

func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool

    window = UIWindow(frame: UIScreen.mainScreen().bounds)
    let storyBoard = UIStoryboard(name: "Main", bundle: nil)
    var entryViewController: UIViewController?

    if NSUserDefaults.standardUserDefaults().boolForKey("hasSeenWelcomeScreen") == true
    
        entryViewController = storyBoard.instantiateViewControllerWithIdentifier("NavigationController") as? UIViewController
    
    else
    
        entryViewController = storyBoard.instantiateViewControllerWithIdentifier("WelcomeViewController") as? UIViewController
        NSUserDefaults.standardUserDefaults().setValue(true, forKey: "hasSeenWelcomeScreen")
        NSUserDefaults.standardUserDefaults().synchronize()
    

    self.window?.rootViewController = entryViewController
    self.window?.makeKeyAndVisible()

    return true

我的WelcomeViewController 是一个简单的视图,其中1 label1 buttona movie 在后台播放(类似于Spotify/Vine 的欢迎屏幕)。调试代码我可以看到初始化方法确实被执行了,但只是当我动态覆盖初始视图

时似乎没有显示的框架
import UIKit
import MediaPlayer
import QuartzCore

class WelcomeViewController: UIViewController 

    var moviePlayerController: MPMoviePlayerController = MPMoviePlayerController()

    @IBOutlet weak var loginButton: UIButton!
    @IBOutlet weak var appNameLabel: UILabel!

    override func viewDidLoad()
    
        super.viewDidLoad()
        buildMoviePreview()
        buildButtonDesign()
    

    override func viewWillAppear(animated: Bool)
    
        self.view.addSubview(self.moviePlayerController.view)
        self.view.addSubview(self.loginButton)
        self.view.addSubview(self.appNameLabel)
    

    override func didReceiveMemoryWarning() 
        super.didReceiveMemoryWarning()
    

    override func prefersStatusBarHidden() -> Bool 
        return true
    

    private func buildButtonDesign()
    
        loginButton.layer.borderColor = UIColor.whiteColor().CGColor
        loginButton.layer.borderWidth = 2.0
        loginButton.layer.cornerRadius = 7.0
    

    private func buildMoviePreview()
    
        let filePath = NSBundle.mainBundle().pathForResource("intro", ofType: "mov")
        self.moviePlayerController.contentURL = NSURL.fileURLWithPath(filePath)
        self.moviePlayerController.movieSourceType = .File
        self.moviePlayerController.repeatMode = .One
        self.moviePlayerController.view.frame = self.view.bounds
        self.moviePlayerController.scalingMode = .AspectFill
        self.moviePlayerController.controlStyle = .None
        self.moviePlayerController.allowsAirPlay = false
        self.moviePlayerController.shouldAutoplay = true
        self.moviePlayerController.play()
    


为了完整起见,这些是使用 XCode UI 调试器时布局中的差异。请注意,即使它们实现相同的 viewController,它们也会有所不同。唯一的区别是一个被编程设置为initial view,而另一个被设置为initial view通过storyboard。


并排呈现问题的屏幕截图

【问题讨论】:

请添加问题截图。 @LeoNatan,完成。干杯。 【参考方案1】:

你的方法……不寻常。 Storyboard 有一个根视图控制器是有原因的,通常在启动时,您只需让应用程序处理加载故事板并将该根视图控制器安装为窗口的主视图控制器。 (加载的故事板在应用程序目标的常规设置中指定为“主界面”)

在这种情况下,我建议您将根视图控制器设置为应用程序的“普通”视图...您希望用户在日常启动应用程序时看到的视图。

将您的“首次启动”视图控制器定义为情节提要中的单独视图控制器,并将根视图控制器的模态转场添加到首次启动视图控制器。

然后在您的 applicationDidFinishLaunching 中,如果用户从未见过第一个启动控制器...只需要求 Storyboard 进行该转场。如果用户已经看过第一个启动演示,则将跳过 segue。

我在您的代码中看到的另一个问题是您的 viewWillAppear 方法。您不必将视图添加为 viewWillAppear 中的子视图...在从 nib 文件加载视图时应该已经设置了这些子视图。

一个例外是您的电影播放器​​的视图,但您的电影播放器​​属于一个单独的视图控制器。该单独的视图控制器与视图控制器层次结构分离,并且没有在正确的时间调用它自己的视图控制器方法。 (所以它永远不会收到像“viewWillAppear”这样的调用,可能会告诉它准备好播放它的电影)。

您可能想要做的是实现“awakeFromNib”并确保电影播放器​​的视图控制器是该视图控制器的子控制器。 (所以在 WelcomeViewController 的 awakeFromNib 中使用 addChildViewController 来确保电影控制器在层次结构中)。

【讨论】:

【参考方案2】:

用户 2 故事板会更好:

与您的欢迎屏幕合二为一 另一个与您应用的其余部分一起使用

应用程序启动将如下所示:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool 

    var storyBoard : UIStoryboard!
    if NSUserDefaults.standardUserDefaults().boolForKey("hasSeenWelcomeScreen") == true 
        changeStoryBoard("Main")
    
    else 
        changeStoryBoard("Welcome")
    

    return true



func changeStoryBoard(name :String) 
    var storyBoard = UIStoryboard(name:name, bundle: nil)
    var viewController: AnyObject! = storyBoard.instantiateInitialViewController() ;
    self.window!.rootViewController = viewController as UIViewController

【讨论】:

以上是关于IOS:程序化 rootViewController 以不同的方式呈现视图到 Storyboard 'isInitialView'的主要内容,如果未能解决你的问题,请参考以下文章

Swift编码总结8

从推送的 viewController 到 rootViewController 的信息

当使用 view.window?.rootViewController 根据 if 语句更改视图时,嵌入式控制器不会被激活

iOS:是不是可以通过编程方式从 iOS 应用程序获取 iTunes 应用程序链接?

IOS 6 在 ios 7 中开发的应用程序支持?

iOS核心笔记——iOS应用程序启动原理