标签:
本文内容参考自 传送门。原文是用 OC 写的,我把它改成了 Swift 的。
我们先来看看效果图:
第一幅图是我们画了一个 “iOS” 的图像,第二幅图是我们点击保存成功,第三幅图是可以在相册中看到我们刚才画的图。
感觉很不错有木有?接下来我们就来说说是怎么实现的。
我们分两部分来说:上半部分的画图板和下半部分的控制区。
上半部分的画图板是我们自定义的 view,我们设置如下属性:
class MyView: UIView { var color = UIColor.redColor() // 线条颜色 var lineWidth : Float = 1.0 // 线条宽度 private var allLine: [Dictionary<String, AnyObject>] = [] // 保存已有的线条 private var cancelLine: [Dictionary<String, AnyObject>] = [] // 保存被撤销的线条 private var bezier = UIBezierPath() // 贝赛尔曲线 }其中线条的颜色和宽度在 controller 中要用到,其余的三个不需要所以我们设置成私有的 private。
先说说后退功能,它其实非常简单。
allLine 和 cancelLine 这两个数组就相当于两个栈。后退时,让已有的一条线出栈,进入到撤销线条的栈中。
具体的代码其实只有短短数行:
func backImage() { // 两个数组相当于两个栈。后退时,让已有的一条线出栈,进入到撤销线条的栈中。 if allLine.isEmpty == false { // 如果数组不为空才执行 cancelLine.append(allLine.last!) // 入栈 allLine.removeLast() // 出栈 setNeedsDisplay() // 重绘界面 } }
func forwardImage() { // 前进时正好与后退相反,让被撤销的一条线条出栈,进入到已有线条的栈中。 if cancelLine.isEmpty == false { // 如果数组不为空才执行 allLine.append(cancelLine.last!) // 入栈 cancelLine.removeLast() // 出栈 setNeedsDisplay() // 重绘界面 } }
1。在每次开始触摸时新建一个贝赛尔曲线并存入数组中,记录下触摸的坐标作为贝赛尔曲线的当前坐标。
2。在滑动时记录每一瞬时的坐标,更新贝赛尔曲线的当前坐标为这个新坐标。
3。重新绘制界面。
4。执行第二步。直到这次触摸结束为止。
开始触摸时的代码如下:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) { bezier = UIBezierPath() // 新建贝塞尔曲线 let point = touches.first!.locationInView(self) // 获取触摸的点 bezier.moveToPoint(point) // 把刚触摸的点设置为bezier的起点 var tmpDic = Dictionary<String, AnyObject>() tmpDic["color"] = color tmpDic["lineWidth"] = lineWidth tmpDic["line"] = bezier allLine.append(tmpDic) // 把线存入数组中 }
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) { let point = touches.first!.locationInView(self) // 获取触摸的点 bezier.addLineToPoint(point) // 把移动的坐标存到贝赛尔曲线中 setNeedsDisplay() // 重绘界面 }
override func drawRect(rect: CGRect) { for i in 0..<allLine.count { let tmpDic = allLine[i] let tmpColor = tmpDic["color"] as! UIColor let tmpWidth = tmpDic["lineWidth"] as! CGFloat let tmpPath = tmpDic["line"] as! UIBezierPath tmpColor.setStroke() tmpPath.lineWidth = tmpWidth tmpPath.stroke() } }
下面我们来说说如何使用它。在 controller 中创建一个 MyView 的实例,MyView 就是上面所说的画图板。
class ViewController: UIViewController { private let drawingBoard = MyView() // 自定义view,也就是画图板部分 private let mySlider = UISlider() // 滑动条,控制线条宽度 private let mySegment = UISegmentedControl(items: ["红", "黑", "绿"]) // 分段控制器,控制线条颜色 private let backBtn = UIButton(type: .Custom) private let saveBtn = UIButton(type: .Custom) private let forwardBtn = UIButton(type: .Custom) }
这些 UI 控件的属性设置没啥好说的,无非就是设置 frame、text、title、titleColor、backgroundColor 之类的,不赘述。
我们来说说这些控件的响应事件即可,其实也很简单。
滑动条的响应事件:
func onClickSlider(slider: UISlider) { drawingBoard.lineWidth = slider.value }
func onClickSegment(segment: UISegmentedControl) { switch segment.selectedSegmentIndex { case 0: drawingBoard.color = UIColor.redColor() case 1: drawingBoard.color = UIColor.blackColor() case 2: drawingBoard.color = UIColor.greenColor() default: drawingBoard.color = UIColor.redColor() } }
func onClickBack(button: UIButton) { drawingBoard.backImage() } func onClickForward(button: UIButton) { drawingBoard.forwardImage() }
func onClickSave(button: UIButton) { UIGraphicsBeginImageContext(drawingBoard.bounds.size) // 开始截取画图板 view.layer.renderInContext(UIGraphicsGetCurrentContext()!) let img : UIImage = UIGraphicsGetImageFromCurrentImageContext() // 截取到的图像 UIGraphicsEndImageContext() // 结束截取 UIImageWriteToSavedPhotosAlbum(img, nil, nil, nil) // 把截取到的图像保存到相册中 // 最后提示用户保存成功即可 let alert = UIAlertView.init(title: "存储照片成功", message: "您已将照片存储于图片库中,打开照片程序即可查看。", delegate: self, cancelButtonTitle: "OK") alert.show() }
backBtn.addTarget(self, action: Selector("onClickBack:"), forControlEvents: .TouchUpInside)
完整源码请见我的 GitHub:https://github.com/963239327/LZNBezierDrawingBoard 别忘了点击右上角的 star 哦~
标签:
原文地址:http://blog.csdn.net/qq_18425655/article/details/51338310