Navigation Controller Problem With SceneDelegate - swift

Hello I'm try the hide my navigation controller's back button for all my views. Because that I'm creating my navigation controller in sceneDelegate it work normal but just back button I can't hide it. I have been try all possibilities I can't fix it. (Also I try to change back button title I didn't change as well)
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let newScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: newScene)
let navigationController = UINavigationController()
navigationController.navigationBar.backItem?.backBarButtonItem?.title = "Test"
navigationController.navigationBar.backItem?.hidesBackButton = true
navigationController.navigationItem.hidesBackButton = true
navigationController.navigationItem.backBarButtonItem?.accessibilityElementsHidden = true
let appStartPoint = Bundle.main.loadNibNamed("CityController", owner: self, options: nil)?.first as! CityController
navigationController.setViewControllers([appStartPoint], animated: true)
window?.rootViewController = navigationController
window?.makeKeyAndVisible()
}

Related

How do i set/change the Root ViewController as a UINavigationController when the user opens the app a second time in swift?

i have an app where the user sets a username in the WelcomeViewController the first time he launches the app. This Username gets stored in Firestore. I want to change the ViewController that gets displayed when the app is opened after the username is set!
I made a function that checks if there is a username stored in FireStore and sets a boolean to true or false depending on the result. I want to change the ViewController that gets displayed as the RootViewController based on the boolean value, if true set the MainVC as RootVC and if it's false the WelcomeVC should be the RootVC.
If tried to set up a func inside the SceneDelegate func scene() but somehow i either get
a crash or a black screen when the app doesnt crash. I dont know what im doing wrong, i have tried every tutorial but nothing is working.
heres my code:
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).
self.window = UIWindow(frame: UIScreen.main.bounds)
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let welcomeVCAsRoot = storyboard.instantiateViewController(withIdentifier: "WelcomeViewController")
let mainVCAsRoot = storyboard.instantiateViewController(withIdentifier: "MainViewController")
if User.shared.userNameOccupied == true {
self.window?.rootViewController = welcomeVCAsRoot
self.window?.makeKeyAndVisible()
} else {
self.window?.rootViewController = mainVCAsRoot
self.window?.makeKeyAndVisible()
}
guard let _ = (scene as? UIWindowScene) else { return }
}
Try to use scene
func scene(_ scene: UIScene,
willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene)
else { fatalError() }
self.window = .init(frame: windowScene.coordinateSpace.bounds)
self.window?.windowScene = windowScene
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let welcomeVCAsRoot = storyboard.instantiateViewController(withIdentifier: "WelcomeViewController")
let mainVCAsRoot = storyboard.instantiateViewController(withIdentifier: "MainViewController")
if User.shared.userNameOccupied == true {
self.window?.rootViewController = welcomeVCAsRoot
} else {
self.window?.rootViewController = mainVCAsRoot
}
self.window?.makeKeyAndVisible()
}
If this will not help - try to add:
class TestViewController: UIViewController {}
and call it from the method scene like:
let testController = TestViewController()
testController.backgroundColor = .blue
self.window?.rootViewController = testController
If you will receive blue screen - something wrong on the controller side, if no - something wrong with my advice, and let me know exactly type of error.
Good luck

Could not find keyboard scene delegate for interaction view

Below iOS 13 my UITextField correctly launches a keyboard and lets the user type in their answer.
Above iOS 13, textFieldDidBeginEditing() is triggered when I tap on the text field, but the keyboard is not shown, so the user cannot give their answer .
Debug console doesn't immediately throw any errors, but eventually the following message comes up, which I think is the key:
Could not find keyboard scene delegate for interaction view
I think the error appears in the later iOSs because scenes become the main thing - and somewhere I needed to set up a delegate to allow the keyboard to appear over the fron of the first scene.
No idea how to do this though!
My UITextField is totally standard. To reproduce the error, I have the following set up code in my SceneDelegate
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let windowScene = UIWindowScene(session: session, connectionOptions: connectionOptions)
self.window = UIWindow(windowScene: windowScene)
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let initialViewController = storyboard.instantiateViewController(withIdentifier: "VC" )
self.window?.rootViewController = initialViewController
self.window?.makeKeyAndVisible()
guard let _ = (scene as? UIWindowScene) else { return }
}
In my actual app - I use this subroutine to launch a tutorial if the user is new (i.e. I need to be able to change the starting view controller)
Something appears to be out of sorts in your SceneDelegate function scene().
Try this code which I grabbed from another project I have at hand.
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
self.window?.rootViewController = ViewController()
self.window?.makeKeyAndVisible()
}

How to make Onboarding work with Scene Delegate in iOS13?

I am trying to set up my onboarding screen in the SceneDelegate.
When I run the code below, it compiles, but just goes to a black screen.
They're many great onboarding tutorials for AppDelegate, but very few for the new SceneDelegate with iOS13. I took this tutorial and tried to apply it to SceneDelegate, but I can't get it to work: https://www.youtube.com/watch?v=y6t1woVd6RQ&t=537s
This is my scene delegate code.
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let launchedBefore = UserDefaults.standard.bool(forKey: "hasLaunched")
self.window = UIWindow(frame: UIScreen.main.bounds)
let launchStoryboard = UIStoryboard(name: "Onboarding", bundle: nil)
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
var vc: UIViewController
if launchedBefore
{
vc = mainStoryboard.instantiateInitialViewController()!
}
else
{
vc = launchStoryboard.instantiateViewController(identifier: "Onboarding")
}
UserDefaults.standard.set(true, forKey: "hasLaunched")
self.window?.rootViewController = vc
self.window?.makeKeyAndVisible()
// guard let _ = (scene as? UIWindowScene) else { return }
}
I've tried it both with commenting out the last guard statement and with not commenting it out.
You're creating the window incorrectly, so you're going to end up with a black screen. It would be a lot better to let the storyboard create the window for you, since you don't know how to do it. Just delete this line completely:
self.window = UIWindow(frame: UIScreen.main.bounds)
You can also cut this line, as it is also otiose (the storyboard will do this for you as well):
self.window?.makeKeyAndVisible()
Your sole responsibility now is to set the value of self.window?.rootViewController. Note that you do not need to say
vc = mainStoryboard.instantiateInitialViewController()!
because that is the root view controller already, given to you by the storyboard. Thus the only thing you need to do, in the architecture you've posited, is replace the root view controller in the situation where the user needs to be onboarded.
(See my github repo for a working example.)
This was solved by matt, see his answer.
Just posting the correct code below for anyone trying to do Onboarding with storyboards in ios13.
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let launchedBefore = UserDefaults.standard.bool(forKey: "hasLaunched")
let launchStoryboard = UIStoryboard(name: "Onboarding", bundle: nil)
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
var vc: UIViewController
if launchedBefore
{
vc = mainStoryboard.instantiateInitialViewController()!
}
else
{
vc = launchStoryboard.instantiateViewController(identifier: "OnboardingScene")
}
UserDefaults.standard.set(true, forKey: "hasLaunched")
self.window?.rootViewController = vc
}

Swift - I want to access information from the SceneDelegate.swift in my viewcontroller

When using an AppDelegate, this line of code would allow me to send variables between app delegate and the view controller.
let firstName = AppDelegate.shared().givenName
What can I do for sceneDelegate?
Well I can suggest you using User Defaults like that:
// Set
UserDefaults.standard.set(self.currentZoomLevel, forKey: "zoomLevel")
// Read
if let zoomLevel = UserDefaults.standard.object(forKey: "zoomLevel") as? CGFloat {
return zoomLevel
}
Another approach should be like that
var window: UIWindow?
// On your scene delegate you have an instance variable Window
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
let window = UIWindow(windowScene: windowScene)
self.window = window
}
With window object, you can access the navigation controller and access your viewController directly:
var wantedViewController: DesiredViewController?
let _ = window?.rootViewController?.children.forEach({ navigation in
if let navController = navigation as? UINavigationController {
if let tabBar = navController.viewControllers.filter({ viewController in
viewController is MyTabBarController
}).first {
if let container = tabBar.children.filter({ $0 is ContainerViewController }).first as? ContainerViewController {
if let innerViewController = container.children.filter({ $0 is RootViewController }).first as? RootViewController {
wantedViewController = innerViewController.orderedViewControllers.filter({ $0 is DesiredViewController }).first as? DesiredViewController
}
}
}
}
})
This code is a little bit complex because of my structure on the project, my view controller is inside a TabController, probably yours can be not that complex and you will find the view controller easier.
Just debug and see how it happens.

push ViewController without storyboard

I try to push a view controller from the main ViewController like this (Swift 4):
#objc func childAction(sender: UIButton!) {
print("Child button tapped")
let vc = childDetailViewController()
self.navigationController?.pushViewController(vc, animated: true)
}
The text is printed, but the viewController is not pushed. What did I missed?
Possible that self.navigationController is nil.
Try to present it: self.present(vc, animated: true)
UPD
Also if it does not help, try temporary change your childDetailViewController to UIViewController() and see what happens. If you see the empty white screen after tap then the problem is on childDetailViewController
Inside AppDelegate's didFinishLaunchingWithOptions do
let fir = FirstVC()
self.window?.rootViewController = UINavigationController(rootViewController: fir)
Then this
self.navigationController?.pushViewController(vc, animated: true)
should work
From Xcode 11 you might have noticed that along with the default files like above, a new file is created named as SceneDelegate.swift. From iOS 13 and later, SceneDelegate takes up some responsibilites from AppDelegate. In particular related to UIWindow from AppDelegate is now UIScene in SceneDelegate. An app can have more than one scene which mostly handles application interface and app content. So, the SceneDelegate is responsible for what’s displayed on the screen in terma of UI and data.
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
let vc = ViewController()
let navigationView = UINavigationController(rootViewController: vc)
window?.rootViewController = navigationView
window?.makeKeyAndVisible()
}