Swift 变量值变化时执行方法
Execute a method when a variable value changes in Swift
我需要在变量值改变时执行函数。
我有一个单例 class,其中包含一个名为 labelChange
的共享变量。此变量的值取自另一个名为 Model
的 class。我有两个 VC class,其中一个有一个按钮和一个标签,第二个只有一个按钮。
当按下第一个 VC class 中的按钮时,我正在使用此功能更新标签:
func updateLabel(){
self.label.text = SharingManager.sharedInstance.labelChange
}
但我想在 labelChange
的值更改时调用相同的方法。所以在单击按钮时,我只会更新 labelChange
值,当发生这种情况时,我想用 labelChange
的新值更新标签。同样在第二个 VC 中,我能够更新 labelChange
值,但是当此值更改时我无法更新标签。
也许属性是解决方案,但谁能告诉我怎么做。
第二次编辑:
单例Class:
class SharingManager {
func updateLabel() {
println(labelChange)
ViewController().label.text = SharingManager.sharedInstance.labelChange
}
var labelChange: String = Model().callElements() {
willSet {
updateLabel()
}
}
static let sharedInstance = SharingManager()
}
第一个VC:
class ViewController: UIViewController {
@IBOutlet weak var label: UILabel!
@IBAction func Button(sender: UIButton) {
SViewController().updateMessageAndDismiss()
}
}
第二个VC:
func updateMessageAndDismiss() {
SharingManager.sharedInstance.labelChange = modelFromS.callElements()
self.dismissViewControllerAnimated(true, completion: nil)
}
@IBAction func b2(sender: UIButton) {
updateMessageAndDismiss()
}
我做了一些改进,但我需要在单例中引用第一个 VC class 的标签。因此,我将在单例中更新 VC 的标签。
当我打印 labelChange
的值时,该值正在更新,一切正常。但是当我尝试从单例更新标签上的值时,我收到一个错误:
unexpectedly found nil while unwrapping an Optional value
错误指向单例的第 4 行 class。
您可以简单地为变量 labelChange
使用 属性 观察器,然后在 didSet
(或 willSet
中调用您想要调用的函数想在设置之前调用它):
class SharingManager {
var labelChange: String = Model().callElements() {
didSet {
updateLabel()
}
}
static let sharedInstance = SharingManager()
}
这在 Property Observers 中有解释。
我不确定为什么在您尝试时不起作用,但如果您遇到问题是因为您尝试调用的函数 (updateLabel
) 位于不同的 class,你可以在 SharingManager
class 中添加一个变量来存储调用 didSet
时要调用的函数,在这种情况下你将设置为 updateLabel
。
已编辑:
因此,如果您想编辑 ViewController 中的标签,您需要在 ViewController class 中使用 updateLabel() 函数来更新标签,但存储单例中的那个函数 class 所以它可以知道要调用哪个函数:
class SharingManager {
static let sharedInstance = SharingManager()
var updateLabel: (() -> Void)?
var labelChange: String = Model().callElements() {
didSet {
updateLabel?()
}
}
}
然后将其设置为您拥有要调用的函数的任何 class,例如(假设 updateLabel 是您要调用的函数):
SharingManager.sharedInstance.updateLabel = updateLabel
当然,您会希望确保负责该函数的视图控制器仍然存在,以便单例 class 可以调用该函数。
如果您需要根据可见的视图控制器调用不同的函数,您可能需要考虑 Key-Value Observing 以便在某些变量的值发生变化时获得通知。
此外,您永远不想像那样初始化视图控制器,然后立即设置视图控制器的 IBOutlets,因为 IBOutlets 在其视图实际加载之前不会被初始化。您需要以某种方式使用现有的视图控制器对象。
希望对您有所帮助。
还有另一种方法,使用 RxSwift:
将 RxSwift 和 RxCocoa pods 添加到您的项目中
修改你的SharingManager
:
import RxSwift
class SharingManager {
static let sharedInstance = SharingManager()
private let _labelUpdate = PublishSubject<String>()
let onUpdateLabel: Observable<String>? // any object can subscribe to text change using this observable
// call this method whenever you need to change text
func triggerLabelUpdate(newValue: String) {
_labelUpdate.onNext(newValue)
}
init() {
onUpdateLabel = _labelUpdate.shareReplay(1)
}
}
在您的 ViewController 中,您可以通过两种方式订阅值更新:
一个。订阅更新,手动更改标签文本
// add this ivar somewhere in ViewController
let disposeBag = DisposeBag()
// put this somewhere in viewDidLoad
SharingManager.sharedInstance.onUpdateLabel?
.observeOn(MainScheduler.instance) // make sure we're on main thread
.subscribeNext { [weak self] newValue in
// do whatever you need with this string here, like:
// self?.myLabel.text = newValue
}
.addDisposableTo(disposeBag) // for resource management
b。将更新直接绑定到 UILabel
// add this ivar somewhere in ViewController
let disposeBag = DisposeBag()
// put this somewhere in viewDidLoad
SharingManager.sharedInstance.onUpdateLabel?
.distinctUntilChanged() // only if value has been changed since previous value
.observeOn(MainScheduler.instance) // do in main thread
.bindTo(myLabel.rx_text) // will setText: for that label when value changed
.addDisposableTo(disposeBag) // for resource management
并且不要忘记在 ViewController 中 import RxCocoa
。
触发事件只需调用
SharingManager.sharedInstance.triggerLabelUpdate("whatever string here")
HERE 你可以找到示例项目。只需执行 pod update
和 运行 工作区文件。
var item = "initial value" {
didSet { //called when item changes
print("changed")
}
willSet {
print("about to change")
}
}
item = "p"
Apple 提供这些 属性 声明类型:-
1.计算属性:-
除了存储属性,类、结构和枚举可以定义计算属性,这些属性实际上并不存储值。相反,它们提供了一个 getter 和一个可选的 setter 来间接检索和设置其他属性和值。
var otherBool:Bool = false
public var enable:Bool {
get{
print("i can do editional work when setter set value ")
return self.enable
}
set(newValue){
print("i can do editional work when setter set value ")
self.otherBool = newValue
}
}
2。只读计算属性:-
具有 getter 但没有 setter 的计算 属性 被称为只读计算 属性。只读计算 属性 总是 returns 一个值,可以通过点语法访问,但不能设置为不同的值。
var volume: Double {
return volume
}
3。 属性 观察者:-
您可以选择在 属性:
上定义其中一个或两个观察者
willSet 在存储值之前被调用。
didSet 在新值存储后立即被调用。
public var totalSteps: Int = 0 {
willSet(newTotalSteps) {
print("About to set totalSteps to \(newTotalSteps)")
}
didSet {
if totalSteps > oldValue {
print("Added \(totalSteps - oldValue) steps")
}
}
}
注意:- 有关更多信息,请继续专业 link
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html
在Swift4中你可以使用Key-Value Observation。
label.observe(\.text, changeHandler: { (label, change) in
// text has changed
})
基本上就是这样,但有一个小问题。 “观察”returns 一个您需要持有的 NSKeyValueObservation 对象! - 当它被解除分配时,您将不会再收到通知。为避免这种情况,我们可以将其分配给将被保留的 属性。
var observer:NSKeyValueObservation?
// then assign the return value of "observe" to it
observer = label.observe(\.text, changeHandler: { (label, change) in
// text has changed,
})
您也可以观察该值是否发生变化或首次设置
observer = label.observe(\.text, changeHandler: { (label, change) in
// just check for the old value in "change" is not Nil
if let oldValue = change.oldValue {
print("\(label.text) has changed from \(oldValue) to \(label.text)")
} else {
print("\(label.text) is now set")
}
})
有关更多信息,请参阅 Apple 文档 here
override var isHighlighted: Bool {
get { super.isHighlighted }
set {
super.isHighlighted = newValue
if newValue {
label.textColor = highlightedTextColor
contentView.backgroundColor = highlightedBackgroundColor
} else {
label.textColor = normalTextColor
contentView.backgroundColor = normalBackgroundColor
}
}
}
我需要在变量值改变时执行函数。
我有一个单例 class,其中包含一个名为 labelChange
的共享变量。此变量的值取自另一个名为 Model
的 class。我有两个 VC class,其中一个有一个按钮和一个标签,第二个只有一个按钮。
当按下第一个 VC class 中的按钮时,我正在使用此功能更新标签:
func updateLabel(){
self.label.text = SharingManager.sharedInstance.labelChange
}
但我想在 labelChange
的值更改时调用相同的方法。所以在单击按钮时,我只会更新 labelChange
值,当发生这种情况时,我想用 labelChange
的新值更新标签。同样在第二个 VC 中,我能够更新 labelChange
值,但是当此值更改时我无法更新标签。
也许属性是解决方案,但谁能告诉我怎么做。
第二次编辑:
单例Class:
class SharingManager {
func updateLabel() {
println(labelChange)
ViewController().label.text = SharingManager.sharedInstance.labelChange
}
var labelChange: String = Model().callElements() {
willSet {
updateLabel()
}
}
static let sharedInstance = SharingManager()
}
第一个VC:
class ViewController: UIViewController {
@IBOutlet weak var label: UILabel!
@IBAction func Button(sender: UIButton) {
SViewController().updateMessageAndDismiss()
}
}
第二个VC:
func updateMessageAndDismiss() {
SharingManager.sharedInstance.labelChange = modelFromS.callElements()
self.dismissViewControllerAnimated(true, completion: nil)
}
@IBAction func b2(sender: UIButton) {
updateMessageAndDismiss()
}
我做了一些改进,但我需要在单例中引用第一个 VC class 的标签。因此,我将在单例中更新 VC 的标签。
当我打印 labelChange
的值时,该值正在更新,一切正常。但是当我尝试从单例更新标签上的值时,我收到一个错误:
unexpectedly found nil while unwrapping an Optional value
错误指向单例的第 4 行 class。
您可以简单地为变量 labelChange
使用 属性 观察器,然后在 didSet
(或 willSet
中调用您想要调用的函数想在设置之前调用它):
class SharingManager {
var labelChange: String = Model().callElements() {
didSet {
updateLabel()
}
}
static let sharedInstance = SharingManager()
}
这在 Property Observers 中有解释。
我不确定为什么在您尝试时不起作用,但如果您遇到问题是因为您尝试调用的函数 (updateLabel
) 位于不同的 class,你可以在 SharingManager
class 中添加一个变量来存储调用 didSet
时要调用的函数,在这种情况下你将设置为 updateLabel
。
已编辑:
因此,如果您想编辑 ViewController 中的标签,您需要在 ViewController class 中使用 updateLabel() 函数来更新标签,但存储单例中的那个函数 class 所以它可以知道要调用哪个函数:
class SharingManager {
static let sharedInstance = SharingManager()
var updateLabel: (() -> Void)?
var labelChange: String = Model().callElements() {
didSet {
updateLabel?()
}
}
}
然后将其设置为您拥有要调用的函数的任何 class,例如(假设 updateLabel 是您要调用的函数):
SharingManager.sharedInstance.updateLabel = updateLabel
当然,您会希望确保负责该函数的视图控制器仍然存在,以便单例 class 可以调用该函数。
如果您需要根据可见的视图控制器调用不同的函数,您可能需要考虑 Key-Value Observing 以便在某些变量的值发生变化时获得通知。
此外,您永远不想像那样初始化视图控制器,然后立即设置视图控制器的 IBOutlets,因为 IBOutlets 在其视图实际加载之前不会被初始化。您需要以某种方式使用现有的视图控制器对象。
希望对您有所帮助。
还有另一种方法,使用 RxSwift:
将 RxSwift 和 RxCocoa pods 添加到您的项目中
修改你的
SharingManager
:import RxSwift class SharingManager { static let sharedInstance = SharingManager() private let _labelUpdate = PublishSubject<String>() let onUpdateLabel: Observable<String>? // any object can subscribe to text change using this observable // call this method whenever you need to change text func triggerLabelUpdate(newValue: String) { _labelUpdate.onNext(newValue) } init() { onUpdateLabel = _labelUpdate.shareReplay(1) } }
在您的 ViewController 中,您可以通过两种方式订阅值更新:
一个。订阅更新,手动更改标签文本
// add this ivar somewhere in ViewController let disposeBag = DisposeBag() // put this somewhere in viewDidLoad SharingManager.sharedInstance.onUpdateLabel? .observeOn(MainScheduler.instance) // make sure we're on main thread .subscribeNext { [weak self] newValue in // do whatever you need with this string here, like: // self?.myLabel.text = newValue } .addDisposableTo(disposeBag) // for resource management
b。将更新直接绑定到 UILabel
// add this ivar somewhere in ViewController let disposeBag = DisposeBag() // put this somewhere in viewDidLoad SharingManager.sharedInstance.onUpdateLabel? .distinctUntilChanged() // only if value has been changed since previous value .observeOn(MainScheduler.instance) // do in main thread .bindTo(myLabel.rx_text) // will setText: for that label when value changed .addDisposableTo(disposeBag) // for resource management
并且不要忘记在 ViewController 中 import RxCocoa
。
触发事件只需调用
SharingManager.sharedInstance.triggerLabelUpdate("whatever string here")
HERE 你可以找到示例项目。只需执行 pod update
和 运行 工作区文件。
var item = "initial value" {
didSet { //called when item changes
print("changed")
}
willSet {
print("about to change")
}
}
item = "p"
Apple 提供这些 属性 声明类型:-
1.计算属性:-
除了存储属性,类、结构和枚举可以定义计算属性,这些属性实际上并不存储值。相反,它们提供了一个 getter 和一个可选的 setter 来间接检索和设置其他属性和值。
var otherBool:Bool = false
public var enable:Bool {
get{
print("i can do editional work when setter set value ")
return self.enable
}
set(newValue){
print("i can do editional work when setter set value ")
self.otherBool = newValue
}
}
2。只读计算属性:-
具有 getter 但没有 setter 的计算 属性 被称为只读计算 属性。只读计算 属性 总是 returns 一个值,可以通过点语法访问,但不能设置为不同的值。
var volume: Double {
return volume
}
3。 属性 观察者:-
您可以选择在 属性:
上定义其中一个或两个观察者willSet 在存储值之前被调用。
didSet 在新值存储后立即被调用。
public var totalSteps: Int = 0 {
willSet(newTotalSteps) {
print("About to set totalSteps to \(newTotalSteps)")
}
didSet {
if totalSteps > oldValue {
print("Added \(totalSteps - oldValue) steps")
}
}
}
注意:- 有关更多信息,请继续专业 link https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html
在Swift4中你可以使用Key-Value Observation。
label.observe(\.text, changeHandler: { (label, change) in
// text has changed
})
基本上就是这样,但有一个小问题。 “观察”returns 一个您需要持有的 NSKeyValueObservation 对象! - 当它被解除分配时,您将不会再收到通知。为避免这种情况,我们可以将其分配给将被保留的 属性。
var observer:NSKeyValueObservation?
// then assign the return value of "observe" to it
observer = label.observe(\.text, changeHandler: { (label, change) in
// text has changed,
})
您也可以观察该值是否发生变化或首次设置
observer = label.observe(\.text, changeHandler: { (label, change) in
// just check for the old value in "change" is not Nil
if let oldValue = change.oldValue {
print("\(label.text) has changed from \(oldValue) to \(label.text)")
} else {
print("\(label.text) is now set")
}
})
有关更多信息,请参阅 Apple 文档 here
override var isHighlighted: Bool {
get { super.isHighlighted }
set {
super.isHighlighted = newValue
if newValue {
label.textColor = highlightedTextColor
contentView.backgroundColor = highlightedBackgroundColor
} else {
label.textColor = normalTextColor
contentView.backgroundColor = normalBackgroundColor
}
}
}