LoginSignup
17
12

More than 5 years have passed since last update.

UIViewに各種Gesture処理を簡単に実装するExtension

Last updated at Posted at 2018-07-15

はじめに

例えば、長押し処理の例...

アプリを作成する上で、Buttonに対して長押し時の処理を実装したいと思ったら、UILongPressGestureRecognizerを使用するのが定石です。

let longPress = UILongPressGestureRecognizer(target: self, action: #selector(self.onLongPressed(_:)))
button.tag = 123
button.addGestureRecognizer(longPress)
@objc func onLongPressed(_ gesture: UILongPressGestureRecognizer) {
    guard let sender = gesture.view as? UIButton else {
        print("Sender is not a button")
        return
    }

    switch (gesture.state) {
    case .began:
        print("longPress start")
    case .ended:
        print("longPress end")
    default:
        break
    }

    // 長押しされたボタンは sender.tag で判別
}

ただ、画面にボタンがたくさんあって、全部に長押し処理をつけなきゃいけない場合、Recognizerを必要数分用意して、handleするメソッドも分岐の嵐になるので、見た目が美しくありません。

UIViewを拡張してみた

closureで書けるようにしてみました。

UIView+GestureClosure.swift
import UIKit

class GestureClosureSleeve<T: UIGestureRecognizer> {
    let closure: (_ gesture: T)->()

    init(_ closure: @escaping (_ gesture: T)->()) {
        self.closure = closure
    }

    @objc func invoke(_ gesture: Any) {
        guard let gesture = gesture as? T else { return }
        closure(gesture)
    }
}

extension UIView {
    func longPress(duration: CFTimeInterval, _ closure: @escaping (_ gesture: UILongPressGestureRecognizer)->()) {
        let sleeve = GestureClosureSleeve<UILongPressGestureRecognizer>(closure)
        let recognizer = UILongPressGestureRecognizer(target: sleeve, action: #selector(GestureClosureSleeve.invoke(_:)))
        recognizer.minimumPressDuration = duration
        self.addGestureRecognizer(recognizer)
        objc_setAssociatedObject(self, String(format: "[%d]", arc4random()), sleeve, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
    }
}

こんな感じで使えます。

button.longPress(duration: 0.1) { (gesture) in
    switch (gesture.state) {
    case .began:
        print("longPress start")
    case .ended:
        print("longPress end")
    default:
        break
    }
}

(2018/07/16 追記)
PanGesture, PinchGestureにも対応できることが確認できました。

extension UIView {
    func pan(_ closure: @escaping (_ gesture: UIPanGestureRecognizer)->()) {
        let sleeve = GestureClosureSleeve<UIPanGestureRecognizer>(closure)
        let recognizer = UIPanGestureRecognizer(target: sleeve, action: #selector(GestureClosureSleeve.invoke(_:)))
        self.addGestureRecognizer(recognizer)
        objc_setAssociatedObject(self, String(format: "[%d]", arc4random()), sleeve, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
    }

    func pinch(_ closure: @escaping (_ gesture: UIPinchGestureRecognizer)->()) {
        let sleeve = GestureClosureSleeve<UIPinchGestureRecognizer>(closure)
        let recognizer = UIPinchGestureRecognizer(target: sleeve, action: #selector(GestureClosureSleeve.invoke(_:)))
        self.addGestureRecognizer(recognizer)
        objc_setAssociatedObject(self, String(format: "[%d]", arc4random()), sleeve, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN)
    }
}

こんな感じで使用できます。

someView.pan { (gesture) in
    switch (gesture.state) {
    case .began:
        print("pan start")
    case .changed:
        print("pan changed")
    case .ended:
        print("pan end")
    default:
        break
    }

    // タップ開始地点からの移動量を元に、方向を判別するサンプル
/*
    let position = gesture.translation(in: self.view)

    if abs(position.x) > abs(position.y) {
        if position.x > 0 {
            // right
        } else {
            // left
        }
    } else {
        if position.y > 0 {
            // down
        } else {
            // up
        }
    }
*/
}

someView.pinch { (gesture) in
    switch (gesture.state) {
    case .began:
        print("pinch start (scale = \(gesture.scale)")
    case .changed:
        print("changed (scale = \(gesture.scale)")
    case .ended:
        print("pinch end (scale = \(gesture.scale)")
    default:
        break
    }
}

他のGestureも気軽に実装することができそうですね。

おわりに

UIControlの操作をclosureで書ける例は、以下のサイトの例を参考にさせてもらっています。
https://stackoverflow.com/questions/25919472/adding-a-closure-as-target-to-a-uibutton

AndroidのonClickListenerの感覚で書けるので、個人的に重宝しています。

どなたかの参考になれば幸いです。

17
12
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
12