Выполните метод, когда значение переменной изменяется в Swift

Мне нужно выполнить функцию при изменении значения переменной.

У меня есть одноэлементный класс, содержащий общую переменную, называемую labelChange. Значения этой переменной берутся из другого класса под названием " Model. У меня есть два класса VC, один из которых имеет кнопку и метку, а вторую - только кнопку.

Когда нажата кнопка в первом классе VC, я обновляю эту метку с помощью этой функции:

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

Но я хочу вызвать тот же метод всякий раз, когда labelChange значение labelChange. Поэтому при нажатии кнопки я обновляю значение labelChange и когда это произойдет, я хочу обновить метку новым значением labelChange. Также во втором VC я могу обновить значение labelChange но я не могу обновить метку, когда это значение изменилось.

Возможно, свойства - это решение, но может ли кто-нибудь показать мне, как это сделать.

Отредактировано второй раз:

Singleton Класс:

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 в singleton. Поэтому я обновлю эту метку VC в singleton.

Когда я печатаю значение labelChange значение обновляется, и все в порядке. Но когда я пытаюсь обновить это значение на ярлыке от singleton, я получаю сообщение об ошибке:

неожиданно найдено нуль при развертывании необязательного значения

и ошибка указывает на 4-ю строку одноточечного класса.

Ответ 1

Вы можете просто использовать наблюдателя свойств для переменной, labelChange и вызвать функцию, которую вы хотите вызвать внутри didSet (или willSet если вы хотите вызвать ее до ее установки):

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

Это объясняется в Property Observer.

Я не уверен, почему это не сработало, когда вы его пробовали, но если у вас проблемы, потому что функция, которую вы пытаетесь вызвать (updateLable), находится в другом классе, вы можете добавить переменную в класс SharingManager для хранения функция, вызываемая при didSet функции didSet, которая в этом случае будет установлена в updateLable.


Отредактировано:

Поэтому, если вы хотите отредактировать метку из ViewController, вам нужно иметь эту функцию updateLable() в классе ViewController для обновления метки, но сохранить эту функцию в классе singleton, чтобы она могла знать, какую функцию вызывать:

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

а затем установите его в любом классе, который у вас есть функция, которую вы хотите вызывать, например (предполагается, что updateLable - это функция, которую вы хотите вызвать):

SharingManager.sharedInstance.updateLable = updateLable

Конечно, вы захотите убедиться, что контроллер вида, который отвечает за эту функцию, все еще существует, поэтому класс singleton может вызывать функцию.

Если вам нужно вызывать разные функции, в зависимости от того, какой вид контроллера видимости видим, вы можете захотеть рассмотреть Key-Value Observing, чтобы получать уведомления, когда изменяется значение для определенных переменных.

Кроме того, вы никогда не хотите инициализировать такой контроллер вида, а затем сразу же установите IBOutlets контроллера вида, так как IBOutlets не инициализируются до тех пор, пока его представление не будет загружено. Вы должны каким-то образом использовать существующий объект контроллера вида.

Надеюсь это поможет.

Ответ 2

Существует другой способ сделать это, используя 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 вы можете подписаться на обновление значения двумя способами:

    а. подписаться на обновления и вручную изменить текст ярлыка

    // 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
    

    б. связывать обновления непосредственно с 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
    

И не забудьте import RxCocoa в ViewController.

Для запуска события просто вызовите

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

ЗДЕСЬ вы можете найти пример проекта. Просто pod update и запустите файл рабочей области.

Ответ 3

Apple предоставляет следующие типы объявлений:

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"

Ответ 5

В Swift 4 вы можете использовать Key-Value Observation.

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

Это в основном это, но есть небольшой улов. "наблюдать" возвращает объект 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("\(child.name) is now set")
    }

}

Для получения дополнительной информации см. Документацию на яблоки здесь