我对iOS和Objective-C以及整个MVC范式都是新手,我一直坚持以下几点:
我有一个充当数据输入表单的视图,我想让用户选择多个产品。产品列在另一个具有UITableViewController的视图中,我已启用多个选项。
如何将数据从一个视图传输到另一个视图?我会将UITableView上的选择保存在一个数组中,但如何将其传递回上一个数据输入表单视图,以便在提交表单时将其与其他数据一起保存到核心数据?
我在网上冲浪,看到一些人在应用程序委托中声明了一个数组。我读了一些关于单态的东西,但我不明白这些是什么,我读了关于创建数据模型的东西。
正确的执行方式是什么?我将如何执行?
有很多方法可以做到这一点,选择正确的方法很重要。可能最大的架构决策之一在于如何在整个应用程序中共享或访问模型代码。
不久前,我写了一篇关于这一点的博客文章:共享模型代码。下面是一个简短的总结:
共享数据
一种方法是在视图控制器之间共享指向模型对象的指针。
在视图控制器(在导航或选项卡栏控制器中)上强制迭代以设置数据在prepareForSegue(如果是故事板)或init(如果是编程的)中设置数据
因为准备segue是最常见的,这里有一个例子:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var next = segue.destinationViewController as NextViewController
next.dataSource = dataSource
}
独立访问
另一种方法是一次处理满屏数据,而不是将视图控制器彼此耦合,将每个视图控制器耦合到它们可以独立访问的单个数据源。
我看到的最常见的方法是单例实例。因此,如果单例对象是DataAccess,则可以在UIViewController的viewDidLoad方法中执行以下操作:
func viewDidLoad() {
super.viewDidLoad()
var data = dataAccess.requestData()
}
还有其他工具也有助于传递数据:
关键价值观察NS通知核心数据NSFetchedResults控制器数据源
核心数据
核心数据的优点在于它具有相反的关系。因此,如果你只想给NotesViewController一个notes对象,你可以这样做,因为它与笔记本等其他东西有相反的关系。如果需要NotesViewController中笔记本上的数据,可以通过执行以下操作返回对象图:
let notebookName = note.notebook.name
在我的博客文章:共享模型代码中了解更多信息
我找到了最简单、最优雅的版本,带有传递块。让我们将等待返回数据的视图控制器命名为“A”,将返回的视图控制器称为“B”。在本例中,我们希望获得两个值:第一个是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);
需要记住的一点是,使用块通常需要管理强引用和弱引用,如这里所述
我建议使用块/闭包和自定义构造函数。
假设您必须将字符串从FirstViewController传递到SecondViewController。
您的第一个视图控制器。
class FirstViewController : UIViewController {
func moveToViewControllerB() {
let second_screen = SecondViewController.screen(string: "DATA TO PASS", call_back: {
[weak self] (updated_data) in
///This closure will be called by second view controller when it updates something
})
self.navigationController?.pushViewController(second_screen, animated: true)
}
}
第二个视图控制器
class SecondViewController : UIViewController {
var incoming_string : String?
var call_back : ((String) -> Void)?
class func screen(string: String?, call_back : ((String) -> Void)?) -> SecondViewController {
let me = SecondViewController(nibName: String(describing: self), bundle: Bundle.main);
me.incoming_string = string
me.call_back = call_back
return me
}
// Suppose its called when you have to update FirstViewController with new data.
func updatedSomething() {
//Executing block that is implemented/assigned by the FirstViewController.
self.call_back?("UPDATED DATA")
}
}