我对iOS和Objective-C以及整个MVC范式都是新手,我一直坚持以下几点:
我有一个充当数据输入表单的视图,我想让用户选择多个产品。产品列在另一个具有UITableViewController的视图中,我已启用多个选项。
如何将数据从一个视图传输到另一个视图?我会将UITableView上的选择保存在一个数组中,但如何将其传递回上一个数据输入表单视图,以便在提交表单时将其与其他数据一起保存到核心数据?
我在网上冲浪,看到一些人在应用程序委托中声明了一个数组。我读了一些关于单态的东西,但我不明白这些是什么,我读了关于创建数据模型的东西。
正确的执行方式是什么?我将如何执行?
有了Swift的倾向,并想要一个简单的例子,如果你使用segue来绕过,这里是我传递数据的go to方法。
它与上述类似,但没有按钮、标签等。只需将数据从一个视图传递到下一个视图。
设置情节提要
有三个部分。
发件人赛格牌手表接收者
这是一个非常简单的视图布局,中间有一个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,而你的页面不在导航控制器下,那么这就是解决问题的方法。
一旦运行,它应该自动切换到接收方视图,并将值从发送方传递给接收方,在控制台中显示该值。
使用通知中心
对于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
}
}
}
我目前正在通过一个名为MCViewFactory的项目为这个问题的开源解决方案做出贡献,这个项目可以在这里找到:
Manticore iOS视图工厂
其想法是模仿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];
}
OP没有提到视图控制器,但有很多答案都提到了,所以我想补充一下LLVM的一些新功能,以便在希望将数据从一个视图控制器传递到另一个视图,然后获取一些结果时更容易。
故事板片段、ARC和LLVM块使我比以往任何时候都容易。上面提到的一些答案已经提到了故事板和片段,但仍然依赖于授权。定义委托当然有效,但有些人可能会发现传递指针或代码块更容易。
使用UINavigator和segue,可以轻松地将信息传递给从属控制器并获取信息。ARC使传递从NSObjects派生的对象的指针变得简单,因此如果您希望从属控制器为您添加/更改/修改某些数据,请将指针传递给可变实例。块使传递动作变得容易,因此如果您希望从属控制器调用更高级别控制器上的动作,请传递一个块。您可以定义块以接受任何数量的对您有意义的参数。如果更适合的话,您还可以设计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];
}
第二个示例显示为第二个参数传递回调块。我喜欢使用块,因为它将相关细节紧密地保存在源代码中——更高级别的源代码中。
// 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];
}];
}