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..
声明协议

该协议主要是在手势选择结束时返回所有链接的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..
最后

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

Demo地址:gestureUnlockDemo

你可能感兴趣的:(swift实现手势解锁)