Matt Ray
Matt Ray

Reputation: 1304

iOS App Goes to Black Screen

I am using a Launch Screen storyboard as well as a Main storyboard in my Swift app. Sometimes the app will show the launch storyboard and then go to a black screen. Other times it correctly goes to the Main storyboard entry point. It almost seems like it is build-dependent in that if I clean and rebuild a few times, a build pop out that behaves properly. Any ideas what may be causing this?

To add some clarification:

This might help: I put in the following code in the offending view:

override func viewDidAppear(animated: Bool) {
    print(self.view.layer.bounds.width);
    print(self.view.layer.bounds.height);

}

When the black screen issue occurs, I get 260 and 17. When the view presents correctly, I get the 5s (simulator) dimensions of 320 and 568. Does that help anyone help me?

Upvotes: 12

Views: 27139

Answers (7)

Max Miranda
Max Miranda

Reputation: 311

For me, my problem was that I accidentally deleted "Application Scene Manifest" in the Info.plist, make sure that's in there!

Upvotes: 1

Abhishek Maurya
Abhishek Maurya

Reputation: 765

I have encountered the same issue today and after debugging for 2hr I got the fix

There are a few things to keep in mind.

  1. Storyboard name should be the same in the following areas:
    • Targets > 'Main target' > General > Deployment info > Main Interface
    • Targets > 'Main target' > Build Settings > UIKit Main Storyboard File Base Name
    • Info.plist > Application Scene Manifest > Scene Configuration > Application Session Role > Item 0 > Storyboard Name
  2. Make sure just below your Storyboard identifier, "Is Initial View Controller" is checked on your main/default ViewController in the storyboard you have set above.
  3. If you have deleted main.storyboard and using another storyboard as your Main Interface or the view hierarchy debugger isn't showing anything it means you have to manually set it as root in SceneDelegate.

To do that refer following code of SceneDelegate.swift

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
    guard let windowScene = (scene as? UIWindowScene) else { return }
    window = UIWindow(windowScene: windowScene)
    let sb = UIStoryboard(name: "HomeLanding", bundle: nil)
    let vc = sb.instantiateViewController(withIdentifier: "HomeLandingVC")
    window?.rootViewController = vc
    window?.makeKeyAndVisible()
}

Note: Setting window = UIWindow(frame: UIScreen.main.bounds) will break UISceneSession system, that's why we are setting it from current scene

  1. Now you have to set sceneConfig delegate class in your AppDelegate

To do that refer to the following code of AppDelegate.swift

func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
    let config = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role)
    config.delegateClass = SceneDelegate.self
    return config
}

Upvotes: -1

Dhruv Saraswat
Dhruv Saraswat

Reputation: 1132

I fixed this issue by adding this property-value pair to the Info.plist file in the array of values inside Application Scene ManifestScene ConfigurationApplication Session RoleItem 0 (Default Configuration).

<key>UISceneStoryboardFile</key>
<string>Main</string>

That above value is actually present in Info.plist by default, I had accidentally deleted it some time back.

Upvotes: 0

amenofis99
amenofis99

Reputation: 31

Make sure your first View Controller (in my case Navigation Controller) is Initial Controller. Find more details here.

Also, make sure when connecting Initial and Second Controller with Segue you press "Root View Controller" Relationship as you can see on picture here.

I hope it helps!

Upvotes: 3

Menelaos Kotsollaris
Menelaos Kotsollaris

Reputation: 5506

I was faced with the same issue in my device. I managed to fix it by setting the application's main interface via xcode: MyApplicationMain InterfaceLaunchScreen.storyboard (or Main.storyboard).

enter image description here

Cheers! :)

Upvotes: 10

Matt Ray
Matt Ray

Reputation: 1304

I finally solved this. After weeks of battling it and trying everything from ripping ALL of my code out, it turns out that the view outlet for my view in the storyboard had become double-linked to both "View" and one of my text fields in that view. Like this:

enter image description here

I have since deleted the linkage to the text field, but show the correct linkage here:

enter image description here

Interestingly, I am unable to reproduce this accidental connection in the UI, so I am not sure how that even happened. Anyway, I hope this helps others that might come across this.

Upvotes: 23

matt
matt

Reputation: 535119

I'd say the problem is this line:

    dispatch_async(dispatch_get_main_queue(), {
        self.performSegueWithIdentifier("toWelcomeView", sender: self);
    });

You're trying to do a segue to another view while this view has not yet appeared (view will appear). That's very iffy, and that's why sometimes it is in fact failing. I'm a bit surprised that you don't report seeing an error message in the Console warning that you're trying to segue from a view controller whose view is not yet in the interface.

As an interim measure, you might move your call to self.RetrieveRegistration() into viewDidAppear: and see if that helps.

But in the long term, you're just going about this the wrong way. Your goal seems to be to launch into one view controller under some circumstances and into another under other circumstances. The place to put all that logic is up front in applicationDidFinishLaunching, so that your rootViewController itself is different depending on the situation.

For example, imagine the following very simple situation: our storyboard contains just two view controller, RegistrationViewController and RootViewController (with a storyboard identifier "root"), with a presentation segue from the first to the second. In RegistrationViewController we ask for registration info, such as a "username", which we place in the NSUserDefaults. Then we segue to the RootViewController. All well and good, but our goal hereafter is that the user should never see RegistrationViewController again. This is easily taken care of in the app delegate:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    if let rvc = self.window?.rootViewController {
        if NSUserDefaults.standardUserDefaults().objectForKey("username") as? String != nil {
            self.window!.rootViewController = rvc.storyboard!.instantiateViewControllerWithIdentifier("root")
        }
    }
    return true
}

Upvotes: 3

Related Questions