I'm fairly new in using Swift Programming and need to pass some data to the Container view.
I have variables passed in to Eyewear Detail View Controller from another VC that I would like to then passed to the Container which is connected to a table.
In this, I have a dictionary of the information I like to put in the table.
I've been scratching my head to find an answer but to no luck on Google Search. Perhaps you guys can help me. Thanks!
You can use - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender to pass the data.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.destinationViewController isKindOfClass:[YourContainedViewController class]]) {
// set data here
}
}
Set segue identifier in storyboard, first of all. You could find the field where can set identifier after click segue link.
View Controller which will call next ViewController
func showSomeViewController() {
self.performSegueWithIdentifier("TestContainer", sender: self);
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "TestContainer")
{
let vc: TestViewController = segue.destinationViewController as! TestViewController
vc.tmpString = "say ho"
}
}
and TestViewController
class TestViewController: UIViewController {
var tmpString: String!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
Related
I have many annotations in a mapview (with rightCalloutAccessory buttons). The button will perform a segue from this mapview to a tableview. I want to pass the tableview a different object (that holds data) depending on which callout button was clicked.
For example: (totally made up)
annotation1 (Austin) -> pass data obj 1 (relevant to Austin)
annotation2 (Dallas) -> pass data obj 2 (relevant to Dallas)
annotation3 (Houston) -> pass data obj 3 and so on... (you get the
idea)
I am able to detect which callout button was clicked.
I'm using prepareForSegue: to pass the data obj to the destination ViewController. Since I cannot make this call take an extra argument for the data obj I require, what are some elegant ways to achieve the same effect (dynamic data obj)?
Any tip would be appreciated.
Simply grab a reference to the target view controller in prepareForSegue: method and pass any objects you need to there. Here's an example...
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Make sure your segue name in storyboard is the same as this line
if ([[segue identifier] isEqualToString:#"YOUR_SEGUE_NAME_HERE"])
{
// Get reference to the destination view controller
YourViewController *vc = [segue destinationViewController];
// Pass any objects to the view controller here, like...
[vc setMyObjectHere:object];
}
}
REVISION: You can also use performSegueWithIdentifier:sender: method to activate the transition to a new view based on a selection or button press.
For instance, consider I had two view controllers. The first contains three buttons and the second needs to know which of those buttons has been pressed before the transition. You could wire the buttons up to an IBAction in your code which uses performSegueWithIdentifier: method, like this...
// When any of my buttons are pressed, push the next view
- (IBAction)buttonPressed:(id)sender
{
[self performSegueWithIdentifier:#"MySegue" sender:sender];
}
// This will get called too before the view appears
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"MySegue"]) {
// Get destination view
SecondView *vc = [segue destinationViewController];
// Get button tag number (or do whatever you need to do here, based on your object
NSInteger tagIndex = [(UIButton *)sender tag];
// Pass the information to your destination view
[vc setSelectedButton:tagIndex];
}
}
EDIT: The demo application I originally attached is now six years old, so I've removed it to avoid any confusion.
Sometimes it is helpful to avoid creating a compile-time dependency between two view controllers. Here's how you can do it without caring about the type of the destination view controller:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.destinationViewController respondsToSelector:#selector(setMyData:)]) {
[segue.destinationViewController performSelector:#selector(setMyData:)
withObject:myData];
}
}
So as long as your destination view controller declares a public property, e.g.:
#property (nonatomic, strong) MyData *myData;
you can set this property in the previous view controller as I described above.
In Swift 4.2 I would do something like that:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let yourVC = segue.destination as? YourViewController {
yourVC.yourData = self.someData
}
}
I have a sender class, like this
#class MyEntry;
#interface MySenderEntry : NSObject
#property (strong, nonatomic) MyEntry *entry;
#end
#implementation MySenderEntry
#end
I use this sender class for passing objects to prepareForSeque:sender:
-(void)didSelectItemAtIndexPath:(NSIndexPath*)indexPath
{
MySenderEntry *sender = [MySenderEntry new];
sender.entry = [_entries objectAtIndex:indexPath.row];
[self performSegueWithIdentifier:SEGUE_IDENTIFIER_SHOW_ENTRY sender:sender];
}
-(void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:SEGUE_IDENTIFIER_SHOW_ENTRY]) {
NSAssert([sender isKindOfClass:[MySenderEntry class]], #"MySenderEntry");
MySenderEntry *senderEntry = (MySenderEntry*)sender;
MyEntry *entry = senderEntry.entry;
NSParameterAssert(entry);
[segue destinationViewController].delegate = self;
[segue destinationViewController].entry = entry;
return;
}
if ([[segue identifier] isEqualToString:SEGUE_IDENTIFIER_HISTORY]) {
// ...
return;
}
if ([[segue identifier] isEqualToString:SEGUE_IDENTIFIER_FAVORITE]) {
// ...
return;
}
}
I came across this question when I was trying to learn how to pass data from one View Controller to another. I need something visual to help me learn though, so this answer is a supplement to the others already here. It is a little more general than the original question but it can be adapted to work.
This basic example works like this:
The idea is to pass a string from the text field in the First View Controller to the label in the Second View Controller.
First View Controller
import UIKit
class FirstViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
// This function is called before the segue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// get a reference to the second view controller
let secondViewController = segue.destinationViewController as! SecondViewController
// set a variable in the second view controller with the String to pass
secondViewController.receivedString = textField.text!
}
}
Second View Controller
import UIKit
class SecondViewController: UIViewController {
#IBOutlet weak var label: UILabel!
// This variable will hold the data being passed from the First View Controller
var receivedString = ""
override func viewDidLoad() {
super.viewDidLoad()
// Used the text from the First View Controller to set the label
label.text = receivedString
}
}
Remember to
Make the segue by control clicking on the button and draging it over to the Second View Controller.
Hook up the outlets for the UITextField and the UILabel.
Set the first and second View Controllers to the appropriate Swift files in IB.
Source
How to send data through segue (swift) (YouTube tutorial)
See also
View Controllers: Passing data forward and passing data back (fuller answer)
For Swift use this,
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var segueID = segue.identifier
if(segueID! == "yourSegueName"){
var yourVC:YourViewController = segue.destinationViewController as YourViewController
yourVC.objectOnYourVC = setObjectValueHere!
}
}
I've implemented a library with a category on UIViewController that simplifies this operation.
Basically, you set the parameters you want to pass over in a NSDictionary associated to the UI item that is performing the segue. It works with manual segues too.
For example, you can do
[self performSegueWithIdentifier:#"yourIdentifier" parameters:#{#"customParam1":customValue1, #"customValue2":customValue2}];
for a manual segue or create a button with a segue and use
[button setSegueParameters:#{#"customParam1":customValue1, #"customValue2":customValue2}];
If destination view controller is not key-value coding compliant for a key, nothing happens. It works with key-values too (useful for unwind segues).
Check it out here
https://github.com/stefanomondino/SMQuickSegue
My solution is similar.
// In destination class:
var AddressString:String = String()
// In segue:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "seguetobiddetailpagefromleadbidder")
{
let secondViewController = segue.destinationViewController as! BidDetailPage
secondViewController.AddressString = pr.address as String
}
}
Just use this function.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let index = CategorytableView.indexPathForSelectedRow
let indexNumber = index?.row
let VC = segue.destination as! DestinationViewController
VC.value = self.data
}
I used this solution so that I could keep the invocation of the segue and the data communication within the same function:
private var segueCompletion : ((UIStoryboardSegue, Any?) -> Void)?
func performSegue(withIdentifier identifier: String, sender: Any?, completion: #escaping (UIStoryboardSegue, Any?) -> Void) {
self.segueCompletion = completion;
self.performSegue(withIdentifier: identifier, sender: sender);
self.segueCompletion = nil
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
self.segueCompletion?(segue, sender)
}
A use case would be something like:
func showData(id : Int){
someService.loadSomeData(id: id) {
data in
self.performSegue(withIdentifier: "showData", sender: self) {
storyboard, sender in
let dataView = storyboard.destination as! DataView
dataView.data = data
}
}
}
This seems to work for me, however, I'm not 100% sure that the perform and prepare functions are always executed on the same thread.
From this answer there seems to be a way to pass data back to a prior View Controller using a Swift closure that is way simpler than using delegates.
Given that the Swift closure is similar to using blocks in Objective-C, how can I do this in my Objective-C project? I don't know Swift and delegate approach seems overly complicated to set one property.
Here is the Swift code:
For sending a UIImage back in my case. In ViewControllerA:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let cropImageViewController = segue.destinationViewController as! CropImageViewController
cropImageViewController.choseImage = { image in
//Do what you want in here!
}
}
In ViewControllerB:
var choseImage : (UIImage -> Void)?
#IBAction func done(sender: AnyObject) {
choseImage?(croppedImage)
self.dismissViewControllerAnimated(true, completion: nil)
}
I can pass data forward using Prepare for Segue in Objective-C but am stumped on how to pass it backwards.
//launch VC
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"edit"]) {
...
destViewController.contact=contact;
}
//set property in presentingVC--THIS DOES NOT WORK.
self.presentingViewController.afterSave = YES;
[self dismissViewControllerAnimated:YES completion:nil];
Here is what you need to do in you crop view controller:
interface (.h):
#interface CropImageViewController : UIViewController
/// Block that returns void , called 'choseImage' and passes back UIImage.
#property (nonatomic,strong) void(^choseImage)(UIImage *image);
#end
implementation (.m):
#implementation CropImageViewController
-(void)done{
// Check if block not nil
if(self.choseImage){
// Block usage with UIImage
self.choseImage(croppedImage);
}
}
#end
and in prepare for segue:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"edit"]) {
CropImageViewController *cropVC = [segue destinationViewController];
[cropVC setChoseImage:^(UIImage *image) {
// Use image
}];
}
}
Is there a way to do a validation before unwind a ViewController?
i have my ViewController1 and there is a unwind function
#IBAction func unwind(segue: UIStoryboardSegue) {
// make stuff with data
}
And a button from ViewController2 is conected with that unwind function, and i pass data from my ViewController2 to ViewController1 in override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
all works fine, but i want to do some validations.
if sender as? UIBarButtonItem == self.saveButton {
if let content = self.textFieldContent.text {
publish.content = content
publish.image = self.image
} else {
self.alertMessage("Content is empty")
}
}
The self.alertMessage() function appears but the ViewController2 is dismissed every time. And i dont want this to happens, how can i achieve that?
Use - (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender method instead if you need prevent unwinding on some conditions.
in my app I've a button that performs a segue programmatically:
- (void)myButtonMethod
{
//execute segue programmatically
[self performSegueWithIdentifier: #"MySegue" sender: self];
}
I would like to know if there is a way to reference the destination view and to pass it some parameters.
I know that in prepareForSegue method, I can refer to it with:myDestinationViewController *vc = [segue destinationViewController];, but I don't know how to this executing the segue programmatically.
Do you have any ideas?
Thanks, yassa
UPDATE:
I'm sorry for this question!!! I simply discovered that, even if the segue is invoked programmatically, the prepareForSegue method is called anyway and so it is possible to pass parameters in the same usual way.
The answer is simply that it makes no difference how the segue is triggered.
The prepareForSegue:sender: method is called in any case and this is where you pass your parameters across.
Old question but here's the code on how to do what you are asking. In this case I am passing data from a selected cell in a table view to another view controller.
in the .h file of the trget view:
#property(weak, nonatomic) NSObject* dataModel;
in the .m file:
#synthesize dataModel;
dataModel can be string, int, or like in this case it's a model that contains many items
- (void)someMethod {
[self performSegueWithIdentifier:#"loginMainSegue" sender:self];
}
OR...
- (void)someMethod {
UIViewController *myController = [self.storyboard instantiateViewControllerWithIdentifier:#"HomeController"];
[self.navigationController pushViewController: myController animated:YES];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([segue.identifier isEqualToString:#"storyDetailsSegway"]) {
UITableViewCell *cell = (UITableViewCell *) sender;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
NSDictionary *storiesDict =[topStories objectAtIndex:[indexPath row]];
StoryModel *storyModel = [[StoryModel alloc] init];
storyModel = storiesDict;
StoryDetails *controller = (StoryDetails *)segue.destinationViewController;
controller.dataModel= storyModel;
}
}
Swift 4:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ExampleSegueIdentifier" {
if let destinationVC = segue.destination as? ExampleSegueVC {
destinationVC.exampleString = "Example"
}
}
}
Swift 3:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "ExampleSegueIdentifier" {
if let destinationVC = segue.destinationViewController as? ExampleSegueVC {
destinationVC.exampleString = "Example"
}
}
}
I understand the problem of performing the segue at one place and maintaining the state to send parameters in prepare for segue.
I figured out a way to do this. I've added a property called userInfoDict to ViewControllers using a category. and I've override perform segue with identifier too, in such a way that If the sender is self(means the controller itself). It will pass this userInfoDict to the next ViewController.
Here instead of passing the whole UserInfoDict you can also pass the specific params, as sender and override accordingly.
1 thing you need to keep in mind. don't forget to call super method in ur performSegue method.
In case if you use new swift version.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ChannelMoreSegue" {
}
}
I have many annotations in a mapview (with rightCalloutAccessory buttons). The button will perform a segue from this mapview to a tableview. I want to pass the tableview a different object (that holds data) depending on which callout button was clicked.
For example: (totally made up)
annotation1 (Austin) -> pass data obj 1 (relevant to Austin)
annotation2 (Dallas) -> pass data obj 2 (relevant to Dallas)
annotation3 (Houston) -> pass data obj 3 and so on... (you get the
idea)
I am able to detect which callout button was clicked.
I'm using prepareForSegue: to pass the data obj to the destination ViewController. Since I cannot make this call take an extra argument for the data obj I require, what are some elegant ways to achieve the same effect (dynamic data obj)?
Any tip would be appreciated.
Simply grab a reference to the target view controller in prepareForSegue: method and pass any objects you need to there. Here's an example...
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// Make sure your segue name in storyboard is the same as this line
if ([[segue identifier] isEqualToString:#"YOUR_SEGUE_NAME_HERE"])
{
// Get reference to the destination view controller
YourViewController *vc = [segue destinationViewController];
// Pass any objects to the view controller here, like...
[vc setMyObjectHere:object];
}
}
REVISION: You can also use performSegueWithIdentifier:sender: method to activate the transition to a new view based on a selection or button press.
For instance, consider I had two view controllers. The first contains three buttons and the second needs to know which of those buttons has been pressed before the transition. You could wire the buttons up to an IBAction in your code which uses performSegueWithIdentifier: method, like this...
// When any of my buttons are pressed, push the next view
- (IBAction)buttonPressed:(id)sender
{
[self performSegueWithIdentifier:#"MySegue" sender:sender];
}
// This will get called too before the view appears
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"MySegue"]) {
// Get destination view
SecondView *vc = [segue destinationViewController];
// Get button tag number (or do whatever you need to do here, based on your object
NSInteger tagIndex = [(UIButton *)sender tag];
// Pass the information to your destination view
[vc setSelectedButton:tagIndex];
}
}
EDIT: The demo application I originally attached is now six years old, so I've removed it to avoid any confusion.
Sometimes it is helpful to avoid creating a compile-time dependency between two view controllers. Here's how you can do it without caring about the type of the destination view controller:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.destinationViewController respondsToSelector:#selector(setMyData:)]) {
[segue.destinationViewController performSelector:#selector(setMyData:)
withObject:myData];
}
}
So as long as your destination view controller declares a public property, e.g.:
#property (nonatomic, strong) MyData *myData;
you can set this property in the previous view controller as I described above.
In Swift 4.2 I would do something like that:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let yourVC = segue.destination as? YourViewController {
yourVC.yourData = self.someData
}
}
I have a sender class, like this
#class MyEntry;
#interface MySenderEntry : NSObject
#property (strong, nonatomic) MyEntry *entry;
#end
#implementation MySenderEntry
#end
I use this sender class for passing objects to prepareForSeque:sender:
-(void)didSelectItemAtIndexPath:(NSIndexPath*)indexPath
{
MySenderEntry *sender = [MySenderEntry new];
sender.entry = [_entries objectAtIndex:indexPath.row];
[self performSegueWithIdentifier:SEGUE_IDENTIFIER_SHOW_ENTRY sender:sender];
}
-(void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:SEGUE_IDENTIFIER_SHOW_ENTRY]) {
NSAssert([sender isKindOfClass:[MySenderEntry class]], #"MySenderEntry");
MySenderEntry *senderEntry = (MySenderEntry*)sender;
MyEntry *entry = senderEntry.entry;
NSParameterAssert(entry);
[segue destinationViewController].delegate = self;
[segue destinationViewController].entry = entry;
return;
}
if ([[segue identifier] isEqualToString:SEGUE_IDENTIFIER_HISTORY]) {
// ...
return;
}
if ([[segue identifier] isEqualToString:SEGUE_IDENTIFIER_FAVORITE]) {
// ...
return;
}
}
I came across this question when I was trying to learn how to pass data from one View Controller to another. I need something visual to help me learn though, so this answer is a supplement to the others already here. It is a little more general than the original question but it can be adapted to work.
This basic example works like this:
The idea is to pass a string from the text field in the First View Controller to the label in the Second View Controller.
First View Controller
import UIKit
class FirstViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
// This function is called before the segue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// get a reference to the second view controller
let secondViewController = segue.destinationViewController as! SecondViewController
// set a variable in the second view controller with the String to pass
secondViewController.receivedString = textField.text!
}
}
Second View Controller
import UIKit
class SecondViewController: UIViewController {
#IBOutlet weak var label: UILabel!
// This variable will hold the data being passed from the First View Controller
var receivedString = ""
override func viewDidLoad() {
super.viewDidLoad()
// Used the text from the First View Controller to set the label
label.text = receivedString
}
}
Remember to
Make the segue by control clicking on the button and draging it over to the Second View Controller.
Hook up the outlets for the UITextField and the UILabel.
Set the first and second View Controllers to the appropriate Swift files in IB.
Source
How to send data through segue (swift) (YouTube tutorial)
See also
View Controllers: Passing data forward and passing data back (fuller answer)
For Swift use this,
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var segueID = segue.identifier
if(segueID! == "yourSegueName"){
var yourVC:YourViewController = segue.destinationViewController as YourViewController
yourVC.objectOnYourVC = setObjectValueHere!
}
}
I've implemented a library with a category on UIViewController that simplifies this operation.
Basically, you set the parameters you want to pass over in a NSDictionary associated to the UI item that is performing the segue. It works with manual segues too.
For example, you can do
[self performSegueWithIdentifier:#"yourIdentifier" parameters:#{#"customParam1":customValue1, #"customValue2":customValue2}];
for a manual segue or create a button with a segue and use
[button setSegueParameters:#{#"customParam1":customValue1, #"customValue2":customValue2}];
If destination view controller is not key-value coding compliant for a key, nothing happens. It works with key-values too (useful for unwind segues).
Check it out here
https://github.com/stefanomondino/SMQuickSegue
My solution is similar.
// In destination class:
var AddressString:String = String()
// In segue:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "seguetobiddetailpagefromleadbidder")
{
let secondViewController = segue.destinationViewController as! BidDetailPage
secondViewController.AddressString = pr.address as String
}
}
Just use this function.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let index = CategorytableView.indexPathForSelectedRow
let indexNumber = index?.row
let VC = segue.destination as! DestinationViewController
VC.value = self.data
}
I used this solution so that I could keep the invocation of the segue and the data communication within the same function:
private var segueCompletion : ((UIStoryboardSegue, Any?) -> Void)?
func performSegue(withIdentifier identifier: String, sender: Any?, completion: #escaping (UIStoryboardSegue, Any?) -> Void) {
self.segueCompletion = completion;
self.performSegue(withIdentifier: identifier, sender: sender);
self.segueCompletion = nil
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
self.segueCompletion?(segue, sender)
}
A use case would be something like:
func showData(id : Int){
someService.loadSomeData(id: id) {
data in
self.performSegue(withIdentifier: "showData", sender: self) {
storyboard, sender in
let dataView = storyboard.destination as! DataView
dataView.data = data
}
}
}
This seems to work for me, however, I'm not 100% sure that the perform and prepare functions are always executed on the same thread.