使用Segue在View Controllers之间传递数据

时间:2013-11-16 09:51:35

标签: ios uiviewcontroller segue

我是iOS新手。我在ViewControllers之间传递数据时遇到问题。 我有三个viewControllers(view_1,view_2和view_3)。

我的设置: -

  • 选择view_1
  • 推送view_2
  • 推送view_3

我想将'view_1'的ViewController引用(id)发送到'view_3'。所以我在'view_1'中包含include "view_3",并将值设置为'view_3'变量(使用view_3 *v3=[[view_3 alloc] init ]; v3.reference=self;)。在控制台中显示:

  

查看控制器: - ; < ViewController:0x89e9540>

在“view_1”中,但在“view_3”中,在控制台中显示

  

查看控制器(null)

但是,当我使用'view_2'传递这些数据时,它的工作。但是怎么样?我想知道这种行为,是否有任何解决方案来创建它?

请帮忙。

6 个答案:

答案 0 :(得分:46)

"将数据传递到目标控制器"当触发segue时,将通过重写方法prepareForSegue:sender:来实现。

通常,您将数据而不是源视图控制器传递给目标视图控制器。 "数据"可能是您的应用程序的某个方面" model"。它是一个像" User"这样的对象,或者可能是一个包含" User"等的数组。

目标视图控制器 视图控制器 这意味着,目标视图控制器不需要导入源视图控制器的标头。

另一方面,视图控制器可能知道目标视图控制器的具体类或目标视图控制器的base 类,因此将导入目标视图控制器的标头。

请参阅:Configuring the Destination Controller When a Segue is Triggered

如果您需要某种"通信协议"在源和目标之间,您可以使用委派与其他视图控制器进行通信。这涉及定义@protocol(例如,具有方法doneButton)和属性delegate,其在目标视图控制器中定义。如果目标视图控制器的特定到目标视图控制器,则应在目标视图控制器的标头中定义该协议。通常,您从目标控制器的角度定义协议,而不是源控制器的要求。

源视图控制器然后创建一个委托(除非它本身就是它)并设置目标视图控制器的delegate。目标视图控制器将委托方法发送给委托,委托处理它。

现在,传递"数据"从VC_A到VC_B应该是直截了当的。您应该阅读一些使用prepareForSegue:sender:的示例。例如,目标视图控制器可能具有属性data,表示应显示的事物。源视图控制器必须在prepareForSegue:sender:

中设置此属性

通过VC_B将数据从VC_A传递到VC_C也应该是直截了当的。

注意:每个视图控制器都可以定制(分离,修改,准备,切片,转换等) data以使其成为合适的{ {1}}用于下一个视图控制器。


如果VC_C需要其源视图控制器VC_B中没有的数据,那么有几种方法可以解决这个问题。但是,这通常是糟糕设计的标志

可以拥有一个应用程序模型,它是全局。假设您的应用模型"是data类型的对象。假设在任何时候只有该应用程序模型的一个实例。然后,该模型是" Singleton",可以从您的应用中的任何位置访问,如下所示:

Document

但是,获取模型实例的首选方法是在第一个视图控制器中,该控制器需要访问它,在这种情况下:VC_A。

然后,VC_A将Document* document = [Document sharedDocument]; 实例传递给下一个视图控制器VC_B。 VC_B将文档对象传递给VC_C。

您应该阅读官方文档" View Controller Programming Guide for iOS"。


示例1

假设您有一个列表"用户"。该列表应显示在表视图控制器中,并且还应该有一个详细视图,显示一个用户的详细信息。

表格视图控制器将有一个"数据"财产Document

UsersTableViewController.h

users

(严格来说,这个@interface UsersTableViewController : UIViewController @property (nonatomic, readonly) NSArray* users; @end 属性并不需要公开。例如,如果表视图在内部获取用户自己的列表,则无需从外部访问它。

"用户" array是表视图的 data ,它应以行的形式显示。每一行都显示一个"摘要"用户。

用户的更多详细信息应显示在详细信息视图控制器中。详细视图控制器的数据是user类型的单个用户。

当用户在表格视图中选中某一行时,将显示详细视图控制器。在显示之前,表视图控制器必须配置详细视图控制器:表视图控制器分配详细视图控制器"数据属性"当前选定的用户。因此,详细视图控制器应具有 public 属性User

user

表视图控制器在@interface UserViewController : UIViewController @property (nonatomic) User* user; @end

中配置详细视图控制器

UsersTableViewController.m

prepareForSegue:sender:

示例2

第二个例子更复杂,使用"代表团"作为在控制器之间建立通信的手段。

警告:

  

这不是一个完整的例子。本示例的目的是演示如何使用" Delegation"。如示例中所示的全功能数据任务实现将需要更多的努力。在这样的场景中,"代表团"将是实现这一目标的最佳方法(恕我直言)。

假设我们想要

  • 显示用户
  • 修改(编辑)用户
  • 创建新用户,
  • 删除用户

从详细视图中。

这些"数据任务"不应由详细视图控制器本身执行,而委托负责这些数据任务。

这些数据操作应由代表处理:

- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([[segue identifier] isEqualToString:@"ShowUserDetails"]) {
        UserViewController* userViewController = [segue destinationViewController];
        userViewController.user = [self.users objectInListAtIndex:[self.tableView indexPathForSelectedRow].row];
    }
}

该协议反映了基本的CRUD方法(创建,读取,更新,删除)。

同样,我们不希望详细视图控制器本身执行这些数据方法,而是由实现@protocol UserDataSourceDelegateProtocol <NSObject> - (User*) viewControllerUser:(UserViewControllerBase*)viewController; - (void) viewController:(UserViewControllerBase*)viewController dismissWithUpdatedUser:(User*)user; - (void) viewController:(UserViewControllerBase*)viewController dismissWithDeletedUser:(User*)user; - (void) viewController:(UserViewControllerBase*)viewController dismissWithCreatedUser:(User*)user; @end 的实例执行此操作。详细视图控制器具有该委托的属性,并且它发送这些&#34;数据任务&#34;代表。

可能有几个详细视图控制器,抽象类UserDataSourceDelegateProtocol的所有子类,它们处理 show 编辑创建任务。可以在表格视图和&#34;显示用户&#34;中执行用户删除。查看控制器:

  • UserViewControllerBase
  • ShowUserViewController
  • EditUserViewController

例如,NewUserViewController会在用户选中&#34;返回&#34;时发送EditUserViewController。按钮AND如果用户已修改用户对象。现在,代表可能允许也可能不允许关闭详细视图。例如,当存在验证错误时,它可能会禁止它。

viewController:dismissWithUpdatedUser:协议可以在根视图控制器中实现,例如表视图控制器。但是,一个单独的类,其唯一的责任是处理数据任务可能更合适。在下面的示例中,表视图控制器也将是此数据处理程序。

UserDataSourceDelegateProtocol可以在额外的标题中定义。

UsersTableViewController.m

UserDataSourceDelegateProtocol

此处,Table View Controller配置Show User Detail View Controller:

#import "UserDataSourceDelegateProtocol.h"
#import "ShowUserViewController.h"


@interface UsersTableViewController () <UserDataSourceDelegateProtocol> 
@property (nonatomic, readonly) NSArray* users;
@end


// This delegate will be called when the detail view controller request 
// the user object which shall be displayed.
- (User*) viewControllerUser:(UserViewControllerBase*)viewController {
    return [self.users objectInListAtIndex:[self.tableView indexPathForSelectedRow].row];
}

&#34;编辑用户&#34;视图控制器通常是&#34;显示用户&#34;的目标视图控制器。视图控制器,当用户选中&#34;编辑&#34;按钮。

&#34;显示用户&#34;视图控制器将设置代理 &#34;编辑用户&#34;视图控制器获取相同的委托:

ShowUserViewController.m

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:UserShowSegueID])
    {
        ShowUserViewController* showViewController = segue.destinationViewController;
        showViewController.delegate = self; // Data Source Handler is self
    }
}

数据委托可以按如下方式处理更新的用户:

UsersTableViewController.m

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:UserEditSegueID])
    {
        EditUserViewController* editViewController = segue.destinationViewController;
        editViewController.delegate = self.delegate; // pass through the data source hanlder
    }
}

答案 1 :(得分:11)

YouTube上的

This Swift project帮助我终于明白了如何做到这一点。

我在类似的行上设置了一个简单的例子。在文本字段中写入一些文本,按下按钮,然后将文本放在下一个视图控制器的标签中。

设置故事板

enter image description here

这不是很困难。在Interface Builder中创建故事板布局。要制作segue,只需 control ,单击按钮并拖动到第二视图控制器。

第一视图控制器

第一视图控制器的代码是

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
    }

}

别忘了

  • 连接UITextFieldUILabel
  • 的出口
  • 将第一个和第二个View Controllers设置为IB中相应的Swift文件。

将数据传递给第三视图控制器的过程将是相同的。

答案 2 :(得分:2)

你有一个解决方案,但不是最恰当的方式。

ViewController3.h

-(void)getUser:(NSString *)strPassedUser;

转到你的ViewController3.m并在@interface上面添加一个像这样的变量

NSString *recievingVariable ;

然后在ViewController3.m内部的一些

-(void)getUser:(NSString *)strPassedUser
{
    recievingVariable = strPassedUser;
}

ViewController1并导入ViewController3 然后这样做..

ViewController3 * vc3 = [ViewController3 alloc]getUser :@"me"];

在这种情况下,您的函数getUser将被调用,您将获得receivingVariable= me

答案 3 :(得分:1)

更好,更轻松的解决方案。

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
UIViewController *vc = [storyboard instantiateViewControllerWithIdentifier:@"webView"];
webView = (WebViewController *)vc;
webView.strWebLink = @"http://www.Google.com/";
[self.navigationController showViewController:vc sender:self];

答案 4 :(得分:1)

使用单例模式:

软件工程中的Singleton模式是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点。

由于它具有唯一的实例,因此在整个应用程序/命名空间中共享类变量和方法。

示例:

class Singleton {
    static let sharedInstance = Singleton()
    var studentId = 1281
}

您可以在App中的任何位置使用它:

var studentId = Singleton.sharedInstance.studentId
print("Student Id: \(studentId)")

答案 5 :(得分:1)

在Swift 4.0中

 // In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    let vc = segue.destination as! SecondViewController
    vc.username = self.username
}

确保

segue.destination在这里真的不同。