web-dev-qa-db-fra.com

Exécutez une méthode quand une valeur de variable change en Swift

J'ai besoin d'exécuter une fonction quand une valeur de variable change.

J'ai une classe singleton contenant une variable partagée appelée labelChange. Les valeurs de cette variable proviennent d'une autre classe appelée Model. J'ai deux classes VC, l’une d’elles a un bouton et une étiquette et la seconde seulement un bouton.

Lorsque le bouton de la première VC classe est enfoncée), je mets à jour l'étiquette avec cette fonction:

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

Mais je veux appeler la même méthode chaque fois que la valeur de labelChange est modifiée. Donc, en cliquant sur le bouton, je ne mettrai à jour que la valeur labelChange et lorsque cela se produira, je souhaite mettre à jour le libellé avec la nouvelle valeur de labelChange. Aussi dans le second VC je peux mettre à jour la valeur labelChange mais je ne peux pas mettre à jour l'étiquette lorsque cette valeur est modifiée.

Peut-être que les propriétés sont la solution mais quelqu'un peut-il me montrer comment le faire.

édité une deuxième fois:

Classe Singleton:

class SharingManager {
    func updateLabel() {
        println(labelChange)
        ViewController().label.text = SharingManager.sharedInstance.labelChange     
    }
    var labelChange: String = Model().callElements() {
        willSet {
            updateLabel()
        }
    }
    static let sharedInstance = SharingManager()
}

Premier VC:

class ViewController: UIViewController {
    @IBOutlet weak var label: UILabel!
    @IBAction func Button(sender: UIButton) {    
       SViewController().updateMessageAndDismiss()
    }
}

Deuxième VC:

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

J'ai apporté quelques améliorations, mais je dois référencer une étiquette de la première classe VC en singleton. Par conséquent, je mettrai à jour cette étiquette de VC en singleton.

Quand j'imprime la valeur de labelChange, la valeur est mise à jour et tout va bien. Mais lorsque j'essaie de mettre à jour cette valeur sur l'étiquette de singleton, je reçois une erreur:

trouvé de manière inattendue à zéro lors du déroulement d'une valeur facultative

et l'erreur pointe dans la 4ème ligne de la classe singleton.

32
Ilir V. Gruda

Vous pouvez simplement utiliser un observateur de propriété pour la variable, labelChange, et appeler la fonction que vous souhaitez appeler à l'intérieur de didSet (ou willSet si vous voulez l'appeler avant. a été mis en):

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

Ceci est expliqué dans Observateurs de propriétés .

Je ne sais pas pourquoi cela n'a pas fonctionné quand vous l'avez essayé, mais si vous rencontrez des problèmes parce que la fonction que vous essayez d'appeler (updateLabel) est dans une classe différente, vous pouvez ajouter une variable dans la classe SharingManager pour stocker la fonction à appeler lorsque didSet a été appelée, que vous définissez sur updateLabel dans ce cas.


Modifié:

Ainsi, si vous souhaitez modifier une étiquette à partir de ViewController, vous souhaitez que cette fonction updateLabel () de la classe ViewController mette à jour l'étiquette, mais stockez cette fonction dans la classe singleton afin qu'elle sache quelle fonction appeler:

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

puis définissez-le dans la classe où vous avez la fonction que vous voulez appeler, comme (en supposant que updateLabel est la fonction que vous voulez appeler):

SharingManager.sharedInstance.updateLabel = updateLabel

Bien sûr, vous voudrez vous assurer que le contrôleur de vue responsable de cette fonction existe toujours, afin que la classe singleton puisse appeler la fonction.

Si vous devez appeler différentes fonctions en fonction du contrôleur de vue visible, vous pouvez envisager Observer valeur par clé pour recevoir des notifications lorsque la valeur de certaines variables change.

En outre, vous ne voudrez jamais initialiser un contrôleur de vue de ce type, puis définir immédiatement les IBOutlets du contrôleur de vue, car les IBOutlets ne sont initialisés que lorsque leur vue est chargée. Vous devez utiliser un objet contrôleur de vue existant d'une manière ou d'une autre.

J'espère que cela t'aides.

34
Dennis

Dans Swift 4, vous pouvez utiliser l'observation clé-valeur.

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

C'est fondamentalement cela, mais il y a un petit piège. "observer" renvoie un objet NSKeyValueObservation que vous devez conserver! - lorsqu’il est désalloué, vous ne recevrez plus de notifications. Pour éviter cela, nous pouvons l'assigner à une propriété qui sera conservée.

var observer:NSKeyValueObservation?
// then assign the return value of "observe" to it
observer = label.observe(\.text, changeHandler: { (label, change) in {
    // text has changed,
}

Vous pouvez également observer si la valeur a changé ou a été définie pour la première fois.

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

}

Pour plus d'informations, veuillez consulter la documentation d'Apple ici

8
Andy

Il existe un autre moyen de le faire, en utilisant RxSwift:

  1. Ajoutez des modules RxSwift et RxCocoa à votre projet.

  2. Modifiez votre 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. Dans votre ViewController, vous pouvez souscrire à la mise à jour de valeur de deux manières:

    une. s'abonner aux mises à jour et modifier manuellement le texte de l'étiquette

    // 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. lier les mises à jour directement à 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
    

Et n'oubliez pas de import RxCocoa dans ViewController.

Pour déclencher un événement, il suffit d'appeler

SharingManager.sharedInstance.triggerLabelUpdate("whatever string here")

ICI vous pouvez trouver un exemple de projet. Il suffit de faire pod update et exécutez le fichier d'espace de travail.

7
Valerii Lider

Apple fournit les types de déclaration de propriété suivants: -

1. Propriétés calculées: -

En plus des propriétés stockées, les classes, structures et énumérations peuvent définir des propriétés calculées, qui ne stockent pas de valeur. Au lieu de cela, ils fournissent un getter et un setter optionnel pour récupérer et définir indirectement les autres propriétés et valeurs.

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. Propriétés calculées en lecture seule: -

Une propriété calculée avec un getter mais aucun séparateur est appelée une propriété calculée en lecture seule. Une propriété calculée en lecture seule renvoie toujours une valeur et est accessible via la syntaxe à points, mais ne peut pas être définie sur une valeur différente.

var volume: Double {
    return volume
}

3. Observateurs immobiliers: -

Vous avez la possibilité de définir l'un ou l'autre ou les deux observateurs sur une propriété:

willSet est appelé juste avant que la valeur ne soit stockée.
didSet est appelé immédiatement après le stockage de la nouvelle valeur.

public  var totalSteps: Int = 0 {
    willSet(newTotalSteps) {
        print("About to set totalSteps to \(newTotalSteps)")
    }
    didSet {
        if totalSteps > oldValue  {
            print("Added \(totalSteps - oldValue) steps")
        }
    }
}

REMARQUE: - Pour plus d'informations, rendez-vous sur le lien professionnel https://developer.Apple.com /library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Properties.html

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