Pageviewcontroller can't instantiate controller from XIB - swift

I have a pageViewController nib with its class pageViewControllerClass. On it I instantiate a myCustomViewController nib with its myCustomViewControllerClass that has outlets.
I use
guard let myViewController1 = UINib(nibName: "myCustomViewControllerClass", bundle: Bundle(for: myCustomViewControllerClass.self)).instantiate(withOwner: self, options: nil).first as? myCustomViewControllerClass else { return }
I add it to view controller using:
setViewControllers([myViewController1], direction: .forward, animated: true, completion: nil)
While there are not outlets connected from myCustomViewController to myCustomViewController class the nib loads fine, but when an outlet is connected my app crashes saying
pageViewController this class is not key value coding-compliant for
the key myOutletName.
I've tried using bundle as nil, setting instantiate(owner:myCustomClass.self), setting bundle as main. So far it keeps crashing. I would greatly appreciate suggestions on what I'm doing wrong.

I have found the solution, I used
let myViewController1 = myCustomViewControllerClass()
and just passed it to the
setViewControllers([myViewController1], direction: .forward, animated: true, completion: nil)
It was a matter of overthinking the solution.

Related

Xcode Moving from Storyboard

I have a Xcode project using storyboards.
I have the typical setup,
Segue in storyboard
Call self.perform("..."....) from view controller
This then calls 'prepare(for segue: UIStoryboardSegue, sender: Any?)' allowing me to write variables in the new view controller before 'viewdidload' is called
Now the project is growing making using storyboards impractical.
We are taking a code approach loading views from code. The issue we are facing is how to pass data to new view before 'viewdidload' is called or what's the correct process to use.
We are loading view using
let nib = UINib(nibName: "TestView", bundle: nil)
let view = nib.instantiate(withOwner: self, options: nil).first as! TestViewController
view.delegate = self
view.dataModel = TestViewDataModel()
present(view, animated: true, completion: nil)
Issue we have is 'instantiate' calls viewdidload but ideally it needs data from 'datamodel'
Thanks
Add a new initializer to TestViewController that takes accepts whatever data you need as parameters.
From within it, call super.init(nibName: "TestView", bundle: nil). UIViewController's initializer will take care of finding the nib, instantiating itself from it, and settings itself as the owner.
class TestViewController: UIViewController {
let dataModel: TestViewDataModel
init(dataModel: TestViewDataModel) {
super.init(nibName: "TestView", bundle: nil)
self.dataModel = dataModel
}
//...
}

Programmatically Segue Between Views in Different Storyboard Files

I have a view HostViewController in Host.storyboard and, in storyboard I am able to segue to AttendDetailViewController in Main.storyboard. However, I want to do this programmatically as follows:
private func attendDetailViewControllerSegue(event: CAEvent) {
let vc = AttendDetailViewController(nibName: "AttendDetailViewController", bundle: nil)
vc.event = event
navigationController?.pushViewController(vc, animated: true)
}
However, when I run this I get the following error:
'NSInternalInconsistencyException', reason: 'Could not load NIB in bundle: 'NSBundle ... with name 'AttendDetailViewController''
I have tried every solution on the internet for this problem, and I feel it might have something to do with the Views being in different storyboards.
Any thoughts on how to do this segue without an exception?
You have to get the storyboard from the bundle:
var hostStoryboard = UIStoryboard(name: "Host", bundle: Bundle.main)
Then, instantiate and present the view controller:
let someViewController = hostStoryboard?.instantiateViewController(withIdentifier: "AttendDetailViewController") as? AttendDetailViewController
self.navigationController?.present(attendDetailViewController!, animated: true)
Don't forget to set the AttendDetailViewController's storyboard identifier in the storyboard.
I also recommend you safely unwrap attendDetailViewController before using it:
let someViewController = hostStoryboard?.instantiateViewController(withIdentifier: "AttendDetailViewController") as? AttendDetailViewController
if let vc = attendDetailViewController {
self.navigationController?.present(vc, animated: true)
}
1- to load from xib ( used when there is AttendDetailViewController.xib file in your project )
let vc = AttendDetailViewController(nibName: "AttendDetailViewController", bundle: nil)
2- to load from storyboard ( used when the vc is inside a storyboard )
let vc = UIStoryboard(name: "Host", bundle: nil)!.instantiateViewController(withIdentifier: "vciD") as? AttendDetailViewController
3- load programmatically ( used when the vc layout is created programmatically )
let vc = AttendDetailViewController()

How to segue programmatically segue

I've just finished a Swift tutorial which does not use Interface Builder. It is all programmatic. Everything looks great but now I have to segue back to the storyboard, I am lost.
I'd like to segue from the "Login" button designed in the Audible tutorial to the next view controller I created in my storyboard called "DashboardVC".
Here is a link to the tutorial and source code. https://www.letsbuildthatapp.com/course_video?id=382
TIA
As is all from code, you don't have segue's; either you have to push a new controller using pushViewController from NavigationController or instantiate a new ViewController, in this case your 'DashboardVC'.
Like this
let viewController = DashboardVC()
viewController.view.backgroundColor = .blueColor() //example
navigationController?.pushViewController(viewController, animated: true)
Or just presenting the controller using this
let vc = DashboardVC()
present(vc, animated: true, completion: nil)
Using Storyboard, should be like this; the view controller identifier have to be set in the storyboard as well
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier :"DashboardVC") as! DashboardVC
self.present(viewController, animated: true)

Presenting a a ViewController on a xib file in SWIFT

I have a xib file which I have created a pop up controller on a UIView. This pop up controller animates up when a button is pressed on a View Controller 1. I then have a button on the UIView which when pressed I want to present another View Controller (View Controller 2). code looks like:
class PopUpViewControllerSwift : UIViewController {
#IBAction func goToVC2(sender: UIButton) {
self.removeAnimate()
let VC2: VC2 = self.storyboard?.instantiateViewControllerWithIdentifier("VC2") as VC2
var modalStyle: UIModalTransitionStyle = UIModalTransitionStyle.CoverVertical
VC2.modalTransitionStyle = modalStyle
presentViewController(VC2, animated: true, completion: nil)
}
}
Although thou when the button is pressed it crashes, no error or callbacks or anything. Obviously this would normally work if it was just a regular View Controller but because I am doing it inside a pop Up View which has been animated on top of another View I think that is the problem?
Can anybody help?
thanks
I did this in a UICollectionView didSelectItemAtIndexPath method.
// Register Nib
let newViewController = NewViewController(nibName: "NewViewController", bundle: nil)
// Present View "Modally"
self.present(newViewController, animated: true, completion: nil)
I set the ID of the ViewController to the same as the file name so I would be sure to reference the right ViewController.
Make sure self.storyboard is not nil.
Also confirm that VC2 is one of the ViewController's identity in the storyboard file.
I hope it could help you.
if VC2 is in a storyboard file, try to use following command to get VC2
let storyboard = UIStoryboard(name: "YourStoryboardName(maybe 'Main')", bundle: nil)
let vc2 = storyboard.instantiateViewControllerWithIdentifier("VC2") as VC2
or if VC2 is in a nib file, try to use following command to get VC2
let vc2 = VC2(nibName: "YourNibName", bundle: nil)
It should be like this:
let vc = MyViewController(nibName: "MyViewController", bundle: nil)
present(vc, animated: true, completion: nil)

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
}