title: CoreGraphicsAndUIBezierPath
date: 2019-06-10 17:05:34
tags:
一.Core Graphics
1.简介
Core Graphics Framework是一套基于C的API框架,使用了Quartz作为绘图引擎
2.CGContextRef
图形上下文,绘图之前需要获取该上下文并传入执行渲染的函数中,有两种方式获取
2.1drawRect
*重写UIView的drawRect:
方法,调用UIGraphicsGetCurrentContext()
即可获得图形上下文
*drawRect:
方法在view第一次显示的时候会自动调用,后面必须通过调用setNeedsDisplay
或者setNeedsDisplayInRect
调用
2.2UIGraphicsBeginImageContextWithOptions
*调用之后必须用UIGraphicsEndImageContext关闭上下文
3.例子
画一个蓝色圆
UIGraphicsBeginImageContextWithOptions(CGSize(width: 100, height: 100), false, 0)
let ctx = UIGraphicsGetCurrentContext()
ctx?.addEllipse(in: CGRect(x: 0, y: 0, width: 100, height: 100))
ctx?.setFillColor(UIColor.blue.cgColor)
ctx?.fillPath()
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
常见api见https://www.jianshu.com/p/43562a8c6c89
4.CGContextSaveGState/CGContextRestoreGState
*用于记录和用于恢复已存储的绘图上下文.
UIGraphicsBeginImageContext(CGSize(width: 100, height: 100))
let context = UIGraphicsGetCurrentContext()
//第一条线*
context?.setStrokeColor(UIColor.red.cgColor)
context?.setLineWidth(2)
context?.move(to: CGPoint(x: 10, y: 10))
context?.addLine(to: CGPoint(x: 10, y: 80))
context?.strokePath()
//储存状态
context?.saveGState()
*//第二条*
context?.setStrokeColor(UIColor.yellow.cgColor)
context?.setLineWidth(5)
context?.move(to: CGPoint(x: 30, y: 10))
context?.addLine(to: CGPoint(x: 30, y: 80))
context?.strokePath()
//恢复状态
context?.restoreGState()
//第三条*
context?.move(to: CGPoint(x: 70, y: 10))
context?.addLine(to: CGPoint(x: 70, y: 80))
context?.strokePath()
let image = UIGraphicsGetImageFromCurrentImageContext()
5.UIGraphicsPushContext/UIGraphicsPopContext
*用于完全更改图形上下文和恢复之前的图形上下文.
*要看切换新的绘图context后,是要继续使用CoreGraphics绘制图形,还是要使用UIKit,前者不需要使用这两个api,后者需要
*CGContextSaveGState
是压栈当前的绘制状态,而UIGraphicsPushContext
:压栈当前的绘制对象,生成新的绘制图层
6.关于uiimage的一些操作
*调用uiimage的draw方法将UIImage绘制在当前的上下文中
6.1平移
let image = UIImage(named: "icon_label_home_pre")
let size = image?.size
UIGraphicsBeginImageContextWithOptions(CGSize(width: (size?.width)! * 2, height: (size?.height)!), false, 1)
image?.draw(at: CGPoint(x: 0, y: 0))
image?.draw(at: CGPoint(x: (size?.width)!, y: 0))
let newimage = UIGraphicsGetImageFromCurrentImageContext()
6.2缩放
let image = UIImage(named: "icon_label_home_pre")
let size = image?.size
UIGraphicsBeginImageContextWithOptions(CGSize(width: (size?.width)! * 2, height: (size?.height)! * 2), false, 1)
image?.draw(in: CGRect(x: 0, y: 0, width: (size?.width)! * 2, height: (size?.height)! * 2))
let newimage = UIGraphicsGetImageFromCurrentImageContext()
*放大会变模糊
6.3裁剪
let image = UIImage(named: "icon_label_home_pre")
let size = image?.size
UIGraphicsBeginImageContextWithOptions(CGSize(width: (size?.width)! / 2, height: (size?.height)!), false, 1)
image?.draw(in: CGRect(x: -(size?.width)!/2, y: 0, width: (size?.width)!, height: (size?.height)!))
let newimage = UIGraphicsGetImageFromCurrentImageContext()
得到图片的右半边
7.CGImage
let image = UIImage(named: "icon_label_home_pre")
let size = image?.size
// 调用cropping对cgimage进行裁剪(对应oc中的CGImageCreateWithImageInRect)
let left = image?.cgImage?.cropping(to: CGRect(x: 0, y: 0, width: (size?.width)! / 2, height: (size?.height)!))
let right = image?.cgImage?.cropping(to: CGRect(x: (size?.width)! / 2, y: 0, width: (size?.width)! / 2, height: (size?.height)!))
UIGraphicsBeginImageContextWithOptions(CGSize(width: (size?.width)! * 1.5, height: (size?.height)!), **false**, 1)
let context = UIGraphicsGetCurrentContext()
//绘制
context?.draw(left!, in: CGRect(x: 0, y: 0, width: (size?.width)! / 2, height: (size?.height)!))
context?.draw(right!, in: CGRect(x: (size?.width)!, y: 0, width: (size?.width)! / 2, height: (size?.height)!))
let newimage = UIGraphicsGetImageFromCurrentImageContext()
*将图片分成两半 绘制在上下文的两边
*当前坐标系坐标原点在左上角, 目标上下文坐标系在左下角,所以绘制出来是上下颠倒的
*解决办法是调用contenxt.draw方法再绘制一次
let image = UIImage(named: "icon_label_home_pre")
let size = image?.size
let left = image?.cgImage?.cropping(to: CGRect(x: 0, y: 0, width: (size?.width)! / 2, height: (size?.height)!))
let right = image?.cgImage?.cropping(to: CGRect(x: (size?.width)! / 2, y: 0, width: (size?.width)! / 2, height: (size?.height)!))
UIGraphicsBeginImageContextWithOptions(CGSize(width: (size?.width)! * 1.5, height: (size?.height)!), false, 1)
let context = UIGraphicsGetCurrentContext()
context?.draw(flip(img: left!), in: CGRect(x: 0, y: 0, width: (size?.width)! / 2, height: (size?.height)!))
context?.draw(flip(img: right!), in: CGRect(x: (size?.width)!, y: 0, width: (size?.width)! / 2, height: (size?.height)!))
let newimage = UIGraphicsGetImageFromCurrentImageContext()
再绘制一次的方法
func flip(img: CGImage) -> CGImage {
let size = CGSize(width: img.width, height: img.height)
UIGraphicsBeginImageContextWithOptions(CGSize(width: (size.width), height: (size.height)), false, 1)
let context = UIGraphicsGetCurrentContext()
context?.draw(img, in: CGRect(x: 0, y: 0, width: (size.width), height: (size.height)))
let newimage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return (newimage?.cgImage)!
}
二、CIFilter与CIImage
*使用之前要将CoreImage框架导入
*CIFilter,一个滤镜对象至少要有一个输入参数,并产生一个输出图片
*CIContext,通过它Core Image可以绘制一个CIFilter产生的结果
let image = UIImage(named: "icon_label_home_pre")
let imageCI = CIImage(cgImage: (image?.cgImage)!)
let grad = CIFilter(name: "CIRadialGradient")
let center = CIVector(x: (image?.size.width)! / 2.0, y: (image?.size.height)! / 2.0)
// 使用setValue:forKey:方法设置滤镜属性
//从白到黑的渐变方式滤镜进行处理
grad?.setValue(center, forKey: "inputCenter")
// 在指定滤镜名时提供所有滤镜键值对*
let dark = CIFilter(name: "CIDarkenBlendMode", parameters: ["inputImage": grad?.outputImage, "inputBackgroundImage": imageCI])
//CIDarkenBlendMode的作用是背景图片样本将被源图片的黑色部分替换掉
let c = CIContext()
let moi = c.createCGImage((dark?.outputImage)!, from: imageCI.extent)
let moi4 = UIImage(cgImage: moi!, scale: (image?.scale)!, orientation: (image?.imageOrientation)!)
let myImageView = UIImageView()
myImageView.image = moi4
myImageView.frame = CGRect(x: 100, y: frameY, width: (moi4.size.width), height: (moi4.size.height))
frameY += (moi4.size.height)
frameY += 20
self.view.addSubview(myImageView)
三、UIBezierPath+CAShapeLayer
*UIBezierPath
是对CGPathRef
数据类型的封装
*绘制以下图案
UIGraphicsBeginImageContext(CGSize(width: 200, height: 200))
let p = UIBezierPath()
// 绘制一个黑色的垂直黑色线,作为箭头的杆子*
p.move(to: CGPoint(x: 100, y: 100))
p.addLine(to: CGPoint(x: 100, y: 19))
p.lineWidth = 20
p.stroke()
// 绘制一个红色三角形箭头*
UIColor.red.set()
p.removeAllPoints()
p.move(to: CGPoint(x: 80, y: 25))
p.addLine(to: CGPoint(x: 100, y: 0))
p.addLine(to: CGPoint(x: 120, y: 25))
p.fill()
// 从箭头杆子上裁掉一个三角形,使用清除混合模式
p.removeAllPoints()
p.move(to: CGPoint(x: 90, y: 101))
p.addLine(to: CGPoint(x: 100, y: 90))
p.addLine(to: CGPoint(x: 110, y: 101))
p.fill(with: .clear, alpha: 1)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
*画一个只有左上角和右上角有圆角的矩型
UIGraphicsBeginImageContext(CGSize(width: 200, height: 200))
let context = UIGraphicsGetCurrentContext()
context?.setStrokeColor(UIColor.black.cgColor)
context?.setLineWidth(3)
let path = UIBezierPath(roundedRect: CGRect(x: 100, y: 100, width: 100, height: 100), byRoundingCorners: [.topLeft,.topRight], cornerRadii: CGSize(width: 100, height: 100))
path.stroke()
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
*绘制二次贝塞尔曲线 一个控制点
UIGraphicsBeginImageContext(CGSize(width: 200, height: 200))
UIColor.red.set()
let path = UIBezierPath()
path.lineWidth = 2
path.move(to: CGPoint(x: 0, y: 200))
path.addQuadCurve(to: CGPoint(x: 200, y: 200), controlPoint: CGPoint(x: 0, y: 10))
path.stroke()
let image = UIGraphicsGetImageFromCurrentImageContext()
*绘制三次贝塞尔曲线 两个控制点
UIGraphicsBeginImageContext(CGSize(width: 200, height: 200))
UIColor.red.set()
let path = UIBezierPath()
path.lineWidth = 2
path.move(to: CGPoint(x: 0, y: 200))
path.addCurve(to: CGPoint(x: 200, y: 100), controlPoint1: CGPoint(x: 100, y: 10), controlPoint2: CGPoint(x: 150, y: 100))
path.stroke()
let image = UIGraphicsGetImageFromCurrentImageContext()
**一般,UIBezierPath配合CAShapeLayer一起使用.UIBezierPath给CAShapeLayer提供路径,CAShapeLayer在提供的路径中进行渲染,绘制出了Shape
let layer = CAShapeLayer()
layer.frame = CGRect(x: 0, y: 610, width: 100, height: 100)
layer.backgroundColor = UIColor.cyan.cgColor
layer.strokeColor = UIColor.black.cgColor
layer.fillColor = UIColor.red.cgColor
self.view.layer.addSublayer(layer)
设置的描边色与填充色没有作用
需要UIBezierPath
来绘制路径,然后使用CAShapeLayer
进行渲染
let path = UIBezierPath(rect: CGRect(x: 0, y: 0, width: 100, height: 100))
layer.path = path.cgPath
*实现三次贝塞尔曲线动画
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: 200))
path.addCurve(to: CGPoint(x: 200, y: 100), controlPoint1: CGPoint(x: 100, y: 10), controlPoint2: CGPoint(x: 150, y: 100))
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.duration = 5
animation.fromValue = 0
animation.toValue = 1
animation.repeatCount = 100
let layer = CAShapeLayer()
layer.strokeColor = UIColor.red.cgColor
layer.fillColor = UIColor.white.cgColor
layer.frame = CGRect(x: 200, y: 410, width: 200, height: 200)
self.view.layer.addSublayer(layer)
layer.path = path.cgPath
layer.lineWidth = 2
layer.strokeStart = 0
layer.strokeEnd = 1
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1) {
layer.add(animation, forKey: **nil**)
}
四、CAGradientLayer
CAGradientLayer是CALayer的一个子类,用来生成渐变色的Layer
五、参考链接
UIImage的一些简单操作https://www.jianshu.com/p/3baddf100b67
UIBezierPath和Core Graphicshttps://www.cnblogs.com/wanghuaijun/p/5870699.html
苹果文档https://developer.apple.com/library/archive/samplecode/QuartzDemo/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007531
CGContextSaveGState与UIGraphicsPushContexthttps://www.jianshu.com/p/be38212c0f79
使用UIBezierPath绘制图形 https://www.cnblogs.com/zhouxihi/p/6253296.html)https://www.cnblogs.com/zhouxihi/p/6253296.html
iOS绘图教程http://www.cnblogs.com/xdream86/archive/2012/12/12/2814552.html
iOS 绘图使用总结 Core Graphics常见api https://www.jianshu.com/p/43562a8c6c89
https://www.jianshu.com/p/139f4fbe7b6b iOS开发之CAShapeLayer初探