Skip to content

Instantly share code, notes, and snippets.

@wujianguo
Created November 3, 2015 09:46
Show Gist options
  • Save wujianguo/5a3e2e19db32c740ace1 to your computer and use it in GitHub Desktop.
Save wujianguo/5a3e2e19db32c740ace1 to your computer and use it in GitHub Desktop.
ios swift Gesture Password
//
// GesturePasswordView.swift
// GesturePassword
//
// Created by wujianguo on 15/10/16.
// Copyright © 2015 wujianguo. All rights reserved.
//
import UIKit
@IBDesignable
class GesturePasswordView: UIControl {
var passwordValue: String {
var v: String = ""
for i in touchCells {
v.append(Character("\(i)"))
}
return v
}
@IBInspectable
var cellLineWidth: CGFloat = 2 { didSet { setNeedsDisplay() } }
@IBInspectable
var cellLineNormalColor: UIColor = UIColor.greenColor() { didSet { setNeedsDisplay() } }
@IBInspectable
var cellLineSuccessColor: UIColor = UIColor.purpleColor() { didSet { setNeedsDisplay() } }
@IBInspectable
var cellLineFailureColor: UIColor = UIColor.redColor() { didSet { setNeedsDisplay() } }
@IBInspectable
var lineSuccColor: UIColor = UIColor.blueColor() { didSet { setNeedsDisplay() } }
@IBInspectable
var lineErrorColor: UIColor = UIColor.redColor() { didSet { setNeedsDisplay() } }
@IBInspectable
var lineWidth: CGFloat = 5 { didSet { setNeedsDisplay() } }
@IBInspectable
var contentInset: UIEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
@IBInspectable
var touchTheme = CellTheme.Success
enum CellTheme { case Normal, Success, Failure }
private func drawCellWith(center: CGPoint, diameter: CGFloat, theme: CellTheme = .Normal) {
let context = UIGraphicsGetCurrentContext()
CGContextSetLineWidth(context, cellLineWidth)
let outterDiameter = diameter - cellLineWidth*2
let outter = CGRectMake(center.x - outterDiameter/2, center.y - outterDiameter/2, outterDiameter, outterDiameter)
switch theme {
case .Normal:
CGContextSetStrokeColorWithColor(context, cellLineNormalColor.CGColor)
case .Success:
CGContextSetStrokeColorWithColor(context, cellLineSuccessColor.CGColor)
CGContextSetFillColorWithColor(context, cellLineSuccessColor.colorWithAlphaComponent(0.3).CGColor)
CGContextAddEllipseInRect(context, outter)
CGContextFillPath(context)
case .Failure:
CGContextSetStrokeColorWithColor(context, cellLineFailureColor.CGColor)
CGContextSetFillColorWithColor(context, cellLineFailureColor.colorWithAlphaComponent(0.3).CGColor)
CGContextAddEllipseInRect(context, outter)
CGContextFillPath(context)
}
CGContextAddEllipseInRect(context, outter)
CGContextStrokePath(context)
if theme != .Normal {
if theme == .Success {
CGContextSetFillColorWithColor(context, cellLineSuccessColor.CGColor)
} else {
CGContextSetFillColorWithColor(context, cellLineFailureColor.CGColor)
}
let inner = CGRectMake(center.x - diameter/8, center.y - diameter/8, diameter/4, diameter/4);
CGContextAddEllipseInRect(context, inner)
CGContextFillPath(context)
}
}
private func drawLine(start: CGPoint, end: CGPoint, theme: CellTheme) {
let context = UIGraphicsGetCurrentContext()
CGContextSetLineWidth(context, lineWidth)
if theme == .Normal || theme == .Success {
CGContextSetStrokeColorWithColor(context, lineSuccColor.CGColor)
} else {
CGContextSetStrokeColorWithColor(context, lineErrorColor.CGColor)
}
CGContextMoveToPoint(context, start.x, start.y)
CGContextAddLineToPoint(context, end.x, end.y)
CGContextStrokePath(context)
}
override func drawRect(rect: CGRect) {
super.drawRect(rect)
let width = rect.width - contentInset.left - contentInset.right
let height = rect.height - contentInset.top - contentInset.bottom
let diameter = min(width, height) / 4
for i in 0..<9 {
drawCellWith(centerOfCell(i), diameter: diameter, theme: cellThemes[i])
}
for i in 0..<touchCells.count {
if i < touchCells.count - 1 {
drawLine(centerOfCell(touchCells[i]), end: centerOfCell(touchCells[i+1]), theme: touchTheme)
} else if lineEndPoint != nil {
drawLine(centerOfCell(touchCells[i]), end: lineEndPoint!, theme: touchTheme)
}
}
}
private func centerOfCell(index: Int) -> CGPoint {
let row = Int(index/3)
let col = Int(index%3)
let width = bounds.width - contentInset.left - contentInset.right
let height = bounds.height - contentInset.top - contentInset.bottom
let diameter = min(width, height) / 4
let paddingX = (width - 3*diameter) / 2
let paddingY = (height - 3*diameter) / 2
return CGPointMake(contentInset.left + diameter/2 + CGFloat(col)*(diameter+paddingX), contentInset.top + diameter/2 + CGFloat(row)*(diameter+paddingY))
}
private func hitCell(point: CGPoint) -> Int? {
let width = bounds.width - contentInset.left - contentInset.right
let height = bounds.height - contentInset.top - contentInset.bottom
let diameter = min(width, height) / 4
for index in 0..<9 {
let c = centerOfCell(index)
let xDist: Double = Double(c.x) - Double(point.x)
let yDist: Double = Double(c.y) - Double(point.y)
let distance = sqrt(xDist*xDist + yDist*yDist)
if distance < Double(diameter/2) {
return index
}
}
return nil
}
private var touchCells = [Int]()
private var lineEndPoint: CGPoint?
private var cellThemes = [CellTheme](count: 9, repeatedValue: .Normal)
override func beginTrackingWithTouch(touch: UITouch, withEvent event: UIEvent?) -> Bool {
touchCells.removeAll()
cellThemes = [CellTheme](count: 9, repeatedValue: .Normal)
if let h = hitCell(touch.locationInView(self)) {
cellThemes[h] = .Success
touchCells.append(h)
}
setNeedsDisplay()
return super.beginTrackingWithTouch(touch, withEvent: event)
}
override func continueTrackingWithTouch(touch: UITouch, withEvent event: UIEvent?) -> Bool {
super.continueTrackingWithTouch(touch, withEvent: event)
if let h = hitCell(touch.locationInView(self)) {
if (touchCells.filter { $0 == h }).count == 0 {
cellThemes[h] = .Success
touchCells.append(h)
}
}
lineEndPoint = touch.locationInView(self)
setNeedsDisplay()
return true
}
override func endTrackingWithTouch(touch: UITouch?, withEvent event: UIEvent?) {
super.endTrackingWithTouch(touch, withEvent: event)
lineEndPoint = nil
setNeedsDisplay()
sendActionsForControlEvents(.ValueChanged)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment