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

Mat*_*ice 1340 model-view-controller objective-c uiviewcontroller ios swift

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

我有一个视图作为数据输入表单,我想让用户选择多个产品.产品列在另一个带有a的视图中,UITableViewController我启用了多个选择.

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

我已经四处浏览并看到一些人在app delegate中声明了一个数组.我读了一些关于单身人士的事情,但不明白这些是什么,我读了一些关于创建数据模型的内容.

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

Mat*_*ice 1656

这个问题似乎在stackoverflow上非常受欢迎,所以我想我会尝试给出一个更好的答案来帮助像iOS这样的iOS世界中的人们.

我希望这个答案足够清楚,让人们理解并且我没有错过任何东西.

传递数据

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

对于这个例子,我们将拥有ViewControllerAViewControllerB

要传递BOOLViewControllerA,ViewControllerB我们将执行以下操作.

  1. 在为... ViewControllerB.h创建属性BOOL

    @property (nonatomic, assign) BOOL isSomethingEnabled;
    
    Run Code Online (Sandbox Code Playgroud)
  2. ViewControllerA你需要告诉它ViewControllerB所以使用一个

    #import "ViewControllerB.h"
    
    Run Code Online (Sandbox Code Playgroud)

    然后你想加载视图的地方,例如.didSelectRowAtIndex或者IBAction你需要在ViewControllerB将它推到导航堆栈之前设置属性.

    ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
    viewControllerB.isSomethingEnabled = YES;
    [self pushViewController:viewControllerB animated:YES];
    
    Run Code Online (Sandbox Code Playgroud)

    这将设置isSomethingEnabledViewControllerBBOOL价值YES.

使用Segues传递数据

如果您使用的是Storyboard,则最有可能使用segues,并且需要使用此过程来传递数据.这与上面的类似,但不是在推送视图控制器之前传递数据,而是使用一个名为的方法

-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
Run Code Online (Sandbox Code Playgroud)

所以要传递一个BOOLfrom ViewControllerA,ViewControllerB我们会做以下事情:

  1. 在为... ViewControllerB.h创建属性BOOL

    @property (nonatomic, assign) BOOL isSomethingEnabled;
    
    Run Code Online (Sandbox Code Playgroud)
  2. ViewControllerA你需要告诉它ViewControllerB所以使用一个

    #import "ViewControllerB.h"
    
    Run Code Online (Sandbox Code Playgroud)
  3. 从创建的SEGUE ViewControllerAViewControllerB的故事板,并给它一个标识符,在这个例子中,我们叫它"showDetailSegue"

  4. 接下来,我们需要将方法添加到ViewControllerA执行任何segue时调用的方法,因此我们需要检测调用哪个segue然后执行某些操作.在我们的示例中,我们将检查"showDetailSegue"是否已执行,我们将传递我们的BOOLViewControllerB

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

    如果您将视图嵌入导航控制器中,则需要将上述方法稍微更改为以下方法

    -(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;
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    这将设置isSomethingEnabledViewControllerBBOOL价值YES.

传递数据

要将数据传回ViewControllerBViewControllerA您需要使用Protocols and DelegatesBlocks,后者可以用作回调的松散耦合机制.

为此,我们将成为ViewControllerA代表ViewControllerB.这允许ViewControllerB发回消息以ViewControllerA使我们能够发回数据.

对于ViewControllerA成为一个代表ViewControllerB它必须符合ViewControllerB"我们有指定的方案.这告诉ViewControllerA它必须实现哪些方法.

  1. ViewControllerB.h,低于#import,但在上面@interface指定协议.

    @class ViewControllerB;
    
    @protocol ViewControllerBDelegate <NSObject>
    - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item;
    @end
    
    Run Code Online (Sandbox Code Playgroud)
  2. 接下来还ViewControllerB.h需要设置一个delegate属性并合成ViewControllerB.m

    @property (nonatomic, weak) id <ViewControllerBDelegate> delegate;
    
    Run Code Online (Sandbox Code Playgroud)
  3. ViewControllerB我们呼吁的消息delegate时,我们弹出视图控制器.

    NSString *itemToPassBack = @"Pass this value back to ViewControllerA";
    [self.delegate addItemViewController:self didFinishEnteringItem:itemToPassBack];
    
    Run Code Online (Sandbox Code Playgroud)
  4. 就是这样ViewControllerB.现在ViewControllerA.h,告诉ViewControllerA导入ViewControllerB并遵守其协议.

    #import "ViewControllerB.h"
    
    @interface ViewControllerA : UIViewController <ViewControllerBDelegate>
    
    Run Code Online (Sandbox Code Playgroud)
  5. ViewControllerA.m落实从我们的协议下面的方法

    - (void)addItemViewController:(ViewControllerB *)controller didFinishEnteringItem:(NSString *)item
    {
        NSLog(@"This was returned from ViewControllerB %@",item);
    }
    
    Run Code Online (Sandbox Code Playgroud)
  6. 在推viewControllerB送到导航堆栈之前,我们需要告诉 ViewControllerBViewControllerA是它的委托,否则我们会收到错误.

    ViewControllerB *viewControllerB = [[ViewControllerB alloc] initWithNib:@"ViewControllerB" bundle:nil];
    viewControllerB.delegate = self
    [[self navigationController] pushViewController:viewControllerB animated:YES];
    
    Run Code Online (Sandbox Code Playgroud)

参考

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

NSNotification中心 这是传递数据的另一种方式.

// add 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];
Run Code Online (Sandbox Code Playgroud)

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

块是匿名函数.

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

定义一个块

@property void(^selectedVoucherBlock)(NSString *); // in ContollerA.h
Run Code Online (Sandbox Code Playgroud)

在需要值的位置添加块处理程序(侦听器)(例如,您需要在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;
    };
}
Run Code Online (Sandbox Code Playgroud)

转到控制器B.

UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
ControllerB *vc = [storyboard instantiateViewControllerWithIdentifier:@"ControllerB"];
vc.sourceVC = self;
    [self.navigationController pushViewController:vc animated:NO];
Run Code Online (Sandbox Code Playgroud)

火块

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

块的另一个工作示例

  • 我们还必须在@protocol定义上面放一个`@class ViewControllerB;`吗?没有它我在行中的ViewControllerB上得到一个"预期类型"错误:` - (void)addItemViewController:(ViewControllerB*)controller didFinishEnteringItem:(NSString*)item;`在`@ protocol`声明中 (24认同)
  • 你不需要代表回传,只需使用放松. (6认同)
  • 这非常有效.正如alan-p所说,不要忘记编写@class ViewControllerB; 在协议之上,否则您将收到"预期类型"错误. (4认同)
  • 当我把"viewControllerB.delegate = self;" 在ViewControllerB中我收到一个错误.从不兼容的类型'ViewControllerB*const __strong'分配给'id <ViewControllerBDelegate>',我不确定我做错了什么.有人可以帮忙吗?另外,我不得不改变:initWithNib - > initWithNibName. (4认同)
  • 如果你使用`NavigationController`你必须使用`[self.navigationController pushViewController:viewController animated:YES];`而不是`[self pushViewController:viewControllerB animated:YES];` (4认同)
  • 值得一提的是,数据可以通过展开segue向后传递. (2认同)

Sur*_*gch 172

迅速

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

将数据传递到下一个View Controller

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

在此输入图像描述

在Interface Builder中创建故事板布局.要制作segue,只需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!
    }

}
Run Code Online (Sandbox Code Playgroud)

第二视图控制器

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

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
    }

}
Run Code Online (Sandbox Code Playgroud)

别忘了

  • 挂钩的出口UITextFieldUILabel.
  • 将第一个和第二个View Controllers设置为IB中相应的Swift文件.

将数据传回上一个View Controller

要将数据从第二个视图控制器传回第一个视图控制器,请使用协议和委托.这个视频是一个非常清晰的过程:

以下是基于视频的示例(稍作修改).

在此输入图像描述

在Interface Builder中创建故事板布局.再次,要制作segue,只需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
    }
}
Run Code Online (Sandbox Code Playgroud)

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

第二视图控制器和协议

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

import UIKit

// protocol used for sending data back
protocol DataEnteredDelegate: class {
    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)
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,protocol它位于View Controller类之外.

而已.现在运行应用程序,您应该能够将数据从第二个视图控制器发送回第一个.

  • @蜜糖.我认为"代表"这个词令人困惑.让我用"工人"这个词."工人"(第一个视图控制器)执行"boss"(第二个视图控制器)告诉它要做的任何事情."老板"不知道它的"工人"是谁; 它可能是任何人.所以在第一个视图控制器("工人"类),它说,我将成为你的"工人".你告诉我在标签上写什么,我会为你做的.因此,`secondViewController.delegate = self`的意思是"我同意成为老板的工作者".有关其他示例和更多解释,请参阅[此答案](http://stackoverflow.com/a/33549729). (8认同)
  • 我见过的大多数Swift更新都是相对较小的语法更改,而不是视图控制器之间数据传递方式的变化.如果我确实知道这样的任何重大变化,我会更新我的答案. (4认同)
  • offtopic-iOS具有一种将参数传递给新的视图控制器的丑陋方式,这令人难以置信-您在进行调用时必须将参数设置在某个地方,而不是其他地方。Android在这方面有一个更好的方法-当您启动一个Activity时,您可以通过其启动Intent传递几乎所有数据。简单。无需投射等。将返回值传递回调用方也是必不可少的事情,无需委托。当然也可以使用丑陋的方法,那里没有问题)) (2认同)

Cal*_*leb 131

MVC中的M用于"模型",而在MVC范例中,模型类的作用是管理程序的数据.模型与视图相反 - 视图知道如何显示数据,但它不知道如何处理数据,而模型知道如何处理数据的所有内容,但不了解如何显示数据.模型可能很复杂,但它们不一定是 - 应用程序的模型可能像字符串或字典数组一样简单.

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


小智 92

有多种方法可以将数据接收到iOS中的不同类.例如 -

  1. 分配另一个类后直接初始化.
  2. 委派 - 用于传回数据
  3. 通知 - 用于一次向多个类广播数据
  4. 保存NSUserDefaults- 以便稍后访问
  5. 单身人士课程
  6. 数据库和其他存储机制,如plist等.

但是对于将值传递给在当前类中完成分配的其他类的简单方案,最常见和首选的方法是在分配后直接设置值.这样做如下: -

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

假设在Controller1类中,您要创建Controller2对象并使用传递的String值推送它.这可以这样做: -

- (void)pushToController2 {

    Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
    [obj passValue:@"String"];
    [self pushViewController:obj animated:YES];
}
Run Code Online (Sandbox Code Playgroud)

在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
Run Code Online (Sandbox Code Playgroud)

您也可以使用与此类似的方式直接设置Controller2类的属性:

- (void)pushToController2 {

    Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
    [obj setStringPassed:@"String"];  
    [self pushViewController:obj animated:YES];
}
Run Code Online (Sandbox Code Playgroud)

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

Controller2 *obj = [[Controller2 alloc] initWithNib:@"Controller2" bundle:nil];
[obj passValue:@“String1” andValues:objArray withDate:date]; 
Run Code Online (Sandbox Code Playgroud)

或者,如果需要传递超过3个与常用功能相关的参数,则可以将值存储到Model类中,并将该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];
Run Code Online (Sandbox Code Playgroud)

所以,如果你想 -

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.
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助


Mat*_*ice 83

经过更多的研究,似乎协议和代表是正确的/ Apple首选方式.

我最终使用了这个例子

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

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

感谢你的帮助

  • 不要使用协议和委托,只需使用展开. (3认同)

Les*_*rna 63

我找到了最简单,最优雅的版本和传递块.让名称视图控制器等待返回的数据为"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);
        };

    }
}
Run Code Online (Sandbox Code Playgroud)

和"B"视图控制器应该声明回调属性,BViewController.h:

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

比实现文件BViewController.m之后我们有所需的值来返回我们的回调应该被调用:

if (self.callback)
    self.callback(value1, value2);
Run Code Online (Sandbox Code Playgroud)

有一点要记住的是,经常使用块需要解释的一样来管理强__weak引用在这里


Jas*_*oss 54

在给出的许多答案中都有一些很好的信息,但没有一个完全解决这个问题.

该问题询问在视图控制器之间传递信息.给出的具体示例询问如何在视图之间传递信息,但鉴于iOS的自称新颖性,原始海报可能意味着在viewControllers之间,而不是视图之间(没有ViewControllers的任何参与).似乎所有答案都集中在两个视图控制器上,但如果应用程序演变为需要在信息交换中涉及两个以上的视图控制器呢?

原始海报还询问了SingletonsAppDelegate的使用.这些问题需要回答.

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

应用场景

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

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

原始问题图

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

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

家庭库存应用图

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

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

无论何时创建或编辑单个项目,它还必须与其他视图控制器同步.例如,如果我们在ViewControllerD中添加一艘船,但尚未投保,那么当用户转到ViewControllerA(奢侈品)和ViewControllerC(整个家庭库存)时,必须出现该船,但是当用户转到ViewControllerB(非保险项目).我们不仅需要关注添加新项目,还需要删除项目(可以从四个视图控制器中的任何一个中删除),或者编辑现有项目(可以从"添加新项目表单"中获取,重新利用相同的项目)用于编辑).

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

解决方案:代表和观察员模式以及单身人士

在方案一中,我们有几个可行的解决方案,正如其他答案所给出的那样

  • 塞格斯
  • 代表
  • 直接在视图控制器上设置属性
  • 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;
}
Run Code Online (Sandbox Code Playgroud)

现在我们已经理解了单例是什么,让我们讨论单例如何适应观察者模式.观察者模式用于一个对象以响应另一个对象的更改.在第二个场景中,我们有四个不同的视图控制器,他们都想知道对底层数据的更改."基础数据"应该属于单个实例,单个实例.通过观察对单身人士所做的改变来完成"了解变化".

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

#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
Run Code Online (Sandbox Code Playgroud)

当家庭库存物品的集合发生变化时,需要让视图控制器知道这种变化.上面的类定义并不能说明这将如何发生.我们需要遵循观察者模式.视图控制器必须正式观察sharedManager.有两种方法可以观察另一个对象:

  • 键值观察(KVO)
  • NSNotificationCenter.

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

许多初学者程序员利用这样一个事实,即在应用程序的生命周期中始终只有一个Application Delegate,可以全局访问.初学程序员使用此事实将对象和功能填充到appDelegate中,以方便从应用程序中的任何其他位置进行访问.仅仅因为AppDelegate是单身并不意味着它应该取代所有其他单身人士.这是一种糟糕的做法,因为它给一个班级带来了太多的负担,打破了良好的面向对象的做法.每个班级应该有一个明确的角色,很容易解释,通常只是通过班级的名称.

每当您的应用程序代表开始变得臃肿时,就开始将功能移除到单例中.例如,Core Data Stack不应该留在AppDelegate中,而应该放在它自己的类coreDataManager类中.

参考


Wea*_*ter 39

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

故事板segues,ARC和LLVM块使我比以往更容易.上面提到的故事板和细分的一些答案已经但仍然依赖于委托.定义委托肯定有效,但有些人可能会发现传递指针或代码块更容易.

使用UINavigators和segues,可以通过简单的方法将信息传递给从属控制器并获取信息.ARC使指向NSObjects派生的东西的指针变得简单,所以如果你想让subservient控制器为你添加/更改/修改一些数据,请将指针传递给一个可变实例.块使传递操作变得容易,因此如果您希望子服务控制器在更高级别的控制器上调用操作,则将其传递给块.您可以定义块以接受对您有意义的任意数量的参数.您还可以设计API以使用多个块,如果更适合的话.

以下是segue胶水的两个简单例子.第一个是直接显示传递输入的一个参数,第二个输出.

// 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];
}
Run Code Online (Sandbox Code Playgroud)

第二个示例显示为第二个参数传递回调块.我喜欢使用块,因为它使相关的细节在源中保持紧密 - 更高级别的源.

// 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];
    }];
}
Run Code Online (Sandbox Code Playgroud)


Yev*_*eni 39

将数据从ViewController 2(目标)传递回viewController 1(Source)是更有趣的事情.假设你使用storyBoard那些是我发现的所有方法:

  • 代表
  • 通知
  • 用户默认值
  • 独生子

这些已经在这里讨论过.

我发现还有更多方法:

- 使用块回调:

prepareForSegue在VC1 中的方法中使用它

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

- 使用故事板放松(退出)

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

-(IBAction)UnWindDone:(UIStoryboardSegue *)segue { }
Run Code Online (Sandbox Code Playgroud)

在storyBoard中将"返回"按钮挂钩到vc的绿色"退出"按钮(展开).现在你有一个"返回"的segue,所以你可以使用VC2的prepareForSegue中的destinationViewController属性,并在它返回之前更改VC1的任何属性.

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

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

    在VC1的prepareForSegue中,您可以更改要共享的任何属性.

在两个展开选项中,您可以设置按钮的tag属性并在prepareForSegue中进行检查.

希望我在讨论中加入一些内容.

:)干杯.


Anu*_*tra 38

有多种共享数据的方法.

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

    [[NSUserDefaults standardUserDefaults] setValue:value forKey:key]
    [[NSUserDefaults standardUserDefaults] objectForKey:key]
    
    Run Code Online (Sandbox Code Playgroud)
  2. 您可以在中创建一个属性viewcontrollerA.创建viewcontrollerAin 的对象viewcontrollerB并为该属性分配所需的值.

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

  • NSUserDefaults的典型用途是存储在应用程序执行之间持续存在的用户首选项,因此除非明确删除,否则此处存储的任何内容都将保留在此处.使用它在应用程序中的视图控制器(或任何其他对象)之间传递信息是一个非常糟糕的主意. (29认同)

小智 28

如果要将数据从一个控制器传递给其他控制器,请尝试使用此代码

FirstViewController.h

@property (nonatomic, retain) NSString *str;
Run Code Online (Sandbox Code Playgroud)

SecondViewController.h

@property (nonatomic, retain) NSString *str1;
Run Code Online (Sandbox Code Playgroud)

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];
 }
Run Code Online (Sandbox Code Playgroud)


Asi*_*bib 27

我很长时间都在寻找这个解决方案,Atlast我发现了它.首先声明SecondViewController.h文件中的所有对象

@interface SecondViewController: UIviewController 
{
    NSMutableArray *myAray;
    CustomObject *object;
}
Run Code Online (Sandbox Code Playgroud)

现在在您的实现文件中为这些对象分配内存

- (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;
}
Run Code Online (Sandbox Code Playgroud)

现在你已经分配了内存Array和对象.现在你可以在推动之前填充内存ViewController

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

-(void)setMyArray:(NSArray *)_myArray;
-(void)setMyObject:(CustomObject *)_myObject;
Run Code Online (Sandbox Code Playgroud)

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

-(void)setMyArray:(NSArray *)_myArray
{
     [myArra addObjectsFromArray:_myArray];
}
-(void)setMyObject:(CustomObject *)_myObject
{
     [object setCustomObject:_myObject];
}
Run Code Online (Sandbox Code Playgroud)

期待你CustomObject必须有一个setter函数.

现在你的基本工作已经完成.去你想要推动的地方SecondViewController并做以下事情

SecondViewController *secondView= [[SecondViewController alloc] initWithNibName:@"SecondViewController " bundle:[NSBundle MainBundle]] ;
[secondView setMyArray:ArrayToPass];
[secondView setMyObject:objectToPass];
[self.navigationController pushViewController:secondView animated:YES ];
Run Code Online (Sandbox Code Playgroud)

注意拼写错误.


kaa*_*r3k 23

1.在第二个View Controller中创建第一个View Controller的实例并创建其属性@property (nonatomic,assign).

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

2.完成选择操作后,将阵列复制到第一个View Controller,当你卸载SecondView时,FirstView将保存阵列数据.

希望这可以帮助.

  • 我不相信这是正确的方法,因为它在视图控制器之间创建了一个非常高的链接.并不是真的坚持MVC. (2认同)

Bod*_*ljo 22

这不是这样做的方法,你应该使用委托,我假设我们有两个视图控制器ViewController1和ViewController2,这个检查是在第一个,当它的状态发生变化时,你想在ViewController2中做一些事情,到以正确的方式实现,你应该做到以下:

将新文件添加到项目中(Objective-C Protocol)文件 - >新建,现在将其命名为ViewController1Delegate或任何您想要的内容,并在@interface和@end指令之间写入它们

@optional

- (void)checkStateDidChange:(BOOL)checked;
Run Code Online (Sandbox Code Playgroud)

现在转到ViewController2.h并添加

#import "ViewController1Delegate.h"
Run Code Online (Sandbox Code Playgroud)

然后将其定义更改为

@interface ViewController2: UIViewController<ViewController1Delegate>
Run Code Online (Sandbox Code Playgroud)

现在转到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");
     }
}
Run Code Online (Sandbox Code Playgroud)

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

@property (weak, nonatomic) id<ViewController1Delegate> delegate; 
Run Code Online (Sandbox Code Playgroud)

现在如果你在一些事件之后在ViewController2中创建ViewController1,那么你应该这样使用NIB文件:

ViewController1* controller = [[NSBundle mainBundle] loadNibNamed:@"ViewController1" owner:self options:nil][0];
controller.delegate = self;
[self presentViewController:controller animated:YES completion:nil];
Run Code Online (Sandbox Code Playgroud)

现在你已经完成了设置,每当你在ViewController1中检测到检查事件发生变化时,你所要做的就是下面的内容

[delegate checkStateDidChange:checked]; // You pass here YES or NO based on the check state of your control
Run Code Online (Sandbox Code Playgroud)

如果我没有正确理解你的问题,请告诉我是否有任何不清楚的事情.


Ani*_*shi 21

如果你想从一个viewController发送数据到另一个viewController,这是一个方法:

假设我们有viewControllers:viewControllerA和viewControllerB

现在在viewControllerB.h中

@interface viewControllerB : UIViewController {

  NSString *string;
  NSArray *array;

}

- (id)initWithArray:(NSArray)a andString:(NSString)s;
Run Code Online (Sandbox Code Playgroud)

在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;

}
Run Code Online (Sandbox Code Playgroud)

在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];

}
Run Code Online (Sandbox Code Playgroud)

这就是你可以在不设置任何委托的情况下将数据从viewControllerA传递给viewControllerB的方法.;)


pet*_*ine 18

在我的例子中,我使用了一个单例类,它可以作为一个全局对象,允许从应用程序的几乎所有位置访问数据.首先是建立一个单例类.请参阅页面," 我的Objective-C单例应该是什么样的? "而我所做的使全局可访问的对象只是导入它,appName_Prefix.pch用于在每个类中应用import语句.要访问此对象并使用,我只需实现类方法即可返回包含其自身变量的共享实例


Chr*_*ley 18

我知道这是一个被打败的主题,但对于那些希望用SWIFT倾斜回答这个问题的人并想要一个简单的例子,这里是我使用segue来绕过数据的传递方法.

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

设置故事板

有三个部分.

  1. 寄件人
  2. 塞格
  3. 收件人

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


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


这是发件人的设置


寄件人


这是接收器的设置.


收件人


最后,segue的设置.


Segue标识符


视图控制器

我们保持这个简单,所以没有按钮,而不是动作,我们只是在应用程序加载时将数据从发送方移动到接收方,然后将传输的值输出到控制台.

此页面获取最初加载的值并将其传递.

import UIKit


class ViewControllerSender: UIViewController {

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

    override func viewDidAppear(animated: Bool) {
        // PASS IDENTIFIER - go to the recieving 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

    }

}
Run Code Online (Sandbox Code Playgroud)

此页面在加载时只将变量的值发送到控制台.到此为止,我们最喜欢的电影应该在那个变量中.

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)")

    }   
}
Run Code Online (Sandbox Code Playgroud)

如果您想使用segue并且没有导航控制器下的页面,那么就可以解决这个问题.

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

Ghost Busters是一个经典的人.


Chr*_*lan 17

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

例如:

FirstViewController字符串值为

StrFirstValue = @"first";
Run Code Online (Sandbox Code Playgroud)

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

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

NSString *strValue;
Run Code Online (Sandbox Code Playgroud)

2>需要在.h文件中声明属性如下面的声明

@property (strong, nonatomic)  NSString *strSecondValue;
Run Code Online (Sandbox Code Playgroud)

3>需要在头部声明下面的FirstViewController.m文件中合成该值

@synthesize strValue;
Run Code Online (Sandbox Code Playgroud)

在FirstViewController.h中:

@property (strong, nonatomic)  NSString *strValue;
Run Code Online (Sandbox Code Playgroud)

4>在FirstViewController中,我们从哪个方法导航到第二个视图,请在该方法中写下面的代码.

SecondViewController *secondView= [[SecondViewController alloc]     
initWithNibName:@"SecondViewController " bundle:[NSBundle MainBundle]];

[secondView setStrSecondValue:StrFirstValue];

[self.navigationController pushViewController:secondView animated:YES ];
Run Code Online (Sandbox Code Playgroud)


小智 17

我目前通过一个名为MCViewFactory的项目为这个问题提供了一个开源解决方案,可以在这里找到:

https://github.com/YetiHQ/manticore-iosviewfactory

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

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

// Register activities

MCViewFactory *factory = [MCViewFactory sharedFactory];

// the following two lines are optional. 
[factory registerView:@"YourSectionViewController"]; 
Run Code Online (Sandbox Code Playgroud)

现在,在VC中,只要您想要移动到新的VC并传递数据,就可以创建新的intent并将数据添加到其字典(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];
Run Code Online (Sandbox Code Playgroud)

所有符合此要求的视图都需要是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];
}
Run Code Online (Sandbox Code Playgroud)

希望你们中的一些人发现这个解决方案有用/有趣


Viv*_*dav 14

在next上创建属性view controller .h并定义getter和setter.

property在NextVC中的NextVC.h中添加它

@property (strong, nonatomic) NSString *indexNumber;
Run Code Online (Sandbox Code Playgroud)

@synthesize indexNumber; 在NextVC.m中

最后

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

vc.indexNumber=@"123";

[self.navigationController vc animated:YES];
Run Code Online (Sandbox Code Playgroud)


swi*_*Boy 12

迅捷5

好吧,马特·普赖斯(Matt Price)的《答案》非常适合传递数据,但我将在最新的Swift版本中重写它,因为我相信新程序员会因为新的语法和方法/框架而放弃具有挑战性的方法,因为原始文章在Objective-C中。

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

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

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


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

步骤1.在ViewControllerB中声明变量

var isSomethingEnabled = false
Run Code Online (Sandbox Code Playgroud)

步骤2.在ViewControllerB的ViewDidLoad方法中打印变量

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

步骤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)
        }
    }
Run Code Online (Sandbox Code Playgroud)

所以这是完整的代码:

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)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

通过Segue传递数据从ViewControllerA到ViewControllerB

步骤1.从ViewControllerA到ViewControllerB创建Segue,并在Storyboard中提供Identifier = showDetailSegue,如下所示

在此处输入图片说明

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

步骤3.在ViewControllerA中,传递Segue时传递isSomethingEnabled的值

所以这是完整的代码:

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

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

步骤1.在ViewControllerB文件中但在类外部声明协议ViewControllerBDelegate

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?)
}
Run Code Online (Sandbox Code Playgroud)

步骤2.在ViewControllerB中声明委托变量实例

var delegate: ViewControllerBDelegate?
Run Code Online (Sandbox Code Playgroud)

步骤3.在ViewControllerB的viewDidLoad方法内发送委托数据

delegate?.addItemViewController(self, didFinishEnteringItem: "Data for ViewControllerA")
Run Code Online (Sandbox Code Playgroud)

步骤4.在ViewControllerA中确认ViewControllerBDelegate

class ViewControllerA: UIViewController, ViewControllerBDelegate  {
// to do
}
Run Code Online (Sandbox Code Playgroud)

步骤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)
            }
        }
Run Code Online (Sandbox Code Playgroud)

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

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

所以这是完整的代码:

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)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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")
    }
}
Run Code Online (Sandbox Code Playgroud)

通过Notification Observer传递数据从ViewControllerB到ViewControllerA

第1步。在ViewControllerB中的Notification viewer中设置和发布数据

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

步骤2.在ViewControllerA中添加Notification Observer

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

步骤3.在ViewControllerA中接收Notification数据值

@objc func methodOfReceivedNotification(notification: Notification) {
        print("Value of notification : ", notification.object ?? "")
    }
Run Code Online (Sandbox Code Playgroud)

所以这是完整的代码:

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 ?? "")
    }
}
Run Code Online (Sandbox Code Playgroud)

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

通过Block传递数据从ViewControllerB到ViewControllerA

步骤1.在ViewControllerB中声明块

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

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

if authorizationCompletionBlock != nil
        {
            authorizationCompletionBlock!(true)
        }
Run Code Online (Sandbox Code Playgroud)

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

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

所以这是完整的代码:

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)
                }
            }
        }
}
Run Code Online (Sandbox Code Playgroud)

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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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


Kor*_*ton 11

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

我写了一篇关于这篇文章的博客文章:共享模型代码.这是一个简短的总结:

共享数据

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

  • 在视图控制器(在导航或标签栏控制器中)进行强力迭代以设置数据
  • 在prepareForSegue(如果是故事板)或init(如果是程序员)中设置数据

因为准备segue是最常见的一个例子:

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

独立访问

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

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

func viewDidLoad() {
    super.viewDidLoad()
    var data = dataAccess.requestData()
}
Run Code Online (Sandbox Code Playgroud)

还有一些附加工具可以帮助传递数据:

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

核心数据

Core Data的优点在于它具有反向关系.因此,如果您只想给NotesViewController提供notes对象,因为它与其他类似笔记本的东西有反向关系.如果您需要NotesViewController中的笔记本上的数据,您可以通过执行以下操作来向后移动对象图:

let notebookName = note.notebook.name
Run Code Online (Sandbox Code Playgroud)

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


小智 10

当您使用.xib文件时,委派是执行此类操作的唯一解决方案,但上述所有答案都适用storyboard于您需要使用委派的.xibs文件.这是唯一的解决方案.

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


Moh*_*ara 10

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];
}
Run Code Online (Sandbox Code Playgroud)

NewsDetailViewController.h

@interface NewsDetailViewController : UIViewController
@property(nonatomic,retain) NSString *newsHeadlineStr;
@end
Run Code Online (Sandbox Code Playgroud)

NewsDetailViewController.m

@synthesize newsHeadlineStr;
Run Code Online (Sandbox Code Playgroud)


kru*_*inh 10

如果你想将数据从ViewControlerOne传递给ViewControllerTwo尝试这些..

在ViewControlerOne.h中执行这些操作

 @property (nonatomic, strong) NSString *str1;
Run Code Online (Sandbox Code Playgroud)

在ViewControllerTwo.h中执行这些操作

 @property (nonatomic, strong) NSString *str2;
Run Code Online (Sandbox Code Playgroud)

在ViewControllerTwo.m中合成str2

@interface ViewControllerTwo ()
@end
@implementation ViewControllerTwo
@synthesize str2;
Run Code Online (Sandbox Code Playgroud)

在ViewControlerOne.m中执行这些操作

 - (void)viewDidLoad
 {
   [super viewDidLoad];

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

 }
Run Code Online (Sandbox Code Playgroud)

按钮点击事件这样做..

-(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];
}
Run Code Online (Sandbox Code Playgroud)

在ViewControllerTwo.m中执行这些操作

- (void)viewDidLoad
{
 [super viewDidLoad];
  NSLog(@"%@",str2);
}
Run Code Online (Sandbox Code Playgroud)


ak_*_*agi 10

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

AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
Run Code Online (Sandbox Code Playgroud)

例如

如果你声明一个,NSArray object *arrayXYZ那么你可以在任何视图控制器中访问它appDelegate.arrayXYZ


Sab*_*abs 9

如果你想从一个viewController发送数据到另一个viewController,这是一个方法:

假设我们有viewControllers:ViewController和NewViewController.

在ViewController.h中

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController
{
    IBOutlet UITextField *mytext1,*mytext2,*mytext3,*mytext4;
}

@property (nonatomic,retain) IBOutlet UITextField *mytext1,*mytext2,*mytext3,*mytext4;

-(IBAction)goToNextScreen:(id)sender;

@end
Run Code Online (Sandbox Code Playgroud)

在ViewController.m中

#import "ViewController.h"

#import "NewViewController.h"

@implementation ViewController
@synthesize mytext1,mytext2,mytext3,mytext4;

-(IBAction)goToNextScreen:(id)sender
{
    NSArray *arr = [NSArray arrayWithObjects:mytext1.text,mytext2.text,mytext3.text,mytext4.text, nil];


    NewViewController *newVc = [[NewViewController alloc] initWithNibName:@"NewViewController" bundle:nil];

    newVc.arrayList = arr;

    [self.navigationController pushViewController:newVc animated:YES];

}
Run Code Online (Sandbox Code Playgroud)

在NewViewController.h中

#import <UIKit/UIKit.h>

@interface NewViewController : UITableViewController
{
    NSArray *arrayList;

    NSString *name,*age,*dob,*mobile;

}

@property(nonatomic, retain)NSArray *arrayList;

@end
Run Code Online (Sandbox Code Playgroud)

在NewViewController.m中

#import "NewViewController.h"

#import "ViewController.h"

@implementation NewViewController
@synthesize arrayList;

#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{

    // Return the number of sections.
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{

    // Return the number of rows in the section.
    return [arrayList count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil)
    {
         cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];      
    }
    // Configure the cell...
    cell.textLabel.text = [arrayList objectAtIndex:indexPath.row];
    return cell;


}

@end
Run Code Online (Sandbox Code Playgroud)

所以这样我们就可以将数据从一个viewcontroller传递到另一个视图控制器......


Ben*_*air 8

我喜欢基于NSProxy的Model对象和Mock对象的想法,如果可以取消用户选择的内容,则提交或丢弃数据.

由于它是单个对象或几个对象,所以很容易传递数据,如果你让我们说UINavigationController控制器,你可以保持对模型内部的引用,所有被推动的视图控制器都可以直接从导航控制器访问它.


App*_*Guy 8

我见过很多人使用这种didSelectRowAtPath方法使其复杂化.我在我的例子中使用Core Data.

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

}
Run Code Online (Sandbox Code Playgroud)

方法内的4行代码就完成了.


har*_*bar 7

对于 SwiftUI

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

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

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

class UserSettings: ObservableObject {
     @Published var score = 0
}
Run Code Online (Sandbox Code Playgroud)


小智 6

有多种方法可以在视图控制器之间传递数据。

  1. 委托协议(向后方向)。
  2. NS通知中心(双向)。
  3. 用户默认(双向)。
  4. 直接财产(正向)。
  5. 关闭(向后方向)。
  6. Segue(前进方向)。


Mat*_*ini 5

这个问题有很多答案,提供了许多不同的方法来执行确实可行的视图控制器通信,但我没有看到任何提到哪个实际上最好使用哪些以及哪些方法要避免.

在实践中,我认为只推荐几种解决方案:

  • 要传递数据:
    • 覆盖使用故事板和segues时的prepare(for:sender:)方法UIViewController
    • 在代码执行视图控制器转换时,通过初始化程序或通过属性传递数据
  • 向后传递数据
    • 更新应用程序共享状态(您可以使用上述任一方法在视图控制器之间传递)
    • 使用代表团
    • 使用unwind segue

解决方案我建议不要使用:

  • 直接引用先前的控制器而不是使用委托
  • 通过单例共享数据
  • 通过app委托传递数据
  • 通过用户默认值共享数据
  • 通过通知传递数据

这些解决方案尽管在短期内工作,但会引入太多的依赖关系,这些依赖关系会破坏应用程序的体系结构并在以后产生更多问题.

对于那些感兴趣的人,我写了一些文章,更深入地解决了这些问题并强调了各种缺点:


Kam*_*min 5

将数据从一个ViewController传递到另一ViewController共有3种类型。

  1. 以编程方式
  2. 塞格
  3. 用户默认值

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

以编程方式 在此处输入图片说明

塞格 在此处输入图片说明

用户默认值 在此处输入图片说明

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


Sac*_*ane 5

使用通知中心

对于斯威夫特 3

let imageDataDict:[String: UIImage] = ["image": image]

// Post a notification
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "notificationName"), object: nil, userInfo: imageDataDict)
// `default` is now a property, not a method call

// Register to receive notification in your class
NotificationCenter.default.addObserver(self, selector: #selector(self.showSpinningWheel(_:)), name: NSNotification.Name(rawValue: "notificationName"), object: nil)

// Handle notification
func showSpinningWheel(_ notification: NSNotification) {
    print(notification.userInfo ?? "")
    if let dict = notification.userInfo as NSDictionary? {
        if let id = dict["image"] as? UIImage {
            // Do something with your image
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

对于斯威夫特 4

let imageDataDict:[String: UIImage] = ["image": image]

// Post a notification
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "notificationName"), object: nil, userInfo: imageDataDict)
// `default` is now a property, not a method call

// Register to receive notification in your class
NotificationCenter.default.addObserver(self, selector: #selector(self.showSpinningWheel(_:)), name: NSNotification.Name(rawValue: "notificationName"), object: nil)

// Handle notification
@objc func showSpinningWheel(_ notification: NSNotification) {
    print(notification.userInfo ?? "")
    if let dict = notification.userInfo as NSDictionary? {
        if let id = dict["image"] as? UIImage {
            // Do something with your image
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 从 Swift 3 开始,您应该删除 NSNotification 和大多数类型的 NS 前缀。同样适用于使用 Swift 原生字典 `[AnyHashable: Any]` 而不是 NSDictionary。顺便说一句/sf/ask/2122991671/?r=SearchResults&amp;s=6|0.0000#30329316 (4认同)

归档时间:

查看次数:

437708 次

最近记录:

6 年,5 月 前