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

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

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

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

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


当前回答

您可以将数据保存在App代理中,以便通过应用程序中的视图控制器访问数据。您只需创建应用程序委托的共享实例:

AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;

例如

如果声明NSArray对象*arrayXYZ,则可以通过appDelegate.arrayXYZ在任何视图控制器中访问它。

其他回答

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

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

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

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

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

我见过很多人使用didSelectRowAtPath方法过度复杂化。我在示例中使用核心数据。

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

}

方法内的四行代码就完成了。

使用通知中心

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

这里有一个更简单的方法。

只需使用全局变量。声明传递给下一个类所需的对象或变量。

例如,我们有两个类-类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文件转换为第二类。

记住那座桥。如果有一座桥,它应该可以从两边走。