コネヒト開発者ブログ

コネヒト開発者ブログ

iOS14からはaddTargetじゃなくてaddAction

こんにちは、コネヒトで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/