How to pass data to another storyboard in Xcode? - iphone

In an iPhone application, how do I pass data from one view (xib file) to another storyboard?
for example,
I have "welcome.xib" which contains nameTextfield and nextButton ,user will type their name into nameTextfield and click next
the app will navigate to "main.storyboard" and display the text from nameTextfield.text on userNameLabel in "main.storyboard"
what I know so far:
I know how to pass data between views (from one xib file to another xib file)
I know how to navigate from view(xib file) to another storyboard

Override the -prepareForSegue:sender: method in your ViewController you want to pass the information from.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
DetailViewController *sc = [segue destinationViewController];
// Here you can set variables in detailViewController. For example if you had a NSString called name
sc.name = #"name_here";
// Or you could do
[sc setName:#"name_here"];
}

Swift 4.2+
Add this to the view controller from wherein you want to send the data -
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard
let destination = segue.destination as? viewControllerName
else {
return
}
destination.variable =data
}
Make sure the name of the variable is the same as the one in where you want to send the data to, also the viewControllerName should be exactly similar.
Then use the following to perform the action either on a button click or inside any completionHandler -
self.performSegue(withIdentifier: "viewControllerIdentifierName", sender: nil)
It is important to also make sure that the storyboard connecting the two should have the same identifier name as what you type inside the withIdentifier or the app will crash.

Related

Navigation controller between two UITableViewControllers in Swift

I am looking for a way to insert a navigation controller between two UITableViewControllers. I have a UITabBarController which then connects to a UITable which then connects to another UITable. I have attached a picture below to give a better reference at what I am talking about.
So from UITabBar, you go to the first table, from that, you make a selection and go to another table. The second table is where I want my back button. I know in the screenshot you can see the back button present but for some reason it does not show it at run time.
Moreover, when I try to embed Navigation Controller in the first table, I get an error in the code below.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let barViewControllers = segue.destination as! UITabBarController
let destinationViewController = barViewControllers.viewControllers?[0] as! MainVC
destinationViewController.TPNumber = txtTpNumber.text!
destinationViewController.intake = intake
let DVC = barViewControllers.viewControllers?[1] as! AttendanceTVC //This is where I get an error
DVC.TPNumber = txtTpNumber.text!
}
If anyone could guide me on how I can solve this? Thanks!
Go to your storyboard and click your Attendance (ViewController), than click your menu "Editor" -> "Embed in" -> UINavigationController.
You can firstly cast this destination as UINavigationController and then you can access the controller embed in UINavigationController by it's topViewController property

Data between view controllers not passing

I am creating an application in which there are 6 view controller in storyboard. The thing is that data is shared between the default view controller and the first one ( say A and B) which i added. i am using the prepareforseque method for passing data. the problem started when i added two more view controller. lets say C and D i created two new swift files and changed the two view controller class name. i created a textbox and button in C and label in D. when i pressed the button, the value of the text field is not passing into the D view controller although i used the same methods and code which i used for A and B. do i have to do anything else when i want to pass data between two newly added view controller.
first viewcontroller in which when a button is pressed value 1 needed to be passed:
class PlaySelectMenu: UIViewController {
var value = Int()
#IBAction func twotofive(sender: AnyObject) {
value = 1
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let nextView : PlayGameView = segue.destinationViewController as! PlayGameView
nextView.x = value
}
}
the second view controller which receive the value and print it
import Foundation
import UIKit
class PlayGameView: UIViewController{
var x = Int()
override func viewDidLoad() {
super.viewDidLoad()
print(x)
}
}
here i have added both the view controller from the object library and not working with the default one which is present in storyboard by default. i dont know why these two viewcontroller are not working. please help.
Regards Dev
One solution would be to write the data out to NSUserDefaults and then read it back from NSUserDefaults in the other view controller. Probably not the proper or correct way to share data between two view controllers, but it's been a reliable work around for me.
Other than that, you'd need to share your code so that we can see what's occurring.
Can you post also the code in your controllers C & D. And also if you have copy/paste the code inside your first two controllers into the two others, are you sure that in your prepareForSegue method you have changed the name of the destination segue ?
Assuming you have created the segue in Storyboard:
All you need is to do is put all of needed updates in prepareForSegue because twotofive is called after prepareForSegue.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
value = 1
let nextView : PlayGameView = segue.destinationViewController as! PlayGameView
nextView.x = value
}
Since you have connected your segue from button click to view controller, when you press button segue is automatically called. Instead of connecting segue from button to VC, connect VC to VC. Then in button click method at the last add below line:
#IBAction func twotofive(sender: AnyObject) {
value = 1
self.performSegueWithIdentifier("<Name of the segue identifier>", sender: self)
}
This will call your prepareForSegue. If you are calling more then one VC using segue from a VC then you can use segue.identifier to check which VC was called as below
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if segue.identifier == "CVC" {
}

Passing Core Data objects from UITableViewCell to another View Controller

Currently I have the application loading up the data in a tableview presented to the user that I want them to select the object they wish to view further details about. From their I want to be able to read out the data in the object to the user in various forms either labels or a tableview dependent on the data.
I have been reading around and trying to understand this stuff but it is all still new to me! I had another guy on here help me out with saving the data and passing the data around to store but reading it out is a seemingly different thing. Any examples in swift language would lead me down the right path I know that I need to use the didselectrow method and call a segue and also that I need a prepare for segue but I am not quite sure how it should look.
I have read multiple posts and some do actually attempt to pass objects but not in the manner in which I am trying to..Are you able to pass whole objects after they have been have been selected in a tableview to another view controller to present all data related to that object or are you only able to pass information from that object to the next viewcontroller? I have examples of prepareforsegue and perform segue before not sure what else I am missing but I am currently not able to pass any information between the tableview and the viewcontroller.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "decktoRecord") {
var detailVC: StatWidgetViewController = segue.destinationViewController as! StatWidgetViewController
detailVC.deckRecord = decktoPass
}
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let indexPath = tableView.indexPathForSelectedRow()
let selectedCell = tableView.cellForRowAtIndexPath(indexPath!) as UITableViewCell?
self.performSegueWithIdentifier("decktoRecord", sender: indexPath);
}
decktoPass is just a variable that contains the type of data entity that is the primary entity of the object.
A little confused by what you're asking but from what I understand, once the user clicks on a cell you want to segue to another view controller where they can edit the the details?
To add an exception breakpoint, open up your left panel with all your files/viewcontrollers, at the very top of it should be a small panel with a few icons, the first one is a folder, click on the second last one (the one that looks like a tag)
click on the plus in the bottom right and click add exception breakpoint, this should let you know where the problem in your code is occurring
Okay to edit the details in another View controller start by preparing the segue from the original view controller
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showTaskDetail" {
let detailVC: TaskDetailViewController = segue.destinationViewController as! TaskDetailViewController
let indexPath = self.tableView.indexPathForSelectedRow()
let thisTask = fetchedResultsController.objectAtIndexPath(indexPath!) as! TaskModel
detailVC.detailTaskModel = thisTask
detailVC.delegate = self
}
else if segue.identifier == "showTaskAdd" {
let addTaskVC:AddTaskViewController = segue.destinationViewController as! AddTaskViewController
addTaskVC.delegate = self
}
}
okay so like the code is showing above, I have a segue called showTaskDetail which shows the details of whatever it is, in my case its a simple task. You said you want to edit this information in another view controller when a user clicks on the row, well you need to be able to get this information in the other view controller.
So create a variable in the other viewcontroller that will hold these values, for me i called it detailTaskModel
var detailTaskModel: TaskModel!
Incase you're confused about what TaskModel is, I'm using CoreData to store my data, and TaskModel is a NSMangedObject class.
let detailVC: TaskDetailViewController = segue.destinationViewController as! TaskDetailViewController
this line you're just specifying what your other view controller is, replace TaskDetailViewController with your swift class.
let detailVC: TaskDetailViewController = segue.destinationViewController as! TaskDetailViewController
this line fetches the data from the row selected
Now you should be able to pass your information into the other view controller and edit it using the detailTaskModel, do you need help with that as well?

Passing Parse objectID created on prepareForSegue

I am trying to pass the Parse Class "Conversation" objectId after it's created to another View Controller. When I check the data does not pass to the variable I point to.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object(s) to the new view controller.
let selectedIndex = self.tableView.indexPathForSelectedRow()?.row
let destinationVC = segue.destinationViewController as ConversationViewController
//Save Conversation Data
var convo = PFObject(className: "Conversation")
convo.saveInBackgroundWithBlock{
(success: Bool!, error: NSError!) -> Void in
if (success != nil) {
//Save Selected Person
participant["conversationId"] = convo.objectId as String!
participant.saveInBackground()
}
else{
NSLog("%#", error)
}
}
//Trying to Pass convo.objectId
destinationVC.newConversationId = convo.objectId as String!
}
The problem with the code is that convo.objectId is not set at the point where you use it. It gets set in the completion block of saveInBackgroundWithBlock:, but that block runs after the code that appears underneath it.
So what to do? If the next vc needs the convo object to be saved before it runs, then the correct pattern is to run the segue after the save. Find the point in your code where you are initiating the segue and replace it with the convo.saveInBackgroundWithBlock. Then do the performSegue from within that block.
Edit - Here's how doing this looks in objective-C. In either language, in order to do this, you must initiate the segue from code. Say you have the segue painted from a table view cell in IB (or storyboard) to the next view controller. Delete that segue, and control-drag a new one starting from the view controller which contains the table. (Select the view controller in IB and drag from there. Then, using the attributes inspector, give that segue and identifier, say, "ConvoSegue").
// since a table selection that starts the action, implement the selection delegate method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// we decide here that the convo object must be saved, and
// a segue should happen to another vc that needs the convo object:
var convo = PFObject(className: "Conversation")
convo saveInBackgroundWithBlock:^(BOOL success, NSError *error) {
if (!error) {
// now that convo is saved, we can start the segue
[self performSegueWithIdentifier:#"ConvoSegue" sender:convo];
} else {
// don't segue, stay here and deal with the error
}
}];
}
Notice in the above, we passed convo as the sender of the segue. This allows access to it in prepareForSegue. You can skip that if convo is a property of this view controller.
Now prepare for segue looks like yours, except without the asynch save...
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
// we don't need the selected row from the table view, because we have
// the convo object as the sender
// let selectedIndex = self.tableView.indexPathForSelectedRow()?.row
let destinationVC = segue.destinationViewController as ConversationViewController
// sorry, back to objective-c here:
PFObject *convo = (PFObject)AnyObject; // need a cast to use properly in objective-c
// deleted your save logic. just pass convo's id
//Trying to Pass convo.objectId
destinationVC.newConversationId = convo.objectId as String!
}

How To Dismiss Popover From Destination View Controller in Swift

I have a main view controller that has been setup in Interface Builder to open a table view controller via popover segue connected to a button. I want to be able to dismiss the popover when an item inside of my popover table view is selected in didSelectRowAtIndexPath. In Objective-c I can just typecast the the segue in the prepareForSegue delegate to a UIStoryboardPopoverSegueand pass along its UIPopoverController to the table view controller. However, in Swift my downcast fails because it sees the segue as type UIStorybaordPopoverPresentationSegue (when stepping through with the debugger) which doesn't appear to be a public API.
Here's my code:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "ShowCollectionsSegue" {
if let collController:CollectionsTableViewController! = segue.destinationViewController as? CollectionsTableViewController {
if let popoverSegue = segue as? UIStoryboardPopoverSegue { // <-- This fails
collController.popover = popoverSegue.popoverController
}
}
}
}
How do I coerce the segue to a UIStoryboardPopoverSegue in order to access its popoverController property?
I'm open to solving the problem of dismissing the popover in response to a table view cell tap a different way, but it seems that when using a segue from the storyboard, the only way to dismiss the popover is by holding onto a reference to the popover controller somehow and the only way to do that as far as I can tell is to cast the segue to a popover segue which Swift doesn't want to let me do. Any ideas?
A strange problem, indeed. I noticed in the documentation, that UIStoryboardPopoverSegue does not inherit from any class. That explains why the cast does not work - UIStoryboardSegue is not its superclass. So I just tried to create a new object - it looks weird but works:
let popoverSegue = UIStoryboardPopoverSegue(
identifier: segue.identifier,
source: self,
destination: segue.destinationViewController as UIViewController)
println("Is there a controller? \(popoverSegue.popoverController.description)")
// YES !!
EDIT
But this controller will not dismiss the popover :(
The fix is to specify the segue in Interface Builder as "Deprecated Segues : Popover". Then the code would be as expected
let popoverSegue = segue as UIStoryboardPopoverSegue
if let destination = segue.destinationViewController as? TableViewController {
destination.delegate = self
self.popoverController = popoverSegue.popoverController
}