我对iOS和Objective-C以及整个MVC范式都是新手,我一直坚持以下几点:

我有一个充当数据输入表单的视图,我想让用户选择多个产品。产品列在另一个具有UITableViewController的视图中,我已启用多个选项。

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

我在网上冲浪,看到一些人在应用程序委托中声明了一个数组。我读了一些关于单态的东西,但我不明白这些是什么,我读了关于创建数据模型的东西。

正确的执行方式是什么?我将如何执行?


当前回答

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

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

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

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

其他回答

在我的例子中,我使用了一个单例类,它可以作为一个全局对象,允许访问应用程序中几乎所有地方的数据。

第一件事是构建一个单例类。请参考页面我的Objective-C单例应该是什么样子?。

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

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

使用通知中心

对于Swift 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
        }
    }
}

对于Swift 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
        }
    }
}

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

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

这个问题在Stack Overflow上似乎很流行,所以我想我会尝试给出一个更好的答案,以帮助像我这样在iOS世界起步的人。

转发数据

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

对于本例,我们将使用ViewControllerA和ViewControllerB

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

在ViewControllerB.h中为BOOL创建一个属性@属性(非原子,赋值)BOOL是SomethingEnabled;在ViewControllerA中,您需要告诉它ViewControllerB,所以使用#import“ViewControllerB.h”

然后,如果要加载视图,例如didSelectRowAtIndex或某个IBAction,则需要在将其推到导航堆栈之前在ViewControllerB中设置属性。

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

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

使用Segues转发数据

如果您使用的是情节提要,则很可能使用segue,需要使用此过程来传递数据。这与上述类似,但不是在推送视图控制器之前传递数据,而是使用名为

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

因此,要将BOOL从ViewControllerA传递到ViewControllerB,我们将执行以下操作:

在ViewControllerB.h中为BOOL创建一个属性@属性(非原子,赋值)BOOL是SomethingEnabled;在ViewControllerA中,您需要告诉它ViewControllerB,因此使用#import“ViewControllerB.h”在情节提要上创建从ViewControllerA到ViewControllerB的片段,并为其提供标识符。在本例中,我们将其称为“showDetailSegue”接下来,我们需要将该方法添加到ViewControllerA,在执行任何segue时调用该方法。因此,我们需要检测调用了哪个segue,然后采取措施。在我们的示例中,我们将检查“showDetailSegue”,如果执行了,我们将把BOOL值传递给ViewControllerB-(void)准备segue:(UIStoryboardSegue*)segue发件人:(id)发件人{if([segue.identifier isEqualToString:@“showDetailSegue”]){ViewControllerB*控制器=(ViewControllerB*)segue.destinationViewController;controller.isSomethingEnabled=是;}}

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

    -(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值YES。

传回数据

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

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

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

在ViewControllerB.h中,在#import下方,但在@interface上方,指定协议。@类ViewControllerB;@协议ViewControllerBDLegate<NSObject>-(void)addItemViewController:(ViewControllerB*)控制器didFinishEnteringItem:(NSString*)项;@完接下来仍然在ViewControllerB.h中,您需要设置委托属性并在ViewController B.m中进行合成@属性(非原子,弱)id<ViewControllerBDLegate>委托;在ViewControllerB中,当我们弹出视图控制器时,我们调用代理上的消息。NSString*itemToPassBack=@“将此值传递回ViewControllerA”;[self.delegate addItemViewController:self-didFinishEnteringItem:itemToPassBack];这就是ViewControllerB。现在在ViewControllerA.h中,告诉ViewControllerA导入ViewControllerB并遵守其协议。#import“ViewControllerB.h”@界面ViewControllerA:UIViewController<ViewControllerBDLegate>在ViewControllerA.m中,根据我们的协议实现以下方法-(void)addItemViewController:(ViewControllerB*)控制器didFinishEnteringItem:(NSString*)项{NSLog(@“这是从ViewControllerB%@返回的”,项);}在将viewControllerB推送到导航堆栈之前,我们需要告诉viewControllerB ViewControllerA是它的委托,否则我们将得到一个错误。ViewControllerB*ViewControllerB=[[ViewControllerB alloc]initWithNib:@“ViewControllerA”捆绑包:nil];viewControllerB.delegate=自身[[self-navigationController]pushViewController:viewControllerB动画:是];


工具书类

在《视图控制器编程指南》中使用委派与其他视图控制器通信代理模式

NS通知中心

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

// 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上的ContorlerB数据)

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

防火砖

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

块的另一个工作示例

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

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