こんにちは、コネヒトでiOSエンジニアをやっていますyanamuraです。
これは iOS Advent Calendar 2020 の 4日目の記事です。
TL;DR
UIKitのUIControl系のView(UIButtonなど)ではタップ時のアクションをコードで実装するときは、標準のAPIだとaddTargetを用いる必要がありました。addTargetだとclosureが使えずいちいち関数を定義しなければならなかったり、@objc
をつける必要があったりと面倒でした。
// addTargetでやるパターン @IBOutlet weak var button: UIButton! override func viewDidLoad() { super.viewDidLoad() button.addTarget(self, action: #selector(doSomething), for: .touchUpInside) } @objc func doSomething() { print("do something") }
iOS14でついにUIControlにaddActionという新しいAPIが追加されこの面倒くささが解決しました!(SwiftUIがでているこの状況では今更感がありますが・・)
このようにシュッとかけるようになっています。
button.addAction(.init { _ in print("do something") }, for: .touchUpInside)
(ちなみにコネヒトでは現在RxSwift(RxCocoa)を使っているので正直addActionの恩恵にあずかることはなさそうです
詳細
UIControlの新しいAPIのaddActionではactionをselectorではなくUIActionを受け取るようになっています。
// UIControl func addAction(_ action: UIAction, for controlEvents: UIControl.Event)
UIActionはinitializerで多くの引数を指定できますが、必須なのはhandlerだけです。
// UIAction convenience init( title: String = "", image: UIImage? = nil, identifier: UIAction.Identifier? = nil, discoverabilityTitle: String? = nil, attributes: UIMenuElement.Attributes = [], state: UIMenuElement.State = .off, handler: @escaping UIActionHandler)
UIActionHandlerは単なるaliasです。
// UIActionHandler typealias UIActionHandler = (UIAction) -> Void
UIControlにはaddAction以外にもinitializerでactionを設定することもできるようになっています。https://developer.apple.com/documentation/uikit/uicontrol/3600494-init
// UIControl convenience init(frame: CGRect, primaryAction: UIAction?)
使い方
わかりやすく書くとこうなります。
let action = UIAction(handler: { _ in print("do something")}) button.addAction(action, for: .touchUpInside)
省略して書くとこのようになります。
button.addAction(.init { _ in print("do something") }, for: .touchUpInside)
addActionが使えるView
addActionはUIControlのAPIなのでUIControlとUIControlを継承しているクラスでは使えます。
ですので、UIButtonをはじめ、UIDatePicker, UIPageControl, UISegmentedControl, UISwitch, UIStepper, UISlider, そして新たに追加されたUIColorWellでaddActionが使えるはずです。
参考
WWDC2020: Build with iOS pickers, menus and actions
https://developer.apple.com/videos/play/wwdc2020/10052/