Выполните метод, когда значение переменной изменяется в 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:
-
Добавьте в проект проекты RxSwift и RxCocoa
-
Измените свой 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
б. связывать обновления непосредственно с 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")
}
}
Для получения дополнительной информации см. Документацию на яблоки здесь