标签:
本文内容参考自 传送门。原文是用 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