swift实现手势解锁

手势解锁

基本逻辑

手势解锁在应用中都很常见,手势基本的连接点一般都是9个。实现逻辑一般都不难,主要是对滑动过程中,手势所到的点跟本身9个链接点的位置判断并且使用贝塞尔曲线在连接点之间添加连线,最后根据链接点的内容进行输出验证。详看以下代码:

1、解锁视图

新建一个名为gesUnLockView类,继承自UIView,重写init(frame:)并且给视图添加UIPanGestureRecognizer手势识别。

class gesUnlockView: UIView {
    let topMargin: CGFloat = 150 //在视图中相对y方向的距离
    var seletedArr = [UIButton]()
    var currentP: CGPoint? //当前位置 
    weak var delegate:LockViewDelegate?
     super.init(frame: frame){
     let tapGes = UIPanGestureRecognizer(target: self, action: #selector(panGesture(_ :)))
            addGestureRecognizer(tapGes)
     }
    }

新增一个名为setButtons的方法,该方法的作用主要是新增9个按钮,为按钮设置不同状态下的图片。到时候我们链接的时候,如果手势经过的点在按钮内的话,则设置该按钮为选中状态.

private func setButtons() {
   for i in 0..<10 {
            print("i -- \(i)")
            let btn = UIButton(type: .custom)
            btn.isUserInteractionEnabled =  false
            btn.setImage(UIImage(systemName: "circle.circle"), for: .normal) //使用SFSymbols图片
            btn.setImage(UIImage(systemName: "circle.circle.fill"), for: .selected)
            btn.tag = i + 1 //设置按钮的tag 到时候密码校验的时候其实就是用一连串的tag组合起来跟最初设定的密码做对比
            addSubview(btn)
        }
    }

重写View的布局方法layoutSubviews(),也可以这个方法主要是对按钮进行布局,布局成3*3 9个链接点的矩阵,代码如下

 override func layoutSubviews() {
  super.layoutSubViews()
  //设置行数 
    let cols = 3
    var x: CGFloat = 0 //按钮在x轴总的间距
    var y: CGFloat = 0 //按钮在x轴总的间距
    let w: CGFloat = 74 //按钮的宽
    let h: CGFloat = 74
    let margin = (bounds.size.width - CGFloat(cols * w)) / CGFloat(cols + 1) //设置间距 间距 = 屏幕的宽度 - 每行按钮的个数*按钮的宽度
    var col: CGFloat = 0 //记录列间距 % 行数 比如我们9个按钮,那么0-2 % 3 都为0 则第一行的三个按钮在布局的时候就不需要加上间距 ,第二行的三个俺就则为3-5 % 3 = 1 则间距就为(margin + w) * 1 以此类推 
    var row: CGFloat = 0 //行间距 作用如上 
    for i in 0..<count-1 {
        let btn: UIButton = self.subviews[i] as! UIButton
        col = CGFloat(i % Int(cols))
        row = CGFloat(i / Int(cols))
        x = margin + col * (margin + w)
        y = row * (margin + w)
        btn.frame = CGRect(x: x, y: y + topMargin , width: w, height: h)
    }
 }

声明协议

该协议主要是在手势选择结束时返回所有链接的button的tag,也就是手势所经过的点所代表的数

protocol gesUnlockView: AnyObject {
   func didPanEnded(password: String)
}
实现手势方法

手势的方法主要是用来确定手势有经过的按钮,如果有经过则设置按钮的为选中状态,并将选中的按钮加入到数组中,然后将按钮的tag拼接成字符串返回,并且将俺就加入到数组中 ,具体实现如下:

  @objc private func panGesture(_ ges: UIPanGestureRecognizer) {
        currentP = ges.location(in: self)//获取当前手势所在的位置
        self.subviews.forEach { btn in //遍历子视图,也就是9个按钮 
            guard let btn1 = btn as? UIButton else { return } 
            guard let position = currentP else { return } 
            if btn1.frame.contains(position) && !btn1.isSelected { //判断按钮的frame内是否包含手势所处的位置
                btn1.isSelected = true 
                self.seletedArr.append(btn1)//将那就加入数组
            }
        }
        //告诉视图需要重新绘制
        setNeedsDisplay() //这一句必须要调用,否则添加贝塞尔曲线不会重绘,也就看不到连接线
        
        if ges.state == .ended {
          self.seletedArr.forEach { btn in
                btn.isSelected = false
                strArr.append("\(btn.tag)") //选择结束后 将tag添加到数组返回
            }
            self.delegate?.didPanEnded(password: strArr.joined())
            self.seletedArr.removeAll() //移除所有添加的按钮
        }

  }
绘制贝塞尔曲线

这一步主要是重写draw方法,根据所选中的按钮添加贝塞尔曲线。实现如下:

 override func draw(_ rect: CGRect) {
        if self.seletedArr.count == 0 {return}
        let bezierPath = UIBezierPath()
        for i in 0..<seletedArr.count { //遍历按钮字典 
            let btn = self.seletedArr[i]
            if i == 0 {
                bezierPath.move(to: btn.center) //如果是第一个点,则将其设置为贝塞尔曲线的起点
            } else {
                bezierPath.addLine(to: btn.center)
            }
        }
        //链接手势所到达的点
        guard let currentP = currentP else { return }
        bezierPath.addLine(to: currentP)
        UIColor.systemPink.set()
        bezierPath.lineWidth = 4
        bezierPath.lineJoinStyle = .round
        bezierPath.lineCapStyle = .round
        bezierPath.stroke()
            
    }
最后

在对应的控制器中实现代理方法并获取代理返回值进行比对。如果跟预设的相同则可验证手势正确,否则验证失败。在这个demo里并未对手势的链接点个数和链接次数进行限制,以及链接错误的提示。小伙伴们可以发散思维,实现对应的逻辑。

Demo地址:gestureUnlockDemo

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

推荐阅读更多精彩内容