使用 Segue 在视图控制器之间传递数据 [英] Passing data between View Controllers using Segue

查看:14
本文介绍了使用 Segue 在视图控制器之间传递数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 iOS 新手.我面临在 ViewControllers 之间传递数据的问题.我有三个视图控制器(view_1、view_2 和 view_3).

这是我的设置:-

  • 选择视图_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=自己;).在控制台中显示:

<块引用>

视图控制器:-;<视图控制器:0x89e9540>

在view_1"但在view_3"中,在控制台中显示

<块引用>

视图控制器(空)

但是当我使用view_2"来传递这个数据时,它的工作.但是如何?我想知道这种行为,有没有办法解决这个问题?

请帮忙.

解决方案

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

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

destination 视图控制器不得了解视图控制器.这意味着,目标视图控制器不需要导入源视图控制器的标头.

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

参见:配置触发 Segue 时的目标控制器

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

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

现在,将数据"从 VC_A 传递到 VC_B 应该是直截了当的.您应该阅读一些使用 prepareForSegue:sender: 的示例.例如,destination 视图控制器可能有一个属性 data 表示它应该显示的事物.源视图控制器必须在 prepareForSegue:sender: 中设置此属性.

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

注意:每个视图控制器可以剪裁(分离、修改、准备、切片、变换等)它的 data来制作它一个合适的 data 用于 next 视图控制器.

<小时>

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

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

Document* document = [Document sharedDocument];

但是,获取模型实例的首选方法是在需要访问它的第一个视图控制器中,在本例中为:VC_A.

然后,VC_A 将一个 Document 实例传递给下一个视图控制器 VC_B.并且 VC_B 将文档对象传递给 VC_C.

您应该阅读官方文档iOS 视图控制器编程指南".

<小时>

示例 1

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

表视图控制器将有一个数据"属性 users:

UsersTableViewController.h中:

@interface UsersTableViewController : UIViewController@property (nonatomic, readonly) NSArray* 用户;@结尾

(严格来说,这个user属性是不需要公开的,比如table view内部自己获取用户列表,就不需要从外部访问.

users"数组是表格视图的数据,应按行显示.每行显示一个用户的摘要".

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

当用户在表格视图中点击某一行时,将显示详细视图控制器.在显示之前,表视图控制器必须配置详细视图控制器:表视图控制器将详细视图控制器的数据属性"分配给当前选定的用户.因此,细节视图控制器应该有一个 public 属性 user:

@interface UserViewController : UIViewController@property (nonatomic) User* user;@结尾

表视图控制器在prepareForSegue:sender::

中配置详细视图控制器

UsersTableViewController.m

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

<小时>

示例 2

第二个例子更复杂,它使用委托"作为在控制器之间建立通信的一种手段.

注意:

<块引用>

这不是一个完整的例子.本示例的目的是演示如何使用委托".如示例中所示,数据任务的全功能实现将需要付出更多的努力.在这种情况下,委托"将是实现此目的的最佳方法(恕我直言).

假设我们想要

  • 显示用户
  • 修改(编辑)用户
  • 新建一个用户,以及
  • 删除用户

从详细视图中.

这些数据任务"不应由细节视图控制器本身执行,而是由一个委托负责这些数据任务.

这些数据操作应由委托处理:

@protocol UserDataSourceDelegateProtocol - (User*) viewControllerUser:(UserViewControllerBase*)viewController;-(无效)viewController:(UserViewControllerBase*)viewControllerdismissWithUpdatedUser:(User*)user;- (void) viewController:(UserViewControllerBase*)viewControllerdismissWithDeletedUser:(User*)user;-(无效)viewController:(UserViewControllerBase*)viewControllerdismissWithCreatedUser:(User*)user;@结尾

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

同样,我们不希望细节视图控制器本身执行这些数据方法,而是由一个实现了UserDataSourceDelegateProtocol的实例来执行.细节视图控制器有这个委托的一个属性,它将这些数据任务"发送给委托.

可能有几个细节视图控制器,抽象类 UserViewControllerBase 的所有子类,它们处理 showeditcreate 任务.可以在表视图和显示用户"视图控制器中删除用户:

  • ShowUserViewController
  • EditUserViewController
  • NewUserViewController

例如 EditUserViewController 将在用户点击后退"按钮并且用户修改用户对象时发送 viewController:dismissWithUpdatedUser: .现在,委托可能会也可能不会允许关闭详细信息视图.例如,当存在验证错误时,它可能会禁止它.

UserDataSourceDelegateProtocol 协议可以在根视图控制器中实现,例如表视图控制器.但是,单独负责处理数据任务的类可能更合适.在下面的示例中,表视图控制器也将是这个数据处理程序.

UserDataSourceDelegateProtocol 可以在额外的头中定义.

UsersTableViewController.m中:

#import "UserDataSourceDelegateProtocol.h"#import "ShowUserViewController.h"@interface UsersTableViewController () @property (nonatomic, readonly) NSArray* 用户;@结尾//当详细视图控制器请求时将调用此委托//应显示的用户对象.- (User*) viewControllerUser:(UserViewControllerBase*)viewController {返回 [self.users objectInListAtIndex:[self.tableView indexPathForSelectedRow].row];}

这里,Table View Controller 配置了 Show User Detail View Controller:

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

编辑用户"视图控制器通常是显示用户"视图控制器的目标视图控制器,当用户点击编辑"按钮时会被查看.

显示用户"视图控制器将为编辑用户"视图控制器获得相同的委托:

ShowUserViewController.m

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{if ([segue.identifier isEqualToString:UserEditSegueID]){EditUserViewController* editViewController = segue.destinationViewController;editViewController.delegate = self.delegate;//通过数据源处理程序}}

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

UsersTableViewController.m中:

- (void) viewController:(UserViewControllerBase*)viewController解雇更新用户:(用户*)用户{if (/* 是有效用户,可以保存 */) {[viewController.presentingViewControllerdismissViewControllerAnimated:YES完成:无];}}

I am new to iOS. I am facing a problem passing the data between ViewControllers. I have three viewControllers (view_1 ,view_2 and view_3).

Here my setup:-

  • Select view_1
  • pushes view_2
  • pushes view_3

I want to send the ViewController reference(id) of 'view_1' to 'view_3'. so i include include "view_3" in 'view_1' and set the value into the variable of 'view_3'( using view_3 *v3=[[view_3 alloc] init ]; v3.reference=self;). In the console it shows:

view controller :- ; <ViewController: 0x89e9540>

in the 'view_1' but in the 'view_3', in console it shows

view controller (null)

But when i used 'view_2' for passing this data its work. But how? I want to know this behaviour, And Is there any solution to creating this?

please help.

解决方案

"Passing data to the destination Controller" when a segue is triggered will be achieved by overriding method prepareForSegue:sender:.

Generally, you pass data and NOT the source view controller, to the destination view controller. "data" may be a certain aspect of your application "model". It's an object like "User", or perhaps an array containing "User", etc.

The destination view controller shall not have any knowledge of the source view controller. That means, the destination view controller does not need to import the header of the source view controller.

On the other hand, the source view controller may have knowledge of the concrete class of the destination view controller or a base class of the destination view controller, and thus will import the header of the destination view controller.

See: Configuring the Destination Controller When a Segue is Triggered

If you need some sort of "communication protocol" between the source and the destination, you might employ Delegation to communicate with other view controllers. This involves the definition of a @protocol (e.g. having a method doneButton) and a property delegate which is defined in the destination view controller. The protocol should be defined in the header of the destination view controller, if it's specific to the destination view controller. Usually, you define tho protocol from the view point of the destination controller, and not from the requirements of the source controller.

The source view controller then creates a delegate (unless, it itself is it already) and sets the delegate of of the destination view controller. The destination view controller will send the delegate methods to the delegate, and the delegate handles it.

Now, passing "data" from a VC_A to VC_B should be straight forward. You should read a few examples which use prepareForSegue:sender:. For example, the destination view controller may have a property data which represents the thing it should display. The source view controller must set this property in prepareForSegue:sender:.

Passing data from VC_A over VC_B to VC_C should be straight forward as well.

Note: Each view controller may tailor (separate, modify, prepare, slice, transform, etc.) its data to make it a suitable data for the next view controller.


If VC_C needs data that is not available in its source view controller VC_B, then there are a couple of approaches to solve this. However, this is usually a sign of bad design.

You could have an application model, which is global. Suppose, your "application model" is an object of type Document. Suppose, at any time there is only one instance of that application model. Then, the model is a "Singleton", which could be accessed from anywhere in your app like so:

Document* document = [Document sharedDocument];

However, the preferred way to get an instance of a model is in the first view controller which requires access to it, in this case: VC_A.

Then, VC_A passes a Document instance to the next view controller VC_B. And VC_B passes the document object to VC_C.

You should read the official documentation "View Controller Programming Guide for iOS".


Example 1

Suppose, you have a list "Users". The list should be displayed in a table view controller, and there should also be a detail view, showing details of one User.

The table view controller will have a "data" property users:

In UsersTableViewController.h:

@interface UsersTableViewController : UIViewController
@property (nonatomic, readonly) NSArray* users;
@end

(Strictly, this user property doesn't need to be public. For example, if the table view internally obtains the list of users itself, there is not need to access it from outside.

The "users" array is the data of the table view which shall be displayed in rows. Each row shows a "summary" of a user.

More details of a user should be displayed in a detail view controller. The detail view controller's data is a single user of type User.

When the user tabs a certain row in the table view, the detail view controller will be displayed. Before it will be displayed, the table view controller must configure the detail view controller: the table view controller assigns the detail view controller's "data property" the current selected user. Thus, the detail view controller should have a public property user:

@interface UserViewController : UIViewController
@property (nonatomic) User* user;
@end

The table view controller configures the detail view controller in prepareForSegue:sender::

In UsersTableViewController.m

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


Example 2

The second example is more complex and uses "Delegation" as a means to establish a communication between controllers.

Caution:

This is not a complete example. The purpose of this example shall demonstrate how to use "Delegation". A full featured implementation of data tasks as shown in the example will require significant more effort. In this scenarios like these, "Delegation" will be the most preferred approach to accomplish this (IMHO).

Suppose we want to

  • Show a User
  • Modify (edit) a User
  • Create New a User, and
  • Delete a User

from within the detail view.

These "data tasks" shall not be performed by the detail view controller itself, instead a delegate is responsible for these data tasks.

These data actions shall be handled by the delegate:

@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

This protocol reflects the basic CRUD methods (Create, Read, Update, Delete).

Again, we don't want the Detail View Controller itself to perform these data methods, but instead this will be performed by an instance implementing the UserDataSourceDelegateProtocol. The detail view controller has a property of this delegate, and it sends these "data tasks" to the delegate.

There may be several detail view controllers, all subclasses of abstract class UserViewControllerBase which handle the show, edit and create tasks. Deletion of a user can be performed in the table view and in the "Show User" view controller:

  • ShowUserViewController
  • EditUserViewController
  • NewUserViewController

For example The EditUserViewController will send viewController:dismissWithUpdatedUser: when the user tabs the "back" button AND if the user has modified the user object. Now, the delegate may or may not allow to dismiss the detail view. It may disallow it when there are validation errors for example.

The UserDataSourceDelegateProtocol protocol may be implemented in the root view controller, the table view controller for example. However, a separate class whose sole responsibility is to handle data tasks may be more appropriate. In the sample below, the table view controller will also be this data handler.

The UserDataSourceDelegateProtocol may be defined in an extra header.

In UsersTableViewController.m:

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

Here, the Table View Controller configures the Show User Detail View Controller:

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

The "Edit User" view controller is usually a destination view controller of the "Show User" view controller, which gets viewed when the user tabs the "Edit" button.

The "Show User" view controller will setup the delegate for the "Edit User" view controller gets the same delegate:

In ShowUserViewController.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
    }
}

The data delegate may handle an updated user as follows:

In UsersTableViewController.m:

- (void) viewController:(UserViewControllerBase*)viewController
 dismissWithUpdatedUser:(User*)user {
    if (/* is valid user and can be saved */) {
        [viewController.presentingViewController dismissViewControllerAnimated:YES
                                                                     completion:nil];
    }
}

这篇关于使用 Segue 在视图控制器之间传递数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆