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

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

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

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

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


当前回答

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

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

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

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

其他回答

敏捷的

这里和Stack Overflow周围有很多解释,但如果你是一个初学者,只是想做一些基本的工作,那么试试看YouTube教程(它帮助我最终了解了如何做)。

YouTube教程:如何通过segue发送数据(Swift)

将数据转发给下一个视图控制器

以下是基于视频的示例。其思想是将一个字符串从第一视图控制器中的文本字段传递到第二视图控制器的标签。

在界面生成器中创建情节提要布局。要制作segue,只需Control单击按钮并拖动到“第二视图控制器”。

第一视图控制器

第一视图控制器的代码为

import UIKit

class FirstViewController: UIViewController {

    @IBOutlet weak var textField: UITextField!

    // This function is called before the segue
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

        // Get a reference to the second view controller
        let secondViewController = segue.destination as! SecondViewController

        // Set a variable in the second view controller with the String to pass
        secondViewController.receivedString = textField.text!
    }

}

第二视图控制器

第二视图控制器的代码是

import UIKit

class SecondViewController: UIViewController {

    @IBOutlet weak var label: UILabel!

    // This variable will hold the data being passed from the First View Controller
    var receivedString = ""

    override func viewDidLoad() {
        super.viewDidLoad()

        // Used the text from the First View Controller to set the label
        label.text = receivedString
    }

}

别忘了

连接UITextField和UILabel的插座。在Interface Builder中将第一个和第二个视图控制器设置为相应的Swift文件。

将数据传回上一个视图控制器

要将数据从第二个视图控制器传递回第一个视图控制器,请使用协议和代理。本视频非常清晰地介绍了这一过程:

YouTube教程:iOS Swift基础教程:协议和委托但也要阅读这篇文章,以确保您不会陷入强大的参考循环。

以下是基于视频的示例(经过一些修改)。

在界面生成器中创建情节提要布局。同样,要制作segue,只需按住Ctrl键将其从按钮拖动到“第二视图控制器”。将segue标识符设置为showSecondViewController。此外,不要忘记使用以下代码中的名称连接出口和操作。

第一视图控制器

第一视图控制器的代码为

import UIKit

class FirstViewController: UIViewController, DataEnteredDelegate {

    @IBOutlet weak var label: UILabel!

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "showSecondViewController" {
            let secondViewController = segue.destination as! SecondViewController
            secondViewController.delegate = self
        }
    }

    func userDidEnterInformation(info: String) {
        label.text = info
    }
}

请注意自定义DataEnteredDelegate协议的使用。

第二视图控制器和协议

第二个视图控制器的代码为

import UIKit

// Protocol used for sending data back
protocol DataEnteredDelegate: AnyObject {
    func userDidEnterInformation(info: String)
}

class SecondViewController: UIViewController {

    // Making this a weak variable, so that it won't create a strong reference cycle
    weak var delegate: DataEnteredDelegate? = nil

    @IBOutlet weak var textField: UITextField!

    @IBAction func sendTextBackButton(sender: AnyObject) {

        // Call this method on whichever class implements our delegate protocol
        delegate?.userDidEnterInformation(info: textField.text!)

        // Go back to the previous view controller
        _ = self.navigationController?.popViewController(animated: true)
    }
}

请注意,该协议在View Controller类之外。

就是这样。现在运行应用程序,您应该能够将数据从第二个视图控制器发送回第一个视图控制器。

有很多方法可以做到这一点,选择正确的方法很重要。可能最大的架构决策之一在于如何在整个应用程序中共享或访问模型代码。

不久前,我写了一篇关于这一点的博客文章:共享模型代码。下面是一个简短的总结:

共享数据

一种方法是在视图控制器之间共享指向模型对象的指针。

在视图控制器(在导航或选项卡栏控制器中)上强制迭代以设置数据在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。

在类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文件转换为第二类。

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

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

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

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

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

经过更多的研究,协议和代表似乎是正确的/苹果首选的方式。

我最终使用了这个示例(在iPhone开发SDK中):

在视图控制器和其他对象之间共享数据

它工作得很好,允许我在视图之间前后传递字符串和数组。