swift3.0对绘图的API进行了优化,看起来更swift了。

看下UI的构造。设置画笔粗细、清空面板和保存到本地

下面直接看画板文件

这里我做的比较复杂,记录触摸到的每个点,再连成路径,其实直接用可变路径CGMutablePath可变路径就可以实现。

成员变量

    public var lineWidth:CGFloat = 1
fileprivate var allLineArray = [[CGPoint]]() //所有的线 记录每一条线
fileprivate var currentPointArray = [CGPoint]() //当前画线的点 画完置空 增加到 线数组中
fileprivate var allPointWidth = [CGFloat]() //所有的线宽

设置触摸时间,开始时记录第一个点并重绘(不重绘就没有只画一个点得效果),移动时不断记录并重绘。

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let point:CGPoint = (event?.allTouches?.first?.location(in: self))!
//路径起点
currentPointArray.append(point)
self.setNeedsDisplay()
} override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let point:CGPoint = (event?.allTouches?.first?.location(in: self))!
//路径
currentPointArray.append(point)
//刷新视图
self.setNeedsDisplay()
}

由于我们的点都是存在数组中,当要清空画板时 只要将数组清空就可以了

    func cleanAll(){
allLineArray.removeAll()
currentPointArray.removeAll()
allPointWidth.removeAll()
self.setNeedsDisplay()
}

下面看下 重绘的主逻辑

 override func draw(_ rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
context?.setLineCap(.round)
context?.setLineJoin(.round) //绘制之前的线
if allLineArray.count > 0 {
//遍历之前的线
for i in 0..<allLineArray.count {
let tmpArr = allLineArray[i]
if tmpArr.count > 0 {
//画线
context?.beginPath()
//取出起始点
let sPoint:CGPoint = tmpArr[0]
context?.move(to: sPoint)
//取出所有当前线的点
for j in 0..<tmpArr.count {
let endPoint:CGPoint = tmpArr[j]
context?.addLine(to: endPoint)
}
context?.setLineWidth(allPointWidth[i])
context?.strokePath()
}
}
} if currentPointArray.count > 0 {
//绘制当前线
context?.beginPath()
context?.setLineWidth(self.lineWidth)
context?.move(to: currentPointArray[0])
print(currentPointArray[0]) for i in 0..<currentPointArray.count {
context?.addLine(to: currentPointArray[i])
print(currentPointArray[i])
}
context?.strokePath()
}
}

保存成图片可很简单,只要截屏设置范围就行

    //保存图片
@IBAction func savePic(_ sender: Any) { let height:CGFloat = self.view.bounds.size.height - self.saveBtn.frame.height - 10
let imageSize :CGSize = CGSize(width: self.view.bounds.size.width, height: height)
UIGraphicsBeginImageContext(imageSize)
view.layer.render(in: UIGraphicsGetCurrentContext()!)
let img:UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
UIImageWriteToSavedPhotosAlbum(img, self, #selector(image(_:didFinishSavingWithError:contextInfo:)), nil)
} //保存图片回调
func image(_ image: UIImage, didFinishSavingWithError error: NSError?, contextInfo:UnsafeRawPointer) {
var resultTitle:String?
var resultMessage:String?
if error != nil {
resultTitle = "错误"
resultMessage = "保存失败,请检查是否允许使用相册"
} else {
resultTitle = "提示"
resultMessage = "保存成功"
}
let alert:UIAlertController = UIAlertController.init(title: resultTitle, message:resultMessage, preferredStyle: .alert)
alert.addAction(UIAlertAction.init(title: "确定", style: .default, handler: nil))
self.present(alert, animated: true, completion: nil)
}

不过千万别忘了给app设置相册的权限

在info.plist中添加Privacy - Photo Library Usage Description属性即可,value值为提示信息

效果:

有兴趣的童靴可可以直接用可变路径实现下 逻辑更简单 完了。

Demo地址

https://github.com/gongxiaokai/paintViewDemo

swift3.0 CoreGraphics绘图-实现画板的更多相关文章

  1. 你知道吗, CoreGraphics绘图系统和Bezier贝塞尔曲线坐标系的顺时针方向是相反的!

    UIBezierPath是对Core Graphics框架的一种上层封装,目的是让绘图需求可以被更方便的使用. 那你有没有发现被UIBezierPath封装后与之前有什么改变? 答:有三个变化. 1. ...

  2. Swift3.0服务端开发(一) 完整示例概述及Perfect环境搭建与配置(服务端+iOS端)

    本篇博客算是一个开头,接下来会持续更新使用Swift3.0开发服务端相关的博客.当然,我们使用目前使用Swift开发服务端较为成熟的框架Perfect来实现.Perfect框架是加拿大一个创业团队开发 ...

  3. 算法与数据结构(十三) 冒泡排序、插入排序、希尔排序、选择排序(Swift3.0版)

    本篇博客中的代码实现依然采用Swift3.0来实现.在前几篇博客连续的介绍了关于查找的相关内容, 大约包括线性数据结构的顺序查找.折半查找.插值查找.Fibonacci查找,还包括数结构的二叉排序树以 ...

  4. Swift3.0变化分享

    Swift 3.0 做出的改变很大,在这篇文章中,我将尽我所能,利用代码样例给大家解释Swift 3.0最重要(要命)的改变,希望大家能够做好升级Swift 3.0 的准备.Swift 3.0的改变不 ...

  5. swift3.0变化总结

    Swift 3.0 做出的改变很大,在这篇文章中,我将尽我所能,利用代码样例给大家解释Swift 3.0最重要(要命)的改变,希望大家能够做好升级Swift 3.0 的准备.Swift 3.0的改变不 ...

  6. 关于for循环------swift3.0

    在程序开发当中,for循环使用的频率无疑是最高的.常用的swift循环是递增式遍历.当然各种循环,swift都能办到.但其大多采用关键字形式实现,大部分开发者更喜欢直接使用C式循环代码.在swift3 ...

  7. Swift2.3 --> Swift3.0 的变化

    Swift3.0语法变化 首先和大家分享一下学习新语法的技巧: 用Xcode8打开自己的Swift2.3的项目,选择Edit->Convert->To Current Swift Synt ...

  8. Swift3.0都有哪些变化

    从写第一篇Swift文章的时候到现在Swift已经从1.2发展到了今天的3.0,这期间由于Swift目前还在发展阶段并不能向下兼容,因此第一篇文章中的部分代码在当前的Xcode环境中已经无法运行.在W ...

  9. iOS开发 swift3.0中文版

    swift3.0中文版: http://pan.baidu.com/s/1nuHqrBb

随机推荐

  1. 提高java编程质量 - (三)三目运算符的两个操作数类型尽量一致

    先看一个例子: package com.test; public class TernaryOperator { public static void main(String[] args) { in ...

  2. zookeeper 应用场景概述

    Zookeeper主要可以干哪些事情:配置管理,名字服务,提供分布式同步以及集群管理. 一 .配置管理 在我们的应用中除了代码外,还有一些就是各种配置.比如数据库连接,远程服务访问地址等.一般我们都是 ...

  3. 原生js二级联动

    今天说的这个是原生js的二级联动,在空白页面里动态添加并作出相对应的效果. 1 //创建两个下拉列表 select标签 是下拉列表 var sel = document.createElement(& ...

  4. android开发中关于继承activity类中方法的调用

    android开发中关于继承activity类中的函数,不能在其他类中调用其方法. MainActivity.java package com.example.testmain; import and ...

  5. 初学MySQL

    MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品.   Mysql默认端口号: 3306 超级用户:root   prompt 修改提示符. ( ...

  6. 容器间通信的三种方式 - 每天5分钟玩转 Docker 容器技术(35)

    容器之间可通过 IP,Docker DNS Server 或 joined 容器三种方式通信. IP 通信 从上一节的例子可以得出这样一个结论:两个容器要能通信,必须要有属于同一个网络的网卡. 满足这 ...

  7. javascript字符串属性及常用方法总结

    length属性:str.length; 常用方法: 1.  str.charAt(n) 查找字符串中的第n个字符,如果不在0~str.length-1之间,则返回一个空字符串 2  .str.ind ...

  8. ListView 介绍

    1. 通过继承Activity实现ListView 1.1 在XML布局文件中实现一个ListView <ListView android:layout_width="wrap_con ...

  9. Linux系统网卡设置

    由于做了虚拟机的克隆,发现克隆机和被克隆机的MAC地址相同了,下面我将要介绍一下linux中网卡的配置步骤,我使用的linux是CentOS release 6.9 (Final) 1.root用户编 ...

  10. python自动化运维学习第一天--day1

    学习python自动化运维第一天自己总结的作业 所使用到知识:json模块,用于数据转化sys.exit 用于中断循环退出程序字符串格式化.format字典.文件打开读写with open(file, ...