GYGestureUnlock
Swift版仿支付寶的手勢解鎖,而且提供方法進(jìn)行參數(shù)修改峻呛,能解決項目開發(fā)中所有手勢解鎖的開發(fā)
效果圖
此項目起因
- 個人學(xué)習(xí)OC版手勢解鎖罗售,感覺只看源碼,沒有太大效果杀饵,于是乎將其完全照抄轉(zhuǎn)化為了神奇的Swift
- 源OC版手勢解鎖
- 為更深入的了解奇妙的Swift
項目簡介
文件包GYTools
GYCircleConst:系統(tǒng)配置參數(shù)莽囤,錯誤語言提示,背景顏色等等切距,可根據(jù)自己的項目需要進(jìn)行修改
GYCircleView:手勢View
GYCircle:單個圓
GestureViewController:設(shè)置密碼和登錄兩種情況的VC
GestureVerifyViewController:驗證手勢和修改手勢的VC-
核心實現(xiàn)方法
GYCircleView:
實現(xiàn)原理:每個圓添加tag拼接為字符串保存本地(項目中可傳至服務(wù)器)進(jìn)行比較
//MARK:- 手勢方法 began - moved - end
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
gestureEndResetMembers()
currentPoint = CGPointZero
let touch = (touches as NSSet).anyObject()let point = touch?.locationInView(self) (subviews as NSArray).enumerateObjectsUsingBlock { (circle, idx, stop) in let cir = circle as! GYCircle if CGRectContainsPoint(cir.frame, point!) { cir.state = CircleState.CircleStateSelected self.circleSet?.addObject(cir) print("添加子View的tag:\(cir.tag)") } } //數(shù)組中最后一個對象的處理 circleSetLastObjectWithState(CircleState.CircleStateLastOneSelected) setNeedsDisplay() } override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) { currentPoint = CGPointZero let touch = (touches as NSSet).anyObject() //獲取手勢所在的點坐標(biāo) let point = touch?.locationInView(self) (subviews as NSArray).enumerateObjectsUsingBlock { (circle, idx, stop) in //我說 let cir = circle as! GYCircle if CGRectContainsPoint(cir.frame, point!) { //此處腦殘了 self.circleSet?.containsObject(cir) != nil // 判斷數(shù)組中是否包含此view 包含不添加 不包含則添加 if self.circleSet!.containsObject(cir) { // print("添加子View的tag:\(cir.tag)") // self.circleSet?.addObject(cir) // self.calAngleAndconnectTheJumpedCircle() } else { self.circleSet?.addObject(cir) // move過程中的連線(包含跳躍連線的處理) self.calAngleAndconnectTheJumpedCircle() } } else { self.currentPoint = point } } guard (self.circleSet != nil) else { return } (self.circleSet! as NSArray).enumerateObjectsUsingBlock { (circle, idx, stop) in let circlel = circle as! GYCircle circlel.state = CircleState.CircleStateSelected // 如果是登錄或者驗證原手勢密碼 就改為對應(yīng)的狀態(tài) if self.type != CircleViewType.CircleViewTypeSetting { circlel.state = CircleState.CircleStateLastOneSelected } } //數(shù)組中最后一個對象的處理 self.circleSetLastObjectWithState(CircleState.CircleStateLastOneSelected) setNeedsDisplay() } override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) { self.hasClean = false guard self.circleSet != nil else { return } let gesture = self.getGestureResultFromCircleSet(self.circleSet!) let length = gesture.length if length == 0 { return } //手勢繪制結(jié)果處理 switch self.type! { case CircleViewType.CircleViewTypeSetting: gestureEndByTypeSettingWithGesture(gesture, length: CGFloat(length)) break case CircleViewType.CircleViewTypeLogin: gestureEndByTypeLoginWithGesture(gesture, length: CGFloat(length)) case CircleViewType.CircleViewTypeVerify: gestureEndByTypeVerifyWithGesture(gesture, length: CGFloat(length)) } //手勢結(jié)束后是否錯誤回顯重繪朽缎,取決于是否延時清空數(shù)組和狀態(tài)復(fù)原 errorToDisplay() } //MARK:- 每添加一個圓,就計算一次方向 func calAngleAndconnectTheJumpedCircle() { if circleSet == nil || circleSet?.count <= 1 { return } //取出最后一個對象 let lastOne = circleSet?.lastObject as! GYCircle //倒數(shù)第二個 let lastTwo = circleSet?.objectAtIndex(self.circleSet!.count - 2) as! GYCircle //計算倒數(shù)第二個的位置 let last_1_x = lastOne.center.x let last_1_y = lastOne.center.y let last_2_x = lastTwo.center.x let last_2_y = lastTwo.center.y //1.計算角度(反正切函數(shù)) let angle = atan2(Float(last_1_y) - Float(last_2_y), Float(last_1_x) - Float(last_2_x)) + Float( M_PI_2) lastTwo.angle = CGFloat(angle) print(lastTwo.angle) //2.處理跳躍連線 let center = centerPointWithPointOneandTwo(lastOne.center, pointTwo: lastTwo.center) let centerCircle = self.enumCircleSetToFindWhichSubviewContainTheCenterPoint(center) if centerCircle != nil { //把跳過的圓加到數(shù)組中,他的位置是倒數(shù)第二個 if !(self.circleSet!.containsObject(centerCircle!)) { //插入數(shù)組中 self.circleSet?.insertObject(centerCircle!, atIndex: (self.circleSet?.count)! - 1) //指定此圓的角度與上一個角度相同话肖。否則會造成移位 centerCircle?.angle = lastTwo.angle } } }
3.實現(xiàn)調(diào)用
switch sender.tag {
case 1:
//設(shè)置手勢密碼
let gesture = GestureViewController()
gesture.type = GestureViewControllerType.Setting
navigationController?.pushViewController(gesture, animated: true)
break
case 2:
//登錄手勢密碼
print(GYCircleConst.getGestureWithKey(gestureOneSaveKey))
if GYCircleConst.getGestureWithKey(gestureFinalSaveKey) != nil {
let gestureVC = GestureViewController()
gestureVC.type = GestureViewControllerType.Login
navigationController?.pushViewController(gestureVC, animated: true)
} else {
let alertView = UIAlertView(title: "溫馨提示", message: "暫未設(shè)置手勢密碼,是否前往設(shè)置", delegate: self, cancelButtonTitle: "取消", otherButtonTitles: "設(shè)置")
alertView.show()
}
break
case 3:
//驗證手勢密碼
let gestureVerifyVc = GestureVerifyViewController()
navigationController?.pushViewController(gestureVerifyVc, animated: true)
break
case 4:
//修改手勢密碼
let gestureVerifyVc = GestureVerifyViewController()
gestureVerifyVc.isToSetNewGesture = true
navigationController?.pushViewController(gestureVerifyVc, animated: true)
break
4.遇到的swift的坑
1)各處nil
2)Swift的重寫GET和SET方法
3)init初始化
4)一些可選值未初始化時會nil導(dǎo)致崩潰
最后北秽,奉上源碼地址求Star,大家一起學(xué)習(xí),3Q。