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

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

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

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

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


当前回答

如果您想将数据从一个viewController发送到另一个viewController,请使用以下方法:

假设我们有viewControllers:viewControllerA和viewControllerB

现在在文件视图ControllerB.h中

@interface viewControllerB : UIViewController {

  NSString *string;
  NSArray *array;

}

- (id)initWithArray:(NSArray)a andString:(NSString)s;

在文件视图ControllerB.m中:

#import "viewControllerB.h"

@implementation viewControllerB

- (id)initWithArray:(NSArray)a andString:(NSString)s {

   array = [[NSArray alloc] init];
   array = a;

   string = [[NSString alloc] init];
   string = s;

}

在文件视图ControllerA.m中:

#import "viewControllerA.h"
#import "viewControllerB.h"

@implementation viewControllerA

- (void)someMethod {

  someArray = [NSArray arrayWithObjects:@"One", @"Two", @"Three", nil];
  someString = [NSString stringWithFormat:@"Hahahahaha"];

  viewControllerB *vc = [[viewControllerB alloc] initWithArray:someArray andString:someString];

  [self.navigationController pushViewController:vc animated:YES];
  [vc release];
}

因此,这就是在不设置任何委托的情况下将数据从viewControllerA传递到viewControllerB的方法。;)

其他回答

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

敏捷的

这里和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类之外。

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

如果要将数据从一个控制器传递到另一个控制器,请尝试以下代码:

文件FirstViewController.h

@property (nonatomic, retain) NSString *str;

SecondViewController.h

@property (nonatomic, retain) NSString *str1;

文件FirstViewController.m

- (void)viewDidLoad
   {
     // Message for the second SecondViewController
     self.str = @"text message";

     [super viewDidLoad];
   }

-(IBAction)ButtonClicked
 {
   SecondViewController *secondViewController = [[SecondViewController alloc] initWithNibName:@"SecondViewController" bundle:nil];
   secondViewController.str1 = str;
  [self.navigationController pushViewController:secondViewController animated:YES];
 }

使用通知中心将数据从一个视图传递到另一个视图。

观察者-收听者模式效果最好。另一种解决方法是在两个类中创建相同的对象。

在类1中创建类2对象。访问要传递的数据对象,设置它们,然后按下视图控制器。

用于UIKit和AppKit的Pure Combine解决方案

让我们举一个简单的例子,在ViewControllers之间传递计数的Int值。

父ViewController(VC)有一个名为count的变量,子ViewController可以让用户更改count的值。一旦用户完成了值的更改,他们将关闭子控制器,然后父VC应该具有更新的值。


父视图控制器

ParentVC从ChildVC获取计数的更新值

class ParentVC: UIViewController {
    var count = 1
    var countObserver: AnyCancellable! // 1

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        let childVC = segue.destination as! ChildVC
        childVC.sliderValue = count // 2
        countObserver = childVC.$sliderValue // 3
            .assign(to: \.count, on: self)
    }
}

countObserver将保留观察ChildVC所做更改的观察者当子级出现时,我们将当前的计数值从父级分配给ChildVC中的一个控件(这里是UISlider),这成为修改计数值的起点。我们观察到sliderValue(它是一个发布者),它会发出用户通过拖动滑块更改的计数值。请注意,我们使用了$sliderValue而不仅仅是sliderValue。


子视图控制器

ChildVC是发出ParentVC感兴趣的值的一个:

class ChildVC: UIViewController {
    @Published var sliderValue: Int = 0 // 1

    @IBOutlet var sliderOutlet: UISlider!

    @IBAction func slided(_ sender: UISlider) {
        sliderValue = Int(sender.value)
    }
}

@Published是存储值并在值更改时发出信号的Publisher。它的行为类似于常规变量,但可以发布可以通过在其前面加上$符号来访问的值。


CurrentValueSubject和PassthroughSubject与@Published

还可以使用CurrentValueSubject代替@Published。唯一的区别是,你必须手动发出信号。在希望控制何时发射的情况下,它非常有用。例如,您可以发射该值,但前提是该值在特定范围内。PassthroughSubject也可以代替@Published或CurrentValueSubject。这里的区别是PassthroughSubject不能保存值,它只能发出信号。当无法在变量中具体表示值时,这可能很有用。