I recently started a new project with the newest Xcode and now the function
self.navigationController?.pushViewController to push a new controller is not working.
I've looked at similar answers and checked my own old projects where it is working, they mentioned the Navigation controller being nil but I thought I addressed that.
I think it has to do with the new SceneDelegate file that is part of how views are managed.
Here is the code of the regarding the first VC that pops up in the app in the SceneDelegate file.
let nav1 = UINavigationController()
let mainView = MainViewController(nibName: nil, bundle: nil) //ViewController = Name of your controller
nav1.viewControllers = [mainView]
// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {
let window = UIWindow(windowScene: windowScene)
// window.rootViewController = UIHostingController(rootView: contentView)
window.rootViewController = nav1
self.window = window
window.makeKeyAndVisible()
}
Remove SceneDelegate.swift file and it's delegate methods.
I had same issue with Xcode 11.1 with new project.
Remove SceneDelegate.swift file and also remove its delegate methods from AppDelegate.swift. This solution worked for me.
My open source code created with Xcode 11 and Swift 5.
https://github.com/gauravparvadiya/swift-news-reddit
Like they said above,
remove SceneDelegate.swift,
the sceneDelegate methods from the AppDelegate.swift
PLUS
remove these from the plist file:
UISceneConfigurations
UIWindowSceneSessionRoleApplication
key: UISceneConfigurationName
value: Default Configuration
key: UISceneDelegateClassName</key>
value: $(PRODUCT_MODULE_NAME).SceneDelegate
key: UISceneStoryboardFile
value: Main
If you fail to remove these, you will get a black view when you run your app. But, the error messages in the console should help guide you to delete the necessary items from the plist.
Related
I have searched practically everywhere for some tutorials on Coordinator Pattern with no storyboard.
Maybe someone can share his code from Github so I can have look?
Is anyone able to recommend something? Tutorial, a book to buy, anything. Thanks!
I recommend you to read this article by Paul Hudson. But it's written by using storyboard.
If you want to use uikit without storyboard, delete Main.storyboard first.
Inside Info.plist file, delete the rows with the following keys.
Application Scene Manifest/Scene Configuration/Application Session Role/Storyboard Name
Main storyboard file base name
In SceneDelegate class of SceneDelegate.swift, add this property.
var coordinator: Coordinator?
Replace the following code in scene(_:willConnectTo:options:) of SceneDelegate.swift
guard let windowScene = (scene as? UIWindowScene) else { return }
let window = UIWindow(windowScene: windowScene)
let navigationController = UINavigationController()
coordinator = MainCoordinator(navigationController: navigationController)
coordinator?.start()
window.rootViewController = navigationController
self.window = window
window.makeKeyAndVisible()
I hope you may keep on learning coordinator pattern.
I have a query which is quite intriguing to me, it happens that I am learning Swift, it seems to me fantatisc the tools that xcode provides make the job a lot easier, but based on my experience in Android projects as a minimum it is recommended to use MVC to maintain the order of the project, same try to swift for which they recommended me to use xib files, it can also be used to work in groups. Until there is everything fantastic but when I tried to implement it I find it too complicated, for which I preferred to use storyboards but the use of libraries and other files make that when loading my storyboard file I delayed too much which is because it is loaded with enough ViewController. Added to this, using the NavigationController in storyboards is easy, it makes navigating a lot easier. My problem is how could I do this with xib files? In a moment I try to do it, but my ViewController loaded vertically and without the navigation bar and I have no idea how to develop it.
By code to load a ViewController is this way.
let Storyboard = UIStoryboard(name: "Main", bundle: nil)
let menuVC = Storyboard.instantiateViewController(withIdentifier: "MenuSelectedViewController") as! MenuSelectedViewController
self.navigationController?.pushViewController(menuVC, animated: true)
And by interface builder with segue
So how can I develop this navigation bar with xib files and my TabBarController ?
You just need UINavigationController(rootViewController: yourViewController)
Here is an example using this
let yourViewController = DiscoverViewController(nibName: "yourViewController", bundle: nil)
yourViewController.tabBarItem.image = UIImage(named: "imageName")
let navigationController = UINavigationController(rootViewController: yourViewController)
// TabBarController
let tabbarController = UITabBarController()
tabbarController.tabBar.tintColor = ThemeColor
tabbarController.tabBar.barTintColor = .white
tabbarController.viewControllers = [navigationController] //add your other controllers here as needed
// Make it root or what ever you want here
self.window?.rootViewController = tabbarController
self.window?.makeKeyAndVisible()
While clicking on the button , i am moving to another view controller using the following code.
var window: UIWindow?
window = UIWindow.init(frame: UIScreen.main.bounds)
window?.autoresizesSubviews = true
window?.autoresizingMask = [.flexibleWidth, .flexibleHeight]
let trackingViewController = LoginCameraViewController.init(screen:
.main)
window?.rootViewController = trackingViewController
window?.addSubview((trackingViewController?.view)!)
window?.makeKeyAndVisible()
window?.layoutSubviews()
For every button click, a new window is added to the application.I want to remove the latest window added.
The number of windows present in the application can be known by using following code.
let windowz = UIApplication.shared.windows
print("subviews",windowz)
I think you get the wrong concept of navigation in iOS. Window is like a root object in which ViewControllers appear. So probably the solution you're looking in a first place is UINavigationController.
Apple Documentation on Navigation
For iOS 13 i was able to do it this way
I created array which contains the window using which this new viewController is being presented,
var arrWindow = [UIWindow]()
arrWindow.append(yourNewWindow)
// Note: This will be stored as strong reference so need to remove it.
Also store your original window in variable
let originalWindow = yourOriginalWindow
// Note: Same goes for this as well ,this will be stored as strong reference so need to remove it.
At the time of removing there are many ways to do it but this was the most suited way for me,
func removeAppendedWindow() {
for window in arrWindow {
if window != originalWindow {
if let index = arrWindow.index(of: window) {
window.isHidden = true
arrWindow.remove(at: index)
}
}
}
}
In the below code windowz is normal array.
let windowz = UIApplication.shared.windows
You can remove last by using
windowz.removeLast()
You should use View Controller instead of adding windows and pop it instead where you are removing the window.
Window is only one object for app and will contain the views.
Please correct your understanding and use View controllers.
I am proudly new to iOS developing and I am trying to build my first app. I am doing a course on an online platform which does the following in the
AppDelegate -> application didFinishLaunchingWithOptions:
let navigationController = window?.rootViewController as! UINavigationController
let notebooksListViewController = navigationController.topViewController as! NotebooksListViewController
notebooksListViewController.dataController = dataController
This app has a Navigation controller which begins with an UIViewController.
I have 2 questions here, first is why this works, I mean, I am in AppDelegate, so the NotebooksListViewController (first view of the app) is not instantiated yet (I think), so why I am able to inject a variable in it?
On the other hand, the second question, is how can I do this in a different scene? I have a TabBarViewController as first scene, and the first tab is a UITableViewController and I want to inject the same way my dataController var, how can I accomplish this? I could not get to do it, neither understand it.
Thanks in advance.
It works, because of some Xcode magic:
In your Target Setting, General tab, the Main Interface entry specifies the name of the Storyboard to be loaded automatically when your app starts up:
In the storyboard, the Initial View Controller then will be instantiated. It seems like this is an UINavigationController.
Since this is done automatically, it just works - until you want to do something special :-)
If you want to start up with a different scene - maybe from a different view controller - you could just change either the Main Interface to another storyboard, the Initial View Controller (inside the storyboard) or both.
Or, you could just start up by yourself, by leaving the Main Interface empty and create your own view controller inside the app delegate (didFinishLaunchingWithOptions), something like
self.window = UIWindow(frame: UIScreen.mainScreen().bounds)
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
if let tabVC = mainStoryboard.instantiateViewControllerWithIdentifier("TabCtrl") as? UITabBarController {
self.window?.rootViewController = tabVC
// Access the subcontrollers, or create them
// Initialize their values
// tabVC.viewControllers[0].data = ...
} else {
// Ooops
}
self.window?.makeKeyAndVisible()
Answer to your first question
as the method name is self explanatory didFinishLaunchingWithOptions means your application is didfinish with launching with options and its about to enter in foreground so here application need to set rootViewController so in this method controller you want to set as view controller is initiated thats why you can inject variable in it
answer to second question
let navigationController = window?.rootViewController as! UITabbarController
let VC = navigationController.childViewController
//Now Using VC you can access all you controller of tabbar controller
let notebooksListViewController = navigationController.topViewController as!
NotebooksListViewController
notebooksListViewController.dataController = dataController
now as shown above you can use VC to access you view controllers
but be careful here because VC return viewcontroller array so you need make checks for perticular VC you want to access
I'm trying to show a storyboard as a sheet right after the window has loaded.
override func windowDidLoad() {
super.windowDidLoad()
let storyboard = NSStoryboard(name: NSStoryboard.Name(rawValue: "Init"), bundle: nil)
let controller = storyboard.instantiateInitialController() as! NSViewController
self.window!.contentViewController?.presentViewControllerAsSheet(controller)
}
Unfortunately, the sheet is shown out of position and behind the window.
When I run the same code inside a button, everything works fine.
Screenshot
How do i correctly show a storyboard sheet after loading a window?
I have more knowledge of the iOS ecosystem but I suppose that you should show new windows only after the origin window has been shown. windowDidLoad is called when the view has been loaded, not when it has been shown.
Therefore, you should probably put your code into viewDidAppear of the contentViewController.