前言:
iOS里进行几何图形绘制大体可分为三类,如下:
- 自定义UIView类,重写draw() 方法并调用UIBezierPath类相关方法
- UIBezierPath类与CAShaperLayer类搭配
- 自定义UIView类,重写draw() 方法并调用UIGraphicsGetCurrentContext类相关方法
以上三种方法几乎可以看作独立的UIView类与Context结合的变体。UIBezierPath类实际上是对Context类的变体。如果不独立View,则须CAShaperLayer类结合,因为要显示一个View,则须向draw()方法发送信息。本文将使用第一种方式进行基本几何图形的绘制。
核心类/方法:
- UIBezierPath:贝塞尔类,提供合成点线面级边宽颜色等基本方法。
- UIGraphicsGetCurrentContext:图型上下文。
- .insetBy():由确定的参数构成的特殊矩形
- CGPoint:由x及y构成的点
- CGRectInset:
- CGFloat:浮点数,用于范围等
- CGSize:用于由x及y构成的确定面积
此外,本例设计的角度为弧度制,即平角的表示为π,周角为2π。矩形包括正方形、长方形。
直线:
数学上是平面内两点确定一条直线。Quartz里是2D模型,因此也是两点间确定唯一一条直线。
自定义一UIView,并重写draw()方法。在VC里面调用该类直接调用其默认构造器,赋予相关frame属性即可,见本段末。
import UIKit
class PaintView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.clear
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func draw(_ rect: CGRect) {
super.draw(rect)
myDrawLine()
}
func myDrawLine(){
// 构建内嵌矩形
let myRect = self.bounds.insetBy(dx: (self.bounds.size.width * 0.05), dy: (self.bounds.size.width * 0.05))
// 基本点的实例化
let pointOfTopLeft = CGPoint(x: myRect.minX, y: myRect.minY)
let pointOfTopRight = CGPoint(x: myRect.maxX, y: myRect.minY)
let pointOfBottomLeft = CGPoint(x: myRect.minX, y: myRect.maxY)
let pointOfBottomRight = CGPoint(x: myRect.maxX, y: myRect.maxY)
let pointOfCenter = CGPoint(x: myRect.midX, y: myRect.midY)
let pointOfTest = CGPoint(x: (myRect.midX * 1.3), y: (myRect.midY * 1.3))
// move()方法为起点,addLine()为添加边的方法
let bezierPath = UIBezierPath()
bezierPath.move(to: pointOfTopLeft)
bezierPath.addLine(to: pointOfBottomLeft)
bezierPath.lineWidth = 5
bezierPath.lineJoinStyle = .round
// 调用该方法将闭合路径为封闭图形
bezierPath.close()
UIColor.orange.setFill()
UIColor.red.setStroke()
bezierPath.fill()
bezierPath.stroke()
}
}
VC调用该类:
override func viewDidLoad(){
super.viewDidLoad()
let paint1 = PaintView(frame: CGRect(x: 100, y: 200, width: 200, height: 200))
self.view.addSubview(paint1)
}
效果:
折线:
折线实际上为不闭合的多个点构成的,接上列,再添加一个或多个点即为曲线。
func myDrawLineAndRect(){
let myRect = self.bounds.insetBy(dx: (self.bounds.size.width * 0.05), dy: (self.bounds.size.width * 0.05))
let pointOfTopLeft = CGPoint(x: myRect.minX, y: myRect.minY)
let pointOfTopRight = CGPoint(x: myRect.maxX, y: myRect.minY)
let pointOfBottomLeft = CGPoint(x: myRect.minX, y: myRect.maxY)
let pointOfBottomRight = CGPoint(x: myRect.maxX, y: myRect.maxY)
let pointOfCenter = CGPoint(x: myRect.midX, y: myRect.midY)
let pointOfTest = CGPoint(x: (myRect.midX * 1.3), y: (myRect.midY * 1.3))
let bezierPath = UIBezierPath()
bezierPath.move(to: pointOfTopLeft)
bezierPath.addLine(to: pointOfBottomLeft)
bezierPath.addLine(to: pointOfBottomRight)
bezierPath.lineWidth = 5
bezierPath.lineJoinStyle = .round
UIColor.red.setStroke()
bezierPath.stroke()
}
效果:
矩形:
矩形即为四个封闭的点构成的图像。实际上三个封闭的点构成的即为三角形,四个点当然是矩形了。仍然接上列,再添加两个点,然后闭合。
func myDrawLineAndRect(){
let myRect = self.bounds.insetBy(dx: (self.bounds.size.width * 0.05), dy: (self.bounds.size.width * 0.05))
let pointOfTopLeft = CGPoint(x: myRect.minX, y: myRect.minY)
let pointOfTopRight = CGPoint(x: myRect.maxX, y: myRect.minY)
let pointOfBottomLeft = CGPoint(x: myRect.minX, y: myRect.maxY)
let pointOfBottomRight = CGPoint(x: myRect.maxX, y: myRect.maxY)
let pointOfCenter = CGPoint(x: myRect.midX, y: myRect.midY)
let pointOfTest = CGPoint(x: (myRect.midX * 1.3), y: (myRect.midY * 1.3))
let bezierPath = UIBezierPath()
bezierPath.move(to: pointOfTopLeft)
bezierPath.addLine(to: pointOfBottomLeft)
bezierPath.addLine(to: pointOfBottomRight)
bezierPath.addLine(to: pointOfTopRight)
bezierPath.lineWidth = 5
bezierPath.lineJoinStyle = .round
bezierPath.close()
UIColor.red.setStroke()
bezierPath.stroke()
}
效果:
曲线:
曲线即贝塞尔曲线,可由如下方法绘制:
addCurve(to: , controlPoint1: , controlPoint2: )
第一个参数是到哪个点结束(CGPoint类型);第二个为控制点(控制曲率,CGPoint类型),第三个为控制点(控制曲率,CGPoint类型)。控制点的作用类似于微分中导数的意义(导数的大小用以决定原函数的增减),只不过是以确切点的位置来决定直线的曲率。当两个控制点一致时,即为二次贝塞尔曲线,不同时则为三次贝塞尔曲线。
func myDrawCurve(){
let myRect = self.bounds.insetBy(dx: (self.bounds.size.width * 0.05), dy: (self.bounds.size.width * 0.05))
let pointOfTopLeft = CGPoint(x: myRect.minX, y: myRect.minY)
let pointOfTopRight = CGPoint(x: myRect.maxX, y: myRect.minY)
let pointOfBottomLeft = CGPoint(x: myRect.minX, y: myRect.maxY)
let pointOfBottomRight = CGPoint(x: myRect.maxX, y: myRect.maxY)
let pointOfCenter = CGPoint(x: myRect.midX, y: myRect.midY)
let pointOfTest = CGPoint(x: (myRect.midX * 1.3), y: (myRect.midY * 1.3))
let bezierPath = UIBezierPath()
bezierPath.move(to: pointOfTopLeft)
bezierPath.addCurve(to: pointOfBottomLeft, controlPoint1: pointOfCenter, controlPoint2: pointOfTest)
bezierPath.lineWidth = 5
bezierPath.lineJoinStyle = .round
UIColor.orange.setFill()
UIColor.black.setStroke()
bezierPath.stroke()
}
效果:
二次贝塞尔
三次贝塞尔
圆:
圆由addArc() 方法进行控制。圆可以被看作是一种特殊的弧(弧度为2π)。
该方法一种常见的重载方法如下:
addArc(withCenter: , radius: , startAngle: , endAngle: , clockwise: )
第一个参数为圆点(CGPonit类型对象);第二个为半径,Int类型;第三个为从x轴(中学时的以普通xy坐标系里与坐标原点重合的圆)正半轴方向开始的角度;第四个为结束角度,第五个默认为true类型即可。
func myDrawCircle(){
let myRect = self.bounds.insetBy(dx: (self.bounds.size.width * 0.05), dy: (self.bounds.size.width * 0.05))
let pointOfCenter = CGPoint(x: myRect.midX, y: myRect.midY)
let bezierPath = UIBezierPath()
bezierPath.addArc(withCenter: pointOfCenter, radius: 100, startAngle: 0, endAngle: (2 * CGFloat(Double.pi)), clockwise: true)
bezierPath.lineWidth = 2
bezierPath.close()
UIColor.green.setStroke()
bezierPath.stroke()
}
效果:
弧:
弧由addArc() 方法进行控制。不闭合的圆即为弧。
func myDrawArc(){
let myRect = self.bounds.insetBy(dx: (self.bounds.size.width * 0.05), dy: (self.bounds.size.width * 0.05))
let pointOfCenter = CGPoint(x: myRect.midX, y: myRect.midY)
let bezierPath = UIBezierPath()
bezierPath.addArc(withCenter: pointOfCenter, radius: 100, startAngle: (0.3 * CGFloat(Double.pi)), endAngle: (1.2 * CGFloat(Double.pi)), clockwise: true)
bezierPath.lineWidth = 2
UIColor.black.setStroke()
bezierPath.stroke()
}
效果:
阴影:
获取Context,绘制基本图形,设置贝塞尔路径并保存;然后设置阴影大小颜色面积等,并保存即可。
func myDrawShadow(){
let context = UIGraphicsGetCurrentContext()
let rect = CGRect(x: 0, y: 0, width: 100, height: 100)
let bezierPath = UIBezierPath(roundedRect: rect, cornerRadius: 20)
context!.saveGState()
let shadowColor = UIColor.black.cgColor
let shadowOffset = CGSize(width: 5, height: 5)
let shadowBlurRadius: CGFloat = 5.0
context?.setShadow(offset: shadowOffset, blur: shadowBlurRadius, color: shadowColor)
UIColor.systemOrange.setFill()
bezierPath.fill()
context!.restoreGState()
}
效果:
后记:点如何绘制?
点的绘制由两点相同的CGPoint控制即可,不再赘述。