写着玩儿的小程序,继续学习swift.运行效果+代码+知识点总结
class Canvas:UIView{
//负责线条的生成、操作与管理
let pathCreator:PathCreator
//是否处于擦除状态
var isInErasering:Bool
//橡皮擦视图
let eraserView:UIView
override init(frame: CGRect) {
isInErasering = false
pathCreator = PathCreator()
eraserView = UIView.init()
eraserView.frame = CGRect(x: 0, y: 0, width: 10, height: 10)
eraserView.backgroundColor = UIColor.white
eraserView.alpha = 0
super.init(frame: frame)
self.backgroundColor = UIColor.black
self.addSubview(eraserView)
let revokeBut = UIButton(type: UIButtonType.system)
revokeBut.frame = CGRect(x: 20, y: 20, width: 80, height: 30)
revokeBut.setTitle("撤销", for: UIControlState.normal)
revokeBut.addTarget(self, action: #selector(revokeButClick), for: UIControlEvents.touchUpInside)
self.addSubview(revokeBut)
let cleanBut = UIButton(type: UIButtonType.system)
cleanBut.frame = CGRect(x: 110, y: 20, width: 80, height: 30)
cleanBut.setTitle("清空", for: UIControlState.normal)
cleanBut.addTarget(self, action: #selector(cleanButClick), for: UIControlEvents.touchUpInside)
self.addSubview(cleanBut)
let eraserBut = UIButton(type: UIButtonType.system)
eraserBut.frame = CGRect(x: 200, y: 20, width:80, height: 30)
eraserBut.setTitle("橡皮", for: UIControlState.normal)
eraserBut.setTitle("画笔", for: UIControlState.selected)
eraserBut.addTarget(self, action: #selector(eraserButClick(but:)), for: UIControlEvents.touchUpInside)
self.addSubview(eraserBut)
let ges = UIPanGestureRecognizer(target: self, action:#selector(handleGes(ges:)))
ges.maximumNumberOfTouches = 1
self.addGestureRecognizer(ges)
}
required public init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override public func layoutSubviews() {
}
@objc private func handleGes(ges:UIPanGestureRecognizer) -> Void {
let point = ges.location(in: self)
switch ges.state {
case UIGestureRecognizerState.began:
if isInErasering {
//擦除状态,显示出橡皮擦
eraserView.alpha = 1
eraserView.center = point
}
//生成新的一笔
pathCreator.addNewPath(to: point,isEraser: isInErasering)
self.setNeedsDisplay()
case UIGestureRecognizerState.changed:
if isInErasering {
//移动橡皮擦
eraserView.center = ges.location(in: self)
}
//更新当前笔画路径
pathCreator.addLineForCurrentPath(to: point,isEraser:isInErasering)
self.setNeedsDisplay()
case UIGestureRecognizerState.ended:
if isInErasering {
//擦除状态,隐藏橡皮擦
eraserView.alpha = 0
eraserView.center = ges.location(in: self)
}
//更新当前笔画路径
pathCreator.addLineForCurrentPath(to: point,isEraser: isInErasering)
self.setNeedsDisplay()
case UIGestureRecognizerState.cancelled:
print("cancel")
case UIGestureRecognizerState.failed:
print("fail")
default:
return
}
}
override public func draw(_ rect: CGRect) {
//画线
pathCreator.drawPaths()
}
@objc private func revokeButClick()->Void{
//撤销操作
pathCreator.revoke()
self.setNeedsDisplay()
}
@objc private func cleanButClick()->Void{
//清空操作
pathCreator.clean()
self.setNeedsDisplay()
}
@objc private func eraserButClick(but:UIButton)->Void{
//切换画图与擦除状态
if but.isSelected {
but.isSelected = false
isInErasering = false
}else{
but.isSelected = true
isInErasering = true
}
}
}
PathCreator:具体线条绘制、管理
//每条子线段信息
struct BezierInfo{
let path:UIBezierPath//具体线段
let color:UIColor//线段对应颜色
init(path:UIBezierPath,color:UIColor){
self.path = path
self.color = color
}
}
class PathCreator{
//所有笔画
private var paths:[NSMutableArray]?
//笔画内当前子线段
private var currentBezierPathInfo:BezierInfo?
//当前笔画的所有子线段
private var currentPath:NSMutableArray?
//当前笔画已经采集处理了几个触摸点
private var pointCountInOnePath = 0
static let colors = [UIColor.red,UIColor.orange,UIColor.yellow,UIColor.green,UIColor.blue,UIColor.gray,UIColor.purple]
init() {
paths = []
}
//添加新笔画
func addNewPath(to:CGPoint,isEraser:Bool)->Void{
//创建起始线段
let path = UIBezierPath()
path.lineWidth = 5
path.move(to: to)
path.lineJoinStyle = CGLineJoin.round
path.lineCapStyle = CGLineCap.round
if !isEraser {
//绑定线段与颜色信息
currentBezierPathInfo = BezierInfo(path: path, color: PathCreator.colors[0])
}else{
//处于擦除模式,颜色与画板背景色相同
currentBezierPathInfo = BezierInfo(path: path, color: UIColor.black)
}
//新建一个笔画
currentPath = NSMutableArray.init()
//将起始线段加入当前笔画
currentPath!.add(currentBezierPathInfo)
pointCountInOnePath = 0
//将当前笔画加入笔画数组
paths!.append(currentPath!)
}
//添加新的点,更新当前笔画路径
func addLineForCurrentPath(to:CGPoint,isEraser:Bool) -> Void {
pointCountInOnePath += 1//同一笔画内,每7个点换一次颜色
if pointCountInOnePath % 7 == 0{//换颜色
if let currentBezierPathInfo = currentBezierPathInfo{
//将当前点加入当前子线段,更新当前子线段路径
currentBezierPathInfo.path.addLine(to: to)
}
//生成新的子线段
let path = UIBezierPath()
path.lineWidth = 5
path.move(to: to)
path.lineJoinStyle = CGLineJoin.round
path.lineCapStyle = CGLineCap.round
if !isEraser{
//给当前子线段设置下一个颜色
currentBezierPathInfo = BezierInfo(path: path, color: PathCreator.colors[currentPath!.count % 7])
}else{
//处于擦除模式,颜色与画板背景色相同
currentBezierPathInfo = BezierInfo(path: path, color: UIColor.black)
}
//将当前子线段加入当前笔画
currentPath!.add(currentBezierPathInfo)
}else{
if let currentBezierPathInfo = currentBezierPathInfo{
//将当前点加入当前子线段,更新当前子线段路径
currentBezierPathInfo.path.addLine(to: to)
}
}
}
func drawPaths()->Void{
//画线
let pathCount = paths!.count
for i in 0..<pathCount{
//取出所有笔画
let onePath = paths![i]
let onePathCount = onePath.count
for j in 0..<onePathCount{
//绘制每条笔画内每个子线段
let pathInfo = onePath.object(at: j) as! BezierInfo
pathInfo.color.set()
pathInfo.path.stroke()
}
}
}
func revoke()->Void{
//移走上一笔画
if paths!.count > 0 {
paths!.removeLast()
}
}
func clean()->Void{
//移走所有笔画
paths!.removeAll()
}
}
(纯)swift与oc采用了不同的运行机制,swift不再采用与oc一样的运行时(runtime)与消息分发机制,selector作为oc运行机制的产物,swift中也对其进行了保留与支持。
@objc修饰符的作用是将swift定义的类、方法等暴露给oc。
于是,下列selector中指定的方法,都要使用@objc进行修饰
cleanBut.addTarget(self, action: #selector(cleanButClick), for: UIControlEvents.touchUpInside)
let ges = UIPanGestureRecognizer(target: self, action:#selector(handleGes(ges:)))
@objc private func handleGes(ges:UIPanGestureRecognizer) -> Void
public init?(coder aDecoder: NSCoder)通过xcode找到该方法是在NSCoding协议中被定义的
public protocol NSCoding {
public func encode(with aCoder: NSCoder)
public init?(coder aDecoder: NSCoder) // NS_DESIGNATED_INITIALIZER
}可以看到,此处并没有进行requird修饰,为什么还要求强制实现该构造方法呢?let x:UInt16 = 100 let y:UInt8 = 10 //x + y会报错,不自动类型转换,更安全 let n = UInt8(x) + y上面例子中,当我们进行值类型之间的类型转换(UInt16->UInt8)时,其实借助的是UInt8的构造方法
/// Create an instance initialized to `value`.
public init(integerLiteral value: UInt8)而当引用类型之间需要进行强制转换时,则需要借助as操作符var people:People? let man:Man = Man() people = man print(people)//可选型变量 let beMan = people as! Man print (beMan)//强制转化后beMan不是可选型
var people:People? let man:Man = Man() people = man print(people)//可选型变量 let beMan = people as! Man? print (beMan)//强制转化后beMan为可选型转换后的结果类型完全由as!后面的目标类型决定,即便原对象在转换之前是可选型对象,但如果转换的目标类型不是可选型,则转换后得到的也就不是一个可选型了
Swift3.0学习实践-一个简单的画板(七色轨迹、可撤销、可清除、带橡皮擦)
原文:http://blog.csdn.net/zhaochen_009/article/details/54908531