web-dev-qa-db-ja.com

Swiftで変数値が変化したときにメソッドを実行する

変数値が変化したときに関数を実行する必要があります。

labelChangeという共有変数を含むシングルトンクラスがあります。この変数の値は、Modelという別のクラスから取得されます。 2つのVCクラスがあり、そのうちの1つにはボタンとラベルがあり、2番目にはボタンしかありません。

最初のVCクラスのボタンが押されると、この関数でラベルを更新します。

func updateLabel(){
    self.label.text = SharingManager.sharedInstance.labelChange
}

しかし、labelChangeの値が変更されるたびに同じメソッドを呼び出したいです。そのため、ボタンをクリックしてlabelChange値のみを更新し、このことが発生した場合は、labelChangeの新しい値でラベルを更新します。また、2番目のVCでlabelChange値を更新できますが、この値が変更されたときにラベルを更新できません。

おそらくプロパティが解決策かもしれませんが、その方法を誰にでも教えてくれます。

2回目の編集:

シングルトンクラス:

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()
    }
}

2番目のVC:

func updateMessageAndDismiss() {
        SharingManager.sharedInstance.labelChange = modelFromS.callElements()
        self.dismissViewControllerAnimated(true, completion: nil)
    }
@IBAction func b2(sender: UIButton) { 
        updateMessageAndDismiss()
}

いくつかの改善を行いましたが、シングルトンの最初のVCクラスからラベルを参照する必要があります。したがって、シングルトンのVCのラベルを更新します。

labelChangeの値を出力すると、値が更新されており、すべて問題ありません。しかし、ラベルのその値をシングルトンから更新しようとすると、エラーが表示されます。

オプション値のアンラップ中に予期せずnilが見つかりました

エラーはシングルトンクラスの4行目を指しています。

32
Ilir V. Gruda

変数labelChangeのプロパティオブザーバーを使用して、didSet内で呼び出す関数を呼び出すことができます(または、その前に呼び出す場合はwillSet設定されています):

class SharingManager {
    var labelChange: String = Model().callElements() {
        didSet {
            updateLabel()
        }
    }
    static let sharedInstance = SharingManager()
}

これは Property Observers で説明されています。

試したときになぜこれが機能しなかったのかはわかりませんが、呼び出している関数(updateLabel)が別のクラスにあるために問題が発生している場合は、変数を追加することができますSharingManagerが呼び出されたときに呼び出す関数を格納するdidSetクラス。この場合はupdateLabelに設定します。


編集済み:

したがって、ViewControllerからラベルを編集する場合は、ViewControllerクラスのupdateLabel()関数でラベルを更新しますが、その関数をシングルトンクラスに格納して、呼び出す関数を認識できるようにします。

class SharingManager {
    static let sharedInstance = SharingManager()
    var updateLabel: (() -> Void)?
    var labelChange: String = Model().callElements() {
        didSet {
            updateLabel?()
        }
    }
}

次に、呼び出したい関数があるクラスのいずれかに設定します(updateLabelが呼び出したい関数であると仮定):

SharingManager.sharedInstance.updateLabel = updateLabel

もちろん、シングルトンクラスが関数を呼び出すことができるように、その関数を担当するView Controllerがまだ存在していることを確認する必要があります。

どのView Controllerが表示されているかに応じて異なる関数を呼び出す必要がある場合、 Key-Value Observing を考慮して、特定の変数の値が変更されるたびに通知を受け取ることができます。

また、そのようなView Controllerを初期化してからView ControllerのIBOutletsをすぐに設定したくないのは、IBOutletsはビューが実際にロードされるまで初期化されないためです。既存のView Controllerオブジェクトを何らかの方法で使用する必要があります。

お役に立てれば。

34
Dennis

Swift 4では、Key-Value Observationを使用できます。

label.observe(\.text, changeHandler: { (label, change) in {
    // text has changed
}

これは基本的にはそうですが、ちょっとした落とし穴があります。 「observe」は、保持する必要がある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のドキュメントを参照してください こちら

8
Andy

RxSwiftを使用する別の方法があります。

  1. RxSwiftポッドとRxCocoaポッドをプロジェクトに追加します

  2. 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)
        }
    }
    
  3. ViewControllerでは、次の2つの方法で値の更新をサブスクライブできます。

    a。更新をサブスクライブし、ラベルテキストを手動で変更する

    // 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を実行し、ワークスペースファイルを実行するだけです。

7
Valerii Lider

アップルはこれらのプロパティ宣言タイプを提供します:-

1。計算されたプロパティ:-

格納されたプロパティに加えて、クラス、構造、および列挙は、実際に値を格納しない計算されたプロパティを定義できます。代わりに、他のプロパティと値を間接的に取得および設定するゲッターとオプションのセッターを提供します。

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。読み取り専用の計算プロパティ:-

ゲッターはあるがセッターはない計算プロパティは、読み取り専用の計算プロパティと呼ばれます。読み取り専用の計算プロパティは常に値を返し、ドット構文を使用してアクセスできますが、別の値に設定することはできません。

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")
        }
    }
}

注:-詳細については、専門のリンクを参照してくださいhttps:// developer。 Apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html

4
var item = "initial value" {
    didSet { //called when item changes
        print("changed")
    }
    willSet {
        print("about to change")
    }
}
item = "p"
1
msk_sureshkumar