Swift.用RunTime和Extension的方式为UIView添加手势方法

效果图

前言:

我们在开发工作中很有可能遇到需要view相应各种手势的需求,使用RunTime+Extension的形式为UIView进行拓展,代码的复用性,可读性都很高.

实现效果:

为UIView及其子类添加了点击,双击,长按,上滑,左滑,右滑,下滑手势方法,调用方法简单轻便.

实现方式:

1.添加滑动手势协议.

2.设置RunTime添加属性时的Key.

3.创建点击,长按,滑动手势类.

4.使用Extension为UIView进行拓展.


1.添加滑动手势协议.

在协议中添加滑动事件以及相应触发方法.

 ///添加点击事件协议
protocol UIViewTapable {
    ///单击事件
    var tapHandlers: [CRTapGestureHanler] { get }
    ///双击事件
    var doubleTapGestureHandlers: [CRTapGestureHanler]{ get }
    ///长点事件
    var longTapGestureHandlers: [CRTapGestureHanler]{ get }
    ///上滑事件
    var upSwipeGestureHandlers: [CRTapGestureHanler]{ get }
    ///左滑事件
    var leftSwipeGestureHandlers: [CRTapGestureHanler]{ get }
    ///右滑事件
    var rightSwipeGestureHandlers: [CRTapGestureHanler]{ get }
    ///下滑事件
    var downSwipeGestureHandlers: [CRTapGestureHanler]{ get }

    func whenTapped(handler:@escaping()->Void)
    func whenDoubleTapped(handler:@escaping()->Void)
    func whenLongPressed(handler:@escaping()->Void)
    func whenUpSwiped(handler:@escaping()->Void)
    func whenLeftSwiped(handler:@escaping()->Void)
    func whenRightSwiped(handler:@escaping()->Void)
    func whenDownSwiped(handler:@escaping()->Void)

}

2.设置RunTime添加属性时的Key.

在这里我们设置所有手势相应的String标识以及与之对应的内存地址Key.

///runtime绑定方法时的key
class EWGestureAssociatedObjectKey {
    ///设置不同手势不同String标识
    static let EWTapGestureAssociatedObjectString  = "EWTapGestureAssociatedObjectString"
    ///获取String标识的内存地址作为runtime属性的Key
    static var EWTapGestureKey = {return Unmanaged<AnyObject>.passUnretained(EWGestureAssociatedObjectKey.EWTapGestureAssociatedObjectString as AnyObject).toOpaque()}()

    static let EWDoubleTapGestureAssociatedObjectString  = "EWDoubleTapGestureAssociatedObjectString"
    static var EWDoubleTapGestureKey = {return Unmanaged<AnyObject>.passUnretained(EWGestureAssociatedObjectKey.EWDoubleTapGestureAssociatedObjectString as AnyObject).toOpaque()}()

    static let EWLongTapGestureAssociatedObjectString  = "EWLongTapGestureAssociatedObjectString"
    static var EWLongTapGestureKey = {return Unmanaged<AnyObject>.passUnretained(EWGestureAssociatedObjectKey.EWLongTapGestureAssociatedObjectString as AnyObject).toOpaque()}()

    static let EWUpSwipeGestureAssociatedObjectString  = "EWUpSwipeGestureAssociatedObjectString"
    static var EWUpSwipeGestureKey = {return Unmanaged<AnyObject>.passUnretained(EWGestureAssociatedObjectKey.EWUpSwipeGestureAssociatedObjectString as AnyObject).toOpaque()}()

    static let EWLeftSwipeGestureAssociatedObjectString  = "EWLeftSwipeGestureAssociatedObjectString"
    static var EWLeftSwipeGestureKey = {return Unmanaged<AnyObject>.passUnretained(EWGestureAssociatedObjectKey.EWLeftSwipeGestureAssociatedObjectString as AnyObject).toOpaque()}()

    static let EWRightSwipeGestureAssociatedObjectString  = "EWRightSwipeGestureAssociatedObjectString"
    static var EWRightSwipeGestureKey = {return Unmanaged<AnyObject>.passUnretained(EWGestureAssociatedObjectKey.EWRightSwipeGestureAssociatedObjectString as AnyObject).toOpaque()}()

    static let EWDownSwipeGestureAssociatedObjectString  = "EWDownSwipeGestureAssociatedObjectString"
    static var EWDownSwipeGestureKey = {return Unmanaged<AnyObject>.passUnretained(EWGestureAssociatedObjectKey.EWDownSwipeGestureAssociatedObjectString as AnyObject).toOpaque()}()

}

3.创建点击,长按,滑动手势类.

单击双击都是点击手势,使用EWTapGesture,上下左右滑动手势使用EWSwipeGesture

 ///单击双击手势
class EWTapGesture: NSObject, UIGestureRecognizerDelegate{
    fileprivate weak var myView:UIView!
    fileprivate let gesture : UITapGestureRecognizer
    ///手势储存外界闭包
    fileprivate var tappedHandler = [EWTapGestureHanler]()

    init(view: UIView,taps: Int = 1) {
        myView = view
        ///手势
        gesture = UITapGestureRecognizer()
        super.init()
        gesture.delegate = self
        gesture.numberOfTapsRequired = taps
        gesture.addTarget(self, action: #selector(EWTapGesture.tapped(_:)))
        myView.addGestureRecognizer(gesture)
        if taps == 1 {
            myView.EWOneTapGesture = self
        }else if taps == 2{
            myView.EWDoubleTapGesture = self
        }

    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    ///将外界传入的block传入手势方法
    fileprivate func registerHandler(_ handler:@escaping EWTapGestureHanler){
        self.tappedHandler.append(handler)
    }
    @objc func tapped(_ sender: UITapGestureRecognizer) {
        for handler in self.tappedHandler{
            handler()
        }
    }
}
///长按手势
class EWLongPressGesture {
    typealias EWLongPressGestureHandler = ()->Void
    fileprivate weak var myView:UIView!
    fileprivate let gesture : UILongPressGestureRecognizer
    fileprivate var tappedHandler = [EWLongPressGestureHandler]()

    init(view:UIView,taps:Int = 1) {
        myView = view
        gesture = UILongPressGestureRecognizer()
        gesture.minimumPressDuration = TimeInterval(taps)
        gesture.addTarget(self, action: #selector(EWTapGesture.tapped(_:)))
        myView.addGestureRecognizer(gesture)
        myView.EWLongTapGesture = self
    }
    ///将外界传入的block传入手势方法
    fileprivate func registerHandler(_ handler:@escaping EWLongPressGestureHandler){
        self.tappedHandler.append(handler)
    }
    @objc func tapped(_ sender: UITapGestureRecognizer) {
        for handler in self.tappedHandler{
            guard gesture.state == .began else { return }
            handler()
        }
    }
}
///滑动手势
class EWSwipeGesture {
    typealias EWSwipeGestureHandler = ()->Void
    fileprivate weak var myView:UIView!
    fileprivate let gesture : UISwipeGestureRecognizer
    fileprivate var tappedHandler = [EWSwipeGestureHandler]()

    init(view:UIView,direction: UISwipeGestureRecognizer.Direction = .up) {
        myView = view
        gesture = UISwipeGestureRecognizer()
        gesture.direction = direction
        gesture.addTarget(self, action: #selector(EWTapGesture.tapped(_:)))
        myView.addGestureRecognizer(gesture)
        switch direction {
        case .up:
            myView.EWUpSwipeGesture = self
        case .left:
            myView.EWLeftSwipeGesture = self
        case .right:
            myView.EWRightSwipeGesture = self
        case .down:
            myView.EWDownSwipeGesture = self
        default:
            return
        }
    }
    fileprivate func registerHandler(_ handler:@escaping EWSwipeGestureHandler){
        self.tappedHandler.append(handler)
    }
    @objc func tapped(_ sender: UITapGestureRecognizer) {
        for handler in self.tappedHandler{
            handler()
        }
    }
}

4.使用Extension为UIView进行拓展.

在这里让UIView遵循UIViewTapable协议,并使用RunTime为其增加手势属性.

 extension UIView: UIViewTapable {
    var tapHandlers: [EWTapGestureHanler]{
        return EWOneTapGesture.tappedHandler
    }
    var doubleTapGestureHandlers: [EWTapGestureHanler]{
        return EWDoubleTapGesture.tappedHandler
    }
    var longTapGestureHandlers: [EWTapGestureHanler]{
        return EWLongTapGesture.tappedHandler
    }
    var upSwipeGestureHandlers: [EWTapGestureHanler] {
        return EWUpSwipeGesture.tappedHandler
    }
    var leftSwipeGestureHandlers: [EWTapGestureHanler] {
        return EWLeftSwipeGesture.tappedHandler
    }
    var rightSwipeGestureHandlers: [EWTapGestureHanler] {
        return EWRightSwipeGesture.tappedHandler
    }
    var downSwipeGestureHandlers: [EWTapGestureHanler] {
        return EWDownSwipeGesture.tappedHandler
    }
    ///runtime的方式为View设置手势属性
    var EWOneTapGesture: EWTapGesture {
        get {
            if let obj = objc_getAssociatedObject(self, EWGestureAssociatedObjectKey.EWTapGestureKey) as? EWTapGesture {
                return obj
            }
            let tapGesture = EWTapGesture(view: self)
            tapGesture.gesture.require(toFail: EWDoubleTapGesture.gesture)
            return tapGesture
        }
        set {
            objc_setAssociatedObject(self, EWGestureAssociatedObjectKey.EWTapGestureKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    var EWDoubleTapGesture: EWTapGesture {
        get {
            if let obj = objc_getAssociatedObject(self, EWGestureAssociatedObjectKey.EWDoubleTapGestureKey) as? EWTapGesture {
                return obj
            }
            return EWTapGesture(view: self,taps: 2)
        }
        set {
            objc_setAssociatedObject(self, EWGestureAssociatedObjectKey.EWDoubleTapGestureKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    var EWLongTapGesture:EWLongPressGesture {
        get {
            if let obj = objc_getAssociatedObject(self, EWGestureAssociatedObjectKey.EWLongTapGestureKey) as? EWLongPressGesture {
                return obj
            }
            return EWLongPressGesture(view: self)
        }
        set {
            objc_setAssociatedObject(self, EWGestureAssociatedObjectKey.EWLongTapGestureKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    var EWUpSwipeGesture: EWSwipeGesture {
        get {
            if let obj = objc_getAssociatedObject(self, EWGestureAssociatedObjectKey.EWUpSwipeGestureKey) as? EWSwipeGesture {
                return obj
            }
            return EWSwipeGesture(view: self)
        }
        set {
            objc_setAssociatedObject(self, EWGestureAssociatedObjectKey.EWUpSwipeGestureKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    var EWLeftSwipeGesture: EWSwipeGesture {
        get {
            if let obj = objc_getAssociatedObject(self, EWGestureAssociatedObjectKey.EWLeftSwipeGestureKey) as? EWSwipeGesture {
                return obj
            }
            return EWSwipeGesture(view: self, direction: .left)
        }
        set {
            objc_setAssociatedObject(self, EWGestureAssociatedObjectKey.EWLeftSwipeGestureKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    var EWRightSwipeGesture: EWSwipeGesture {
        get {
            if let obj = objc_getAssociatedObject(self, EWGestureAssociatedObjectKey.EWRightSwipeGestureKey) as? EWSwipeGesture {
                return obj
            }
            return EWSwipeGesture(view: self, direction: .right)
        }
        set {
            objc_setAssociatedObject(self, EWGestureAssociatedObjectKey.EWRightSwipeGestureKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    var EWDownSwipeGesture: EWSwipeGesture {
        get {
            if let obj = objc_getAssociatedObject(self, EWGestureAssociatedObjectKey.EWDownSwipeGestureKey) as? EWSwipeGesture {
                return obj
            }
            return EWSwipeGesture(view: self, direction: .down)
        }
        set {
            objc_setAssociatedObject(self, EWGestureAssociatedObjectKey.EWDownSwipeGestureKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }

    func whenTapped(handler: @escaping () -> Void){
        self.isUserInteractionEnabled = true
        self.EWOneTapGesture.registerHandler(handler)
    }

    func whenDoubleTapped(handler:@escaping ()->Void){
        self.isUserInteractionEnabled = true
        self.EWDoubleTapGesture.registerHandler(handler)
    }

    func whenLongPressed(handler:@escaping ()->Void){
        self.isUserInteractionEnabled = true
        self.EWLongTapGesture.registerHandler(handler)
    }

    func whenUpSwiped(handler: @escaping () -> Void) {
        self.isUserInteractionEnabled = true
        self.EWUpSwipeGesture.registerHandler(handler)
    }

    func whenLeftSwiped(handler: @escaping () -> Void) {
        self.isUserInteractionEnabled = true
        self.EWLeftSwipeGesture.registerHandler(handler)
    }

    func whenRightSwiped(handler: @escaping () -> Void) {
        self.isUserInteractionEnabled = true
        self.EWRightSwipeGesture.registerHandler(handler)
    }

    func whenDownSwiped(handler: @escaping () -> Void) {
        self.isUserInteractionEnabled = true
        self.EWDownSwipeGesture.registerHandler(handler)
    }
}

使用方法:

将UIView+TappedExtension.swift文件拖入项目,调用时:

tapView.whenTapped {
    EWToast.showTopWithText(text: "点击事件", duration: 1)
}
tapView.whenUpSwiped {
    EWToast.showCenterWithText(text: "上滑事件", duration: 1)
}
tapView.whenLeftSwiped {
    EWToast.showCenterWithText(text: "左滑事件", duration: 1)
}
tapView.whenRightSwiped {
    EWToast.showCenterWithText(text: "右滑事件", duration: 1)
}
tapView.whenDownSwiped {
    EWToast.showCenterWithText(text: "下滑事件", duration: 1)
}
tapView.whenLongPressed {
    EWToast.showCenterWithText(text: "长按事件", duration: 1)
}
tapView.whenDoubleTapped {
    EWToast.showBottomWithText(text: "双击事件", duration: 1)
}

demo地址:EWViewTappedExtension

有问题欢迎探讨.

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容