UISplitViewController automatically 'segues' to detail viewController - swift

I'm setting up a UISplitViewController programatically. This code appears to automatically segue to the detailViewController which is unwanted behaviour. Instead I would like it to present the masterViewController and let the user chose the detailViewController with didSelectRowAt IndexPath. Any help appreciated.
let splitViewController = UISplitViewController(nibName: nil, bundle: nil)
let masterNavigationController = UINavigationController(rootViewController: MasterViewController(nibName: nil, bundle: nil))
let detailNavigationController = UINavigationController(rootViewController: DetailViewController(nibName: nil, bundle: nil))
splitViewController.viewControllers = [masterNavigationController, detailNavigationController]
present(splitViewController, animated: true, completion: nil)

Implement the UISplitViewControllerDelegate method splitViewController(_:showDetail:sender:) (documentation here) to override the behaviour of your split vc.
In your specific case it should return true all the time except the time when the split vc is presented initially. In this case, you could set up a flag variable, e.g.
var isInitialState: Bool = true
then set it to false once the split vc has been presented completely – here I'm not sure when would be the best time, but I would guess
override func viewDidAppear(_ animated: Bool) {
[...]
isInitialState = false
[...]
}

Related

using pushViewController in containerView Not work

I have created containerView which contain tableViewController.
The main issue, when user click on information, tableViewController must show the information which user clicked.
it show the information when I use present(<#T##UIViewController#>, animated: <#T##Bool#>, completion: <#T##(() -> Void)?#>)
but doesnt work if I call self.navigationController?.pushViewController
the main issue is when I make vc.info = info it doesnt work, the info has value but using injection the value in another class is nil.
Here is my code:
func showLoginDetailsOnIpad(encryptedDataBase: info) {
self.view.addSubview(loginContainer)
let mainStoryboard : UIStoryboard?
mainStoryboard = UIStoryboard(name: "StoryboardiPad", bundle: nil)
let vc = mainStoryboard!.instantiateViewController(withIdentifier: "tableVC") as! tableVC
vc.info = info
vc.showingLoginInfo = true
vc.modalPresentationStyle = .automatic
self.navigationController?.pushViewController(vc, animated: true)
}
Explanation
You mentioned the use of a containerView. The table controller being displayed inside the containerView, doesn't automatically get a reference to the nearest navigationController.
So in this line: self.navigationController?.pushViewController(vc, animated: true)
self.navigationController might actually be nil. You can confirm this by printing its value before running the push method like so:
print(self.navigationController) // See if this prints nil.
self.navigationController?.pushViewController(vc, animated: true)
Fixes
To fix the issue, I suggest to pass a reference to the parent controller's navigationController to the child controller - through the embed segue.
In your parent controller, you can use this code:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? UITableViewController{
destination.navigationController = self.navigationController
}
}
And in your table view controller, leave the code as is.

Sign Out button to Present Login View Controller - Swift 5

Hi I'm kind of new to Swift and I can't figure this out. I am trying to create a sign out button that would take user to the login page. I used the following two methods but the first one doesn't do anything and the second one is throwing Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value and it refers to the code with the customButton:
//this one doesn't do anything
#objc func SignOut(){
let vc = self.storyboard?.instantiateViewController(identifier: "LoginViewController") as! LoginViewController
let appDelegate = UIApplication.shared.delegate
appDelegate?.window??.rootViewController = vc
}
//this one is throwing an error
let vc = CustomViewController()
self.present(vc, animated: true, completion: nil)
//the Fatal error refers to this code
override func viewDidLoad() {
super.viewDidLoad()
self.customButton.addTarget(self, action: #selector(customButtonPressed), for: .touchUpInside)
}
Also, I was wondering if AppDelegate is the right approach or if I should use SceneDelegate. Any help would be greatly appreciated.
Try to do this
#objc func SignOut(){
let vc = self.storyboard?.instantiateViewController(identifier: "LoginViewController") as! LoginViewController
self.view.window?.rootViewController = vc
}
let vc = self.storyboard?.instantiateViewController(identifier: "LoginViewController") as! LoginViewController
vc.modalPresentationStyle = .fullScreen
self.present(vc, animated: true, completion: nil)
Please make sure the identifier is matching the one inside storyboard for this viewController

Swift - How switch between storyboards with back button?

I'm in a storyboard and I need to go to another storyboard by tapping a button. Well, this I've already done, but I need to go back to previous storyboard. Here is my code:
func goContact() {
let storyboard = UIStoryboard(name: "FAQ", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "ContactViewController")
self.navigationController?.show(vc, sender: nil)
}
By tapping a button on my UITableViewCell (where I have a protocol), this function (goContact) is called and I switch to another storyboard (FAQ). But, how can I go back to main.storyboard?
I've already tried do this too:
func goContact() {
let storyboard = UIStoryboard(name: "FAQ", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "ContactViewController")
self.navigationController?.show(vc, sender: nil)
// self.navigationController?.pushViewController(vc, animated: true)
// self.present(vc, animated: true, completion: nil)
}
Use only storyboard property , it references to Main storyboard of the app or you can write it like let storyboard = UIStoryboard(name: "Main", bundle: nil)
To go back after push do
self.navigationController?.popViewController(animated: true)
You should push your new view controller onto the navigation controller using push, like in your commented-out code. That will cause the new view controller to have a back button, which will pop it for you without needing any extra code.

Swift: Can't use navigationController.pushViewController inside a function

I am transitioning between views programatically using the code below and it gets repeated quite a lot so I wanted to create a global function but I can't seem to be able to get the hang of it.
The code works when called inside a ViewController class, so I suppose the problem is that my function doesn't know which VC do I want to call navigationController.pushViewController on, but I don't know how to reference the VC either as an argument passed to the function, or better yet with something like .self to take the current VC class the function is called in.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "ExamplesControllerVC")
self.navigationController?.pushViewController(vc, animated: true)
The error I get if I try to run that as a function in a separate file is:
Use of unresolved identifier 'navigationController'; did you mean 'UINavigationController'?
So the function I'd like to create and call is something like:
showVC("ExamplesControllerVC")
Any ideas?
Whatever function this code is in needs to be updated to take a parameter of type UIViewController.
func showMain(on vc: UIViewController) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "ExamplesControllerVC")
vc.navigationController?.pushViewController(vc, animated: true)
}
Now you can call this as:
showMain(on: someViewController)
Or add this function to an extension on UIViewController then your use of self works just fine.
extension UIViewController {
func showMain() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "ExamplesControllerVC")
self.navigationController?.pushViewController(vc, animated: true)
}
}
Do you want to do something like this?:
extension UIViewController {
func presentView(withIdentifier: String) {
if let newVC = self.storyboard?.instantiateViewController(withIdentifier: withIdentifier) {
self.present(newVC, animated: true, completion: nil)
}
}
}
You can call it this way:
self.presentView(withIdentifier: "yourIdentifier")

Swift presentViewController

I programatically have multiple View Controllers in an iOS Swift Project. I do not have the storyboards and would like to avoid them if possible. Is there a way to switch to another viewcontroller.swift file (We will call it view2.swift) and have it be part of a function that a button calls?I have tried the following:
let storyboard: UIStoryboard = UIStoryboard(name: "myTabBarName", bundle: nil)
let vc: UIViewController = storyboard.instantiateViewControllerWithIdentifier("myVCID") as UIViewController
self.presentViewController(vc, animated: true, completion: nil)
The above works with storyboards, but I want another view2.swift to be called. Can this be done?
Try this:
let vc = ViewController() //change this to your class name
self.presentViewController(vc, animated: true, completion: nil)
With Swift3:
self.present(vc, animated: true, completion: nil)
For those getting blank/black screens this code worked for me.
let vc = self.storyboard?.instantiateViewController(withIdentifier: myVCID) as! myVCName
self.present(vc, animated: true, completion: nil)
To set the "Identifier" to your VC just go to identity inspector for the VC in the storyboard. Set the 'Storyboard ID' to what ever you want to identifier to be. Look at the image below for reference.
For reference, because this question is one of the first Google result.
Breaking change in Swift 3:
The method presentViewController is replaced by the method present.
You can use it like the old one:
self.present(viewControllerToPresent, animated: true, completion: nil)
Example to open the camera:
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = UIImagePickerControllerSourceType.camera
imagePicker.allowsEditing = false
self.present(imagePicker, animated: true, completion: nil)
Swift 3 and Swift 4
let vc = self.storyboard?.instantiateViewController(withIdentifier: "idMyViewControllerName") as! MyViewControllerName
self.present(vc, animated: true, completion: nil)
For me, I had two views in two separate nav controllers. I had to use a combination of the above.
var vc = self.storyboard?.instantiateViewControllerWithIdentifier("WelcomeViewController") as! WelcomeViewController
var navigationController = UINavigationController(rootViewController: vc)
self.presentViewController(navigationController, animated: true, completion: nil)
Swift 3.x
let secondVC = self.storyboard?.instantiateViewController(withIdentifier: "VC-ID" as! yourViewController
let navigationVC = UINavigationController(rootViewController: secondVC)
self.present(navigationVC, animated: true, completion: nil)
Using Swift 2.1+
let vc = self.storyboard?.instantiateViewControllerWithIdentifier("settingsVC") as! SettingsViewController
self.presentViewController(vc, animated: true, completion: nil)
Solved the black screen by adding a navigation controller and setting the second view controller as rootVC.
let vc = ViewController()
var navigationController = UINavigationController(rootViewController: vc)
self.presentViewController(navigationController, animated: true, completion: nil
Just use this : Make sure using nibName otherwise preloaded views of xib will not show :
var vc : ViewController = ViewController(nibName: "ViewController", bundle: nil) //change this to your class name
self.presentViewController(vc, animated: true, completion: nil)
You don't need to instantiate the ViewController in Storyboard just to get present() ViewController to work. That's a hackish solution.
If you see a black/blank screen when presenting a VC, it might be because you're calling present() from viewDidLoad() in the First/RootViewController, but the first View isn't ready yet.
Call present() from viewDidAppear to fix this, i.e.:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let yourVC = YourViewController()
self.present(yourVC, animated: true, completion: nil)
}
Once any "View" has appeared in your App, you can start calling present() from viewDidLoad().
Using UINavigationController (as suggested in an answer) is another option, but it might be an overkill to solve this issue. You might end up complicating the user flow. Use the UINavigationController based solution only if you want to have a NavigatonBar or want to return to the previous view controller.
You can use below code :
var vc = self.storyboard?.instantiateViewControllerWithIdentifier("YourViewController") as! YourViewController;
vc.mode_Player = 1
self.presentViewController(vc, animated: true, completion: nil)
Another possibility is that you do not have the XIB included in your Build target (which is what happened to me).
This could happen if you have a different target for Dev, Test & Release Builds (which you should have anyway).
I had a similar issue but in my case, the solution was to dispatch the action as an async task in the main queue
DispatchQueue.main.async {
let vc = self.storyboard?.instantiateViewController(withIdentifier: myVCID) as! myVCName
self.present(vc, animated: true, completion: nil)
}
It's already answered. But adding one more thing while presenting a UIViewController, If anyone is trying to add UIModalPresentationStyle :
if directly presenting the UIViewController as fullScreen:
let viewController = UIViewController()
viewController.modalPresentationStyle = .fullScreen
self.present(viewController, animated: true)
If there is UINavigationController with root view controller as UIViewController:
let viewController = UIViewController()
let navigationController = UINavigationController(rootViewController: viewController)
navigationController.modalPresentationStyle = .fullScreen
self.present(navigationController, animated: true)
More helpful answers:
Presenting modal in iOS 13 fullscreen
You can use code:
if let vc = self.storyboard?.instantiateViewController(withIdentifier: "secondViewController") as? secondViewController {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.window?.rootViewController = vc
}