I have a ViewController (BViewController) that's inheriting from another UIViewController Subclass (AViewController). (The reason I want to do this is I'm reusing the same view in storyboard 3+ times for different screens.)
When I call:
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "AViewController") as! BViewController
self.show(vc, sender: self)
I get this error:
Could not cast value of type 'test.AViewController' (0x10d08b478) to 'test.BViewController' (0x10d08b3f0).
Here are my subclasses, they have nothing in them.
class AViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
-
class BViewController: AViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
The class in Storyboard is set to AViewController because I'm trying to share IBOutlets across all children without recreating the views. There is only one View Controller Scene in my UIStoryboard.
According to the answer in this thread, it isn't possible to reuse a single UIViewController Scene with multiple subclasses with UIStoryBoard. It is however possible with nib files.
How to use single storyboard uiviewcontroller for multiple subclass
You have to set your view controller's class to BViewController in your storyboard
You probably don't put the right view controller identifier:
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "BViewController") as! BViewController
self.show(vc, sender: self)
(BViewController instead of AViewController)
EDIT:
Here's an example: I have a SignupVC view controller in my storyboard, but its storyboard ID is "signup_vc"
Related
Here is my first .xib ViewController how can I go this ViewController to storyboard ViewController
class FirstViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
Here is storyboard secondViewController and I would like to go again this ViewController to .xib ViewController
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
From .xib to .storyboard:
let sb = UIStoryboard(name: "the-name-of-the-storyboard", bundle: nil)
let ctrl = sb.instantiateViewController(withIdentifier: "SecondViewControllerIdentifier")
self.present(ctrl, animated: true, completion: nil)
Keep in mind that SecondViewControllerIdentifier is the identifier of the storyboard scene; it is not the class name of your controller
From .storyboard to .xib:
let ctrl = FirstViewController()
self.present(ctrl, animated: true, completion: nil)
Assuming that either FirstViewController.xib or First.xib exists
Our App should be able to go from the main storyboard to another storyboard which contains a splitViewController that leads to a searchbar.
We created the searchbar in another storyboard and connected it to the navigation controller like this:
#IBAction func artikelButton(_ sender: Any) {
let viewController = self.storyboard!.instantiateViewController(withIdentifier:"MasterViewController")
self.navigationController?.pushViewController(viewController, animated: true)
}
If you then press that button the app crashes with the SIGABRT error.
The other 2 Buttons work fine, the difference is that they are .xib files.
Here's how we made the .xib buttons:
#IBAction func infoButton(_ sender: Any) {
let vc = InfoViewController(
nibName: "InfoViewController",bundle: nil)
navigationController?.pushViewController(vc, animated: true)
}
If you load your viewcontroller from XIB. then self.storyboard will always nil.
if you are using multiple storyboard or multiple XIB's A better solution
have abstract method practice in your every viewcontroller to access from storyboard
Just like add following (change viewcontroller name and identifier as per yours )
// MARK: - Abstract Methods
public class func viewController () -> LoginVC {
return StoryBoard.main.instantiateViewController(withIdentifier: StoryBoard.controller.LoginVC) as! LoginVC
}
// Now access it with LoginVC.viewController() and do push or present whatever your operation you requred
I manage everything in constant file
public struct StoryBoard {
static let main = UIStoryboard.init(name: "Main", bundle: nil)
struct controller {
static let LoginVC = "LoginView"
}
}
I have a navigationController where I pushViewController two viewcontrollers, OneViewController and TwoViewController. When I rotating the device on the pushed TwoViewController, and then popToViewController (see code) back to OneViewController the view on OneViewController is not updated / rotated.
Update: I have created a small sample project, where I have added a navigationController and two ViewControllers - and then doing the same thing. That is actually working, the only different I can think of is that the Views in my application that fails, is that I load the views from Xib files.
All the constraints in the view is setup in Interface Builder - and saved to the xib file.
I have the following in the view that I reference in Interface Builder as the ViewControllers view:
func loadViewFromNib() -> UIView? {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: String(describing: type(of: self)), bundle: bundle)
return nib.instantiate(
withOwner: self,
options: nil).first as? UIView
}
override public func awakeFromNib() {
super.awakeFromNib()
// Not setting up any constraints in code of setting any frame here.
}
If I then rotate on the OneViewController it then renders fine.
Do you know why it is not updating when using popToViewController?
let viewControllers: [UIViewController] = self.navigationController!.viewControllers
for aViewController in viewControllers {
if aViewController is OneViewController {
aViewController.view.updateConstraints()
aViewController.view.setNeedsDisplay()
aViewController.view.layoutIfNeeded()
aViewController.view.setNeedsLayout()
self.navigationController!.popToViewController(aViewController, animated: true)
break
}
}
On the view I have also tried this:
override func viewWillShow(passingInAnyObject: AnyObject?) {
self.updateConstraints()
self.setNeedsDisplay()
self.layoutIfNeeded()
self.setNeedsLayout()
}
Call the functionality which you want to update on the viewcontroller to which you pop in viewWillAppear method:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
}
Super strange, but the following solved the problem:
Setting: aViewController.view.frame.size = UIScreen.main.bounds.size
Change the following:
let viewControllers: [UIViewController] = self.navigationController!.viewControllers
for aViewController in viewControllers {
if aViewController is OneViewController {
aViewController.view.updateConstraints()
aViewController.view.setNeedsDisplay()
aViewController.view.layoutIfNeeded()
aViewController.view.setNeedsLayout()
self.navigationController!.popToViewController(aViewController, animated: true)
break
}
}
TO:
let viewControllers: [UIViewController] = self.navigationController!.viewControllers
for aViewController in viewControllers {
if aViewController is OneViewController {
aViewController.view.frame.size = UIScreen.main.bounds.size
self.navigationController!.popToViewController(aViewController, animated: true)
break
}
}
In various tutorials on how to use SegmentControllers, TabBarControllers, etc. it is configured such that the variable representing the view gets its value from an instantiation of the storyboard:
private lazy var summaryViewController: SummaryViewController = {
// Load Storyboard
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
// Instantiate View Controller
var viewController = storyboard.instantiateViewController(withIdentifier: "SummaryViewController") as! SummaryViewController
// Add View Controller as Child View Controller
self.add(asChildViewController: viewController)
return viewController
}()
Why does this code not just get an instance of SummaryViewController?
Adding an instance of a VC from your Storyboard, adds all of the logic and outlets you add in the storyboard. Let's say you have the following (obviously simple) VC:
class MyVC : UIViewController {
func viewDidLoad() {
}
#IBAction buttonPressed(sender : UIButton) {
/// Do something
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "mySegue"{
var vc = segue.destinationViewController as! WhateverViewController
}
}
}
where the buttonPressed: func is connected to a button in IB, and you also have a segue with a 'mySegue' identifier. Initializing your VC from the storyboard gives you access to all of these things. You absolutely can instantiate and push a VC, without the use of the storyboard, but you should not do so, when the VC you are pushing has wired IBOutlets, IBActions, etc.... If you want to do this in code, try the following:
let myNewVC = PushedViewController()
self.navigationController?.pushViewController(myNewVC, animated : true)
This will push the myNewVC onto your navigation stack, back button and all, and without using the storyboard.
I have a view in my xib file which contain buttons. i want to move to a ViewController when i will press the button (#IBAction). I have used below code
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewControllerWithIdentifier("About") as! AboutViewController
self.presentViewController(nextViewController, animated: true, completion: nil)
I am getting the error "Value of type 'SlideMenuView' has no member 'presentViewController'.
because my class is a UIView type :
class SlideMenuView: UIView {
}
so how can I navigate to other view controller.
That is beacuase the class you are trying to present from is a UIView and not a UIViewController. It has no Present method.
I'm guessing your view (SlideMenuView) is embedded inside a viewcontroller. what you need to do is implement a delegate, and inform your containing viewController to present next Viewcontroller.
code below:
#protocol SlideMenuViewDelegate: class {
func slideMenuViewAboutButtonClicked(menuView: SlideMenuView)
class SlideMenuView: UIView {
weak var delegate: SlideMenuViewDelegate?
#IBAction func aboutButtonClicked(sender: AnyObject) {
self.delegate?.slideMenuViewAboutButtonClicked(self)
}
now, in your viewController, implement this delegate method:
func slideMenuViewAboutButtonClicked(menuView: SlideMenuView) {
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewControllerWithIdentifier("About") as! AboutViewController
self.presentViewController(nextViewController, animated: true, completion: nil)
}
Also, dont forget to assign the sliderMenuView object the viewcontroller as a delegate.
something like:
self.sliderMenuView.delegate = self // (self == the containing viewController
I did it in a different way. In class file
class SlideMenuView: UIView {
var navigationController: UINavigationController? // Declare a navigation controller variable
// And create a method which take a navigation controller
func prepareScreen(navController: UINavigationController)-> UIView {
navigationController = navController
let nibView = NSBundle.mainBundle().loadNibNamed("SlideMenuView", owner: self, options: nil)[0] as! UIView
self.addSubview(nibView)
return nibView
}
// In Button action
#IBAction func btnAction(sender: UIButton) {
var storyBoard = UIStoryboard(name: "Main", bundle: nil)
let nextViewController = storyBoard!.instantiateViewControllerWithIdentifier("NextViewController") as! UIViewController
navigationController?.pushViewController(nextViewController, animated: true)
}
}
// For calling from UIViewController
slideBarMenuIstance.prepareScreen(self.navigationController!)