swift實現(xiàn)手勢解鎖

手勢解鎖

基本邏輯

手勢解鎖在應用中都很常見,手勢基本的連接點一般都是9個。實現(xiàn)邏輯一般都不難,主要是對滑動過程中,手勢所到的點跟本身9個鏈接點的位置判斷并且使用貝塞爾曲線在連接點之間添加連線,最后根據(jù)鏈接點的內(nèi)容進行輸出驗證。詳看以下代碼:

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個按鈕,為按鈕設(shè)置不同狀態(tài)下的圖片。到時候我們鏈接的時候,如果手勢經(jīng)過的點在按鈕內(nèi)的話,則設(shè)置該按鈕為選中狀態(tài).

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 //設(shè)置按鈕的tag 到時候密碼校驗的時候其實就是用一連串的tag組合起來跟最初設(shè)定的密碼做對比
            addSubview(btn)
        }
    }

重寫View的布局方法layoutSubviews(),也可以這個方法主要是對按鈕進行布局,布局成3*3 9個鏈接點的矩陣,代碼如下

 override func layoutSubviews() {
  super.layoutSubViews()
  //設(shè)置行數(shù) 
    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) //設(shè)置間距 間距 = 屏幕的寬度 - 每行按鈕的個數(shù)*按鈕的寬度
    var col: CGFloat = 0 //記錄列間距 % 行數(shù) 比如我們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)
    }
 }

聲明協(xié)議

該協(xié)議主要是在手勢選擇結(jié)束時返回所有鏈接的button的tag,也就是手勢所經(jīng)過的點所代表的數(shù)

protocol gesUnlockView: AnyObject {
   func didPanEnded(password: String)
}
實現(xiàn)手勢方法

手勢的方法主要是用來確定手勢有經(jīng)過的按鈕,如果有經(jīng)過則設(shè)置按鈕的為選中狀態(tài),并將選中的按鈕加入到數(shù)組中,然后將按鈕的tag拼接成字符串返回,并且將俺就加入到數(shù)組中 ,具體實現(xiàn)如下:

  @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內(nèi)是否包含手勢所處的位置
                btn1.isSelected = true 
                self.seletedArr.append(btn1)//將那就加入數(shù)組
            }
        }
        //告訴視圖需要重新繪制
        setNeedsDisplay() //這一句必須要調(diào)用,否則添加貝塞爾曲線不會重繪,也就看不到連接線
        
        if ges.state == .ended {
          self.seletedArr.forEach { btn in
                btn.isSelected = false
                strArr.append("\(btn.tag)") //選擇結(jié)束后 將tag添加到數(shù)組返回
            }
            self.delegate?.didPanEnded(password: strArr.joined())
            self.seletedArr.removeAll() //移除所有添加的按鈕
        }

  }
繪制貝塞爾曲線

這一步主要是重寫draw方法,根據(jù)所選中的按鈕添加貝塞爾曲線。實現(xiàn)如下:

 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) //如果是第一個點,則將其設(shè)置為貝塞爾曲線的起點
            } 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()
            
    }
最后

在對應的控制器中實現(xiàn)代理方法并獲取代理返回值進行比對。如果跟預設(shè)的相同則可驗證手勢正確,否則驗證失敗。在這個demo里并未對手勢的鏈接點個數(shù)和鏈接次數(shù)進行限制,以及鏈接錯誤的提示。小伙伴們可以發(fā)散思維,實現(xiàn)對應的邏輯。

Demo地址:gestureUnlockDemo

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • 我們已經(jīng)學習完了Quartz2D的一些基本的用法,在實際開發(fā)過程中,經(jīng)常使用Quartz2D,可以幫助我們少使用蘋...
    mkb2閱讀 1,338評論 0 13
  • 題記 在平常的生活中,我們大概經(jīng)常遇見手勢滑動解鎖---也就是九宮格啊,已經(jīng)出現(xiàn)好久了,雖然隨著Apple的指紋解...
    劉光軍_MVP閱讀 1,486評論 0 8
  • iOS指紋解鎖和手勢解鎖 [TOC] 前言 一直想寫博客來著,一來可以記錄一些自己學習和研究的東西,二來也可以將自...
    yinxing29閱讀 760評論 0 5
  • 用兩張圖告訴你,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料? 從這篇文章中你...
    hw1212閱讀 13,980評論 2 59
  • 每次打開網(wǎng)易郵箱大師進來第一個界面就是解鎖,這種設(shè)計很好的保護用戶的隱私。手勢解鎖也是一種很好的密碼方式,今天小編...
    Fluent閱讀 750評論 1 5

友情鏈接更多精彩內(nèi)容