How to set initial controller programmatically from storyboard in Cocoa using Swift 4 Xcode 10?
Asked Answered
P

2

7

Introduction:

I want to create an app that displays some "user guide" on its first launch, I've already finished the part that detects if the app is first launch, but when it comes to the setting view controller part, I can't figure out how to do it.

Current Situation:

I've done a lot of researches and seen a lot of Q/A articles on topics like "How to set initial view controller programmatically in swift", but basically all of them are using ios development as an example, which none of their UIWindow or UINavigationController(rootViewController:) matches in the OSX development. And then I found out it seems that there is no such thing as didFinishLaunchingWithOptions in the AppDelegate.swift file, instead, there's only an applicationDidFinishLaunching(_ aNotification: Notification).

Question:

I already unchecked the Is Initial Controller option for both of my View Controllers, and both of them has Storyboard ID (identifier?) and their own specified class.

MainPageController

UserGuidePageController

Or should I specify and identify my Window Controllers too? Cuz' I have no idea about what to do with the Window Controllers..

What do I need to do in order to set the initial view controller for my application in AppDelegate?

P.S. Here is the picture of my interface view:

Storyboard interface

AppDelegate.swift

import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

  let storyboard = NSStoryboard(name: NSStoryboard.Name("Main"), bundle: Bundle.main)
  var window = MainWindow().window

  func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) - > Bool {
    return true
  }

  func applicationDidFinishLaunching(_ aNotification: Notification) {
    // Insert code here to initialize your application
    let firstLaunch = UserDefaults.isFirstLaunch()
    let mainPageController = storyboard.instantiateController(withIdentifier: NSStoryboard.Name("MainPageController")) as? MainPageController
    let userGuidePageController = storyboard.instantiateController(withIdentifier: NSStoryboard.Name("MainPageController")) as? UserGuidePageController
    if !firstLaunch {
      print("first run")
      window?.contentViewController = userGuidePageController
    } else {
      print("not first run")
      window?.contentViewController = mainPageController
    }
  }

  func applicationWillTerminate(_ aNotification: Notification) {
    // Insert code here to tear down your application
  }
}

I tried this way, but it didn't work out pretty well, is there anything wrong with my code?

Phosphorous answered 3/10, 2018 at 11:27 Comment(3)
Mac OS Cocoa is based around windows, not view controllers. Simply decide what window to show first.Resentment
@Resentment okay, but how specifically? Can you show me how to do it by code plz?Phosphorous
https://mcmap.net/q/1019798/-how-does-xcode-decide-which-xib-to-show-firstResentment
I
2

at the beginning you need to handle such scenario from the app delegate class for iOS APPs

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {


let shouldShowUserGuide = // your cached key from user defaults for example

let mainController = //MainPageController Instance
let userGuide = //UserGuidePageController Instance


if shouldShowUserGuide {
window.rootViewController = userGuide 
}else{
window.rootViewController = mainController
}
window.makeKeyAndVisisble()
  return true
}

// this code I just write it on sublime not from xcode so may be you need to format it and fix any syntax problems

For OSX MainWindow: NSWindowController

func applicationDidFinishLaunching(_ aNotification: Notification) {
        let storyboard = NSStoryboard(name: NSStoryboard.Name("Main"), bundle: Bundle.main)
        var window = storyboard.instantiateController(withIdentifier: "MainWindow") as! NSWindowController
        let firstLaunch = true//UserDefaults.isFirstLaunch()
        let mainPageController = storyboard.instantiateController(withIdentifier: "MainController") as! NSViewController
        let userGuidePageController = storyboard.instantiateController(withIdentifier: "UserGuideController") as! NSViewController
        if firstLaunch {
            print("first run")
            window.contentViewController = userGuidePageController
        } else {
            print("not first run")
            window.contentViewController = mainPageController
        }
        window.showWindow(self) // needed as long as your window controller has no static window content (No Content View Controller)
    }
Incontrovertible answered 3/10, 2018 at 12:12 Comment(8)
Thanks, but what should I do with the original applicationDidFinishLaunching() one?Phosphorous
But Cocoa does not have didFinishLaunchingWithOptions, it only has something similar with it called applicationDidFinishLaunching()Phosphorous
if you overrided the mothod into your app delegate, just add the method body to yours, else copy the whole code into your appDelegateIncontrovertible
could you take a \screen shot of your appdelegate to figure out what's the problem with youIncontrovertible
I posted my AppDelegate.swift in my description.Phosphorous
man I updated the answer to fit both iOS and Coca, try the code above if it works let men knowIncontrovertible
I tried yours but it didn’t work, but I figured out another way to do it, but thanks though!!Phosphorous
On MacOS, applicationDidFinishLaunching is called after the initial window controller viewDidLoad (which instantiates its content view controller). With this solution, the initial viewcontroller is still created then either destroyed (and visible to the user) or just left open in a new tab.Boudicca
B
1

MacOS and iOS have different order of initialization function calls. applicationDidFinishLaunching does not get called until after the initial Window Controller viewDidLoad method gets called. But that is okay. You can do your logic in the Window Controller viewDidLoad method.

  1. Drag and drop a WindowController onto the storyboard and give it a Story Board ID. Also, set the Custom Class name. For example, "EntryWindowWC". BUT do not connect it to a content view controller. You will do that programmatically.

  2. Create a NSWindowController swift class

import Cocoa

class EntryWindowWC: NSWindowController
{
    override func windowDidLoad()
    {
        super.windowDidLoad()
    
        let storyboard = NSStoryboard(name: NSStoryboard.Name("Main"), bundle: Bundle.main)
        
        if (Security.shared.isLifetimeMember())
        {
            let homepage = storyboard.instantiateController(withIdentifier: "homepageStoryId") as! NSViewController
            window!.contentViewController = homepage
        }
        else
        {
            let subscription = storyboard.instantiateController(withIdentifier: "subscriptionStoryId") as! NSViewController
            window!.contentViewController = subscription
        }
    }
}
  1. Build, Run, and done.
Boudicca answered 6/6, 2021 at 21:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.