如何通过prepareForSegue传递一个对象?

360

我在一个地图视图中有许多注释(带有rightCalloutAccessory按钮)。按钮将从这个mapview执行一个segue到tableview。根据点击的哪个callout按钮,我想要向tableview传递不同的对象(保存数据)。

例如:(完全虚构)

  • annotation1(奥斯汀) ->传递数据对象1(与奥斯汀相关)
  • annotation2(达拉斯) ->传递数据对象2(与达拉斯相关)
  • annotation3(休斯顿) ->传递数据对象3等等...(你明白了吧)

我能够检测到点击了哪个callout按钮。

我正在使用prepareForSegue:将数据对象传递给目标ViewController。由于我无法使此调用接受所需的数据对象的额外参数,因此有什么优雅的方法可以实现相同的效果(动态数据对象)?

任何提示都将不胜感激。


10个回答

673

只需要在prepareForSegue:方法中获取目标视图控制器的引用并将所需的任何对象传递给它即可。以下是一个示例...

- (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];
    }
}

修订:您还可以使用performSegueWithIdentifier:sender:方法,基于选定或按钮按下来激活转换到新视图。

例如,假设我有两个视图控制器。第一个包含三个按钮,第二个需要在转换之前知道哪个按钮被按下。您可以将这些按钮与代码中的IBAction相连接,该IBAction使用performSegueWithIdentifier:方法,像这样...

// 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];
    }
}

编辑:我最初附加的演示应用程序现在已经六年了,所以我已经将其删除以避免任何混淆。


1
谢谢,但我想动态设置 [vc setMyObjectHere:object];。例如,button1的obj1,button2的obj2。问题是我无法传递参数。有没有解决方法? - chizzle
2
我已经更新了我的帖子,并提供了一个可下载的示例,以便更好地说明我的问题。 - Simon
每次我们重写prepareForSegue时,为什么不包括[super prepareForSegue]呢? - pnizzle
没错.. 这是一个很好的例子,涉及到了那个让人抓狂的 "default .. super" 代码... http://stackoverflow.com/a/25474548/294884 希望能对某些人有所帮助。请投票支持,因为只有少数(精英)人会关注关于segue的问题! :-) - Fattie
注意! 在此阶段不要尝试访问UI组件,因为它们尚未被分配准备好。 - Mohammad Abdurraafay
显示剩余4条评论

82

有时避免两个视图控制器之间的编译时依赖关系会很有帮助。以下是无需关心目标视图控制器类型即可完成的方法:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.destinationViewController respondsToSelector:@selector(setMyData:)]) {
        [segue.destinationViewController performSelector:@selector(setMyData:) 
                                              withObject:myData];
    } 
}

只要你的目标视图控制器声明了一个公共属性,例如:

@property (nonatomic, strong) MyData *myData;

你可以按照我上面所描述的在前一个视图控制器中设置这个属性。


12
这实际上是一个观点问题。我还没有遇到过不想严格控制视图控制器的情况,尽管我很理解你所说的,而在适当的情况下这可能很重要。 - Simon
2
@Simon:是的,你只需要选择最适合你的方法。比如说,在我现在正在开发的应用中,我的方法非常有意义,因为我不断地添加需要相同数据对象的视图控制器。能够仅通过一个segue连接它们,并确保它们获取正确的数据,这非常方便。 - TotoroTotoro
10
这不是观点问题,事实上是错误的 :) “被接受的答案不是做这件事情的最佳方式”这个说法是错误的。它应该写成“在某些情况下,你需要这样做……” - Fattie
2
我更喜欢Simon的方法。因为它可以在编译时告诉我错误。例如,如果我在目标视图控制器中错过了myData的声明,我会立即知道。但对于你的情况,你的方法似乎也不错! - Abdurrahman Mubeen Ali
15
这种方法确实会创建依赖性,因为它要求目标视图控制器具有setMyData:方法。这是一种依赖关系。您使用选择器来避免编译错误的事实应该被视为您方法的弱点而不是优点。令我震惊的是,有多少开发者已经失去了在运行时错误之前应该优先考虑编译时错误的概念。 - Nate
显示剩余2条评论

23

在Swift 4.2中,我会这样做:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if let yourVC = segue.destination as? YourViewController {
        yourVC.yourData = self.someData
    }
}

你需要调用:super.prepare(for: segue, sender: sender)吗? - Andrew K

16

我有一个发送者类,像这样

@class MyEntry;

@interface MySenderEntry : NSObject
@property (strong, nonatomic) MyEntry *entry;
@end

@implementation MySenderEntry
@end

我在传递对象到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;
    }
}

我们是在使用prepareForSegue还是在实现它,假设prepareForSegue本身会被调用,因此我们不需要做类似于[self prepareForSegue]的操作。 - mfaani

16

当我尝试学习如何从一个视图控制器传递数据到另一个视图控制器时,我遇到了这个问题。然而我需要一些可视化的帮助来学习,因此这个答案是对已经存在的其他答案的补充。它比原来的问题更加通用,但可以适应工作。

这个基本的例子的工作方式如下:

enter image description here

其思路是将第一个视图控制器中的文本字段的字符串传递到第二个视图控制器中的标签。

第一个视图控制器

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!
    }

}

第二视图控制器

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
    }

}

记得要

  • 通过在按钮上 控制(Control) + 点击并将其拖动到第二个视图控制器来创建过渡。
  • UITextFieldUILabel 的插座连接起来。
  • 在IB中将第一个和第二个视图控制器设置为相应的Swift文件。

来源

如何通过segue传递数据(Swift) (YouTube教程)

另请参阅

视图控制器:向前传递数据和向后传递数据 (更详细的答案)


6

对于 Swift ,请使用以下方法:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    var segueID = segue.identifier

    if(segueID! == "yourSegueName"){

        var yourVC:YourViewController = segue.destinationViewController as YourViewController

        yourVC.objectOnYourVC = setObjectValueHere!

    }
}

4
我已经为UIViewController实现了一个库,其中包含一个类别,可以简化此操作。基本上,您将要传递的参数设置为NSDictionary,并将其与执行segue的UI项目相关联。它也适用于手动segue。
例如,您可以这样做:
[self performSegueWithIdentifier:@"yourIdentifier" parameters:@{@"customParam1":customValue1, @"customValue2":customValue2}];

对于手动segue,或者创建带有segue的按钮并使用

[button setSegueParameters:@{@"customParam1":customValue1, @"customValue2":customValue2}];

如果目标视图控制器不符合键值编码的要求,则不会发生任何事情。它也可以使用键值(对于解开segue很有用)。 在这里查看 https://github.com/stefanomondino/SMQuickSegue


2

我的解决方案与此类似。

// 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
    }
}

1

只需使用此函数。

 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

}

0
我使用了这个解决方案,以便在同一个函数内保持segue的调用和数据通信:
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)
}

一个使用案例可能是这样的:
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
        }
    }
}

这对我来说似乎是有效的,但我不能百分之百确定perform和prepare函数总是在同一个线程上执行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接