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

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

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

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

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


当前回答

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

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

其他回答

敏捷的

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

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

在下一个视图controller.h文件中创建属性,并定义getter和setter。

在NextVC上的NextVC.h中添加此属性:

@property (strong, nonatomic) NSString *indexNumber;

Add

@综合索引编号;在NextVC.m中

最后一个

NextVC *vc = [[NextVC alloc]init];

vc.indexNumber = @"123";

[self.navigationController vc animated:YES];

苹果公司的一种方法是使用Segues。您需要使用prepareForSegue()函数。

有很多很棒的教程,这里有一个:释放你的内部应用程序开发人员第21部分:在控制器之间传递数据

此外,请阅读有关使用segues的Apple文档:使用Segues

雨燕5Matt Price的回答非常适合传递数据,但我我将在最新的Swift版本中重写它,因为我相信由于新的语法和方法/框架,正如最初的帖子在Objective-C中那样。

在视图控制器之间传递数据有多种选项。

使用导航控制器推送使用Segue使用代理使用通知查看器使用块

我将用最新的iOS框架在Swift中重写他的逻辑


通过导航控制器传递数据推送:从ViewControllerA到ViewControllerB

步骤1。在ViewControllerB中声明变量

var isSomethingEnabled = false

步骤2。在ViewControllerB的ViewDidLoad方法中打印变量

override func viewDidLoad() {
    super.viewDidLoad()
    // Print value received through segue, navigation push
    print("Value of 'isSomethingEnabled' from ViewControllerA: ", isSomethingEnabled)
}

步骤3。在ViewControllerA中通过导航控制器时传递数据

if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
    viewControllerB.isSomethingEnabled = true
    if let navigator = navigationController {
        navigator.pushViewController(viewControllerB, animated: true)
    }
}

以下是完整的代码:

视图控制器A

import UIKit

class ViewControllerA: UIViewController  {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    // MARK: Passing data through navigation PushViewController
    @IBAction func goToViewControllerB(_ sender: Any) {

        if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
            viewControllerB.isSomethingEnabled = true
            if let navigator = navigationController {
                navigator.pushViewController(viewControllerB, animated: true)
            }
        }
    }
}

视图控制器B

import UIKit

class ViewControllerB: UIViewController {

    // MARK:  - Variable for Passing Data through Navigation push
    var isSomethingEnabled = false

    override func viewDidLoad() {
        super.viewDidLoad()
        // Print value received through navigation push
        print("Value of 'isSomethingEnabled' from ViewControllerA: ", isSomethingEnabled)
    }
}

通过Segue传递数据:从ViewControllerA到ViewControllerB

步骤1。创建从ViewControllerA到ViewControllerB的Segue,并在Storyboard中提供Identifier=showDetailSegue,如下所示

步骤2。在ViewControllerB中声明一个名为isSomethingEnabled的可行值并打印其值。

步骤3。在ViewControllerA中,传递Segue时传递SomethingEnabled的值

以下是完整的代码:

视图控制器A

import UIKit

class ViewControllerA: UIViewController  {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    // MARK:  - - Passing Data through Segue  - -
    @IBAction func goToViewControllerBUsingSegue(_ sender: Any) {
        performSegue(withIdentifier: "showDetailSegue", sender: nil)
    }

    // Segue Delegate Method
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if (segue.identifier == "showDetailSegue") {
            let controller = segue.destination as? ViewControllerB
            controller?.isSomethingEnabled = true//passing data
        }
    }
}

视图控制器B

import UIKit

class ViewControllerB: UIViewController {
    var isSomethingEnabled = false

    override func viewDidLoad() {
        super.viewDidLoad()
        // Print value received through segue
        print("Value of 'isSomethingEnabled' from ViewControllerA: ", isSomethingEnabled)
    }
}

通过委托传递数据:从ViewControllerB到ViewControllerA

步骤1。在ViewControllerB文件中声明协议ViewControllerBDlegate,但在类之外

protocol ViewControllerBDelegate: NSObjectProtocol {

    // Classes that adopt this protocol MUST define
    // this method -- and hopefully do something in
    // that definition.
    func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?)
}

步骤2。在ViewControllerB中声明委托变量实例

var delegate: ViewControllerBDelegate?

步骤3。在ViewControllerB的viewDidLoad方法中发送委托的数据

delegate?.addItemViewController(self, didFinishEnteringItem: "Data for ViewControllerA")

步骤4。确认ViewControllerB在ViewControllerA中保留

class ViewControllerA: UIViewController, ViewControllerBDelegate  {
// to do
}

步骤5。确认将在ViewControllerA中实现委托

if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
    viewControllerB.delegate = self//confirming delegate
    if let navigator = navigationController {
        navigator.pushViewController(viewControllerB, animated: true)
    }
}

步骤6。实现用于在ViewControllerA中接收数据的委托方法

func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?) {
    print("Value from ViewControllerB's Delegate", item!)
}

以下是完整的代码:

视图控制器A

import UIKit

class ViewControllerA: UIViewController, ViewControllerBDelegate  {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    // Delegate method
    func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?) {
        print("Value from ViewControllerB's Delegate", item!)
    }

    @IBAction func goToViewControllerForDelegate(_ sender: Any) {

        if let viewControllerB = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewControllerB") as? ViewControllerB {
            viewControllerB.delegate = self
            if let navigator = navigationController {
                navigator.pushViewController(viewControllerB, animated: true)
            }
        }
    }
}

视图控制器B

import UIKit

//Protocol decleare
protocol ViewControllerBDelegate: NSObjectProtocol {
    // Classes that adopt this protocol MUST define
    // this method -- and hopefully do something in
    // that definition.
    func addItemViewController(_ controller: ViewControllerB?, didFinishEnteringItem item: String?)
}

class ViewControllerB: UIViewController {
    var delegate: ViewControllerBDelegate?

    override func viewDidLoad() {
        super.viewDidLoad()
        // MARK:  - - - -  Set Data for Passing Data through Delegate  - - - - - -
        delegate?.addItemViewController(self, didFinishEnteringItem: "Data for ViewControllerA")
    }
}

通过通知查看器传递数据:从ViewControllerB到ViewControllerA

步骤1。在ViewControllerB中的通知查看器中设置和发布数据

let objToBeSent = "Test Message from Notification"
        NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: objToBeSent)

步骤2。在ViewControllerA中添加通知查看器

NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)

步骤3。在ViewControllerA中接收通知数据值

@objc func methodOfReceivedNotification(notification: Notification) {
    print("Value of notification: ", notification.object ?? "")
}

以下是完整的代码:

视图控制器A

import UIKit

class ViewControllerA: UIViewController{

    override func viewDidLoad() {
        super.viewDidLoad()

        // Add observer in controller(s) where you want to receive data
        NotificationCenter.default.addObserver(self, selector: #selector(self.methodOfReceivedNotification(notification:)), name: Notification.Name("NotificationIdentifier"), object: nil)
    }

    // MARK: Method for receiving Data through Post Notification
    @objc func methodOfReceivedNotification(notification: Notification) {
        print("Value of notification: ", notification.object ?? "")
    }
}

视图控制器B

import UIKit

class ViewControllerB: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // MARK:Set data for Passing Data through Post Notification
        let objToBeSent = "Test Message from Notification"
        NotificationCenter.default.post(name: Notification.Name("NotificationIdentifier"), object: objToBeSent)
    }
}

通过块传递数据:从ViewControllerB到ViewControllerA

步骤1。在ViewControllerB中声明块

var authorizationCompletionBlock:((Bool)->())? = {_ in}

步骤2。在ViewControllerB中设置块中的数据

if authorizationCompletionBlock != nil
{
    authorizationCompletionBlock!(true)
}

步骤3。在ViewControllerA中接收块数据

// Receiver Block
controller!.authorizationCompletionBlock = { isGranted in
    print("Data received from Block is: ", isGranted)
}

以下是完整的代码:

视图控制器A

import UIKit

class ViewControllerA: UIViewController  {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    // MARK:Method for receiving Data through Block
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if (segue.identifier == "showDetailSegue") {
            let controller = segue.destination as? ViewControllerB
            controller?.isSomethingEnabled = true

            // Receiver Block
            controller!.authorizationCompletionBlock = { isGranted in
                print("Data received from Block is: ", isGranted)
            }
        }
    }
}

视图控制器B

import UIKit

class ViewControllerB: UIViewController {

    // MARK: Variable for Passing Data through Block
    var authorizationCompletionBlock:((Bool)->())? = {_ in}

    override func viewDidLoad() {
        super.viewDidLoad()

        // MARK: Set data for Passing Data through Block
        if authorizationCompletionBlock != nil
        {
            authorizationCompletionBlock!(true)
        }
    }
}

你可以在我的GitHub上找到完整的示例应用程序。如果你对此有任何疑问,请告诉我。

我目前正在通过一个名为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];
}