UIAction と UIControlEvent
少し調査部分が長くなってしまったのでざっと読みたい人は「まとめ」の段をどうぞ
UIButton
でメソッドの実行を設定するには親クラスの UIControl
で定義されている以下のメソッドを使用する。
func addTarget(_ target: Any?, action: Selector, for controlEvents: UIControl.Event)
https://developer.apple.com/documentation/uikit/uicontrol/1618259-addtarget
実装例
let button = UIButton(type: .system) button.addTarget(self, action: #selector(someMethod), for: .touchUpInside)
iOS 14 では UIAction
を使った代替メソッドが UIControl
に追加されている。
func addAction(_ action: UIAction, for controlEvents: UIControl.Event)
実装例
let button = UIButton(type: .system) let action = UIAction(title: "title") { action in // do something } button.addAction(action, for: .touchUpInside)
暗黙的なトリガータイミングの指定
UIControl
には addAction
メソッドの他に以下のイニシャライザが追加されている。
convenience init(frame: CGRect, primaryAction: UIAction?)
https://developer.apple.com/documentation/uikit/uicontrol/3600494-init
このイニシャライザを使用した場合 UIControlEvent
の指定がされていない。
ではどのタイミングで処理が発生するのか。
以下のコードでイニシャライザを使って UIAction
を指定した時の UIControlEvent
を確認してみる。
let action = UIAction(title: "title") { action in // do something } let button = UIButton(frame: frame, primaryAction: action) print(button.allControlEvents) // UIControlEvents(rawValue: 8192)
結果は UIControlEvents(rawValue: 8192)
となった。
これは UIControlEvent
の primaryActionTriggered
の値と一致する。
@available(iOS 9.0, *) public static var primaryActionTriggered: UIControl.Event { get }
https://developer.apple.com/documentation/uikit/uicontrol/event/1618222-primaryactiontriggered
ドキュメントには以下のように書かれているが説明が短い。
A semantic action triggered by buttons.
調べてみたところ以下のメソッドのヘッダコメントにもう少し説明があった。
@available(iOS 14.0, *) public convenience init(frame: CGRect, primaryAction: UIAction?)
Initializes the control and adds primaryAction for the UIControlEventPrimaryActionTriggered control event. Subclasses of UIControl may alter or add behaviors around the usage of primaryAction, see subclass documentation of this initializer for additional information.
ちゃんと UIControlEventPrimaryActionTriggered
を使うという記載があったが、じゃあそのトリガーのタイミングがいつかという具体的な説明はされていない。
サブクラスのドキュメントを見ろと書いてあるので見てみる。
UIButton
convenience init(frame: CGRect, primaryAction: UIAction?)
https://developer.apple.com/documentation/uikit/uibutton/3600349-init
The action to perform when the button is selected. The button registers this action for the primaryActionTriggered control event and sets the title and image properties to the action’s title and image.
という事で、ボタンが選択された時に実行すると書かれている。
UISegmentedControl
convenience init(frame: CGRect, actions: [UIAction])
https://developer.apple.com/documentation/uikit/uisegmentedcontrol/3600580-init
No overview available.
ドキュメント無し。
ヘッダコメントの方に記述があった。
Initializes the segmented control with the given frame and segments constructed from the given UIActions. Segments will prefer images over titles when both are provided. Selecting a segment calls UIAction.actionHandler as well as handlers for the ValueChanged and PrimaryActionTriggered control events.
選択すると valueChanged
と PrimaryActionTriggered
の両方がトリガーされるらしい。
調査結果
結局 UIControlEventPrimaryActionTriggered
の詳細な説明は見つからなかった。
コードを書いて試してみたところ実際の動作としては
UIButton
は選択時、おそらく touchUpInside
に相当。
UISegmentedControl
UISwitch
UIStepper
UIDatePicker
も選択時で valueChanged
に相当。
UISlider
はスライダー操作時にトリガーで、挙動を見た感じでは valueChanged
以外に touchDown
touchUpInside
touchUpOutside
あたりも入っていそうな動きをしていた。
最後に UITextField
はキーボードのリターンキーを押したタイミングでトリガーされ、テキスト入力のフォーカスは外れないという挙動だった。UITextField
+ editingDidEnd
の場合はフォーカスが外れた時にトリガーされ、editingDidEndOnExit
はフォーカスが外れただけではトリガーされずにリターンキーを押すとトリガーされつつフォーカスが外れるという挙動になるので primaryActionTriggered
はこれらとは異なった挙動になる事が分かった。
まとめ
iOS 14.5で確認
primaryAction
を使ったイニシャライザで UIAction
を設定すると暗黙的に UIControlPrimaryActionTriggered
が設定される。
Implicitly using UIControlEventPrimaryActionTrigg…
UIControlPrimaryActionTriggered
はクラスによって挙動が異なる。
Class | UIControlPrimaryActionTriggered と同じような挙動の UIControlEvent |
---|---|
UIButton | touchUpInside |
UISegmentedControl, UISwitch, UIStepper, UIDatePicker | valueChanged |
UISlider | [valueChanged, touchDown, touchUpInside, touchUpOutside] |
UITextField | None |
UITextField
の場合 UIControlPrimaryActionTriggered
と同じ挙動になる UIControlEvent
は無い。
UITextField の UIControlEvent | リターンキーを押した時の挙動 | トリガーのタイミング |
---|---|---|
editingDidEnd | フォーカスが外れない | フォーカスが外れた時 |
editingDidEndOnExit | フォーカスが外れる | リターンキーを押してフォーカスが外れた時(リターンキー以外でフォーカスが外れた場合はトリガーされない) |
primaryActionTriggered | フォーカスが外れない | リターンキーを押した時 |
UISlider
と UITextField
で UIControlPrimaryActionTriggered
を使用する時は単純に単一の UIControlEvent
を用いた時とは挙動が異なる事を頭の片隅に置いておいた方が良いかも知れない。
また、これらの挙動の違いを理解した上で初期化済みのインスタンスに対して UIControlPrimaryActionTriggered
でアクションを設定したいという場合は以下の様に実装する事も可能。
let action = UIAction(title: "title") { action in // do something } let textField = UITextField(frame: frame) textField.addAction(action, for: .primaryActionTriggered)
以上