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

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

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

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

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


当前回答

MVC中的M代表“模型”,而在MVC范式中,模型类的作用是管理程序的数据。模型与视图相反——视图知道如何显示数据,但它不知道如何处理数据,而模型知道如何使用数据,但不知道如何显示。模型可能很复杂,但不一定如此——应用程序的模型可能与字符串或字典数组一样简单。

控制器的作用是在视图和模型之间进行调解。因此,它们需要对一个或多个视图对象和一个或更多模型对象的引用。假设您的模型是一个字典数组,每个字典代表表中的一行。应用程序的根视图显示该表,它可能负责从文件加载数组。当用户决定向表中添加新行时,他们点击某个按钮,控制器将创建一个新的(可变的)字典并将其添加到数组中。为了填充该行,控制器创建一个详细视图控制器并为其提供新字典。详细视图控制器填充字典并返回。字典已经是模型的一部分,因此不需要发生其他任何事情。

其他回答

共享数据有多种方法。

您始终可以使用NSUserDefaults共享数据。设置要与所选键共享的值,并在下一个视图控制器中从与该键关联的NSUserDefault获取值。[[NSUserDefaults standardUserDefaults]setValue:key:key的值][[NSUserDefaults standardUserDefaults]objectForKey:key]您只需在viewcontrollerA中创建一个属性。在viewcontrollerB中创建viewcontrollerA的对象,并为该属性分配所需的值。您也可以为此创建自定义代理。

使用通知中心

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

这个问题有很多答案,提供了许多不同的方法来执行视图控制器通信,这些方法确实有效,但我没有看到任何地方提到哪种方法实际上最好使用,哪种方法最好避免。

在实践中,我认为只建议几个解决方案:

要向前传递数据:使用情节提要和片段时,重写UIViewController的prepare(for:sender:)方法执行视图控制器转换时通过初始化器或财产传递数据困难代码向后传递数据更新应用程序共享状态(您可以使用上述任一方法在视图控制器之间传递该状态)使用委托使用放松段

我建议不要使用的解决方案:

直接引用上一个控制器,而不是使用委派通过单例共享数据通过应用程序代理传递数据通过用户默认值共享数据通过通知传递数据

这些解决方案虽然在短期内有效,但引入了太多的依赖性,这将扰乱应用程序的架构,并在以后产生更多问题。

对于感兴趣的人,我写了一些文章,更深入地讨论了这些问题,并强调了各种缺点:

iOS视图控制器如何相互通信如何构建iOS应用程序的代码通过一个实例了解iOS开发的核心架构原则

嗯,我们有几种方法可以使用委派系统或使用故事板Segue:

使用setter和getter方法,如viewController.h@属性(保留,非原子)NSString*str;现在,在viewController.m中@合成str;这里我有一个PDF URL和一个segue到另一个viewController,像这样,pdfObject是我的pdfModel。它基本上是一个NSOBJECT类。str=[NSString stringWithFormat:@“%@”,pdfObject.objPath];NSLog(@“pdfUrl:***:%@:***:”,pdfUrl);[self-performSegueWithIdentifier:@“programPDFViewController_segue”sender:self];#pragma标记-导航//在基于情节提要的应用程序中,您通常需要在导航之前做一些准备-(void)准备segue:(UIStoryboardSegue*)segue发件人:(id)发件人{if([[segue identifier]isEqualToString:@“programPDFViewController_segue”]){programPDFViewController*pdfVC=[segue destinationViewController];[pdfVC setRecivedPdfUrl:str];}}现在,我成功地接收了我的PDF URL字符串和其他ViewController,并在webview中使用该字符串。。。在处理这样的委托时,我有一个NSObject实用程序类,其中包含我的dateFormatter、sharedInstance、EscapeWhiteSpaceCharacters、convertImageToGrayScale等方法,以及我在整个应用程序中使用过的其他方法,现在在文件实用程序中使用。在这种情况下,您不需要在每次将数据从一个视图控制器解析到另一个视图时都创建变量。有一次,您在fileutility.h中创建了一个字符串变量。只需将其设为零并再次使用。@接口实用程序:NSObject文件实用程序.h:+(实用程序*)共享实例;@属性(非原子,保留)NSString*strUrl;现在在文件实用程序.m中:@实施实用程序+(实用程序*)共享实例{静态实用程序*sharedObj=nil;如果(sharedObj==nil){sharedObj=[[utilities alloc]init];}返回sharedObj;}现在完成了,请访问文件firstViewController.m并调用委托NSString*str=[NSString stringWithFormat:@“%@”,pdfObject.objPath];[连接共享实例].strUrl=nil;[连接共享实例].strUrl=str;现在直接转到文件secondViewController.m,在不创建变量的情况下使用它我所做的一切:-(无效)视图将出现:(BOOL)动画{[超级视图将出现:是];[self-webViewMethod:[Connection sharedInstance].strUrl];}-(void)WebViewMethod:(NSString)Url{//使用webview。享受编码:D}

这种委托工作在内存管理方面是可靠的。

有了Swift的倾向,并想要一个简单的例子,如果你使用segue来绕过,这里是我传递数据的go to方法。

它与上述类似,但没有按钮、标签等。只需将数据从一个视图传递到下一个视图。

设置情节提要

有三个部分。

发件人赛格牌手表接收者

这是一个非常简单的视图布局,中间有一个segue。



这是发件人的设置



这是接收器的设置。



最后,segue的设置。



视图控制器

我们保持这个简单,所以没有按钮,没有动作。我们只是在应用程序加载时将数据从发送方移动到接收方,然后将传输的值输出到控制台。

此页面获取初始加载的值并将其传递。

import UIKit


class ViewControllerSender: UIViewController {

    // THE STUFF - put some information into a variable
    let favoriteMovie = "Ghost Busters"

    override func viewDidAppear(animated: Bool) {
        // PASS IDENTIFIER - go to the receiving view controller.
        self.performSegueWithIdentifier("goToReciever", sender: self)
    }

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

        // GET REFERENCE - ...to the receiver view.
        var viewControllerReceiver = segue.destinationViewController as? ViewControllerReceiver

        // PASS STUFF - pass the variable along to the target.
        viewControllerReceiver!.yourFavMovie = self.favoriteMovie

    }
}

此页面仅在加载时将变量的值发送到控制台。至此,我们最喜欢的电影应该在这个变量中。

import UIKit

class ViewControllerReceiver: UIViewController {

    // Basic empty variable waiting for you to pass in your fantastic favorite movie.
    var yourFavMovie = ""

    override func viewDidLoad() {
        super.viewDidLoad()

        // And now we can view it in the console.
        println("The Movie is \(self.yourFavMovie)")

    }
}

如果你想使用segue,而你的页面不在导航控制器下,那么这就是解决问题的方法。

一旦运行,它应该自动切换到接收方视图,并将值从发送方传递给接收方,在控制台中显示该值。