I'm trying to create a custom UIView that will, when tapped, segue to a new view controller. I'd like to do this programmatically rather than in Interface Builder. How do I get a reference to the destination view controller when I'm doing this? I assume I don't just create one and set it as the next view or something, so where does the reference come from? Since when it's done in the storyboard you simply choose where you want the segue to have as the destination, it's not an issue there, however programmatically I don't understand where that reference comes from.
I'm doing this in Swift as well. I don't think that really makes a big difference, but it might change something.
I tried using a UIView with a tapGestureRecognizer added, which then performed the segue when tapped, however I was getting an unrecognized selector error.
If you declared your UIViewController in storyboard you have to set its Storyboard Id, and then, instead of performing segue you can use something like this (didn't run it, but done exact the same thing in obj-c, so I think it will do well):
let storyboard : UIStoryboard = UIStoryboard(name: "myStoryboardName", bundle: nil);
let vc : MyUIViewController = storyboard.instantiateViewControllerWithIdentifier("myVCStoryboardID") as MyUIViewController;
self.presentViewController(vc, animated: true, completion: nil);
Related
I am having a weird issue where my app crashes when I am trying to push a new view controller. I have set up a swipe gesture and want to segue to another view controller when a swipe is detected. When I run these 2 lines of code ...
let viewController:ViewController = ViewController()
self.navigationController?.pushViewController(viewController, animated: true)
The app crashes not specifically on either of those lines of code but rather in my ViewController class when in my viewDidLoad method I run this piece of code...
imageView.layer.masksToBounds = true
If I comment that out it crashes when I set the auto-correction type of my textField. What am I doing wrong?
First place I look when the view immediately crashes is in the Outlets for that ViewController in InterfaceBuilder. I look for anything that shows up with an exclamation mark. That usually means I renamed an outlet or broke a connection somehow. Delete anything broken by pressing the little x by the item that's messed up. I'll attach a photo so you can see.
It seems you're loading a ViewController that exists in storyboard with
let viewController:ViewController = ViewController()
which will result in nil outlets , so you have to use
let viewController = storyboard.instantiateViewController(withIdentifier: "VCID") as! ViewController
and give that vc a storyboard identifier like VCID
I think I have solved the issue but it has a weird side effect. Instead of using the line of code in #Sh_Khan's answer, I used ...
let viewController = nav?.storyboard!.instantiateViewController(withIdentifier: "mainVC") as! ViewController
The variable nav is equal to the navigation controller of the current view-controller. This seems to work without any hiccups but for some reason the back button does not disappear from the navigation controller after the segue is preformed. Does anybody know a solution to this, if so leave a comment and I will update my answer.
EDIT:
Another issue is that it wipes everything changed on that ViewController by the user clear. Is there another way to instantiate a ViewController without clearing it?
Follow what #Sh_Khan has said and in addition to that make sure that the view that you are making the push segue from is embedded in a Navigation controller.
I think my problem is best expressed by first giving an outline of what I'm trying to accomplish and then giving my implementation, followed by the problem I've ran into.
Goal
I'm attempting to make a profile page within my iOS app that can be instantiated from the Storyboard, given a user ID, and then fetch all the meta data from the server.
Implementation
In my storyboard, I have designed the ViewController and linked the ImageViews, buttons et cetera as #IBOutlets. The user's (person using the app.) homepage is instantiated by the Storyboard, but the rest are pushed in by code. In order to grab the data, I have a function loadDataForID(ID: Int)
Now, the problem I have is that #instantiateViewControllerWithIdentifier(String) returns a UIViewController that I cannot upcast to my ProfileViewController, in order to call the data fetching function.
So, my next thought was to manually create the ProfileViewController, call the helper function and then push the view, but then I ran into another problem: since all the views are #IBOutlets, and this controller wasn't instantiated by the Storyboard, they're all nil references.
As I'm new to iOS development, I'm almost certain there's a better way to implement this, but I'm completely stuck; I don't know where to go from here. Is there a way around these issues or should I do things a different way?
You can cast instantiateViewControllerWithIdentifier(String) as ProfileViewController as shown into below code:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let profileVC = storyboard.instantiateViewControllerWithIdentifier("ProfileViewControllerID") as! ProfileViewController
//Now you can access property of ProfileViewController here
self.presentViewController(profileVC, animated: true, completion: nil)
For the following steps:
drag two view controllers using storyboard, add a button on the first VC
ctrl drag the button on the first VC to the second VC and choose present segue
embed first VC with navigation controller
My questions are:
What happens under the hood when program starts? In particular, how and where will be the first and second VC created?
If not same, what's the difference compared with code below?
let vc = storyboard.instantiateViewControllerWithIdentifier("someViewController") as! UIViewController
Swift code will be preferred but Objective-C is also fine. Thanks in advance.
What you are doing using:
let vc = storyboard.instantiateViewControllerWithIdentifier("someViewController") as! UIViewController
is that you are doing what the storyboard is supposed to do. You are forcefully preparing for the segue. When you simply connect the two views using the segue on the storyboard, you are telling the compiler that the connection exists. When you run the code the second viewcontroller is instantiated the instant you click on the button and performs a prepareForSegue.
All in all, its all about manually overriding the delegate methods that are called when the storyboard works.
I recently upgraded to iOS 9 beta / Xcode 7, and one segue just stopped working. I have tried many different things, including completely deleting the view, and class file, and re creating both. For whatever, it gets to the point of the segue, and then it just does not complete that line. I have no idea what to do.
I have tried the regular
self.performSegueWithIdentifier("goToRules", sender: self)
as well as
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let rulesInViewController = storyBoard.instantiateViewControllerWithIdentifier("RulesInViewController") as! RulesInViewController
self.presentViewController(rulesInViewController, animated:true, completion:nil)
as well as
let secondViewController = self.storyboard!.instantiateViewControllerWithIdentifier("RulesInViewController") as! RulesInViewController
self.navigationController!.pushViewController(secondViewController, animated: true)
and none of them work. It is becoming very frustrating, and I just don't know what to do anymore. Is there anything that I could be doing wrong? I completely re did the view controller, and it still doesn't work. ALSO, it is an extremely basic view. All it has is one button, a logo, and a text view of rules for the app... The only code within that class is in the viewDidLoad, and it is defining the rules text.
In storyboard, check the identity inspector tab of your 'RulesInViewController' scene. It should have your custom class 'RulesInViewController' and module should have current -
Depending on the user's action, I need to move the user from one view controller to another depending on if they complete a task by time or complete a task by pressing a button. Both call the same method completeSession()
I've gone into the storyboard and added a Storyboard ID for each screen that is the same as the view controller class. So for this question, class JournalViewController has a storyboard ID of JournalViewController, same with SecondViewController
func completeSession() -> Void {
... some other stuff, unrelated ...
let journalViewController = self.storyboard?.instantiateViewControllerWithIdentifier("JournalViewController") as ViewController
self.presentViewController(journalViewController, animated: true, completion: nil)
}
This isn't working and causing my app to crash with a reason: Storyboard <UIStoryboard> doesn't contain a view controller with identifier JournalViewController, yet JournalViewController.swift contains:
class JournalViewController: UIViewController, UITextView Delegate {
Any idea how to push from SecondViewController to JournalViewController without error?
I think you can probably check the post here:
What is a StoryBoard ID and how can i use this?
So, you have to understand how you can connect your code with your graphics. Before Storyboard comes out, we use NSBundle to load the graphics. So, the same thing here is how you are going to load your graphics. Because Storyboard does not simply just contain one screen. So, you will have something to identify them. That's why we need that.
To answer my own question, this code worked, the missing piece was going into Storyboards and giving each item a storyboard ID.
instantiateViewControllerWithIdentifier relies on your storyboard ID to grab it successfully so that the app knows where to push the user to.
I hope that helps.
let journalViewController = self.storyboard?.instantiateViewControllerWithIdentifier("JournalViewController") as JournalViewController
self.presentViewController(journalViewController, animated: true, completion: nil)