動作環境
xcode9.4.1
Swift4.1.2
やりたかったこと
UISwitchをAction接続し、値が変わったときに呼ばれるメソッドvalueChanged(_sender: UISwitch)
を作りました。
そのメソッド内で、特定の処理を行い、処理が完了しなかった場合はUISwitchの値を元に戻す動きを実装したいと考えました。
今回の処理は時間がかかるものであったため、valueChange(_sender: UISwitch)
が呼ばれた直後にUISwitchの値を戻し、処理が完了した場合に再度、UISwitchの値を変更するように実装しました。
setOn()
メソッドについて
UISwitchの値の変更には、setOn()
メソッドを用いました。
このメソッドは、Appleのドキュメントによると、
Setting the switch to either position does not result in an action message being sent.
つまり、setOn()
メソッドが呼ばれ、UISwitchの値が変わっても、valueChanged(_sender: UISwitch)
は呼ばれないということのようです。
一旦実装してみたが…
@IBAction func valueChange(_ sender: UISwitch) {
let isOn = switch.isOn
switch.setOn(!isOn, animated: false)
/* 時間のかかる後続の処理 */
}
簡略化しているが、上記の様に、valueChange(_sender: UISwitch)
が呼ばれた直後に、setOn()
を用い、UISwitchの値を反転させています。
しかし、このsetOn()
が呼ばれた直後、再びvalueChange(_sender: UISwitch)
が呼ばれ、二重に処理が行われてしまいました。
原因と対策
setOn()
からはアクションメッセージが送られないから、valueChange(_sender: UISwitch)
は呼ばれないはず…と思い、調査をしました。
すると、どうやらこのsetOn()
はメインスレッドで呼ばれなかった場合には、アクションメッセージが送られてしまうようでした。
そこで、以下のようにコードを変更しました。
@IBAction func valueChange(_ sender: UISwitch) {
let isOn = switch.isOn
DispatchQueue.main.async {
self.switch.setOn(!isOn, animated: false)
}
/* 時間のかかる後続の処理 */
}
実行してみると、setOn()
が行われても、valueChange(_sender: UISwitch)
は呼ばれず、想定通りの動作を行うことができました。
参考文献