我对iOS和Objective-C以及整个MVC范式都是新手,我一直坚持以下几点:
我有一个充当数据输入表单的视图,我想让用户选择多个产品。产品列在另一个具有UITableViewController的视图中,我已启用多个选项。
如何将数据从一个视图传输到另一个视图?我会将UITableView上的选择保存在一个数组中,但如何将其传递回上一个数据输入表单视图,以便在提交表单时将其与其他数据一起保存到核心数据?
我在网上冲浪,看到一些人在应用程序委托中声明了一个数组。我读了一些关于单态的东西,但我不明白这些是什么,我读了关于创建数据模型的东西。
正确的执行方式是什么?我将如何执行?
我建议使用块/闭包和自定义构造函数。
假设您必须将字符串从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")
}
}
这里有一个更简单的方法。
只需使用全局变量。声明传递给下一个类所需的对象或变量。
例如,我们有两个类-类A和类B。
在类A中,通常包含:
#import "classA.h"
@interface classA()
@end
@implementation classA
-(void)viewDidLoad
{
...
}
-(void)didReceiveMemoryWarning
{
...
}
B类包含:
#import "classB.h"
@interface classB()
@end
@implementation classB
-(void)viewWillLoad
{
...
}
-(void)didReceiveMemoryWarning
{
...
}
现在,将第二个类classB导入classA:
#import "classA.h"
#import "classB.h" // --- Import classB to classA.
@interface classA()
@end
@implementation classA
-(void)viewDidLoad
{
...
}
-(void)didReceiveMemoryWarning
{
...
}
现在我们有一座桥去二等B班。现在,要将变量或对象声明为全局变量,请在第一个类的.m文件中声明它,如下所示:
在A.h类中
#import "classA.h"
#import "classB.h"
@interface classA()
@end
NSString *temp; // ---- Declare any object/variable as global.
@implementation classA
-(void)viewDidLoad
{
...
temp=@"Hello";
...
}
-(void)didReceiveMemoryWarning
{
...
}
这里,对象temp是NSString类的全局对象。要访问任何类中的全局对象或变量,只需在第二个类中重新声明对象或变量。例如,如下所示:
在B.m类中
#import "classB.h"
@interface classB()
@end
extern NSString *temp; //----use `extern` keyword for using the global object/variable in classB that was declared in classA.
@implementation classB
-(void)viewDidLoad
{
...
LabeL.text=temp;
...
}
-(void)didReceiveMemoryWarning
{
...
}
现在,第二个类可以访问该值。易于理解的此方法可用于任意数量的类。
注:
您应该将第二个类的.h文件导入到第一个类。但不需要进口将第一类的.h文件转换为第二类。
记住那座桥。如果有一座桥,它应该可以从两边走。
在Swift中传递数据有很多解决方案。
向前传递数据
我最喜欢的两种转发数据的方式是依赖注入(DI)和属性观察者
依赖注入
class CustomView : UIView {
init(_ with model : Model) {
// Do what you want with data
}
}
财产观察员
class CustomView : UIView {
var model : Model? {
didSet {
// Do what you want with data after assign data to model
}
willSet {
// Do what you want with data before assign data to model
}
}
}
向后传递数据
还喜欢将数据传递到上一个VC/视图的方法:
协议和代表
protocol CustomViewDelegate : class {
func addItemViewController(_ with data: Model?)
}
weak var delegate : CustomViewDelegate?
class AnotherCustomView: UIView {
let customView = AnotherCustomView()
init() {
customView.delegate = self
}
}
extention AnotherCustomView : CustomViewDelegate {
func addItemViewController(_ with data: Model?) {
// Do what you want with data
}
}
关闭
class AnotherCustomView : UIView {
init(addItem: @escaping (_ value : Model?) -> ()) {
// Do what you want with data
}
}
class CustomView : UIView {
init() {
let customView = AnotherCustomView { [weak self] model in
// Do what you want with data
}
}
}
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];
}];
}