在Swift中,你如何在视图控制器之间传递数据?

16

我尝试创建全局变量,并在视图加载时更新信息,但数据没有被渲染。

全局变量

var viewName:String = ""
var viewDuration:String = ""
var viewPeriod:String = ""
var viewMinAmp:String = ""
var viewMaxAmp:String = ""
var viewStep:String = ""
var viewType:String = ""

除了使用全局变量,还有更有效的传递信息的方法吗?

@IBOutlet var txtName: UITextField!
@IBOutlet var txtDuration: UITextField!
@IBOutlet var txtPeriod: UITextField!
@IBOutlet var txtMinAmp: UITextField!
@IBOutlet var txtMaxAmp: UITextField!
@IBOutlet var txtStep: UITextField!
@IBOutlet var txtType: UITextField!

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    setInfo(viewName, duration: viewDuration, period: viewPeriod, minAmp: viewMinAmp, maxAmp: viewMaxAmp, step: viewStep, type: viewType)
}

func setInfo(name: String, duration: String, period: String, minAmp: String, maxAmp: String, step: String, type: String) {
    txtName.text = name
    txtDuration.text = duration
    txtPeriod.text = period
    txtMinAmp.text = minAmp
    txtMaxAmp.text = maxAmp
    txtStep.text = step
    txtType.text = type
}

4
在初始化UIViewControllers时,定义属性并设置它们。不要使用全局变量,这是一种不好的实践方法。 - AndrewShmig
我该如何从一个控制器中访问另一个控制器的方法?@AndrewShmig - Tomasero
你如何在Swift中访问其他方法? :) - AndrewShmig
请参考此答案获取一个基本示例。 - Suragch
5个回答

34

一个解决方法是在包含要传递到目标视图控制器的数据的视图控制器中重写prepareForSegue(segue:sender :)。

override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
    if (segue.identifier == "YourSegueName") {
        //get a reference to the destination view controller
        let destinationVC:ViewControllerClass = segue.destinationViewController as! ViewControllerClass

        //set properties on the destination view controller
        destinationVC.name = viewName
        //etc...
    }
}

1
这在Swift 2.1中可行吗?我使用完全相同的代码,但我得到了“无法将类型UIViewController转换为指定类型NextViewController”的错误。 - Deepak Thakur
我还没有在Swift 2.1中尝试过它;但是,听起来你可能需要显式地转换目标视图控制器。 let destinationVC: ViewControllerClass = segue.destinationViewController as ViewControllerClass - Scott Mielcarski
这仍然需要使用全局变量。有没有关于如何避免在视图控制器之间传递数据时使用全局变量或避免共享状态的想法? - Pradeep Banavara
为什么创建 ViewControllerClass 的副本会改变真实对象的属性? - Declan McKenna

24

适用于Swift 3.0

final class Shared {
     static let shared = Shared() //lazy init, and it only runs once

     var stringValue : String!
     var boolValue   : Bool!
}

设置stringValue

Shared.shared.stringValue = "Hi there"

获取stringValue的值

 if let value = Shared.shared.stringValue {
        print(value)
 }

对于Swift 3.0以下版本

您可以使用单例类在视图之间传递数据。这是一种简单有效的方法。这是我的ShareData.swift类:

import Foundation

class ShareData {
    class var sharedInstance: ShareData {
        struct Static {
            static var instance: ShareData?
            static var token: dispatch_once_t = 0
        }

        dispatch_once(&Static.token) {
            Static.instance = ShareData()
        }

        return Static.instance!
    }


    var someString : String! //Some String

    var selectedTheme : AnyObject! //Some Object

    var someBoolValue : Bool!
}

现在在我的ViewControllerOne中,我可以设置上面的变量。

//Declare Class Variable

let shareData = ShareData.sharedInstance

override func viewDidLoad() {
    self.shareData.someString ="Some String Value"
}

在我的ViewControllerTwo中,我可以访问someString作为

let shareData = ShareData.sharedInstance
override func viewDidLoad() {
    NSLog(self.sharedData.someString) // It will print Some String Value
}

这只是一个新的空白项目。 - iEmad
这听起来是正确的方法,特别是如果实例在应用程序中是唯一的,例如userInfo,一次只能登录一个用户,因此可以是共享的单例实例。 - adubey
@PhilipS 你使用的是哪个版本的Swift?还有什么错误信息? - Saqib Omer
@PhilipS,请使用我回答中的Swift 3代码。您正在使用错误的代码。 - Saqib Omer
@PhilipS .stringValue只是一个变量。在类中声明一个double类型的变量,如:var someDouble: Double!,并且可以在任何地方通过Shared.shared.someDouble进行访问。 - Saqib Omer
显示剩余4条评论

5

个人而言,我更喜欢以下方式:

  • 如果您想在两个视图控制器之间进行跳转(从 A 跳转到 B),就像导航中的 -pushViewController:animated:,您可以为 Controller B 定义一个模型属性并公开它,然后在从 Controller A 跳转之前显式设置此属性,这非常简单明了;

  • 如果您想从 Controller B 向后跳转到 A,请使用 Delegate+Protocol 模式。Controller B 起草一个公共协议并拥有一个“delegate”属性,任何想成为 Controller B 委托的对象都应遵守并实现其协议(可选)。然后,在向后跳转之前,Controller B 使其委托执行协议中列出的某些操作,数据可以通过这种方式传输;

  • 在某些情况下,您可能希望将数据从一个控制器(或多个控制器)传输到其他多个控制器,如果是这种情况,请使用通知机制。

苹果在官方文档中详细介绍了委托模式、通知模式,请在 XCode 中查看,:)


1

只需要遵循3个步骤,假设您想将数据从ViewControllerA传递到ViewControllerB:

  1. 在ViewControllerA和ViewControllerB之间创建一个segue
  2. 在其属性检查器中使用标识符为segue命名
  3. 在ViewControllerA中覆盖prepareForSegue(segue:UIStoryboardSegue!,sender:AnyObject!)

对于第三步:

  • 如果您没有使用Swift 2.1,请参考@Scott Mielcarski在这个问题中的答案
  • 对于那些使用Swift 2.1或者遇到错误"Cannot convert value of type 'UIViewController' to specified type 'your view Controller class name'"后,在按照@Scott Mielcarski在这个问题中的答案之后,请使用:let destinationVC:ViewControllerClass = segue.destinationViewController as! ViewControllerClass代替let destinationVC:ViewControllerClass = segue.destinationViewController

    这在Swift 2.1.1上经过测试,对我有效。


0

如果你不是真的想在视图控制器之间传递数据,而只是想存储一个全局变量,你可以这样做:

这篇文章很好地解释了如何在Swift 5中实现这一点:https://www.hackingwithswift.com/example-code/system/how-to-save-user-settings-using-userdefaults

总结:

设置值:

let defaults = UserDefaults.standard
defaults.set("value", forKey: "key")

获取字符串值的方法:
let key = defaults.object(forKey: "StringKey") as? [String] ?? [String]()

获取整数值:

let key = defaults.integer(forKey: "IntegerKey")

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接