IOS simulator is black when I load my app - ios

My IOS simulator is black when I load my app. My Main Interface is set to the correct storyboard. I have a storyboard entry point setup also.
The home screen works but my app does not. Any suggestions
I have googled but can not find the fix to my question. Thanks!

Check if your app is using SceneDelegate class & also check if there exists a AppManifest File in Info.plist .If that's the case you have to provide App's Entry point inside Scene Delegate Class in method.
For Example
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let initialViewController = UIStoryboard(name: "Storyboard", bundle: nil).instantiateViewController(identifier: "DatePicker") as! DatePicker
let navigation = UINavigationController(rootViewController: initialViewController)
// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = tabBarCnt
self.window = window
window.makeKeyAndVisible()
}
}

Related

Programmatically embedding in navigation controller (Xcode 14.2, io2 16+)

I had a working program consisting basically of a table view controller embedded in a navigation controller, and I decided to try to get rid to some mysterious/obnoxious warnings that first appeared in iOS 16 ("UINavigationBar decoded as unlocked for UINavigationController, or navigationBar delegate set up incorrectly"). Following online suggestions, I got rid of the navigation controller on the storyboard, and added some code to
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window?.windowScene = windowScene
window?.makeKeyAndVisible()
// ViewController: UITableViewController is my VC class
let viewController = ViewController()
let navViewController = UINavigationController(rootViewController: viewController)
window?.rootViewController = navViewController
}
to embed it programmatically. The program would then crash with a message that stating that it could not deque a cell with identifier "itemCell", and "must register a nib or a class for the identifier or connect a prototype cell in a storyboard". I solved that by adding the line
self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "itemCell")
in ViewDidLoad but I don't understand why this was necessary, since the identifier for the prototype cell is still there in the storyboard, just as it was before. Can someone illuminate me? Thanks.
This:
let viewController = ViewController()
let navViewController = UINavigationController(rootViewController: viewController)
window?.rootViewController = navViewController
won't give you what you want.
In Storyboard, you have to give ViewController a Storyboard ID (such as "FirstViewController"), then instantiate it via code:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController = storyboard.instantiateViewController(withIdentifier: "FirstViewController")
let navViewController = UINavigationController(rootViewController: initialViewController)
Edit
Assuming the view controller we ID as "FirstViewController" in Storyboard is a table view controller, and we want to load it as the "root" controller for a navigation controller, the full code block would be:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window?.windowScene = windowScene
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController = storyboard.instantiateViewController(withIdentifier: "FirstViewController")
let navViewController = UINavigationController(rootViewController: initialViewController)
window?.rootViewController = navViewController
window?.makeKeyAndVisible()
}

SceneDelegate with Conditional Storyboards

Prior to Xcode 11, one could conditionally set which Storyboard to start with on an iOS app inside AppDelegate. Now with the addition of SceneDelegate it is to my understanding that you can instantiate that same logic there, which I am having issues with.
If I set the Storyboard Name inside Application Scene Manifest I can get the app to always start on the same Storyboard, but I want to conditionally start on one of two Storyboards.
I've tried something like this both with Storyboard Name set and not set inside Application Scene Manifest but can't seem to get anything to work. Any Ideas?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let _ = scene as? UIWindowScene else {return}
let storyboard: UIStoryboard
let initViewController: UIViewController
if (User.sharedInstance.isLoggedIn()) {
storyboard = UIStoryboard(name: "Main", bundle: nil)
initViewController = storyboard.instantiateViewController(withIdentifier: "MainTabs") as UIViewController
self.window?.rootViewController = initViewController
} else {
storyboard = UIStoryboard(name: "Login", bundle: nil)
initViewController = storyboard.instantiateViewController(withIdentifier: "LoginLanding") as UIViewController
self.window?.rootViewController = initViewController
}
self.window?.makeKeyAndVisible()
}
Looks like the problem is I wasn't setting the window with the scene.
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let windowScene = scene as? UIWindowScene else {return}
window = UIWindow(windowScene: windowScene)
// if user is not logged in then don't use Main storyboard
if (!User.sharedInstance.isLoggedIn()) {
let storyboard = UIStoryboard(name: "Login", bundle: nil)
window?.rootViewController = storyboard.instantiateViewController(withIdentifier: "LoginLanding")
}
}

Loading screen off centre based on launch storyboard?

I have 2 storyboards: Onboarding and Main. Everything works great as far as execution goes. The issue is that I have loading screen at the start of Main.storyboard that looks different depending on which storyboard launches.
Scenario 1: New user launches the app, it goes through Onboarding.storyboard then Main.storyboard launches and the loading screen looks as it should like this: Correct Screen
Scenario 2: User launches the app for the second time, therefore Onboarding.storyboard is not launched: Loading screen is off centre like this: Wrong Screen
Long story short: If there is no Onboarding.storyboard, it doesn't look right. The issue is only shown on iPad landscape; portrait looks fine.
All of my loading screen code is inside my first view controller of Main.Storyboard so I'm guessing that the issue is the order in which the code is called?
Onboarding.Storyboard only has 1 view controller
Main.Storyboard goes: TabBarController -> NavController -> FirstVC
I placed my SceneDelegate code below along with the loading screen code of FirstVC.
In case you want to dig in deeper, I'm using RevealingSplashView available on github. Since I don't think that's the issue I didn't get into that further.
SceneDelegate:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(frame: windowScene.coordinateSpace.bounds)
window?.windowScene = windowScene
let onboardingStoryboard = UIStoryboard(name: "Onboarding", bundle: nil)
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
var vc: UIViewController
if UserDefaults.standard.value(forKey: "firstTimer") == nil {
vc = onboardingStoryboard.instantiateInitialViewController()!
} else {
vc = mainStoryboard.instantiateInitialViewController()!
}
self.window?.rootViewController = vc
self.window?.makeKeyAndVisible()
}
FirstVC relevant code:
class firstVC: UIViewController {
var revealingSplashView : RevealingSplashView!
override func viewDidLoad() {
super.viewDidLoad()
showLoadingScreen()
}
func showLoadingScreen() {
revealingSplashView = RevealingSplashView(iconImage: UIImage(named: "Icon")!, iconInitialSize: CGSize(width: 150, height: 150), backgroundImage: UIImage(named: "loadBackground")!)
revealingSplashView.animationType = .heartBeat
revealingSplashView.startAnimation()
view.addSubview(revealingSplashView)
}
Check to see that your layout constraints are the same in both of your storyboards. I'd recommend just setting them to center horizontal and center vertical on both of them.
In your scene delegate callback, why are you calling instantiateInitialViewController() twice per storyboard?
In my iOS 12 project, I used storyboards to kick things off. In app delegate didFinishLaunching, I would override and set the window if and only if it was a new user. So my "Main" storyboard would kick in most of the time.
Same technique could be applied here. Check out the Info.plist values you need to set for UIScene storyboard to work. You can override the window if you want to show the first time onboarding, otherwise let the system instantiate your main storyboard.
Further testing revealed that if I return SceneDelegate to its "factory default" state, therefore launching Main.storyboard, I had no issues.
To fix the issue I simply changed my SceneDelegate to:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
if UserDefaults.standard.value(forKey: "firstTimer") == nil {
window = UIWindow(frame: windowScene.coordinateSpace.bounds)
window?.windowScene = windowScene
let onboardingStoryboard = UIStoryboard(name: "Onboarding", bundle: nil)
var vc: UIViewController
vc = onboardingStoryboard.instantiateInitialViewController()!
self.window?.rootViewController = vc
self.window?.makeKeyAndVisible()
}
}

Trying to find a better approach to custom tab bar

I'm trying to create a custom TabBar.
My approach so far, was to create one UIViewController (Let's call it the TabBarController), In the TabBarController, I added a childVC (Let's call it UserViewController).
I couldn't figure out a way to change the UserViewController without making the TabBarController to disappear.
I started to think maybe this is not a good option, and that there most be a better, actually working way to do so.
All I really trying to achieve is a TabBarController, separated from the UserViewController, the TabBarController will always be displayed on the bottom of the screen, and by clicking the items in it, the UserViewController will change accordingly.
I searched for hours, tried different solutions, nothing worked. Really hope you could guide me, maybe share a tutorial or article you have about this.
Create a subclass of UITabBarViewController.
class TabBarController: UITabBarController {
var previousTabIndex: Int?
override func viewDidLoad() {
super.viewDidLoad()
let viewControllerOne = ViewController()
viewControllerOne.tabBarItem.image = UIImage(named: "one")
viewControllerOne.tabBarItem.title = "One"
let viewControllerTwo = ViewController()
viewControllerTwo.tabBarItem.image = UIImage(named: "two")
viewControllerTwo.tabBarItem.title = "Two"
let viewControllerThree = ViewController()
viewControllerThree.tabBarItem.title = "Premium"
viewControllerThree.tabBarItem.image = UIImage(named: "three")
let tabBarList = [
viewControllerOne,
viewControllerTwo,
viewControllerThree
]
self.viewControllers = tabBarList.map {
let nav = UINavigationController(rootViewController: $0)
return nav
}
}
}
And then in SceneDelegate, set window.rootViewController = TabBarController()
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
window.rootViewController = TabBarController()
self.window = window
window.makeKeyAndVisible()
}
}
If you have created your project in older version of xCode, assign TabBarController() to window.rootController in didFinishLaunchingWithOptions of AppDelegate

Making project created in Xcode 11 backwards compatible

I've created a new project using Xcode 11. I'm used to creating my UIWindow's root view controller programmatically and I first noticed this new SceneDelegate file. After some research I found an article describing how to create root view controller using this new UIScene API and it seems to work properly. However, I couldn't find a way to do the same thing for lower iOS versions because AppDelegate class now does not have a window property anymore. So my question is how do I create a root view controller programmatically for other iOS versions?
Here's the code if someone faces the same thing.
iOS 13 workflow
Code taken from this article.
In your SceneDelegate file:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let windowScene = (scene as? UIWindowScene) else { return }
self.window = UIWindow(frame: windowScene.coordinateSpace.bounds)
self.window?.windowScene = windowScene
let controller = UIViewController()
let navigationController = UINavigationController(rootViewController: controller)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
}
iOS 12 and lower
In your AppDelegate file:
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if #available(iOS 13.0, *) { return true }
self.window = UIWindow(frame: UIScreen.main.bounds)
let controller = UIViewController()
let navigationController = UINavigationController(rootViewController: controller)
self.window?.rootViewController = navigationController
self.window?.makeKeyAndVisible()
return true
}
It is important to hold UIWindow in a variable in order for it to show up.

Resources