在视图控制器之间传递数据

发布于 2024-10-20 16:23:37 字数 567 浏览 6 评论 0 原文

我是 iOS 和 Objective-C 以及整个 的新手MVC 范例,我坚持以下几点:

我有一个充当数据输入表单的视图,我想为用户提供选择多个产品的选项。产品使用 UITableViewController 列在另一个视图上,并且我启用了多个选择。

如何将数据从一个视图传输到另一个视图?我将把 UITableView 上的选择保存在一个数组中,但是如何将其传递回之前的数据输入表单视图,以便在提交时将其与其他数据一起保存到 Core Data形式?

我四处浏览,看到有些人在应用程序委托中声明了一个数组。我读了一些关于 singletons 的内容,但我不明白这些是什么,我读了一些关于创建的内容数据模型。

执行此操作的正确方法是什么?我将如何进行?

I'm new to iOS and Objective-C and the whole MVC paradigm and I'm stuck with the following:

I have a view that acts as a data entry form and I want to give the user the option to select multiple products. The products are listed on another view with a UITableViewController and I have enabled multiple selections.

How do I transfer the data from one view to another? I will be holding the selections on the UITableView in an array, but how do I then pass that back to the previous data entry form view so it can be saved along with the other data to Core Data on submission of the form?

I have surfed around and seen some people declare an array in the app delegate. I read something about singletons, but I don't understand what these are and I read something about creating a data model.

What would be the correct way of performing this and how would I go about it?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

樱花落人离去 2024-10-27 16:23:38

NewsViewController

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
  [tbl_View deselectRowAtIndexPath:indexPath animated:YES];
  News *newsObj = [newstitleArr objectAtIndex:indexPath.row];
  NewsDetailViewController *newsDetailView = [[NewsDetailViewController alloc] initWithNibName:@"NewsDetailViewController" bundle:nil];

  newsDetailView.newsHeadlineStr = newsObj.newsHeadline;

  [self.navigationController pushViewController:newsDetailView animated:YES];
}

NewsDetailViewController.h

@interface NewsDetailViewController : UIViewController
@property(nonatomic,retain) NSString *newsHeadlineStr;
@end

NewsDetailViewController.m

@synthesize newsHeadlineStr;

NewsViewController

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
  [tbl_View deselectRowAtIndexPath:indexPath animated:YES];
  News *newsObj = [newstitleArr objectAtIndex:indexPath.row];
  NewsDetailViewController *newsDetailView = [[NewsDetailViewController alloc] initWithNibName:@"NewsDetailViewController" bundle:nil];

  newsDetailView.newsHeadlineStr = newsObj.newsHeadline;

  [self.navigationController pushViewController:newsDetailView animated:YES];
}

NewsDetailViewController.h

@interface NewsDetailViewController : UIViewController
@property(nonatomic,retain) NSString *newsHeadlineStr;
@end

NewsDetailViewController.m

@synthesize newsHeadlineStr;
等风来 2024-10-27 16:23:38

当您使用 .xib 文件时,委派是执行此类操作的唯一一种解决方案。但是,之前的所有答案都是针对 .xibs 文件的 storyboard。您需要使用委托。这是您可以使用的唯一解决方案。

另一个解决方案是使用单例类模式。初始化一次并在整个应用程序中使用它。

Delegation is the only one solution to perform such operations when you are using .xib files. However, all previous answers are for storyboard for .xibs files. You need to use delegation. That's the only solution you can use.

Another solution is use the singleton class pattern. Initialize it once and use it in your entire app.

别挽留 2024-10-27 16:23:38

对于 SwiftUI

@EnvironmentObject 视为在大量视图上使用 @ObservedObject 的更智能、更简单的方法。您可以在视图 A 中创建一些数据,然后将其传递给视图 B,然后传递给视图 C,然后传递给视图 D,然后最后使用它,而不是在视图 A 中创建一些数据,然后将其传递给视图 B、视图 C、视图 D,然后再最终使用它,您可以在视图中创建数据并将其放入环境中,以便视图 B、C 和 D自动有权访问它。

注意:环境对象必须由祖先视图提供 - 如果 SwiftUI 找不到正确类型的环境对象,您将会崩溃。这也适用于预览,所以要小心。

例如,这是一个存储用户设置的可观察对象:

class UserSettings: ObservableObject {
     @Published var score = 0
}

For SwiftUI

Think of @EnvironmentObject as a smarter, simpler way of using @ObservedObject on lots of views. Rather than creating some data in view A, then passing it to view B, then view C, then view D before finally using it, you can create it in view and put it into the environment so that views B, C, and D will automatically have access to it.

Note: Environment objects must be supplied by an ancestor view – if SwiftUI can’t find an environment object of the correct type you’ll get a crash. This applies for previews too, so be careful.

As an example, here’s an observable object that stores user settings:

class UserSettings: ObservableObject {
     @Published var score = 0
}
与之呼应 2024-10-27 16:23:38

有 3 种类型可以将数据从一个 ViewController 传递到另一个 ViewController
视图控制器。

  1. 以编程方式
  2. 分隔
  3. UserDefaults

此处演示项目链接 - https:// github.com/kamanijasmin13/Swift-Pass-data- Between-viewcontrollers

以编程方式
输入图像描述这里

继续
输入图片描述此处

用户默认值
输入图像描述这里

演示项目链接在这里 - https://github.com/kamanijasmin13/Swift-Pass-data- Between-viewcontrollers

There are 3 types for passing data one ViewController to another
ViewController.

  1. Programatically
  2. Segue
  3. UserDefaults

Demo Project Link Here - https://github.com/kamanijasmin13/Swift-Pass-data-between-viewcontrollers

Programatically
enter image description here

Segue
enter image description here

UserDefaults
enter image description here

Demo Project Link Here - https://github.com/kamanijasmin13/Swift-Pass-data-between-viewcontrollers

日裸衫吸 2024-10-27 16:23:38

我喜欢基于 NSProxy 的模型对象和模拟对象的想法,以便在用户选择可以取消的情况下提交或丢弃数据。

传递数据很容易,因为它是单个对象或几个对象,如果您有一个 UINavigationController 控制器,您可以将对模型的引用保留在内部,并且所有推送的视图控制器都可以直接从导航控制器访问它。

I like the idea of model objects and mock objects based on NSProxy to commit or discard data if what the user selects can be cancelled.

It's easy to pass data around since it's a single object or couple of objects and if you have, let's say, a UINavigationController controller, you can keep the reference to the model inside and all pushed view controllers can access it directly from the navigation controller.

稳稳的幸福 2024-10-27 16:23:38

我见过很多人使用 didSelectRowAtPath 方法使这个过程变得复杂。我在示例中使用 核心数据

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{

    // This solution is for using Core Data
    YourCDEntityName * value = (YourCDEntityName *)[[self fetchedResultsController] objectAtIndexPath: indexPath];

    YourSecondViewController * details = [self.storyboard instantiateViewControllerWithIdentifier:@"nameOfYourSecondVC"]; // Make sure in storyboards you give your second VC an identifier

    // Make sure you declare your value in the second view controller
    details.selectedValue = value;

    // Now that you have said to pass value all you need to do is change views
    [self.navigationController pushViewController: details animated:YES];

}

方法内四行代码就完成了。

I have seen a lot of people over complicating this using the didSelectRowAtPath method. I am using Core Data in my example.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{

    // This solution is for using Core Data
    YourCDEntityName * value = (YourCDEntityName *)[[self fetchedResultsController] objectAtIndexPath: indexPath];

    YourSecondViewController * details = [self.storyboard instantiateViewControllerWithIdentifier:@"nameOfYourSecondVC"]; // Make sure in storyboards you give your second VC an identifier

    // Make sure you declare your value in the second view controller
    details.selectedValue = value;

    // Now that you have said to pass value all you need to do is change views
    [self.navigationController pushViewController: details animated:YES];

}

Four lines of code inside the method and you are done.

满地尘埃落定 2024-10-27 16:23:37

这个问题在 StackOverflow 上似乎很受欢迎,所以我想我会尝试给出一个更好的答案来帮助像我一样刚开始接触 iOS 世界的人。

转发数据

将数据从另一个视图控制器转发到另一个视图控制器。如果您想将对象/值从一个视图控制器传递到另一个可能推送到导航堆栈的视图控制器,则可以使用此方法。

对于此示例,我们将使用 ViewControllerAViewControllerB

BOOL 值从 ViewControllerA 传递到 ViewControllerB 我们将执行以下操作。

  1. ViewControllerB.h中为BOOL创建一个属性

     @property(非原子,分配)BOOL isSomethingEnabled;
    
  2. ViewControllerA 中,您需要告诉它有关 ViewControllerB 的信息所以使用

    <前><代码>#import“ViewControllerB.h”

在要加载视图的位置使用 Then,例如 didSelectRowAtIndex 或某些 IBAction,在将其推送到导航堆栈之前,您需要在 ViewControllerB 中设置该属性。

    ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
    viewControllerB.isSomethingEnabled = YES;
    [self pushViewController:viewControllerB animated:YES];

这会将 ViewControllerB 中的 isSomethingEnabled 设置为 BOOLYES

使用 Segue 转发数据

如果您使用 Storyboard,则很可能会使用 Segue,并且需要此过程来转发数据。这与上面类似,但不是在推送视图控制器之前传递数据,而是使用名为

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender

So 的方法将 BOOLViewControllerA 传递到 ViewControllerB 我们将执行以下操作:

  1. ViewControllerB.h 中为 BOOL 创建一个属性

     @property (非原子,分配) BOOL isSomethingEnabled;
    
  2. ViewControllerA 中,您需要告诉它有关 ViewControllerB 的信息,所以使用

    <前><代码>#import“ViewControllerB.h”

  3. 创建从 ViewControllerAViewControllerB 的转场> 在故事板上并为其指定一个标识符。在此示例中,我们将其称为“showDetailSegue”

  4. 接下来,我们需要将方法添加到执行任何 segue 时调用的 ViewControllerA 中。因此,我们需要检测调用了哪个 segue,然后执行某些操作。在我们的示例中,我们将检查 "showDetailSegue",如果执行了,我们会将 BOOL 值传递给 ViewControllerB

     -(void)prepareForSegue:(UIStoryboardSegue *)segue 发件人:(id)sender{
         if([segue.identifier isEqualToString:@"showDetailSegue"]){
             ViewControllerB *controller = (ViewControllerB *)segue.destinationViewController;
             控制器.isSomethingEnabled = YES;
         }
     }
    

如果您有自己的观点嵌入导航控制器中,您需要将上面的方法稍微更改为以下内容

    -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
        if([segue.identifier isEqualToString:@"showDetailSegue"]){
            UINavigationController *navController = (UINavigationController *)segue.destinationViewController;
            ViewControllerB *controller = (ViewControllerB *)navController.topViewController;
            controller.isSomethingEnabled = YES;
        }
    }

这会将 ViewControllerB 中的 isSomethingEnabled 设置为 BOOL是的。

传回数据

要将数据从ViewControllerB传回ViewControllerA,您需要使用协议和委托,后者可以用作回调的松耦合机制。

为此,我们将使 ViewControllerA 成为 ViewControllerB 的委托。这允许 ViewControllerB 向 ViewControllerA 发送消息,使我们能够发送数据。

要使 ViewControllerA 成为 ViewControllerB 的委托,它必须符合我们必须指定的 ViewControllerB 协议。这告诉 ViewControllerA 它必须实现哪些方法。

  1. ViewControllerB.h 中,在 #import 下方、@interface 上方指定协议。

     @class ViewControllerB;
    
     @protocol ViewControllerBDelegate ;
     - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item;
     @结尾
    
  2. 接下来还是在ViewControllerB.h中,您需要设置一个delegate属性并在ViewControllerB.m中综合

     @property (非原子,弱) id ;代表;
    
  3. ViewControllerB 当我们弹出视图控制器时,我们在委托上调用一条消息。

     NSString *itemToPassBack = @"将此值传递回 ViewControllerA";
     [self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];
    
  4. 这就是ViewControllerB。现在,在 ViewControllerA.h 中,告诉 ViewControllerA 导入 ViewControllerB 并遵守其协议。

    <前><代码>#import“ViewControllerB.h”

    @interface ViewControllerA : UIViewController ;

  5. ViewControllerA.m中实现我们协议中的以下方法

     - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item
     {
         NSLog(@"这是从 ViewControllerB %@ 返回的", item);
     }
    
  6. 在将 viewControllerB 推送到导航堆栈之前,我们需要告诉 ViewControllerB ViewControllerA 是它的委托,否则我们会得到一个错误。

     ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
     viewControllerB.delegate = self
     [[self navigationController] PushViewController:viewControllerB 动画:是];
    

参考文献

  1. 视图控制器编程指南中的使用委派与其他视图控制器通信
  2. 委托模式

NSNotification center

这是传递数据的另一种方式。

// Add an observer in controller(s) where you want to receive data
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDeepLinking:) name:@"handleDeepLinking" object:nil];

-(void) handleDeepLinking:(NSNotification *) notification {
    id someObject = notification.object // Some custom object that was passed with notification fire.
}

// Post notification
id someObject;
[NSNotificationCenter.defaultCenter postNotificationName:@"handleDeepLinking" object:someObject];

将数据从一个类传递回另一个类(类可以是任何控制器、网络/会话管理器、UIView 子类或任何其他类)

块是匿名函数。

此示例传递数据从控制器B控制器A

定义块

@property void(^selectedVoucherBlock)(NSString *); // in ContollerA.h

添加块处理程序(侦听器)

在您需要值的地方(例如,您需要 ControllerA 中的 API 响应,或者需要 A 上的 ContorllerB 数据)

// In ContollerA.m

- (void)viewDidLoad {
    [super viewDidLoad];
    __unsafe_unretained typeof(self) weakSelf = self;
    self.selectedVoucherBlock = ^(NSString *voucher) {
        weakSelf->someLabel.text = voucher;
    };
}

转到控制器 B

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
ControllerB *vc = [storyboard instantiateViewControllerWithIdentifier:@"ControllerB"];
vc.sourceVC = self;
    [self.navigationController pushViewController:vc animated:NO];

Fire block

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:
(NSIndexPath *)indexPath {
    NSString *voucher = vouchersArray[indexPath.row];
    if (sourceVC.selectVoucherBlock) {
        sourceVC.selectVoucherBlock(voucher);
    }
    [self.navigationController popToViewController:sourceVC animated:YES];
}

块的另一个工作示例

This question seems to be very popular here on Stack Overflow so I thought I would try and give a better answer to help out people starting in the world of iOS like me.

Passing Data Forward

Passing data forward to a view controller from another view controller. You would use this method if you wanted to pass an object/value from one view controller to another view controller that you may be pushing on to a navigation stack.

For this example, we will have ViewControllerA and ViewControllerB

To pass a BOOL value from ViewControllerA to ViewControllerB we would do the following.

  1. in ViewControllerB.h create a property for the BOOL

     @property (nonatomic, assign) BOOL isSomethingEnabled;
    
  2. in ViewControllerA you need to tell it about ViewControllerB so use an

     #import "ViewControllerB.h"
    

Then where you want to load the view, for example, didSelectRowAtIndex or some IBAction, you need to set the property in ViewControllerB before you push it onto the navigation stack.

    ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
    viewControllerB.isSomethingEnabled = YES;
    [self pushViewController:viewControllerB animated:YES];

This will set isSomethingEnabled in ViewControllerB to BOOL value YES.

Passing Data Forward using Segues

If you are using Storyboards you are most likely using segues and will need this procedure to pass data forward. This is similar to the above but instead of passing the data before you push the view controller, you use a method called

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender

So to pass a BOOL from ViewControllerA to ViewControllerB we would do the following:

  1. in ViewControllerB.h create a property for the BOOL

     @property (nonatomic, assign) BOOL isSomethingEnabled;
    
  2. in ViewControllerA you need to tell it about ViewControllerB, so use an

     #import "ViewControllerB.h"
    
  3. Create the segue from ViewControllerA to ViewControllerB on the storyboard and give it an identifier. In this example we'll call it "showDetailSegue"

  4. Next, we need to add the method to ViewControllerA that is called when any segue is performed. Because of this we need to detect which segue was called and then do something. In our example, we will check for "showDetailSegue" and if that's performed, we will pass our BOOL value to ViewControllerB

     -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
         if([segue.identifier isEqualToString:@"showDetailSegue"]){
             ViewControllerB *controller = (ViewControllerB *)segue.destinationViewController;
             controller.isSomethingEnabled = YES;
         }
     }
    

If you have your views embedded in a navigation controller, you need to change the method above slightly to the following

    -(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
        if([segue.identifier isEqualToString:@"showDetailSegue"]){
            UINavigationController *navController = (UINavigationController *)segue.destinationViewController;
            ViewControllerB *controller = (ViewControllerB *)navController.topViewController;
            controller.isSomethingEnabled = YES;
        }
    }

This will set isSomethingEnabled in ViewControllerB to BOOL value YES.

Passing Data Back

To pass data back from ViewControllerB to ViewControllerA you need to use Protocols and Delegates or Blocks, the latter can be used as a loosely coupled mechanism for callbacks.

To do this we will make ViewControllerA a delegate of ViewControllerB. This allows ViewControllerB to send a message back to ViewControllerA enabling us to send data back.

For ViewControllerA to be a delegate of ViewControllerB it must conform to ViewControllerB's protocol which we have to specify. This tells ViewControllerA which methods it must implement.

  1. In ViewControllerB.h, below the #import, but above @interface you specify the protocol.

     @class ViewControllerB;
    
     @protocol ViewControllerBDelegate <NSObject>
     - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item;
     @end
    
  2. Next still in the ViewControllerB.h, you need to set up a delegate property and synthesize in ViewControllerB.m

     @property (nonatomic, weak) id <ViewControllerBDelegate> delegate;
    
  3. In ViewControllerB we call a message on the delegate when we pop the view controller.

     NSString *itemToPassBack = @"Pass this value back to ViewControllerA";
     [self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];
    
  4. That's it for ViewControllerB. Now in ViewControllerA.h, tell ViewControllerA to import ViewControllerB and conform to its protocol.

     #import "ViewControllerB.h"
    
     @interface ViewControllerA : UIViewController <ViewControllerBDelegate>
    
  5. In ViewControllerA.m implement the following method from our protocol

     - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item
     {
         NSLog(@"This was returned from ViewControllerB %@", item);
     }
    
  6. Before pushing viewControllerB to navigation stack we need to tell ViewControllerB that ViewControllerA is its delegate, otherwise we will get an error.

     ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
     viewControllerB.delegate = self
     [[self navigationController] pushViewController:viewControllerB animated:YES];
    

References

  1. Using Delegation to Communicate With Other View Controllers in the View Controller Programming Guide
  2. Delegate Pattern

NSNotification center

It's another way to pass data.

// Add an observer in controller(s) where you want to receive data
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleDeepLinking:) name:@"handleDeepLinking" object:nil];

-(void) handleDeepLinking:(NSNotification *) notification {
    id someObject = notification.object // Some custom object that was passed with notification fire.
}

// Post notification
id someObject;
[NSNotificationCenter.defaultCenter postNotificationName:@"handleDeepLinking" object:someObject];

Passing Data back from one class to another (A class can be any controller, Network/session manager, UIView subclass or any other class)

Blocks are anonymous functions.

This example passes data from Controller B to Controller A

Define a block

@property void(^selectedVoucherBlock)(NSString *); // in ContollerA.h

Add block handler (listener)

Where you need a value (for example, you need your API response in ControllerA or you need ContorllerB data on A)

// In ContollerA.m

- (void)viewDidLoad {
    [super viewDidLoad];
    __unsafe_unretained typeof(self) weakSelf = self;
    self.selectedVoucherBlock = ^(NSString *voucher) {
        weakSelf->someLabel.text = voucher;
    };
}

Go to Controller B

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
ControllerB *vc = [storyboard instantiateViewControllerWithIdentifier:@"ControllerB"];
vc.sourceVC = self;
    [self.navigationController pushViewController:vc animated:NO];

Fire block

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:
(NSIndexPath *)indexPath {
    NSString *voucher = vouchersArray[indexPath.row];
    if (sourceVC.selectVoucherBlock) {
        sourceVC.selectVoucherBlock(voucher);
    }
    [self.navigationController popToViewController:sourceVC animated:YES];
}

Another Working Example for Blocks

您的好友蓝忘机已上羡 2024-10-27 16:23:37

Swift

这里和 StackOverflow 周围有大量的解释,但如果你是一个初学者,只是想尝试一些基本的工作,请尝试观看这个 YouTube 教程(它帮助我最终理解了如何做到这一点)。

将数据转发到下一个视图控制器

以下是基于视频的示例。这个想法是将字符串从第一个视图控制器中的文本字段传递到第二个视图控制器中的标签。

输入图像描述此处

在 Interface Builder 中创建故事板布局。要进行转场,您只需 Control 单击按钮并拖动到第二个视图控制器。

第一个视图控制器

第一个视图控制器的代码是

import UIKit

class FirstViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!

    // This function is called before the segue
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        // Get a reference to the second view controller
        let secondViewController = segue.destination 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 的出口。
  • 将第一个和第二个视图控制器设置为 Interface Builder 中相应的 Swift 文件。

将数据传递回前一个视图控制器

要将数据从第二个视图控制器传递回第一个视图控制器,您可以使用协议和委托。该视频非常清晰地介绍了该过程:

以下是基于视频的示例(经过一些修改)。

输入图像描述这里

在 Interface Builder 中创建故事板布局。同样,要进行转场,您只需 Control 从按钮拖动到第二个视图控制器。将 Segue 标识符设置为 showSecondViewController。另外,不要忘记使用以下代码中的名称连接插座和操作。

第一个视图控制器

第一个视图控制器的代码

import UIKit

class FirstViewController: UIViewController, DataEnteredDelegate {

    @IBOutlet weak var label: UILabel!

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "showSecondViewController" {
            let secondViewController = segue.destination as! SecondViewController
            secondViewController.delegate = self
        }
    }

    func userDidEnterInformation(info: String) {
        label.text = info
    }
}

请注意我们自定义的 DataEnteredDelegate 协议的使用。

第二个视图控制器和协议

第二个视图控制器的代码是

import UIKit

// Protocol used for sending data back
protocol DataEnteredDelegate: AnyObject {
    func userDidEnterInformation(info: String)
}

class SecondViewController: UIViewController {

    // Making this a weak variable, so that it won't create a strong reference cycle
    weak var delegate: DataEnteredDelegate? = nil

    @IBOutlet weak var textField: UITextField!

    @IBAction func sendTextBackButton(sender: AnyObject) {

        // Call this method on whichever class implements our delegate protocol
        delegate?.userDidEnterInformation(info: textField.text!)

        // Go back to the previous view controller
        _ = self.navigationController?.popViewController(animated: true)
    }
}

请注意,协议位于视图控制器类之外。

就是这样。现在运行应用程序,您应该能够将数据从第二个视图控制器发送回第一个视图控制器。

Swift

There are tons and tons of explanations here and around Stack Overflow, but if you are a beginner just trying to get something basic to work, try watching this YouTube tutorial (It's what helped me to finally understand how to do it).

Passing data forward to the next View Controller

The following is an example based on the video. The idea is to pass a string from the text field in the First View Controller to the label in the Second View Controller.

Enter image description here

Create the storyboard layout in the Interface Builder. To make the segue, you just Control click on the button and drag over to the Second View Controller.

First View Controller

The code for the First View Controller is

import UIKit

class FirstViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!

    // This function is called before the segue
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        // Get a reference to the second view controller
        let secondViewController = segue.destination as! SecondViewController

        // Set a variable in the second view controller with the String to pass
        secondViewController.receivedString = textField.text!
    }

}

Second View Controller

And the code for the Second View Controller is

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
    }

}

Don't forget

  • Hook up the outlets for the UITextField and the UILabel.
  • Set the first and second View Controllers to the appropriate Swift files in Interface Builder.

Passing data back to the previous View Controller

To pass data back from the second view controller to the first view controller, you use a protocol and a delegate. This video is a very clear walk though of that process:

The following is an example based on the video (with a few modifications).

Enter image description here

Create the storyboard layout in the Interface Builder. Again, to make the segue, you just Control drag from the button to the Second View Controller. Set the segue identifier to showSecondViewController. Also, don't forget to hook up the outlets and actions using the names in the following code.

First View Controller

The code for the First View Controller is

import UIKit

class FirstViewController: UIViewController, DataEnteredDelegate {

    @IBOutlet weak var label: UILabel!

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "showSecondViewController" {
            let secondViewController = segue.destination as! SecondViewController
            secondViewController.delegate = self
        }
    }

    func userDidEnterInformation(info: String) {
        label.text = info
    }
}

Note the use of our custom DataEnteredDelegate protocol.

Second View Controller and Protocol

The code for the second view controller is

import UIKit

// Protocol used for sending data back
protocol DataEnteredDelegate: AnyObject {
    func userDidEnterInformation(info: String)
}

class SecondViewController: UIViewController {

    // Making this a weak variable, so that it won't create a strong reference cycle
    weak var delegate: DataEnteredDelegate? = nil

    @IBOutlet weak var textField: UITextField!

    @IBAction func sendTextBackButton(sender: AnyObject) {

        // Call this method on whichever class implements our delegate protocol
        delegate?.userDidEnterInformation(info: textField.text!)

        // Go back to the previous view controller
        _ = self.navigationController?.popViewController(animated: true)
    }
}

Note that the protocol is outside of the View Controller class.

That's it. Running the app now, you should be able to send data back from the second view controller to the first.

梦在深巷 2024-10-27 16:23:37

MVC 中的 M 代表“模型”,在 MVC 范例中,模型类的作用是管理程序的数据。模型与视图相反——视图知道如何显示数据,但它不知道如何处理数据,而模型知道如何处理数据的一切,但不知道如何显示数据。模型可以很复杂,但不一定如此——您的应用程序的模型可能像字符串或字典数组一样简单。

控制器的作用是在视图和模型之间进行调解。因此,它们需要对一个或多个视图对象和一个或多个模型对象的引用。假设您的模型是一个字典数组,每个字典代表表中的一行。应用程序的根视图显示该表,并且它可能负责从文件加载数组。当用户决定向表中添加新行时,他们点击某个按钮,您的控制器会创建一个新的(可变)字典并将其添加到数组中。为了填充该行,控制器创建一个详细视图控制器并为其提供新的字典。详细视图控制器填充字典并返回。字典已经是模型的一部分,所以不需要发生任何其他事情。

The M in MVC is for "Model" and in the MVC paradigm the role of model classes is to manage a program's data. A model is the opposite of a view -- a view knows how to display data, but it knows nothing about what to do with data, whereas a model knows everything about how to work with data, but nothing about how to display it. Models can be complicated, but they don't have to be -- the model for your app might be as simple as an array of strings or dictionaries.

The role of a controller is to mediate between view and model. Therefore, they need a reference to one or more view objects and one or more model objects. Let's say that your model is an array of dictionaries, with each dictionary representing one row in your table. The root view for your app displays that table, and it might be responsible for loading the array from a file. When the user decides to add a new row to the table, they tap some button and your controller creates a new (mutable) dictionary and adds it to the array. In order to fill in the row, the controller creates a detail view controller and gives it the new dictionary. The detail view controller fills in the dictionary and returns. The dictionary is already part of the model, so nothing else needs to happen.

腹黑女流氓 2024-10-27 16:23:37

iOS 中不同类可以通过多种方式接收数据。例如 -

  1. 分配另一个类后直接初始化。
  2. 委托 - 用于传回数据
  3. 通知 - 用于一次将数据广播到多个类
  4. 保存在 NSUserDefaults 中 - 用于稍后访问
  5. 单例类
  6. 数据库和其他存储机制,例如 p-list 文件等。

但是对于将值传递给另一个类(其分配在当前类中完成)的简单场景,最常见和首选的方法是分配后直接设置值。其完成方式如下:

我们可以使用两个控制器来理解它 - Controller1 和 Controller2

假设在 Controller1 类中您要创建 Controller2 对象并通过传递的 String 值推送它。可以这样做:

- (void)pushToController2 {

    Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
    [obj passValue:@"String"];
    [self pushViewController:obj animated:YES];
}

在Controller2类的实现中会有这个函数:

@interface Controller2  : NSObject

@property (nonatomic, strong) NSString* stringPassed;

@end

@implementation Controller2

@synthesize stringPassed = _stringPassed;

- (void) passValue:(NSString *)value {

    _stringPassed = value; // Or self.stringPassed = value
}

@end

您也可以直接设置Controller2类的属性,方法类似:

- (void)pushToController2 {

    Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
    [obj setStringPassed:@"String"];
    [self pushViewController:obj animated:YES];
}

要传递多个值,可以使用多个参数例如:

Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
[obj passValue:@“String1” andValues:objArray withDate:date];

或者,如果您需要传递三个以上与公共功能相关的参数,您可以将这些值存储在模型类中,并将该 modelObject 传递给下一个类。

ModelClass *modelObject = [[ModelClass alloc] init];
modelObject.property1 = _property1;
modelObject.property2 = _property2;
modelObject.property3 = _property3;

Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
[obj passmodel: modelObject];

简而言之,如果您想 -

  1. 设置私有变量第二类通过调用自定义函数并传递值来初始化值。
  2. setProperties 通过直接使用 setter 方法对其进行初始化来完成。
  3. 以某种方式传递超过 3-4 个彼此相关的值,然后创建一个模型类并将值设置为其对象,并使用上述任何过程传递该对象。

There are various ways by which data can be received by a different class in iOS. For example -

  1. Direct initialization after the allocation of another class.
  2. Delegation - for passing data back
  3. Notification - for broadcasting data to multiple classes at a single time
  4. Saving in NSUserDefaults - for accessing it later
  5. Singleton classes
  6. Databases and other storage mechanisms, like p-list files, etc.

But for the simple scenario of passing a value to a different class whose allocation is done in the current class, the most common and preferred method would be the direct setting of values after allocation. This is done as follows:

We can understand it using two controllers - Controller1 and Controller2

Suppose in Controller1 class you want to create the Controller2 object and push it with a String value being passed. This can be done as this:

- (void)pushToController2 {

    Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
    [obj passValue:@"String"];
    [self pushViewController:obj animated:YES];
}

In the implementation of the Controller2 class there will be this function as:

@interface Controller2  : NSObject

@property (nonatomic, strong) NSString* stringPassed;

@end

@implementation Controller2

@synthesize stringPassed = _stringPassed;

- (void) passValue:(NSString *)value {

    _stringPassed = value; // Or self.stringPassed = value
}

@end

You can also directly set the properties of the Controller2 class in the similar way as this:

- (void)pushToController2 {

    Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
    [obj setStringPassed:@"String"];
    [self pushViewController:obj animated:YES];
}

To pass multiple values, you can use the multiple parameters like:

Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
[obj passValue:@“String1” andValues:objArray withDate:date];

Or if you need to pass more than three parameters which are related to a common feature, you can store the values in a model class and pass that modelObject to the next class

ModelClass *modelObject = [[ModelClass alloc] init];
modelObject.property1 = _property1;
modelObject.property2 = _property2;
modelObject.property3 = _property3;

Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
[obj passmodel: modelObject];

So in short, if you want to -

  1. set the private variables of the second class initialise the values by calling a custom function and passing the values.
  2. setProperties do it by directlyInitialising it using the setter method.
  3. pass more that 3-4 values related to each other in some manner, then create a model class and set values to its object and pass the object using any of the above process.
李白 2024-10-27 16:23:37

经过更多研究后,似乎协议委托是正确的/Apple首选的执行此操作的方式。

我最终使用了这个例子(在iPhone开发SDK中):

在视图控制器和其他对象之间共享数据

它工作得很好,允许我在视图之间前后传递字符串和数组。

After more research it seemed that protocols and delegates were the correct/Apple preferred way of doing this.

I ended up using this example (in the iPhone development SDK):

Sharing data between view controllers and other objects

It worked fine and allowed me to pass a string and an array forward and back between my views.

止于盛夏 2024-10-27 16:23:37

我发现最简单、最优雅的版本带有传递块。
我们将等待返回数据的视图控制器命名为“A”,将返回视图控制器命名为“B”。在此示例中,我们想要获取 2 个值:第一个值是 Type1,第二个值是 Type2。

假设我们使用 Storyboard,第一个控制器设置回调块,例如在 segue 准备期间:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.destinationViewController isKindOfClass:[BViewController class]])
    {
        BViewController *viewController = segue.destinationViewController;

        viewController.callback = ^(Type1 *value1, Type2 *value2) {
            // optionally, close B
            //[self.navigationController popViewControllerAnimated:YES];

            // let's do some action after with returned values
            action1(value1);
            action2(value2);
        };

    }
}

并且“B”视图控制器应该声明回调属性,BViewController.h:

// it is important to use "copy"
@property (copy) void(^callback)(Type1 *value1, Type2 *value2);

比在实现文件 BViewController.m 中我们获得返回回调所需的值后应该是调用:

if (self.callback)
    self.callback(value1, value2);

要记住的一件事是,使用块通常需要管理强引用和__弱引用,如解释的此处

I find simplest and most elegant version with passing blocks.
Let's name view controller that waits for returned data as "A" and returning view controller as "B". In this example we want to get 2 values: first of Type1 and second of Type2.

Assuming we use Storyboard, first controller sets callback block, for example during segue preparation:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.destinationViewController isKindOfClass:[BViewController class]])
    {
        BViewController *viewController = segue.destinationViewController;

        viewController.callback = ^(Type1 *value1, Type2 *value2) {
            // optionally, close B
            //[self.navigationController popViewControllerAnimated:YES];

            // let's do some action after with returned values
            action1(value1);
            action2(value2);
        };

    }
}

and "B" view controller should declare callback property, BViewController.h:

// it is important to use "copy"
@property (copy) void(^callback)(Type1 *value1, Type2 *value2);

Than in implementation file BViewController.m after we have desired values to return our callback should be called:

if (self.callback)
    self.callback(value1, value2);

One thing to remember is that using block often needs to manage strong and __weak references like explained here

时光沙漏 2024-10-27 16:23:37

许多答案都提供了一些很好的信息,但没有一个完全解决问题。

该问题询问有关在视图控制器之间传递信息的问题。给出的具体示例询问在视图之间传递信息,但考虑到 iOS 的自我声明的新颖性,原始发布者可能意味着在视图控制器之间,而不是在视图之间(没有视图控制器的任何参与)。似乎所有的答案都集中在两个视图控制器上,但是如果应用程序发展到需要在信息交换中涉及两个以上的视图控制器怎么办?

原发帖者还询问了SingletonsAppDelegate的使用。这些问题需要得到解答。

为了帮助其他人看到这个问题并想要完整的答案,我将尝试提供它。

应用场景

与其进行高度假设的抽象讨论,不如考虑具体的应用程序。为了帮助定义两个视图控制器的情况和两个以上视图控制器的情况,我将定义两个具体的应用场景。

场景一:最多有两个视图控制器需要共享信息。

见图一。

原始问题图

应用程序中有两个视图控制器。有一个ViewControllerA(数据输入表单)和ViewControllerB(产品列表)。产品列表中选择的项目必须与数据输入表单中文本框中显示的项目相匹配。在这种情况下,ViewControllerA 和 ViewControllerB 必须直接相互通信,而不能与其他视图控制器通信。

场景二:两个以上的视图控制器需要共享相同的信息。

见图二。

家庭库存应用程序图

应用程序中有四个视图控制器。它是一个基于选项卡的应用程序,用于管理家庭库存。三个视图控制器呈现相同数据的不同过滤视图:

  • ViewControllerA - 奢侈品
  • ViewControllerB - 非保险物品
  • ViewControllerC - 整个家庭库存
  • ViewControllerD - 添加新物品表单

任何时候创建或编辑单个物品时,它也必须与其他物品同步视图控制器。例如,如果我们在 ViewControllerD 中添加一艘船,但尚未投保,那么当用户前往 ViewControllerA(奢侈品)和 ViewControllerC(整个家居库存)时,该船一定会出现,但当用户前往ViewControllerB(非保险物品)。我们不仅需要关注添加新项目,还需要关注删除项目(这可能从四个视图控制器中的任何一个允许),或编辑现有项目(这可能从“添加新项目表单”允许,重新利用相同的项目)用于编辑)。

由于所有视图控制器确实需要共享相同的数据,因此所有四个视图控制器都需要保持同步,因此每当任何单个视图控制器更改底层数据时,都需要与所有其他视图控制器进行某种通信。很明显,我们不希望在这种情况下每个视图控制器直接与其他视图控制器通信。如果不明显,请考虑我们是否有 20 个不同的视图控制器(而不仅仅是 4 个)。每当一个视图控制器做出更改时,通知其他 19 个视图控制器有多困难且容易出错?

解决方案:委托、观察者模式和单例

一中,我们有几个可行的解决方案,因为其他答案已经给出了

  • segues
  • 委托
  • 直接在视图控制器上设置属性
  • NSUserDefaults (实际上是一个糟糕的选择)

在场景 第二,我们还有其他可行的解决方案:

  • 观察者模式
  • 单例

单例 是类的一个实例,该实例是其生命周期中唯一存在的实例。单例因其是单一实例而得名。通常使用单例的开发人员有特殊的类方法来访问它们。

+ (HouseholdInventoryManager*) sharedManager; {
    static dispatch_once_t onceQueue;
    static HouseholdInventoryManager* _sharedInstance;

    // dispatch_once is guaranteed to only be executed
    // once in the lifetime of the application
    dispatch_once(&onceQueue, ^{
        _sharedInstance = [[self alloc] init];
    });
    return _sharedInstance;
}

现在我们了解了什么是单例,让我们讨论一下单例如何适应观察者模式。观察者模式用于一个对象响应另一对象的更改。在第二种情况下,我们有四个不同的视图控制器,它们都想了解底层数据的更改。 “底层数据”应该属于单个实例,即单例。 “了解变化”是通过观察对单例所做的变化来完成的。

家庭库存应用程序将具有一个类的单个实例,该类旨在管理库存项目列表。经理将管理一系列家居用品。以下是数据管理器的类定义:

#import <Foundation/Foundation.h>

@class JGCHouseholdInventoryItem;

@interface HouseholdInventoryManager : NSObject
/*!
 The global singleton for accessing application data
 */
+ (HouseholdInventoryManager*) sharedManager;


- (NSArray *) entireHouseholdInventory;
- (NSArray *) luxuryItems;
- (NSArray *) nonInsuredItems;

- (void) addHouseholdItemToHomeInventory:(JGCHouseholdInventoryItem*)item;
- (void) editHouseholdItemInHomeInventory:(JGCHouseholdInventoryItem*)item;
- (void) deleteHoueholdItemFromHomeInventory:(JGCHouseholdInventoryItem*)item;
@end

当主库存项目的集合发生更改时,视图控制器需要了解此更改。上面的类定义并没有明确说明这是如何发生的。我们需要遵循观察者模式。视图控制器必须正式观察sharedManager。有两种方法可以观察另一个对象:

  • Key-Value-Observing (KVO)
  • NSNotificationCenter。

在场景二中,我们没有可以使用 KVO 观察到的 HouseholdInventoryManager 的单个属性。因为我们没有一个易于观察的属性,所以在这种情况下,观察者模式必须使用 NSNotificationCenter 来实现。四个视图控制器中的每一个都会订阅通知,并且sharedManager会在适当的时候向通知中心发送通知。库存管理器不需要了解有关视图控制器或任何其他类的实例的任何信息,这些类可能有兴趣了解库存项目集合何时发生变化; NSNotificationCenter 负责处理这些实现细节。视图控制器只是订阅通知,数据管理器只是发布通知。

许多初学者程序员利用了这样一个事实:在应用程序的生命周期中始终只有一个可全局访问的应用程序委托。初级程序员利用这一事实将对象和功能填充到 appDelegate 中,以方便从应用程序中的其他位置进行访问。仅仅因为 AppDelegate 是单例并不意味着它应该取代所有其他单例。这是一种糟糕的做法,因为它给一个类带来了太多负担,破坏了良好的面向对象实践。每个类都应该有一个清晰的角色,并且很容易解释,通常只需通过类的名称即可。

每当您的应用程序委托开始变得臃肿时,就开始将功能删除到单例中。例如,核心数据堆栈不应该留在AppDelegate中,而应该放在它自己的类中,即coreDataManager类中。

参考

There is some good information in many of the answers given, but none address the question fully.

The question asks about passing information between view controllers. The specific example given asks about passing information between views, but given the self-stated newness to iOS, the original poster likely meant between viewControllers, not between views (without any involvement from the ViewControllers). It seems that all the answers focus on two view controllers, but what if the app evolves to need to involve more than two view controllers in the information exchange?

The original poster also asked about Singletons and the use of the AppDelegate. These questions need to be answered.

To help anyone else looking at this question, who wants a full answer, I'm going to attempt to provide it.

Application Scenarios

Rather than having a highly hypothetical, abstract discussion, it helps to have concrete applications in mind. To help define a two-view-controller situation and a more-than-two-view-controller situation, I am going to define two concrete application scenarios.

Scenario one: maximum two view controllers ever need to share information.

See diagram one.

Diagram of original problem

There are two view controllers in the application. There is a ViewControllerA (Data Entry Form), and View Controller B (Product List). The items selected in the product list must match the items displayed in the text box in the data entry form. In this scenario, ViewControllerA and ViewControllerB must communicate directly with each other and no other view controllers.

Scenario two: more than two view controllers need to share the same information.

See diagram two.

Home inventory application diagram

There are four view controllers in the application. It is a tab-based application for managing home inventory. Three view controllers present differently filtered views of the same data:

  • ViewControllerA - Luxury Items
  • ViewControllerB - Non-insured Items
  • ViewControllerC - Entire Home Inventory
  • ViewControllerD - Add New Item Form

Any time an individual item is created or edited, it must also synchronize with the other view controllers. For example, if we add a boat in ViewControllerD, but it is not yet insured, then the boat must appear when the user goes to ViewControllerA (Luxury Items), and also ViewControllerC (Entire Home Inventory), but not when the user goes to ViewControllerB (Non-insured Items). We need be concerned with not only adding new items, but also deleting items (which may be allowed from any of the four view controllers), or editing existing items (which may be allowed from the "Add New Item Form", repurposing the same for editing).

Since all the view controllers do need to share the same data, all four view controllers need to remain in synchronization, and therefore there needs to be some sort of communication to all other view controllers, whenever any single view controller changes the underlying data. It should be fairly obvious that we do not want each view controller communicating directly with each other view controller in this scenario. In case it is not obvious, consider if we had 20 different view controllers (rather than just 4). How difficult and error-prone would it be to notify each of the other 19 view controllers any time one view controller made a change?

The Solutions: Delegates and the Observer Pattern, and Singletons

In scenario one, we have several viable solutions, as other answers have given

  • segues
  • delegates
  • setting properties on view controllers directly
  • NSUserDefaults (actually a poor choice)

In scenario two, we have other viable solutions:

  • Observer Pattern
  • Singletons

A singleton is an instance of a class, that instance being the only instance in existence during its lifetime. A singleton gets its name from the fact that it is the single instance. Normally developers who use singletons have special class methods for accessing them.

+ (HouseholdInventoryManager*) sharedManager; {
    static dispatch_once_t onceQueue;
    static HouseholdInventoryManager* _sharedInstance;

    // dispatch_once is guaranteed to only be executed
    // once in the lifetime of the application
    dispatch_once(&onceQueue, ^{
        _sharedInstance = [[self alloc] init];
    });
    return _sharedInstance;
}

Now that we understand what a singleton is, let's discuss how a singleton fits into the observer pattern. The observer pattern is used for one object to respond to changes by another object. In the second scenario, we have four different view controllers, who all want to know about changes to the underlying data. The "underlying data" should belong to a single instance, a singleton. The "know about changes" is accomplished by observing changes made to the singleton.

The home inventory application would have a single instance of a class which is designed to manage a list of inventory items. The manager would manage a collection of household items. The following is a class definition for the data manager:

#import <Foundation/Foundation.h>

@class JGCHouseholdInventoryItem;

@interface HouseholdInventoryManager : NSObject
/*!
 The global singleton for accessing application data
 */
+ (HouseholdInventoryManager*) sharedManager;


- (NSArray *) entireHouseholdInventory;
- (NSArray *) luxuryItems;
- (NSArray *) nonInsuredItems;

- (void) addHouseholdItemToHomeInventory:(JGCHouseholdInventoryItem*)item;
- (void) editHouseholdItemInHomeInventory:(JGCHouseholdInventoryItem*)item;
- (void) deleteHoueholdItemFromHomeInventory:(JGCHouseholdInventoryItem*)item;
@end

When the collection of home inventory items changes, the view controllers need to be made aware of this change. The class definition above does not make it obvious how this will happen. We need to follow the observer pattern. The view controllers must formally observe the sharedManager. There are two ways to observe another object:

  • Key-Value-Observing (KVO)
  • NSNotificationCenter.

In scenario two, we do not have a single property of the HouseholdInventoryManager which could be observed using KVO. Because we do not have a single property which is easily observable, the observer pattern, in this case, must be implemented using NSNotificationCenter. Each of the four view controllers would subscribe to notifications, and the sharedManager would send notifications to the notification center when appropriate. The inventory manager does not need to know anything about the view controllers or instances of any other classes which may be interested in knowing when the collection of inventory items changes; the NSNotificationCenter takes care of these implementation details. The View Controllers simply subscribe to notifications, and the data manager simply posts notifications.

Many beginner programmers take advantage of the fact that there is always exactly one Application Delegate in the lifetime of the application, which is globally accessible. Beginning programmers use this fact to stuff objects and functionality into the appDelegate as a convenience for access from anywhere else in the application. Just because the AppDelegate is a singleton doesn't mean it should replace all other singletons. This is a poor practice as it places too much burden on one class, breaking good object-oriented practices. Each class should have a clear role that is easily explained, often just by the name of the class.

Any time your Application Delegate starts to get bloated, start to remove functionality into singletons. For example, the Core Data Stack should not be left in the AppDelegate, but should instead be put in its own class, a coreDataManager class.

References

梦幻的味道 2024-10-27 16:23:37

将数据从 ViewController 2(目标)传递回 viewController 1(源)是更有趣的事情。
假设您使用storyBoard,这些是我发现的所有方法:

  • 委托
  • 通知
  • 用户默认
  • 单例

这些已经在这里讨论过。

我发现还有更多方法:

使用Block回调:

在VC1中的prepareForSegue方法中使用

NextViewController *destinationVC = (NextViewController *) segue.destinationViewController;
[destinationVC setDidFinishUsingBlockCallback:^(NextViewController *destinationVC)
{
    self.blockLabel.text = destination.blockTextField.text;
}];

使用storyboards Unwind(退出)

实现一个方法在 VC 1 中使用 UIStoryboardSegue 参数,如下所示:

-(IBAction)UnWindDone:(UIStoryboardSegue *)segue { }

在 StoryBoard 中,将“返回”按钮挂接到 VC 的绿色退出按钮(展开)上。现在你有了一个“返回”的segue,因此你可以在VC2的prepareForSegue中使用destinationViewController属性,并且
在 VC1 返回之前更改 VC1 的任何属性。

  • 使用故事板Undwind(退出)的另一种选择 - 您可以使用您在VC1中编写的方法

     -(IBAction)UnWindDone:(UIStoryboardSegue *)segue {
         NextViewController *nextViewController = segue.sourceViewController;
         self.unwindLabel.text = nextViewController.unwindPropertyPass;
     }
    

,并且在VC1的prepareForSegue中您可以更改您想要共享的任何属性。

在这两个展开选项中,您都可以设置按钮的 tag 属性并在prepareForSegue 中检查它。

Passing data back from ViewController 2 (destination) to viewController 1 (source) is the more interesting thing.
Assuming you use storyBoard, these are all the ways I found out:

  • Delegate
  • Notification
  • User defaults
  • Singleton

Those were discussed here already.

I found there are more ways:

Using Block callbacks:

Use it in the prepareForSegue method in the VC1

NextViewController *destinationVC = (NextViewController *) segue.destinationViewController;
[destinationVC setDidFinishUsingBlockCallback:^(NextViewController *destinationVC)
{
    self.blockLabel.text = destination.blockTextField.text;
}];

Using storyboards Unwind (Exit)

Implement a method with a UIStoryboardSegue argument in VC 1,like this one:

-(IBAction)UnWindDone:(UIStoryboardSegue *)segue { }

In the storyBoard, hook the "return" button to the green Exit button (Unwind) of the vc. Now you have a segue that "goes back" so you can use the destinationViewController property in the prepareForSegue of VC2 and
change any property of VC1 before it goes back.

  • Another option of using storyboards Undwind (Exit) - you can use the method you wrote in VC1

     -(IBAction)UnWindDone:(UIStoryboardSegue *)segue {
         NextViewController *nextViewController = segue.sourceViewController;
         self.unwindLabel.text = nextViewController.unwindPropertyPass;
     }
    

And in the prepareForSegue of VC1 you can change any property you want to share.

In both unwind options, you can set the tag property of the button and check it in the prepareForSegue.

十秒萌定你 2024-10-27 16:23:37

OP 没有提到视图控制器,但很多答案都提到了,我想补充一下 LLVM 的一些新功能允许在想要将数据从一个视图控制器传递到另一个视图控制器时变得更容易,然后得到一些结果。

Storyboard segues、ARC 和 LLVM 块使这对我来说比以往任何时候都更容易。上面的一些答案已经提到了故事板和续集,但仍然依赖于委托。定义委托当然有效,但有些人可能会发现传递指针或代码块更容易。

使用 UINavigators 和 segues,可以通过简单的方法将信息传递给从属控制器并取回信息。 ARC 使传递指向从 NSObject 派生的对象的指针变得简单,因此如果您希望从属控制器为您添加/更改/修改某些数据,请将其传递给可变实例的指针。块使传递操作变得容易,因此如果您希望从属控制器调用更高级别控制器上的操作,请向其传递一个块。您可以定义该块来接受对您有意义的任意数量的参数。如果更适合的话,您还可以将 API 设计为使用多个块。

这里有两个关于连接胶的简单例子。第一个很简单,显示了为输入传递的一个参数,第二个为输出传递的参数。

// Prepare the destination view controller by passing it the input we want it to work on
// and the results we will look at when the user has navigated back to this controller's view.

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    [[segue destinationViewController]

     // This parameter gives the next controller the data it works on.
     segueHandoffWithInput:self.dataForNextController

     // This parameter allows the next controller to pass back results
     // by virtue of both controllers having a pointer to the same object.
     andResults:self.resultsFromNextController];
}

第二个示例显示为第二个参数传递回调块。我喜欢使用块,因为它使源代码(更高级别的源代码)中的相关细节紧密结合在一起。

// Prepare the destination view controller by passing it the input we want it to work on
// and the callback when it has done its work.

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    [[segue destinationViewController]

     // This parameter gives the next controller the data it works on.
     segueHandoffWithInput:self.dataForNextController

     // This parameter allows the next controller to pass back results.
     resultsBlock:^(id results) {
         // This callback could be as involved as you like.
         // It can use Grand Central Dispatch to have work done on another thread for example.
        [self setResultsFromNextController:results];
    }];
}

The OP didn't mention view controllers but so many of the answers do, that I wanted to chime in with what some of the new features of the LLVM allow to make this easier when wanting to pass data from one view controller to another and then getting some results back.

Storyboard segues, ARC and LLVM blocks make this easier than ever for me. Some answers above mentioned storyboards and segues already but still relied on delegation. Defining delegates certainly works but some people may find it easier to pass pointers or code blocks.

With UINavigators and segues, there are easy ways of passing information to the subservient controller and getting the information back. ARC makes passing pointers to things derived from NSObjects simple so if you want the subservient controller to add/change/modify some data for you, pass it a pointer to a mutable instance. Blocks make passing actions easy so if you want the subservient controller to invoke an action on your higher level controller, pass it a block. You define the block to accept any number of arguments that makes sense to you. You can also design the API to use multiple blocks if that suits things better.

Here are two trivial examples of the segue glue. The first is straightforward showing one parameter passed for input, the second for output.

// Prepare the destination view controller by passing it the input we want it to work on
// and the results we will look at when the user has navigated back to this controller's view.

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    [[segue destinationViewController]

     // This parameter gives the next controller the data it works on.
     segueHandoffWithInput:self.dataForNextController

     // This parameter allows the next controller to pass back results
     // by virtue of both controllers having a pointer to the same object.
     andResults:self.resultsFromNextController];
}

This second example shows passing a callback block for the second argument. I like using blocks because it keeps the relevant details close together in the source - the higher level source.

// Prepare the destination view controller by passing it the input we want it to work on
// and the callback when it has done its work.

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    [[segue destinationViewController]

     // This parameter gives the next controller the data it works on.
     segueHandoffWithInput:self.dataForNextController

     // This parameter allows the next controller to pass back results.
     resultsBlock:^(id results) {
         // This callback could be as involved as you like.
         // It can use Grand Central Dispatch to have work done on another thread for example.
        [self setResultsFromNextController:results];
    }];
}
知你几分 2024-10-27 16:23:37

共享数据的方法有多种。

  1. 您始终可以使用 NSUserDefaults 共享数据。设置您想要与您选择的键共享的值,并从与下一个视图控制器中的该键关联的 NSUserDefault 获取值。

    [[NSUserDefaults standardUserDefaults] setValue:value forKey:key]
    [[NSUserDefaults standardUserDefaults] objectForKey:key]
    
  2. 您只需在viewcontrollerA中创建一个属性即可。在 viewcontrollerB 中创建一个 viewcontrollerA 对象,并将所需的值分配给该属性。

  3. 您还可以为此创建自定义委托。

There are multiple methods for sharing data.

  1. You can always share data using NSUserDefaults. Set the value you want to share with respect to a key of your choice and get the value from NSUserDefault associated to that key in the next view controller.

    [[NSUserDefaults standardUserDefaults] setValue:value forKey:key]
    [[NSUserDefaults standardUserDefaults] objectForKey:key]
    
  2. You can just create a property in viewcontrollerA. Create an object of viewcontrollerA in viewcontrollerB and assign the desired value to that property.

  3. You can also create custom delegates for this.

惜醉颜 2024-10-27 16:23:37

斯威夫特5

Matt Price的答案非常适合传递数据,但我
我将在最新 Swift 版本中重写它,因为我相信新的
由于新的语法,程序员发现它不再具有挑战性
方法/框架,正如原始帖子是在 Objective-C 中一样。

在视图控制器之间传递数据有多种选项。

  1. 使用导航控制器推送
  2. 使用 Segue
  3. 使用委托
  4. 使用通知观察器
  5. 使用 Block

我将使用最新的 iOS 框架在 Swift 中重写他的逻辑


通过导航控制器推送传递数据从 ViewControllerA 到 ViewControllerB

第 1 步。 在 ViewControllerB 中声明变量

var isSomethingEnabled = false

第 2 步。 打印ViewControllerB' ViewDidLoad 方法中的变量

override func viewDidLoad() {
    super.viewDidLoad()
    // Print value received through segue, navigation push
    print("Value of 'isSomethingEnabled' from ViewControllerA: ", isSomethingEnabled)
}

第 3 步。 在 ViewControllerA 中通过导航控制器推送数据时传递数据

if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
    viewControllerB.isSomethingEnabled = true
    if let navigator = navigationController {
        navigator.pushViewController(viewControllerB, animated: true)
    }
}

因此,这里是完整的代码:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController  {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    // MARK: Passing data through navigation PushViewController
    @IBAction func goToViewControllerB(_ sender: Any) {

        if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
            viewControllerB.isSomethingEnabled = true
            if let navigator = navigationController {
                navigator.pushViewController(viewControllerB, animated: true)
            }
        }
    }
}

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {

    // MARK:  - Variable for Passing Data through Navigation push
    var isSomethingEnabled = false

    override func viewDidLoad() {
        super.viewDidLoad()
        // Print value received through navigation push
        print("Value of 'isSomethingEnabled' from ViewControllerA: ", isSomethingEnabled)
    }
}

通过 Segue 传递数据从 ViewControllerA 到 ViewControllerB

第 1 步。 创建从 ViewControllerA 到 ViewControllerB 的 Segue,并在 Storyboard 中指定 Identifier = showDetailSegue,如下所示

< a href="https://i.sstatic.net/fWzeW.png" rel="noreferrer">在此处输入图像描述

步骤 2. 在 ViewControllerB 中声明一个名为 isSomethingEnabled 的可行变量并打印其值。

第3步。在ViewControllerA中传递isSomethingEnabled的值,同时传递Segue

所以这里是完整的代码:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController  {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    // MARK:  - - Passing Data through Segue  - -
    @IBAction func goToViewControllerBUsingSegue(_ sender: Any) {
        performSegue(withIdentifier: "showDetailSegue", sender: nil)
    }

    // Segue Delegate Method
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if (segue.identifier == "showDetailSegue") {
            let controller = segue.destination as? ViewControllerB
            controller?.isSomethingEnabled = true//passing data
        }
    }
}

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {
    var isSomethingEnabled = false

    override func viewDidLoad() {
        super.viewDidLoad()
        // Print value received through segue
        print("Value of 'isSomethingEnabled' from ViewControllerA: ", isSomethingEnabled)
    }
}

通过委托传递数据从 ViewControllerB 到 ViewControllerA

第 1 步。声明协议ViewControllerBDelegate在 ViewControllerB 文件中,但在类之外

protocol ViewControllerBDelegate: NSObjectProtocol {

    // Classes that adopt this protocol MUST define
    // this method -- and hopefully do something in
    // that definition.
    func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?)
}

第 2 步。 在 ViewControllerB 中声明 Delegate 变量实例

var delegate: ViewControllerBDelegate?

第 3 步。 在 ViewControllerB 的 viewDidLoad 方法内部发送委托数据

delegate?.addItemViewController(self, didFinishEnteringItem: "Data for ViewControllerA")

第 4 步。 确认 ViewControllerA 中的 ViewControllerBDelegate

class ViewControllerA: UIViewController, ViewControllerBDelegate  {
// to do
}

第 5 步。 确认您将在 ViewControllerA 中实现委托

if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
    viewControllerB.delegate = self//confirming delegate
    if let navigator = navigationController {
        navigator.pushViewController(viewControllerB, animated: true)
    }
}

第 6 步。 在 ViewControllerA 中实现用于接收数据的委托方法

func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?) {
    print("Value from ViewControllerB's Delegate", item!)
}

所以这里 是以下完整代码:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController, ViewControllerBDelegate  {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    // Delegate method
    func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?) {
        print("Value from ViewControllerB's Delegate", item!)
    }

    @IBAction func goToViewControllerForDelegate(_ sender: Any) {

        if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
            viewControllerB.delegate = self
            if let navigator = navigationController {
                navigator.pushViewController(viewControllerB, animated: true)
            }
        }
    }
}

ViewControllerB

import UIKit

//Protocol decleare
protocol ViewControllerBDelegate: NSObjectProtocol {
    // Classes that adopt this protocol MUST define
    // this method -- and hopefully do something in
    // that definition.
    func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?)
}

class ViewControllerB: UIViewController {
    var delegate: ViewControllerBDelegate?

    override func viewDidLoad() {
        super.viewDidLoad()
        // MARK:  - - - -  Set Data for Passing Data through Delegate  - - - - - -
        delegate?.addItemViewController(self, didFinishEnteringItem: "Data for ViewControllerA")
    }
}

通过通知观察器传递数据从 ViewControllerB 到 ViewControllerA

步骤1.在ViewControllerB中的通知观察者中设置并发布数据

let objToBeSent = "Test Message from Notification"
        NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: objToBeSent)

步骤2.在ViewControllerA中添加通知观察者

NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)

步骤3.接收通知ViewControllerA 中的数据值

@objc func methodOfReceivedNotification(notification: Notification) {
    print("Value of notification: ", notification.object ?? "")
}

所以这里是完整的代码:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController{

    override func viewDidLoad() {
        super.viewDidLoad()

        // Add observer in controller(s) where you want to receive data
        NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)
    }

    // MARK: Method for receiving Data through Post Notification
    @objc func methodOfReceivedNotification(notification: Notification) {
        print("Value of notification: ", notification.object ?? "")
    }
}

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // MARK:Set data for Passing Data through Post Notification
        let objToBeSent = "Test Message from Notification"
        NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: objToBeSent)
    }
}

通过块传递数据从 ViewControllerB 到 ViewControllerA

步骤1.在ViewControllerB中声明块

var authorizationCompletionBlock:((Bool)->())? = {_ in}

步骤2.在ViewControllerB中的块中设置数据

if authorizationCompletionBlock != nil
{
    authorizationCompletionBlock!(true)
}

步骤3.在ViewControllerA中接收块数据

// Receiver Block
controller!.authorizationCompletionBlock = { isGranted in
    print("Data received from Block is: ", isGranted)
}

所以这里是完整的代码:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController  {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    // MARK:Method for receiving Data through Block
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if (segue.identifier == "showDetailSegue") {
            let controller = segue.destination as? ViewControllerB
            controller?.isSomethingEnabled = true

            // Receiver Block
            controller!.authorizationCompletionBlock = { isGranted in
                print("Data received from Block is: ", isGranted)
            }
        }
    }
}

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {

    // MARK: Variable for Passing Data through Block
    var authorizationCompletionBlock:((Bool)->())? = {_ in}

    override func viewDidLoad() {
        super.viewDidLoad()

        // MARK: Set data for Passing Data through Block
        if authorizationCompletionBlock != nil
        {
            authorizationCompletionBlock!(true)
        }
    }
}

您可以在我的 GitHub 上找到完整的示例应用程序 如果您对此有任何疑问,请告诉我。

Swift 5

Well Matt Price's answer is perfectly fine for passing data, but I
am going to rewrite it, in the latest Swift version because I believe new
programmers find it quit challenging due to new syntax and
methods/frameworks, as original post is in Objective-C.

There are multiple options for passing data between view controllers.

  1. Using Navigation Controller Push
  2. Using Segue
  3. Using Delegate
  4. Using Notification Observer
  5. Using Block

I am going to rewrite his logic in Swift with the latest iOS framework


Passing Data through Navigation Controller Push: From ViewControllerA to ViewControllerB

Step 1. Declare variable in ViewControllerB

var isSomethingEnabled = false

Step 2. Print Variable in ViewControllerB' ViewDidLoad method

override func viewDidLoad() {
    super.viewDidLoad()
    // Print value received through segue, navigation push
    print("Value of 'isSomethingEnabled' from ViewControllerA: ", isSomethingEnabled)
}

Step 3. In ViewControllerA Pass Data while pushing through Navigation Controller

if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
    viewControllerB.isSomethingEnabled = true
    if let navigator = navigationController {
        navigator.pushViewController(viewControllerB, animated: true)
    }
}

So here is the complete code for:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController  {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    // MARK: Passing data through navigation PushViewController
    @IBAction func goToViewControllerB(_ sender: Any) {

        if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
            viewControllerB.isSomethingEnabled = true
            if let navigator = navigationController {
                navigator.pushViewController(viewControllerB, animated: true)
            }
        }
    }
}

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {

    // MARK:  - Variable for Passing Data through Navigation push
    var isSomethingEnabled = false

    override func viewDidLoad() {
        super.viewDidLoad()
        // Print value received through navigation push
        print("Value of 'isSomethingEnabled' from ViewControllerA: ", isSomethingEnabled)
    }
}

Passing Data through Segue: From ViewControllerA to ViewControllerB

Step 1. Create Segue from ViewControllerA to ViewControllerB and give Identifier = showDetailSegue in Storyboard as shown below

enter image description here

Step 2. In ViewControllerB Declare a viable named isSomethingEnabled and print its value.

Step 3. In ViewControllerA pass isSomethingEnabled's value while passing Segue

So here is the complete code for:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController  {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    // MARK:  - - Passing Data through Segue  - -
    @IBAction func goToViewControllerBUsingSegue(_ sender: Any) {
        performSegue(withIdentifier: "showDetailSegue", sender: nil)
    }

    // Segue Delegate Method
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if (segue.identifier == "showDetailSegue") {
            let controller = segue.destination as? ViewControllerB
            controller?.isSomethingEnabled = true//passing data
        }
    }
}

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {
    var isSomethingEnabled = false

    override func viewDidLoad() {
        super.viewDidLoad()
        // Print value received through segue
        print("Value of 'isSomethingEnabled' from ViewControllerA: ", isSomethingEnabled)
    }
}

Passing Data through Delegate: From ViewControllerB to ViewControllerA

Step 1. Declare Protocol ViewControllerBDelegate in the ViewControllerB file, but outside the class

protocol ViewControllerBDelegate: NSObjectProtocol {

    // Classes that adopt this protocol MUST define
    // this method -- and hopefully do something in
    // that definition.
    func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?)
}

Step 2. Declare Delegate variable instance in ViewControllerB

var delegate: ViewControllerBDelegate?

Step 3. Send data for delegate inside viewDidLoad method of ViewControllerB

delegate?.addItemViewController(self, didFinishEnteringItem: "Data for ViewControllerA")

Step 4. Confirm ViewControllerBDelegate in ViewControllerA

class ViewControllerA: UIViewController, ViewControllerBDelegate  {
// to do
}

Step 5. Confirm that you will implement a delegate in ViewControllerA

if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
    viewControllerB.delegate = self//confirming delegate
    if let navigator = navigationController {
        navigator.pushViewController(viewControllerB, animated: true)
    }
}

Step 6. Implement delegate method for receiving data in ViewControllerA

func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?) {
    print("Value from ViewControllerB's Delegate", item!)
}

So here is the complete code for:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController, ViewControllerBDelegate  {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    // Delegate method
    func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?) {
        print("Value from ViewControllerB's Delegate", item!)
    }

    @IBAction func goToViewControllerForDelegate(_ sender: Any) {

        if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
            viewControllerB.delegate = self
            if let navigator = navigationController {
                navigator.pushViewController(viewControllerB, animated: true)
            }
        }
    }
}

ViewControllerB

import UIKit

//Protocol decleare
protocol ViewControllerBDelegate: NSObjectProtocol {
    // Classes that adopt this protocol MUST define
    // this method -- and hopefully do something in
    // that definition.
    func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?)
}

class ViewControllerB: UIViewController {
    var delegate: ViewControllerBDelegate?

    override func viewDidLoad() {
        super.viewDidLoad()
        // MARK:  - - - -  Set Data for Passing Data through Delegate  - - - - - -
        delegate?.addItemViewController(self, didFinishEnteringItem: "Data for ViewControllerA")
    }
}

Passing Data through Notification Observer: From ViewControllerB to ViewControllerA

Step 1. Set and post data in the notification observer in ViewControllerB

let objToBeSent = "Test Message from Notification"
        NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: objToBeSent)

Step 2. Add Notification Observer in ViewControllerA

NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)

Step 3. Receive Notification data value in ViewControllerA

@objc func methodOfReceivedNotification(notification: Notification) {
    print("Value of notification: ", notification.object ?? "")
}

So here is the complete code for:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController{

    override func viewDidLoad() {
        super.viewDidLoad()

        // Add observer in controller(s) where you want to receive data
        NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)
    }

    // MARK: Method for receiving Data through Post Notification
    @objc func methodOfReceivedNotification(notification: Notification) {
        print("Value of notification: ", notification.object ?? "")
    }
}

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // MARK:Set data for Passing Data through Post Notification
        let objToBeSent = "Test Message from Notification"
        NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: objToBeSent)
    }
}

Passing Data through Block: From ViewControllerB to ViewControllerA

Step 1. Declare block in ViewControllerB

var authorizationCompletionBlock:((Bool)->())? = {_ in}

Step 2. Set data in block in ViewControllerB

if authorizationCompletionBlock != nil
{
    authorizationCompletionBlock!(true)
}

Step 3. Receive block data in ViewControllerA

// Receiver Block
controller!.authorizationCompletionBlock = { isGranted in
    print("Data received from Block is: ", isGranted)
}

So here is the complete code for:

ViewControllerA

import UIKit

class ViewControllerA: UIViewController  {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    // MARK:Method for receiving Data through Block
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if (segue.identifier == "showDetailSegue") {
            let controller = segue.destination as? ViewControllerB
            controller?.isSomethingEnabled = true

            // Receiver Block
            controller!.authorizationCompletionBlock = { isGranted in
                print("Data received from Block is: ", isGranted)
            }
        }
    }
}

ViewControllerB

import UIKit

class ViewControllerB: UIViewController {

    // MARK: Variable for Passing Data through Block
    var authorizationCompletionBlock:((Bool)->())? = {_ in}

    override func viewDidLoad() {
        super.viewDidLoad()

        // MARK: Set data for Passing Data through Block
        if authorizationCompletionBlock != nil
        {
            authorizationCompletionBlock!(true)
        }
    }
}

You can find complete sample Application at my GitHub Please let me know if you have any question(s) on this.

掩耳倾听 2024-10-27 16:23:37

如果要将数据从一个控制器传递到另一个控制器,请尝试以下代码:

File FirstViewController.h

@property (nonatomic, retain) NSString *str;

SecondViewController.h

@property (nonatomic, retain) NSString *str1;

File FirstViewController.m

- (void)viewDidLoad
   {
     // Message for the second SecondViewController
     self.str = @"text message";

     [super viewDidLoad];
   }

-(IBAction)ButtonClicked
 {
   SecondViewController *secondViewController = [[SecondViewController alloc] initWithNibName:@"SecondViewController" bundle:nil];
   secondViewController.str1 = str;
  [self.navigationController pushViewController:secondViewController animated:YES];
 }

If you want to pass data from one controller to other, try this code:

File FirstViewController.h

@property (nonatomic, retain) NSString *str;

SecondViewController.h

@property (nonatomic, retain) NSString *str1;

File FirstViewController.m

- (void)viewDidLoad
   {
     // Message for the second SecondViewController
     self.str = @"text message";

     [super viewDidLoad];
   }

-(IBAction)ButtonClicked
 {
   SecondViewController *secondViewController = [[SecondViewController alloc] initWithNibName:@"SecondViewController" bundle:nil];
   secondViewController.str1 = str;
  [self.navigationController pushViewController:secondViewController animated:YES];
 }
谁把谁当真 2024-10-27 16:23:37

这是一个非常古老的答案,这是反模式。请使用代表。不要使用这种方法!!

1. 在第二个视图控制器中创建第一个视图控制器的实例,并使其属性@property (nonatomic,assign)

2. 分配此视图控制器的 SecondviewController 实例。

2.完成选择操作后,将数组复制到第一个视图控制器。当您卸载第二个视图时,第一个视图将保存数组数据。

This is a very old answer and this is anti pattern. Please use delegates. Do not use this approach!!

1. Create the instance of the first view controller in the second view controller and make its property @property (nonatomic,assign).

2. Assign the SecondviewController instance of this view controller.

2. When you finish the selection operation, copy the array to the first View Controller. When you unload the second view, the first view will hold the array data.

孤芳又自赏 2024-10-27 16:23:37

我寻找这个解决方案很长时间,终于找到了。首先,在 SecondViewController.h 文件中声明所有对象,就像

@interface SecondViewController: UIviewController
{
    NSMutableArray *myAray;
    CustomObject *object;
}

现在在实现文件中一样,为这些对象分配内存,如下所示:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
     self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
     if (self)
     {
         // Custom initialization
         myAray=[[NSMutableArray alloc] init];
         object=[[CustomObject alloc] init];
     }
     return self;
}

现在您已经为 Array 和对象分配了内存。现在,您可以在推送此 ViewController 之前填充该内存。

转到 SecondViewController.h 并编写两个方法:

-(void)setMyArray:(NSArray *)_myArray;
-(void)setMyObject:(CustomObject *)_myObject;

在实现文件中,您可以实现以下功能:

-(void)setMyArray:(NSArray *)_myArray
{
     [myArra addObjectsFromArray:_myArray];
}

-(void)setMyObject:(CustomObject *)_myObject
{
     [object setCustomObject:_myObject];
}

期望您的 CustomObject 必须具有一个 setter 函数。

现在你的基本工作已经完成。转到您想要推送 SecondViewController 的位置并执行以下操作:

SecondViewController *secondView= [[SecondViewController alloc] initWithNibName:@"SecondViewController " bundle:[NSBundle MainBundle]] ;
[secondView setMyArray:ArrayToPass];
[secondView setMyObject:objectToPass];
[self.navigationController pushViewController:secondView animated:YES ];

注意拼写错误。

I was searching this solution for long time, and at last I found it. First of all, declare all the objects in your SecondViewController.h file like

@interface SecondViewController: UIviewController
{
    NSMutableArray *myAray;
    CustomObject *object;
}

Now in your implementation file, allocate the memory for those objects like this:

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
     self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
     if (self)
     {
         // Custom initialization
         myAray=[[NSMutableArray alloc] init];
         object=[[CustomObject alloc] init];
     }
     return self;
}

Now you have allocated the memory for Array and object. Now you can fill that memory before pushing this ViewController.

Go to your SecondViewController.h and write two methods:

-(void)setMyArray:(NSArray *)_myArray;
-(void)setMyObject:(CustomObject *)_myObject;

In the implementation file, you can implement the function:

-(void)setMyArray:(NSArray *)_myArray
{
     [myArra addObjectsFromArray:_myArray];
}

-(void)setMyObject:(CustomObject *)_myObject
{
     [object setCustomObject:_myObject];
}

Expecting that your CustomObject must have a setter function with it.

Now your basic work is done. Go to the place where you want to push the SecondViewController and do the following stuff:

SecondViewController *secondView= [[SecondViewController alloc] initWithNibName:@"SecondViewController " bundle:[NSBundle MainBundle]] ;
[secondView setMyArray:ArrayToPass];
[secondView setMyObject:objectToPass];
[self.navigationController pushViewController:secondView animated:YES ];

Take care for spelling mistakes.

勿忘初心 2024-10-27 16:23:37

这不是这样做的方法。您应该使用代表。

我假设我们有两个视图控制器,ViewController1 和 ViewController2,并且此检查位于第一个视图控制器中,当其状态发生变化时,您想要在 ViewController2 中执行某些操作。为了以正确的方式实现这一点,您应该执行以下操作:

将新文件添加到您的项目(Objective-C 协议)菜单文件新建。现在将其命名为 ViewController1Delegate 或任何您想要的名称,并在 @interface 和 @end 指令之间编写这些内容:

@optional

- (void)checkStateDidChange:(BOOL)checked;

现在转到 ViewController2.h 并添加:

#import "ViewController1Delegate.h"

然后将其定义更改为:

@interface ViewController2: UIViewController<ViewController1Delegate>

现在转到 ViewController2.m 并在实现中添加:

- (void)checkStateDidChange:(BOOL)checked {
     if (checked) {
           // Do whatever you want here
           NSLog(@"Checked");
     }
     else {
           // Also do whatever you want here
           NSLog(@"Not checked");
     }
}

现在转到ViewController1.h 并添加以下属性:

@property (weak, nonatomic) id<ViewController1Delegate> delegate;

现在,如果您在某个事件发生后在 ViewController2 内创建 ViewController1,那么您应该使用 NIB 文件以这种方式执行此操作:

ViewController1* controller = [[NSBundle mainBundle] loadNibNamed:@"ViewController1" owner:self options:nil][0];
controller.delegate = self;
[self presentViewController:controller animated:YES completion:nil];

现在您已全部设置完毕。每当您检测到 ViewController1 中的 check 更改事件时,您所要做的就是以下内容:

[delegate checkStateDidChange:checked]; // You pass here YES or NO based on the check state of your control

This is not the way to do it. You should use delegates.

I'll assume we have two view controllers, ViewController1 and ViewController2, and this check thing is in the first one and when its state changes, you want to do something in ViewController2. To achieve that in the proper way, you should do the below:

Add a new file to your project (Objective-C Protocol) menu FileNew. Now name it ViewController1Delegate or whatever you want and write these between the @interface and @end directives:

@optional

- (void)checkStateDidChange:(BOOL)checked;

Now go to ViewController2.h and add:

#import "ViewController1Delegate.h"

Then change its definition to:

@interface ViewController2: UIViewController<ViewController1Delegate>

Now go to ViewController2.m and inside the implementation add:

- (void)checkStateDidChange:(BOOL)checked {
     if (checked) {
           // Do whatever you want here
           NSLog(@"Checked");
     }
     else {
           // Also do whatever you want here
           NSLog(@"Not checked");
     }
}

Now go to ViewController1.h and add the following property:

@property (weak, nonatomic) id<ViewController1Delegate> delegate;

Now if you are creating ViewController1 inside ViewController2 after some event, then you should do it this way using NIB files:

ViewController1* controller = [[NSBundle mainBundle] loadNibNamed:@"ViewController1" owner:self options:nil][0];
controller.delegate = self;
[self presentViewController:controller animated:YES completion:nil];

Now you are all set. Whenever you detect the event of check changed in ViewController1, all you have to do is the below:

[delegate checkStateDidChange:checked]; // You pass here YES or NO based on the check state of your control
旧城空念 2024-10-27 16:23:37

如果您想将数据从一个视图控制器发送到另一个视图控制器,可以使用以下一种方法:

假设我们有视图控制器:viewControllerA 和 viewControllerB

现在位于文件 viewControllerB.h

@interface viewControllerB : UIViewController {

  NSString *string;
  NSArray *array;

}

- (id)initWithArray:(NSArray)a andString:(NSString)s;

文件中 viewControllerB.m

#import "viewControllerB.h"

@implementation viewControllerB

- (id)initWithArray:(NSArray)a andString:(NSString)s {

   array = [[NSArray alloc] init];
   array = a;

   string = [[NSString alloc] init];
   string = s;

}

在文件 viewControllerA.m 中:

#import "viewControllerA.h"
#import "viewControllerB.h"

@implementation viewControllerA

- (void)someMethod {

  someArray = [NSArray arrayWithObjects:@"One", @"Two", @"Three", nil];
  someString = [NSString stringWithFormat:@"Hahahahaha"];

  viewControllerB *vc = [[viewControllerB alloc] initWithArray:someArray andString:someString];

  [self.navigationController pushViewController:vc animated:YES];
  [vc release];
}

这就是您如何将数据从 viewControllerA 传递到 viewControllerB 而无需设置任何委托。 ;)

If you want to send data from one to another viewController, here's a way to do it:

Say we have viewControllers: viewControllerA and viewControllerB

Now in file viewControllerB.h

@interface viewControllerB : UIViewController {

  NSString *string;
  NSArray *array;

}

- (id)initWithArray:(NSArray)a andString:(NSString)s;

In file viewControllerB.m:

#import "viewControllerB.h"

@implementation viewControllerB

- (id)initWithArray:(NSArray)a andString:(NSString)s {

   array = [[NSArray alloc] init];
   array = a;

   string = [[NSString alloc] init];
   string = s;

}

In file viewControllerA.m:

#import "viewControllerA.h"
#import "viewControllerB.h"

@implementation viewControllerA

- (void)someMethod {

  someArray = [NSArray arrayWithObjects:@"One", @"Two", @"Three", nil];
  someString = [NSString stringWithFormat:@"Hahahahaha"];

  viewControllerB *vc = [[viewControllerB alloc] initWithArray:someArray andString:someString];

  [self.navigationController pushViewController:vc animated:YES];
  [vc release];
}

So this is how you can pass data from viewControllerA to viewControllerB without setting any delegate. ;)

ι不睡觉的鱼゛ 2024-10-27 16:23:37

凭借 Swift 的倾向并想要一个简单的示例,如果您使用 Segue 来解决问题,这里是我传递数据的首选方法。

它与上面类似,但没有按钮、标签等。只是简单地将数据从一个视图传递到下一个视图。

设置情节提要

共有三个部分。

  1. 发送者
  2. Segue
  3. 接收者

这是一个非常简单的视图布局,它们之间有一个 segue。


非常简单的视图注意:没有导航控制器


这是发送者的设置


The Sender


这是接收方的设置。


接收者


最后,segue 的设置。


Segue 标识符


视图控制器

我们保持简单,因此没有按钮和操作。我们只是在应用程序加载时将数据从发送方移动到接收方,然后将传输的值输出到控制台。

该页面获取最初加载的值并将其传递。

import UIKit


class ViewControllerSender: UIViewController {

    // THE STUFF - put some information into a variable
    let favoriteMovie = "Ghost Busters"

    override func viewDidAppear(animated: Bool) {
        // PASS IDENTIFIER - go to the receiving view controller.
        self.performSegueWithIdentifier("goToReciever", sender: self)
    }

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

        // GET REFERENCE - ...to the receiver view.
        var viewControllerReceiver = segue.destinationViewController as? ViewControllerReceiver

        // PASS STUFF - pass the variable along to the target.
        viewControllerReceiver!.yourFavMovie = self.favoriteMovie

    }
}

该页面仅在加载时将变量的值发送到控制台。至此,我们最喜欢的电影应该在该变量中。

import UIKit

class ViewControllerReceiver: UIViewController {

    // Basic empty variable waiting for you to pass in your fantastic favorite movie.
    var yourFavMovie = ""

    override func viewDidLoad() {
        super.viewDidLoad()

        // And now we can view it in the console.
        println("The Movie is \(self.yourFavMovie)")

    }
}

如果您想使用 Segue 并且您的页面没有位于导航控制器下,这就是您可以解决此问题的方法。

一旦运行,它应该自动切换到接收者视图,并将值从发送者传递到接收者,并在控制台中显示该值。

幽灵克星是一个经典的人。

With a Swift slant and want a bare-bones example, here is my go-to method for passing data if you are using a segue to get around.

It is similar to the above but without the buttons, labels and such. Just simply passing data from one view to the next.

Setup The Storyboard

There are three parts.

  1. The Sender
  2. The Segue
  3. The Receiver

This is a very simple view layout with a segue between them.


Very simple view layout. Note: No navigation controller


Here is the setup for the sender


The Sender


Here is the setup for the receiver.


The Receiver


Lastly, the setup for the segue.


The Segue Identifier


The View Controllers

We are keeping this simple so no buttons and not actions. We are simply moving data from the sender to the receiver when the application loads and then outputting the transmitted value to the console.

This page takes the initially loaded value and passes it along.

import UIKit


class ViewControllerSender: UIViewController {

    // THE STUFF - put some information into a variable
    let favoriteMovie = "Ghost Busters"

    override func viewDidAppear(animated: Bool) {
        // PASS IDENTIFIER - go to the receiving view controller.
        self.performSegueWithIdentifier("goToReciever", sender: self)
    }

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

        // GET REFERENCE - ...to the receiver view.
        var viewControllerReceiver = segue.destinationViewController as? ViewControllerReceiver

        // PASS STUFF - pass the variable along to the target.
        viewControllerReceiver!.yourFavMovie = self.favoriteMovie

    }
}

This page just sends the value of the variable to the console when it loads. By this point, our favorite movie should be in that variable.

import UIKit

class ViewControllerReceiver: UIViewController {

    // Basic empty variable waiting for you to pass in your fantastic favorite movie.
    var yourFavMovie = ""

    override func viewDidLoad() {
        super.viewDidLoad()

        // And now we can view it in the console.
        println("The Movie is \(self.yourFavMovie)")

    }
}

That is how you can tackle it if you want to use a segue and you don't have your pages under a navigation controller.

Once it is run, it should switch to the receiver view automatically and pass the value from the sender to the receiver, displaying the value in the console.

Ghost Busters is a classic folks.

狼性发作 2024-10-27 16:23:37

就我而言,我使用了一个单例类,它可以作为全局对象工作,允许从应用程序中的几乎所有位置访问数据。

首先是建立一个单例类。请参阅页面我的 Objective-C 单例应该是什么样子喜欢?

为了使该对象全局可访问,我只需将其导入到 appName_Prefix.pch 中,用于在每个类中应用 import 语句。

为了访问并使用这个对象,我简单地实现了一个类方法来返回共享实例,该实例包含它自己的变量。

In my case, I used a singleton class which could work as a global object allowing accesses to the data from almost everywhere in the app.

The first thing is to build a singleton class. Please refer to the page What should my Objective-C singleton look like?.

And to make the object globally accessible, I simply imported it in appName_Prefix.pch which was for applying import statement in every classes.

To access this object and to use it, I simply implemented a class method to return the shared instance, which contains its own variables.

温柔少女心 2024-10-27 16:23:37

在 FirstViewController 和 SecondViewController 之间传递数据如下

例如:

FirstViewController String value as

StrFirstValue = @"first";

所以我们可以使用以下步骤在第二个类中传递这个值:

  1. 我们需要在 < 中创建一个字符串对象em>SecondViewController.h 文件

     NSString *strValue;
    
  2. 需要在 .h 文件中声明一个属性,如下所示

     @property (强,非原子) NSString *strSecondValue;
    
  3. 需要在标头声明下方的 FirstViewController.m 文件中合成该值

     @synthesize strValue;
    

    在文件FirstViewController.h中:

     @property (强,非原子) NSString *strValue;
    
  4. 在 FirstViewController 中,我们从哪个方法导航到第二个视图,请在该方法中写入以下代码。

     SecondViewController *secondView= [[SecondViewController 分配]
     initWithNibName:@"SecondViewController " 包:[NSBundle MainBundle]];
    
     [secondView setStrSecondValue:StrFirstValue];
    
     [self.navigationController PushViewController:secondView 动画:是];
    

Passing data between FirstViewController to SecondViewController as below

For example:

FirstViewController String value as

StrFirstValue = @"first";

So we can pass this value in the second class using the below steps:

  1. We need to create a string object in the SecondViewController.h file

     NSString *strValue;
    
  2. Need to declare a property as the below declaration in the .h file

     @property (strong, nonatomic)  NSString *strSecondValue;
    
  3. Need synthesize that value in the FirstViewController.m file below the header declaration

     @synthesize strValue;
    

    And in file FirstViewController.h:

     @property (strong, nonatomic)  NSString *strValue;
    
  4. In FirstViewController, from which method we navigate to the second view, please write the below code in that method.

     SecondViewController *secondView= [[SecondViewController alloc]
     initWithNibName:@"SecondViewController " bundle:[NSBundle MainBundle]];
    
     [secondView setStrSecondValue:StrFirstValue];
    
     [self.navigationController pushViewController:secondView animated:YES ];
    
空名 2024-10-27 16:23:37

我目前正在通过一个名为 MCViewFactory 的项目为这个问题的开源解决方案做出贡献,该项目可以在这里找到:

Manticore iOS View Factory

这个想法是模仿 Android 的意图范例,使用全局工厂来管理您正在查看的视图,并使用“意图”在视图之间切换和传递数据。所有文档都位于 GitHub 页面上,但这里有一些亮点:

您可以在 .XIB 文件中设置所有视图,并将它们注册到应用程序委托中,同时初始化工厂。

// Register activities

MCViewFactory *factory = [MCViewFactory sharedFactory];

// The following two lines are optional.
[factory registerView:@"YourSectionViewController"];

现在,在视图控制器 (VC) 中,只要您想要移动到新的 VC 并传递数据,您就可以创建一个新意图并将数据添加到其字典 (savedInstanceState) 中。然后,只需设置工厂的当前意图:

MCIntent* intent = [MCIntent intentWithSectionName:@"YourSectionViewController"];
[intent setAnimationStyle:UIViewAnimationOptionTransitionFlipFromLeft];
[[intent savedInstanceState] setObject:@"someValue" forKey:@"yourKey"];
[[intent savedInstanceState] setObject:@"anotherValue" forKey:@"anotherKey"];
// ...
[[MCViewModel sharedModel] setCurrentSection:intent];

所有符合此要求的视图都需要是 MCViewController 的子类,它允许您重写新的 onResume: 方法,从而允许您访问传入的数据。

-(void)onResume:(MCIntent *)intent {
    NSObject* someValue = [intent.savedInstanceState objectForKey:@"yourKey"];
    NSObject* anotherValue = [intent.savedInstanceState objectForKey:@"anotherKey"];

    // ...

    // Ensure the following line is called, especially for MCSectionViewController
    [super onResume:intent];
}

I am currently contributing to an open source solution to this problem through a project called MCViewFactory, which may be found here:

Manticore iOS View Factory

The idea is imitate Android's intent paradigm, using a global factory to manage which view you are looking at and using "intents" to switch and pass data between views. All the documentation is on the GitHub page, but here are some highlights:

You setup all your views in .XIB files and register them in the app delegate, while initializing the factory.

// Register activities

MCViewFactory *factory = [MCViewFactory sharedFactory];

// The following two lines are optional.
[factory registerView:@"YourSectionViewController"];

Now, in your view controller (VC), anytime you want to move to a new VC and pass data, you create a new intent and add data to its dictionary (savedInstanceState). Then, just set the current intent of factory:

MCIntent* intent = [MCIntent intentWithSectionName:@"YourSectionViewController"];
[intent setAnimationStyle:UIViewAnimationOptionTransitionFlipFromLeft];
[[intent savedInstanceState] setObject:@"someValue" forKey:@"yourKey"];
[[intent savedInstanceState] setObject:@"anotherValue" forKey:@"anotherKey"];
// ...
[[MCViewModel sharedModel] setCurrentSection:intent];

All of your views that conform to this need to be subclasses of MCViewController, which allow you to override the new onResume: method, allowing you access to the data you've passed in.

-(void)onResume:(MCIntent *)intent {
    NSObject* someValue = [intent.savedInstanceState objectForKey:@"yourKey"];
    NSObject* anotherValue = [intent.savedInstanceState objectForKey:@"anotherKey"];

    // ...

    // Ensure the following line is called, especially for MCSectionViewController
    [super onResume:intent];
}
So要识趣 2024-10-27 16:23:37

在下一个视图控制器 .h 文件中创建属性并定义 getter 和 setter。

在 nextVC 上的 NextVC.h 中添加此属性

@property (strong, nonatomic) NSString *indexNumber;

在 NextVC.m 中添加

@synthesize indexNumber;

最后

NextVC *vc = [[NextVC alloc]init];

vc.indexNumber = @"123";

[self.navigationController vc animated:YES];

Create the property in the next view controller .h file and define getters and setters.

Add this property in NextVC.h on nextVC:

@property (strong, nonatomic) NSString *indexNumber;

Add

@synthesize indexNumber; in NextVC.m

And last

NextVC *vc = [[NextVC alloc]init];

vc.indexNumber = @"123";

[self.navigationController vc animated:YES];
雪落纷纷 2024-10-27 16:23:37

有很多方法可以做到这一点,选择正确的方法很重要。最大的架构决策之一可能在于如何在整个应用程序中共享或访问模型代码。

我不久前写了一篇关于此的博客文章:共享模型代码。下面是一个简短的总结:

共享数据

一种方法是在视图控制器之间共享指向模型对象的指针。

  • 在视图控制器(在导航或选项卡栏控制器中)上进行强力迭代以设置数据
  • 在prepareForSegue(如果是故事板)或init(如果是编程)中设置数据

因为准备segue是最常见的,这里是一个示例:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    var next = segue.destinationViewController as NextViewController
    next.dataSource = dataSource
}

独立访问

另一种方法是一次处理充满数据的屏幕,而不是将视图控制器彼此耦合,而是将每个视图控制器耦合到它们可以独立访问的单个数据源。

我见过的最常见的方法是 singleton 实例。因此,如果您的单例对象是 DataAccess,您可以在 UIViewController 的 viewDidLoad 方法中执行以下操作:

func viewDidLoad() {
    super.viewDidLoad()
    var data = dataAccess.requestData()
}

还有其他工具也可以帮助传递数据:

  • 键值观察
  • NSNotification
  • 核心数据
  • NSFetchedResultsController
  • 数据源

核心数据

核心数据的好处在于它具有反向关系。因此,如果您只想为 NotesViewController 提供注释对象,则可以,因为它与笔记本等其他内容具有反比关系。如果您需要 NotesViewController 中笔记本上的数据,您可以通过执行以下操作来返回对象图:

let notebookName = note.notebook.name

在我的博客文章中阅读有关此内容的更多信息:共享模型代码

There are tons of ways to do this and it's important to pick the right one. Probably one of the biggest architectural decisions lies on how the model code will be shared or accessed throughout the app.

I wrote a blog post about this a while back: Sharing Model Code. Here's a brief summary:

Shared data

One approach is to share pointers to the model objects between view controllers.

  • Brute force iteration on view controllers (in Navigation or Tab Bar Controller) to set the data
  • Set data in prepareForSegue (if storyboards) or init (if programmatic)

Since prepare for segue is the most common here is an example:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    var next = segue.destinationViewController as NextViewController
    next.dataSource = dataSource
}

Independent access

Another approach is to handle a screen full of data at a time and instead of coupling the view controllers to each other couple each view controller to single data source that they can get to independently.

The most common way I've seen this done is a singleton instance. So if your singleton object was DataAccess you could do the following in the viewDidLoad method of UIViewController:

func viewDidLoad() {
    super.viewDidLoad()
    var data = dataAccess.requestData()
}

There are addition tools that also help pass along data:

  • Key-Value Observing
  • NSNotification
  • Core Data
  • NSFetchedResultsController
  • Data Source

Core Data

The nice thing about Core Data is that it has inverse relationships. So if you want to just give a NotesViewController the notes object you can because it'll have an inverse relationship to something else like the notebook. If you need data on the notebook in the NotesViewController you can walk back up the object graph by doing the following:

let notebookName = note.notebook.name

Read more about this in my blog post: Sharing Model Code

请止步禁区 2024-10-27 16:23:37

如果您想将数据从 ViewControlerOne 传递到 ViewControllerTwo,请尝试这些...

在 ViewControlerOne.h 中执行这些操作:

 @property (nonatomic, strong) NSString *str1;

在 ViewControllerTwo.h 中执行这些操作:

 @property (nonatomic, strong) NSString *str2;

在 ViewControllerTwo.m 中合成 str2:

@interface ViewControllerTwo ()
@end
@implementation ViewControllerTwo
@synthesize str2;

在 ViewControlerOne.m 中执行这些操作:

 - (void)viewDidLoad
 {
   [super viewDidLoad];

   // Data or string you wants to pass in ViewControllerTwo...
   self.str1 = @"hello world";
 }

O 按钮单击事件,执行此操作:

-(IBAction)ButtonClicked
{
  // Navigation on buttons click event from ViewControlerOne to ViewControlerTwo with transferring data or string..
  ViewControllerTwo *objViewTwo = [self.storyboard instantiateViewControllerWithIdentifier:@"ViewControllerTwo"];
  obj.str2 = str1;
  [self.navigationController pushViewController: objViewTwo animated:YES];
}

在 ViewControllerTwo.m 中执行以下操作:

- (void)viewDidLoad
{
  [super viewDidLoad];
  NSLog(@"%@", str2);
}

If you want to pass data from ViewControlerOne to ViewControllerTwo, try these...

Do these in ViewControlerOne.h:

 @property (nonatomic, strong) NSString *str1;

Do these in ViewControllerTwo.h:

 @property (nonatomic, strong) NSString *str2;

Synthesize str2 in ViewControllerTwo.m:

@interface ViewControllerTwo ()
@end
@implementation ViewControllerTwo
@synthesize str2;

Do these in ViewControlerOne.m:

 - (void)viewDidLoad
 {
   [super viewDidLoad];

   // Data or string you wants to pass in ViewControllerTwo...
   self.str1 = @"hello world";
 }

O the buttons click event, do this:

-(IBAction)ButtonClicked
{
  // Navigation on buttons click event from ViewControlerOne to ViewControlerTwo with transferring data or string..
  ViewControllerTwo *objViewTwo = [self.storyboard instantiateViewControllerWithIdentifier:@"ViewControllerTwo"];
  obj.str2 = str1;
  [self.navigationController pushViewController: objViewTwo animated:YES];
}

Do these in ViewControllerTwo.m:

- (void)viewDidLoad
{
  [super viewDidLoad];
  NSLog(@"%@", str2);
}
听闻余生 2024-10-27 16:23:37

您可以将数据保存在应用程序委托中,以便跨应用程序中的视图控制器访问它。您所要做的就是创建应用程序委托的共享实例:

AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;

例如

如果您声明一个 NSArray 对象 *arrayXYZ,那么您可以通过以下方式在任何视图控制器中访问它: appDelegate.arrayXYZ

You can save data in an App delegate to access it across view controllers in your application. All you have to do is create a shared instance of an app delegate:

AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;

For Example

If you declare a NSArray object *arrayXYZ, then you can access it in any view controller by appDelegate.arrayXYZ.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文