I've this situation when I've decided to reuse and extend one of my AViewController which also extends UITableViewController with its view in UIStoryboard A.
So I've created a new protocol. BViewController which extends AViewController in the other UIStroryboard. I've added other UITabelViewController with class BViewController, Cell creation and all the other tableViews is on AViewController side. In the extended view I want to provide a new source of data for TableView.
I thought, since I'm extending existing VC with view. I don't have to recreate IBOutlets like tableView, cell etc. in a new view.
But at this point it seams that I should recreate a view in the other Storyboard? I'm I missing something?
The content of the view hierarchy for a scene in your storyboard is not inherited by another scene regardless of if you use the same subclass of UIViewController or not. If you don't want to recreate the view hierarchy in your new scene, then you may need to put your view hierarchy in a .xib file and use UINib to do manual nib loading.
You may try this solution.
if let destinationVC = storyboard.instantiateViewController(withIdentifier: "AViewControllerIdentify") as? AViewController {
/// Runtime to set new sub viewcontroller class.
object_setClass(destinationVC, BViewController.self)
if let vc = destinationVC as? BViewController {
/// add you logic here.
}
}
Related
I am trying to display a MapKit view when the user clicks a button where MapScreen is of class UIViewController and MKMapKitdelegate.
When I perform this sender on the UIButton all I am getting is the background color of the other screens in my TabBarController.
let mapController = MapScreen()
navigationController?.pushViewController(mapController, animated: true)
I would expect to see the map as is defined in MapScreen(). I am using this same method to display many other view controllers and it works great.
If your view controller defined in storyboard you should not use plain init to initialize an object. As official documentation states:
When using a storyboard to define your view controller and its associated views, you never initialize your view controller class directly. Instead, view controllers are instantiated by the storyboard either automatically when a segue is triggered or programmatically when your app calls the instantiateViewController(withIdentifier:) method of a storyboard object. When instantiating a view controller from a storyboard, iOS initializes the new view controller by calling its init(coder:) method instead of this method and sets the nibName property to a nib file stored inside the storyboard.
So, In order to create view controller from storyboard you should call instantiateViewController(withIdentifier:) function of UIStoryboard object:
let storyboard = UIStoryboard(name: "MainStoryboard", bundle: .main)
let viewController = storyboard.instantiateViewController(withIdentifier: "Your view controller identifier")
Hope this helps.
Why can't I create the view controller objects using sth like
resultVC = ResultViewController() instead of the following way.
let storyboard = UIStoryboard (name: "Main", bundle: nil)
let resultVC = storyboard.instantiateViewController(withIdentifier: "ResultViewController") as! ResultViewController
// Communicate the match
resultVC.match = self.match
self.navigationController?.pushViewController(resultVC, animated: true)
Everything is depends on your logic. There are three basic ways by which you can create UIViewController.
Storyboard : you have storyboard, design your VC and instantiate it via storyboard.
In this case, you have to tell system in which storyboard your VC have and what is its ID.
As you done in above code.
Referal : https://developer.apple.com/library/content/documentation/General/Conceptual/Devpedia-CocoaApp/Storyboard.html
Xib/Nib: Like storyboard, you can use xib/nib and design your VC. Here just you need to alloc the VC by the xib name.
Referal : https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html
Programatically: Here you donot need any type of xib/ storyboard. You have to do everything by code. your VC design will be in your respective VC file. Here you have to just alloc that file.
Referal : How to make a view controller without a xib or a storyboard vc identifier?
or
starting ios project without storyboard
Difference: Which is more efficient way? StoryBoard or XIB?
If you still unclear, then ask.
You use this method to create view controller objects that you want to
manipulate and present programmatically in your application. Before
you can use this method to retrieve a view controller, you must
explicitly tag it with an appropriate identifier string in Interface
Builder.
So, you doesn't just allocate your class, but associate your class with appropriative screen in your Interface Builder.
Anyway, you can create your controller like you want resultVC = ResultViewController() but you should create all UI in code instead of using Interface Builder
Using resultVC = ResultViewController() will create a view controller of type ResultViewController but without any links to your storyboard, which is unlikely to be of any use to you (why are you using a storyboard if not to gain these links?). You need to instantiate it from the storyboard to gain these links.
I have an storyboard with a ViewController and inside the ViewController I have a UICollectionView with a prototype cell.
I already have an "MyCollectionViewController" (because I try to wrap my CollectionViewController in a ViewController). Now I want to reuse that controller but I can't figure out how to connect the CollectionView from the storyboard with a new CollectionViewController.
Assigning the CollectionView from the CollectionViewController to the Outlet in the ViewController doesn't seem to work.
I know I could make the cell prototype a .xib file and create the CollectionView in code. But my employer prefers having everything in the storyboard for easier maintenance.
EDIT:
The answer from chkn works great.
To connect the parent view controller to the container you can override the PrepareSegue method like this.
public override void PrepareForSegue (UIStoryboardSegue segue, NSObject sender)
{
base.PrepareForSegue (segue, sender);
if (segue.SourceViewController == this) {
if (segue.DestinationViewController.GetType () == typeof(MyChildViewController)) {
MyChildViewController childViewController = segue.DestinationViewController as MyChildViewController;
}
}
}
You can't assign a view from one view controller into an outlet on another view controller.
You can, however, have a single view controller containing the collection view, and then embed that view controller onto other view controllers using container views.
Simply create the view controller hosting the UICollectionView (it's easy to just use a UICollectionViewController, but it doesn't matter). Then on each view controller you want to embed it in, drag a Container View from the toolbox and remove the default view controller that comes with it. Then, Ctrl+drag from the Container View to the shared UICollectionView controller you want to embed.
Your storyboard might look something like this:
That example is available here:
https://github.com/chkn/StoryboardExamples/tree/master/CollectionViewReuse
What is the functional difference between instantiating a View Controller from the storyboard and creating a new instance of it? For example:
#import "SomeViewController.h"
...
SomeViewController *someViewController = [SomeViewController new];
versus
#import "SomeViewController.h"
...
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle: nil];
SomeViewController *someViewController = [storyboard instantiateViewControllerWithIdentifier:#"SomeViewController"];
In either case, is someViewController effectively the same thing?
The main difference is in how the subviews of your UIViewController get instantiated.
In the second case, all the views you create in your storyboard will be automatically instantiated for you, and all the outlets and actions will be set up as you specified in the storyboard.
In the first, case, none of that happens; you just get the raw object. You'll need to allocate and instantiate all your subviews, lay them out using constraints or otherwise, and hook up all the outlets and actions yourself. Apple recommends doing this by overriding the loadView method of UIViewController.
In the second case, the view controller will load its view from the storyboard and you will be happy.
In the first case, it won't. Unless you've taken other steps (like overriding loadView or viewDidLoad or creating a xib named SomeViewController.xib), you'll just get an empty white view and be sad.
In Swift you can do the same with,
var someVC = self.storyboard?.instantiateViewControllerWithIdentifier("SomeViewController") as! SomeViewController
You will need to give the Identifier in the Storyboard to the SomeViewController and tick the checkmark to Use Storyboard ID
It is not the same thing. In the storyboard you probably have some UI elements laid out. They might have constraints and properties setup through the storyboard. When you instantiate the viewcontroller via the storyboard, you are getting all the instructions for where those subviews are and what their properties are. If you just say [SomeViewController new] you are not getting all the instructions that the storyboard has for the view controller.
A nice test will be to add a UIViewController to a storyboard and drag a red view onto it. Instantiate it using both methods and see what the differences are.
simple swift 3 extension
fileprivate enum Storyboard : String {
case main = "Main"
}
fileprivate extension UIStoryboard {
static func loadFromMain(_ identifier: String) -> UIViewController {
return load(from: .main, identifier: identifier)
}
static func load(from storyboard: Storyboard, identifier: String) -> UIViewController {
let uiStoryboard = UIStoryboard(name: storyboard.rawValue, bundle: nil)
return uiStoryboard.instantiateViewController(withIdentifier: identifier)
}
}
// MARK: App View Controllers
extension UIStoryboard {
class func loadHomeViewController() -> HomeViewController {
return loadFromMain("HomeViewController") as! HomeViewController
}
}
In case you don't want to instantiate a new VC using instantiateViewControllerWithIdentifier but accessing the instance created by the storyboard from the AppDelegate:
create a property in AppDelegate.h so it will be accessible from classes using it
#property (nonatomic, strong) myViewControllerClass*vC;
in viewDidLoad inside myViewControllerClass.m I access the shared instance of AppDelegate and feed the property with self: [AppDelegate sharedInstance].vC = self;
I had to use this solution in a complex storyboard and still can't get over the fact that I cannot find an easy way to access all (or at least the ones I need) objects in storyboard simply by addressing their identifiers.
another thing to check for is if the viewcontroller that's throwing the error has a storyboardIdentifier, you can check the storyboard xib file.
the identifier was missing in my case, the error stopped when i added it
I have created a special class of UIView that has certain properties, and I did so programmatically because it is usually blank but will at times contain other views. I know that if I create a UIView programmatically I can do something like [specialView addSubview: aView];
My problem is that there is a UIView that I created in storyboard and I need to add that UIView as a subview on my special view. I have connected it as a property in my ViewController but when I do the same code as above, nothing happens. Thoughts?
MyViewController *myViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"MyScene"];
[specialView addSubview:myViewController.theViewToAdd];
And don't forget to give such an identifier to your scene (view controller) in Interface Builder.
Another solution is you can add the viewcontroller as a childviewcontroller.
func displayContentController(content: UIViewController) {
addChildViewController(content)
self.view.addSubview(content.view)
content.didMoveToParentViewController(self)
}
To remove :
func hideContentController(content: UIViewController) {
content.willMoveToParentViewController(nil)
content.view.removeFromSuperview()
content.removeFromParentViewController()
}
That UIViewController which houses the view you want may not be instantiated. So you would need to instantiate that controller and grab the view from there.