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!
}
Related
I have two view controllers (autoClaimViewController and reviewAutoViewController). In autoClaimViewController, I have a dictionary (reviewTableViewData) that is made up of a struct called claimData (it contains two string variables and a UIImage variable). When the user hits the "Review" button, I want the reviewTableViewData dictionary to be passed to the second view controller so that it's data can be displayed on a table view in the second view controller (reviewAutoViewController). How do I pass this dictionary to the other view controller?
Please make your answers understandable for a beginner - I'm still learning. I'm moving between view controllers using storyboard segues.
Thanks.
Additional Question: Will the images that I stored in the variables be passed when the dictionary is passed? In other words, do images work like Integers and Strings, where they can be passed between variables without an issue?
My code:
struct claimData {
var images = UIImage()
var imageTitle = String()
var relatedUnrelated = String()
}
class autoClaimViewController: UIViewController {
var reviewTableViewData = [claimData]()
#IBAction func reviewButton(_ sender: Any) {
//PASSES THE INFORMATION TO THE REVIEW VIEW CONTROLLER
//FIX THIS... IT NEEDS TO SEND THE reviewTableViewData ARRAY.
//MAKE A DICTIONARY IN THE REVIEW CONTROLLER THAT RECEIVES THE DICTIONARY FROM THIS VIEW CONTROLLER.
let vc2 = reviewAutoViewController()
//Find out how to transfer a dictionary from one view controller to another
}
I think your best bet is to set up a segue in your storyboards which takes you from 1st view to the second view. Here is a guide.
https://developer.apple.com/library/archive/featuredarticles/ViewControllerPGforiPhoneOS/UsingSegues.html
In both views have a place to store the information you want to pass. So create a var in the secon view to hold reviewTableViewData from the first view.
Then in the 1st view you call
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.destination is reviewAutoViewController
{
let vc = segue.destination as? TertiaryViewController
vc?.reviewTableViewData = reviewTableViewData
}
}
This get the data ready to be sent.
Then you perform the segue and the data should be passed for you. Perfomr the seugue with this func triggered by the button or whatever you use to tranisiton between views.
func performSegue(withIdentifier identifier: String,
sender: Any?){
enter code here
}
I asked that question before, and I got the solution to what he sought. Now, I need to amplify my question. Using delegates, how can I create a Delegate to the ViewController send data to the ContainerView and ContainerView send data to the ViewController?
Well, I don't know if this is entirely what you're looking for, but what I've always done in this situation is kept a record of each view controller inside the other's class.
For example if your container view has the embed segue with identifier "Embed Segue" then your classes might look like:
Superview Class
Class ViewControllerOne: UIViewController {
var data = "This is my data I may want to change"
var subView: ViewControllerTwo?
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "Embed Segue" {
let destinationVC = segue.destinationViewController as! ViewControllerTwo
destinationVC.superView = self
self.subView = destinationVC
}
}
}
Embedded Class
Class ViewControllerTwo: UIViewController {
var data = "This is the other view controller's copy of that data"
var superView: ViewControllerOne?
}
Then you can pass data between these View Controllers simply by referencing self.subView.data and self.superView.data respectively.
Edit: For ViewControllerTwo to pass data back to ViewControllerOne, it would then simply have to reference self.superView.data. e.g:
Class ViewControllerTwo: UIViewController {
var data = "This is the other view controller's copy of that data"
var superView: ViewControllerOne?
func passDataBack() {
self.superView.data = self.data
}
}
This would then update the data variable in the first 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?
I am getting "unexpectedly found nil while unwrapping an Optional value" because in my code below I am trying to assign value to webview before its initialize. I am trying to transition from Master to Detail view controller.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showDetail" {
if let indexPath = self.tableView.indexPathForSelectedRow() {
let object = self.fetchedResultsController.objectAtIndexPath(indexPath) as NSManagedObject
let controller = (segue.destinationViewController as UINavigationController).topViewController as DetailViewController
controller.detailItem = object
controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
Detail View Code:
var detailItem: AnyObject? {
didSet {
// Update the view.
self.configureView()
}
}
func configureView() {
// Update the user interface for the detail item.
if let detailContent = detailItem?.valueForKey("content") as? String{
self.webView.loadHTMLString(detailContent as String, baseURL:nil)
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.configureView()
}
It is failing because my Webview in Nil. How do I come around this situation where my outlets are not initialized while setting them.
Please help.
Thanks.
Stop and think about the order in which things happen:
prepareForSegue - The destination view controller exists, but that's all. It has no view and its outlets have not been set. You can set its non-outlet properties but that's all you can do.
The segue starts to happen.
The destination view controller gets viewDidLoad. Now it has a view and its outlets are set.
The segue completes and the destination view controller gets viewWillAppear: and later, viewDidAppear:. Now its view is actually in the interface.
So clearly you cannot permit configureView to assume that the web view exists, because the first time it is called, namely in prepareForSegue, it doesn't exist. configureView needs to test explicitly whether self.webView is nil, and if it is, it should do nothing:
func configureView() {
// Update the user interface for the detail item.
if self.webView == nil { return } // no web view, bail out
if let detailContent = detailItem?.valueForKey("content") as? String{
self.webView.loadHTMLString(detailContent as String, baseURL:nil)
}
}
After that, everything will be fine. viewDidLoad will subsequently be called, and configureView will be called again - and this time, both detailItem and the web view exist, so all will be well.
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.