程序员公众号【麦小丁】征集优质文章Swift

Swift--UIBezierPath和CAShapeLayer

2018-11-28  本文已影响30人  乐逍遥的笔记

UIBezierPath和CAShapeLayer画线

(注意:该文章仅对Swift语言的画图部分的学习做简单总结,熟悉两种语言的小伙伴,可以随意)

<一>UIBezierPath(UIBezierPath官方介绍)UIBezierPath继承自NSObject对象。我们可以使用这个类来指定路径的几何形状。路径可以定义简单的形状,如矩形、椭圆和弧,也可以定义包含直线和曲线段混合的复杂多边形。定义形状之后,可以使用该类的其他方法在当前绘图上下文中呈现路径。

UIBezierPath中的方法

<1>创建UIBezierPath对象

(1)创建一个矩形路径的UIBezierPath对象

public convenience init(rect: CGRect)

(2)创建一个矩形路径的UIBezierPath对象,并且四个角为圆角,cornerRadius是圆角半径

public convenience init(roundedRect rect: CGRect, cornerRadius: CGFloat)

(3)创建一个矩形路径的UIBezierPath对象, byRoundingCorners可以设置某些角为圆角,cornerRadius是圆角半径

public convenience init(roundedRect rect: CGRect, byRoundingCorners corners: UIRectCorner, cornerRadii: CGSize)

//UIRectCornerd的枚举
public struct UIRectCorner : OptionSet {

    public init(rawValue: UInt)

    
    public static var topLeft: UIRectCorner { get }

    public static var topRight: UIRectCorner { get }

    public static var bottomLeft: UIRectCorner { get }

    public static var bottomRight: UIRectCorner { get }

    public static var allCorners: UIRectCorner { get }
}

(4)创建一个圆形路径的UIBezierPath对象,也可以用此方法画画圆弧,arcCenter:圆心,radius:半径,startAngle:圆形路径的起点,endAngle:圆形路径的终点,clockwise:方向是否是顺时针方向

public convenience init(arcCenter center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool)

(5)创建并返回一个新的UIBezierPath对象,该对象由核心图形路径的内容初始化。

public convenience init(cgPath CGPath: CGPath)

(6)创建一个UIBezierPath对象

public init()

(7)创建并返回一个新的UIBezierPath对象,该对象的路径是当前对象的路径的反向。

open func reversing() -> UIBezierPath
<2>构建UIBezierPath路径

(1) UIBezierPath路径的起点

open func move(to point: CGPoint)

(2)UIBezierPath路径沿途经过的点

open func addLine(to point: CGPoint)

(3) UIBezierPath画圆形或弧线,arcCenter:圆心,radius:半径,startAngle:圆形路径的起点,endAngle:圆形路径的终点,clockwise:方向是否是顺时针方向

open func addArc(withCenter center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool)

(4) UIBezierPath画二次贝塞尔曲线,endPoint:贝塞尔曲线终点,controlPoint:贝塞尔曲线控制点

open func addQuadCurve(to endPoint: CGPoint, controlPoint: CGPoint)

(5)UIBezierPath画二次贝塞尔曲线,endPoint:贝塞尔曲线终点,controlPoint1:第一个贝塞尔曲线控制点, controlPoint2:第二个贝塞尔曲线控制点

open func addCurve(to endPoint: CGPoint, controlPoint1: CGPoint, controlPoint2: CGPoint)

(6)关闭最近添加的子路径。

open func close()

(7)删除所有子路径。

open func removeAllPoints()

(8)将指定路径对象的内容追加到接收方的路径。

open func append(_ bezierPath: UIBezierPath)

(9)展示核心图形的路径。

open var cgPath: CGPath

(10)图形路径中的当前点

open var currentPoint: CGPoint { get }
<3>UIBezierPath的其他属性
**** UIBezierPath的线宽
open var lineWidth: CGFloat  ()
****描边时路径端点的形状。
/* Line cap styles. */描边时路径端点的形状的枚举

public enum CGLineCap : Int32 {

    
    case butt//表示path延伸到终点时 将不在延伸

    case round//表示path延伸到终点时 画一个圆形 半径为UIBezierPath的线宽的一半

    case square//表示path延伸到终点时 画一个正方形 边长为UIBezierPath的线宽
}
open var lineCapStyle: CGLineCap
****The shape of the joints between connected segments of a stroked path.(曲线路径连接部分之间的形状)
/* Line join styles. */

public enum CGLineJoin : Int32 {

    
    case miter//路径连接部分 尖状

    case round//路径连接部分 圆形

    case bevel//路径连接部分 平型  没有尖状
}
open var lineJoinStyle: CGLineJoin
****The limiting value that helps avoid spikes at junctions between connected line segments.
open var miterLimit: CGFloat
****曲线路径段绘制精度
open var flatness: CGFloat
****指示绘制路径时是否使用奇偶绕线规则。
open var usesEvenOddFillRule: Bool
****设置路径的行描模式。
open func setLineDash(_ pattern: UnsafePointer<CGFloat>?, count: Int, phase: CGFloat)
open func getLineDash(_ pattern: UnsafeMutablePointer<CGFloat>?, count: UnsafeMutablePointer<Int>?, phase: UnsafeMutablePointer<CGFloat>?)

<4>UIBezierPath画出路径

(1)使用当前绘图属性绘制路径所包围的区域。

open func fill()

(2)使用当前绘制属性沿接收方路径绘制一条线。

open func stroke()

(3)使用指定的混合模式和透明度值绘制接收方路径包围的区域。

open func fill(with blendMode: CGBlendMode, alpha: CGFloat)

(4)使用指定的混合模式和透明度值沿接收方路径绘制一条线。

open func stroke(with blendMode: CGBlendMode, alpha: CGFloat)

(5)将接收方路径包围的区域与当前图形上下文的裁剪路径相交,使结果形状成为当前的裁剪路径。

open func addClip()

(6)用于判断当前path组成的范围内,是否包含某个点(CGPoint)

open func contains(_ point: CGPoint) -> Bool

(7)The bounding rectangle of the path.

open var bounds: CGRect { get }

(8)使用指定的仿射变换矩阵对路径中的所有点进行变换。

open func apply(_ transform: CGAffineTransform)

<二> CAShapeLayer(CAShapeLayer官方介绍)CAShapeLayer是继承自CALayer类,属于QuartzCore框架。一般画图都需要UIBezierPath和CAShapeLayer结合来实现,UIBezierPath的作用主要是绘制路径,CAShapeLayer主要是对UIBezierPath的路径进行渲染,显示路径,并将CAShapeLayer对象添加到subLayer中,当然CAShapeLayer也可以完成动画等一系列操作,笔者总结一下画图过程中CAShapeLayer类中主要用到的方法。

CAShapeLayer中的方法

(1) CAShapeLayer的初始化

public init()

(2)定义要绘制的路径。

open var path: CGPath?

(3)路径的起点和终点连接的直线和路径上的其他线构成一块区域,fillColor是这块区域的填充色

open var fillColor: CGColor?

(4)填充路径时使用的填充规则。可选non-zero和even-odd两种,默认non-zero

open var fillRule: String

(5)路径的颜色

open var strokeColor: CGColor?

(6)线宽

open var lineWidth: CGFloat

(7)绘制路径时使用的斜接极限。可以做成动画。

/* The miter limit used when stroking the path. Defaults to ten.
     * Animatable. */
    
    open var miterLimit: CGFloat

(8)绘制路径终点的相对位置。可以做成动画。 区间为0到1

open var strokeEnd: CGFloat

上面是介绍了绘图的UIBezierPath和CAShapeLayer的基本属性和方法,我们只需要创建出一个UIBezierPath对象和CAShapeLayer对象,然后将UIBezierPath对象的cgPath属性赋值给CAShapeLayer对象的path属性,layer层添加CAShapeLayer对象即可绘制各种图形。即:

layer.path = path.cgPath
self.view.layer.addSublayer(layer)

<一>画直线

效果图:
屏幕快照 2018-12-17 上午9.56.09.png
实现
 // 线的路径
        let linePath = UIBezierPath.init()
        //MARK: 动画
        
        // 起点
        linePath.move(to: CGPoint.init(x: 20, y: 300))
        // 其他点
        linePath.addLine(to: CGPoint.init(x: 200, y: 200))
        //可以添加n多个点 可为折线,直线等
//        linePath.addLine(to: CGPoint.init(x: 90, y: 70))
        
        let lineLayer = CAShapeLayer.init()
        
        lineLayer.lineWidth = 1
        lineLayer.strokeColor = UIColor.green.cgColor
        lineLayer.path = linePath.cgPath
        lineLayer.fillColor = UIColor.clear.cgColor
        //动画1
        let animation = CABasicAnimation(keyPath: "strokeEnd")
        animation.fromValue = 0
        animation.toValue = 1
        animation.duration = 2
        lineLayer.add(animation, forKey: "")
        self.view.layer.addSublayer(lineLayer)

<二>画阴影图

自己的实现思路:路径的起点和终点连接的直线和路径上的其他线构成一块区域。利用CAShapeLayer的填充色完成
效果图: 屏幕快照 2018-12-17 上午10.04.03.png
实现
// 线的路径
        let linePath = UIBezierPath.init()
        //MARK: 动画
        
        // 起点
        linePath.move(to: CGPoint.init(x: 20, y: 400))
        // 其他点
        linePath.addLine(to: CGPoint.init(x: 20, y: 300))
        linePath.addLine(to: CGPoint.init(x: 30, y: 200))
        linePath.addLine(to: CGPoint.init(x: 50, y: 267))
        linePath.addLine(to: CGPoint.init(x: 100, y: 267))
        linePath.addLine(to: CGPoint.init(x: 150, y: 200))
        linePath.addLine(to: CGPoint.init(x: 200, y: 188))
        linePath.addLine(to: CGPoint.init(x: 200, y: 400))
        
        let lineLayer = CAShapeLayer.init()
        
        lineLayer.lineWidth = 1
        lineLayer.strokeColor = UIColor.green.cgColor
        lineLayer.path = linePath.cgPath
        lineLayer.fillColor = UIColor.orange.withAlphaComponent(0.3).cgColor//闭合区域的填充色
        //动画1
        let animation = CABasicAnimation(keyPath: "strokeEnd")
        animation.fromValue = 0
        animation.toValue = 1
        animation.duration = 2
        lineLayer.add(animation, forKey: "")
        self.view.layer.addSublayer(lineLayer)

<三>画多边形

自己的实现思路:绘制完成路径后,使用path.close()//图层封闭方法。
效果图: 屏幕快照 2018-12-17 上午10.07.46.png
实现
let path = UIBezierPath.init()
        ////layer起点
        path.move(to: CGPoint.init(x: 30, y: NavigationBarHeight+50))
        ////layer整条路径上经过的其他点  用此方法也可以画多边形
        path.addLine(to: CGPoint.init(x: 30, y: NavigationBarHeight+100))
        path.addLine(to: CGPoint.init(x: 200, y: NavigationBarHeight+100))
//        path.addLine(to: CGPoint.init(x: 200, y: NavigationBarHeight+50))
        path.close()//图层封闭
        let layer = CAShapeLayer.init()
        layer.path = path.cgPath
        layer.lineWidth = 1
        layer.strokeColor = UIColor.orange.cgColor
        layer.fillColor = UIColor.clear.cgColor//填充色
        
        self.view.layer.addSublayer(layer)

<四>画圆形或椭圆

自己的实现思路:画圆形或椭圆,可以使用绘制一个view,为其添加蒙版效果实现,若视图为长方形 ,则绘制椭圆 ,正方形,则为圆形。另外还可以使用UIBezierPath类中的open func addArc(withCenter center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool)画圆。
效果图:
屏幕快照 2018-12-17 上午10.20.13.png
实现
class PaintCilcleViewVC: BaseViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        ////画圆形或椭圆
        drawCircleView()
        //画圆形或椭圆  2
        drawCircleViewSecond()
    }
    /**
     画圆形或椭圆  1
     */
    func drawCircleView() {
        // 需要圆视图 若视图为长方形 则绘制椭圆 正方形,则为圆形
        let circleView = UIView.init(frame: CGRect.init(x: (kScreenWidth-200)/2, y: NavigationBarHeight*2, width: 200, height: 100))
        circleView.backgroundColor = UIColor.orange
        self.view.addSubview(circleView)
        
        // 线的路径
        let path = UIBezierPath.init(ovalIn:circleView.bounds)
        let pathLayer = CAShapeLayer.init()
        pathLayer.lineWidth = 1
        pathLayer.strokeColor = UIColor.green.cgColor
        pathLayer.path = path.cgPath
        pathLayer.fillColor = nil
        circleView.layer.addSublayer(pathLayer)
        circleView.layer.mask = pathLayer // layer 的 mask属性,添加蒙版
    }
    /**
     画圆形
     */
    func drawCircleViewSecond() {
        let path = UIBezierPath.init()
        path.addArc(withCenter: CGPoint.init(x: kScreenWidth/2, y: 500), radius: 30, startAngle: 0, endAngle: 2*CGFloat(Double.pi), clockwise: true)
        
        let layer = CAShapeLayer.init()
        layer.lineWidth = 1
        layer.strokeColor = UIColor.green.cgColor
        layer.path = path.cgPath
        layer.fillColor = nil
        self.view.layer.addSublayer(layer)
    }
}

<五>画圆角矩形

自己的实现思路:首先需要我们创建一个需要绘制圆角的View,绘制四个圆角,可以使用public convenience init(roundedRect rect: CGRect, cornerRadius: CGFloat)该方法,如果想要实现某个角为圆角或者多个角为圆角,可以使用public convenience init(roundedRect rect: CGRect, byRoundingCorners corners: UIRectCorner, cornerRadii: CGSize)方法。
效果图:
屏幕快照 2018-12-17 上午10.25.26.png
实现
class PaintRoundedRectangleVC: BaseViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        //四角切圆角
        drawQuadRangle()
        //单角切圆角
        drawUnicornous()
    }
    /**
     四角切圆角
     */
    func drawQuadRangle() {
        // 需要画圆角矩形
        let view = UIView.init(frame: CGRect.init(x: (kScreenWidth-200)/2, y: NavigationBarHeight, width: 100, height: 100))
        view.backgroundColor = UIColor.orange
        self.view.addSubview(view)
        
        //线的路径
        let path = UIBezierPath.init(roundedRect: view.bounds, cornerRadius: 20)
        let layer = CAShapeLayer.init()
        layer.lineWidth = 1
        layer.strokeColor = UIColor.green.cgColor
        layer.path = path.cgPath
        view.layer.mask = layer
    }
    /**
     单角切圆角 (多个角进行圆角切)
     */
    func drawUnicornous() {
        // 需要画圆角矩形
        let view = UIView.init(frame: CGRect.init(x: (kScreenWidth-200)/2, y: NavigationBarHeight+150, width: 100, height: 100))
        view.backgroundColor = UIColor.orange
        self.view.addSubview(view)
        
        //线的路径
        
        //此方法可以用来对多个角进行圆角切
//        let path = UIBezierPath.init(roundedRect: view.bounds, byRoundingCorners: UIRectCorner(rawValue: UIRectCorner.RawValue(UInt8(UIRectCorner.topLeft.rawValue) | UInt8(UIRectCorner.bottomRight.rawValue))) , cornerRadii: CGSize.init(width: 20, height: 0))
        //此方法可以用来对单个角进行圆角切
        let path = UIBezierPath.init(roundedRect: view.bounds, byRoundingCorners: UIRectCorner.topLeft, cornerRadii: CGSize.init(width: 20, height: 0))
        
        let layer = CAShapeLayer.init()
        layer.lineWidth = 1
        layer.strokeColor = UIColor.green.cgColor
        layer.path = path.cgPath
        view.layer.mask = layer
    }
}

<六>画弧线

自己的实现思路:画圆是从0绘制到2pi的位置,画弧线就是角度可以自己随意绘制。
效果图:
屏幕快照 2018-12-17 上午10.32.39.png
实现
class PaintTrajectoryViewVC: BaseViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        //在矩形中画一条圆弧
        paintTrajectory()
        //折线和弧线构成的曲线
        lineAndTrajectoryView()
    }

    /**
     在矩形中画一条圆弧
     */
    func paintTrajectory() {
        let view = UIView.init(frame: CGRect.init(x: (kScreenWidth-200)/2, y: NavigationBarHeight, width: 100, height: 100))
        view.backgroundColor = UIColor.orange
        self.view.addSubview(view)
        
        //线的路径
        let viewCenter = CGPoint.init(x: view.frame.width/2, y: view.frame.height/2)// 画弧的中心点,相对于view
        let path = UIBezierPath.init(arcCenter: viewCenter, radius: 33, startAngle: CGFloat(0), endAngle: CGFloat(1.6*Double.pi), clockwise: true)
        
        let layer = CAShapeLayer.init()
        layer.lineWidth = 1
        layer.strokeColor = UIColor.green.cgColor
        layer.fillColor = nil
        layer.path = path.cgPath
        view.layer.addSublayer(layer)
    }
    
    /**
     折线和弧线构成的曲线
     */
    func lineAndTrajectoryView() {
        let view = UIView.init(frame: CGRect.init(x: (kScreenWidth-200)/2, y: NavigationBarHeight+200, width: 150, height: 150))
        view.backgroundColor = UIColor.orange
        self.view.addSubview(view)
        
        //线的路径
        let viewCenter = CGPoint.init(x: view.frame.width/2, y: view.frame.height/2)// 画弧的中心点,相对于view
        
        let path = UIBezierPath.init()
        
        path.move(to: CGPoint.init(x: 0, y: 0))
        path.addLine(to: viewCenter)
        
        // 添加一条弧线
        path.addArc(withCenter: viewCenter, radius: 50, startAngle: 0, endAngle: CGFloat(Double.pi), clockwise: true)
        let layer = CAShapeLayer.init()
        layer.lineWidth = 1
        layer.strokeColor = UIColor.green.cgColor
        layer.fillColor = nil
        layer.path = path.cgPath
        view.layer.addSublayer(layer)
    }
}

<七>绘制饼状图

自己的实现思路:绘制饼状图其实就是绘制n个圆弧,可以将其CAShapeLayer的fillColor设置为透明色,strokeColor是圆弧的颜色,可以设置为自己想要的颜色,通过设置lineWidth路径的宽度来调整圆弧的大小。饼状图也可是实现点击效果,重写func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)方法来实现。
效果图:
屏幕快照 2018-12-17 上午10.39.02.png
实现
/*饼状图*/
class RYQPieChartView: UIView {
    
    //设置圆点
    var centerPoint:CGPoint!
    var radius:CGFloat!
    var layerWidth = 40*m6Scale//圆环宽度
    
    var startAngle:Float = 0
    var endAngle:Float = 0
    var allValue:Float = 0
    var dataSource = [456, 567, 559]
    var colors = [UIColor.green, UIColor.orange, UIColor.gray]
    var outBezerArrs = [Any]()
    var outLayers = [Any]()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        radius = frame.size.height*0.4-layerWidth
        centerPoint = CGPoint.init(x: frame.size.width/2, y: frame.size.height/2)
        //画图
        drawPieChartView()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }
}

/**
 画图
 */

extension RYQPieChartView {
    func drawPieChartView() {
        
        for index in 0...dataSource.count-1 {
            let value = dataSource[index]
            allValue = allValue+Float(value)
        }
        
        //for循环画图
        for index in 0...dataSource.count-1 {
            bezierPaint(index: index)
        }
    }
    //贝塞尔和CASherLayer画图
    func bezierPaint(index:Int) {
        
        let targetValue = dataSource[index]
        let ratioString = String(format: "%.5f", Float(targetValue)/Float(allValue))
        
        endAngle = startAngle + (Float(ratioString)!-0.005)*2*Float(Double.pi)
        //bezierPath形成闭合的扇形路径  外弧形
        let bezierOutPath = UIBezierPath.init()
        // 添加一条弧线
        bezierOutPath.addArc(withCenter: centerPoint, radius: radius, startAngle: CGFloat(startAngle), endAngle: CGFloat(endAngle), clockwise: true)
        
        //////外弧形渲染
        let outLayer = CAShapeLayer.init()
        outLayer.lineWidth = layerWidth
        outLayer.fillColor =  clear.cgColor
        outLayer.strokeColor = colors[index].cgColor
        outLayer.path = bezierOutPath.cgPath
        self.layer.addSublayer(outLayer)
        let start = dataSource[index]
        let scaleString = String(format: "%.5f", Float(start)/Float(allValue))
        startAngle = startAngle+(Float(scaleString)!-1)*2*Float(Double.pi)
        //动画1
        let animation = CABasicAnimation(keyPath: "strokeEnd")
        animation.fromValue = 0
        animation.toValue = 1
        animation.duration = 1
        outLayer.add(animation, forKey: "")
        outBezerArrs.append(bezierOutPath)
        outLayers.append(outLayer)
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        let touch:UITouch = ((touches as NSSet).anyObject()! )as!UITouch
        let point = touch.location(in: self)
        
        var start:Float = 0
        var end:Float = 0
        
        for index in 0...outBezerArrs.count-1 {
            let targetValue = dataSource[index]
            let ratioString = String(format: "%.5f", Float(targetValue)/Float(allValue))
            
            end = start + (Float(ratioString)!-0.005)*2*Float(Double.pi)
            let bezierOutPath = outBezerArrs[index] as! UIBezierPath
            let outLayer = outLayers[index] as! CAShapeLayer
            layerWidth = 40*m6Scale
            if bezierOutPath.contains(point) {
                layerWidth = 60*m6Scale
            }
            
            //移除path上的所有点 重新绘制
            bezierOutPath.removeAllPoints()
            // 添加一条弧线
            bezierOutPath.addArc(withCenter: centerPoint, radius: radius, startAngle: CGFloat(start), endAngle: CGFloat(end), clockwise: true)
            //外弧形渲染
            outLayer.path = bezierOutPath.cgPath
            outLayer.lineWidth = layerWidth
            
            let newstart = dataSource[index]
            let scaleString = String(format: "%.5f", Float(newstart)/Float(allValue))
            start = start+(Float(scaleString)!-1)*2*Float(Double.pi)
        }
    }
}

<八>不规则渐变色图形

自己的实现思路:使用CGContextRef属性,获取上下文生成图片,再次绘制UIBezierPath,将生成的图片填充到CAShapeLayer的填充色中。
效果图:
屏幕快照 2018-12-17 上午10.44.19.png
实现

class IrregularGradientVC: BaseViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        //绘制UIBezierPath路径
        let path = UIBezierPath.init()
        path.move(to: CGPoint.init(x: 0, y: NavigationBarHeight))
        path.addLine(to: CGPoint.init(x: 0, y: 150+NavigationBarHeight))
        path.addCurve(to: CGPoint.init(x: kScreenWidth, y: 150+NavigationBarHeight), controlPoint1: CGPoint.init(x: kScreenWidth*0.3, y: 200+NavigationBarHeight), controlPoint2: CGPoint.init(x: kScreenWidth*0.8, y: 350+NavigationBarHeight))
        path.addLine(to: CGPoint.init(x: kScreenWidth, y: NavigationBarHeight))
        
        //绘制渐变 图片
        let img = drawLinearGradient(startColor: UIColor.green.cgColor, endColor: UIColor.red.cgColor)

        let layer = CAShapeLayer.init()
        //本质上生成一张渐变色图片 作为layer的填充背景
        layer.fillColor = UIColor.init(patternImage: img).cgColor
        layer.path = path.cgPath
        self.view.layer.addSublayer(layer)
    }

    /**
     绘制渐变
     */
    func drawLinearGradient(startColor:CGColor, endColor:CGColor) -> UIImage {
        
        //创建CGContextRef
        UIGraphicsBeginImageContext(self.view.bounds.size)
        let context = UIGraphicsGetCurrentContext()
        
        let path = UIBezierPath.init()
        path.move(to: CGPoint.init(x: 0, y: NavigationBarHeight))
        path.addLine(to: CGPoint.init(x: 0, y: kScreenHeight))
        path.addLine(to: CGPoint.init(x: kScreenWidth, y: kScreenHeight))
        path.addLine(to: CGPoint.init(x: kScreenWidth, y: 0))
        
        let colorSpace = CGColorSpaceCreateDeviceRGB()
        let locations = [CGFloat(0.0), CGFloat(1.0)]
        let colors = [startColor, endColor]
        let gradient = CGGradient.init(colorsSpace: colorSpace, colors: colors as CFArray, locations: locations)
        
        let pathRect: CGRect = path.cgPath.boundingBox
        //具体方向可根据需求修改
        let startPoint = CGPoint.init(x: pathRect.minX, y: pathRect.midY)
        let endPoint = CGPoint.init(x: pathRect.maxX, y: pathRect.midY)
        
        context?.saveGState()
        context?.addPath(path.cgPath)
        context?.clip()
        context?.drawLinearGradient(gradient!, start: startPoint, end: endPoint, options: CGGradientDrawingOptions(rawValue: 0))
        context?.restoreGState()
        //获取绘制的图片
        let img = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        
        return img!
    }

}

<九>二次贝塞尔曲线

效果图:
屏幕快照 2018-12-17 上午10.59.31.png
实现
class QuadRaticBezierVC: BaseViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        quadRaticBezierView()
        
        sameEndPointQuadRaticBezierView()
    }
    /**
     总结:起点和终点的距离越小,趋向控制点结束越早,趋向终点开始越早,曲线弧度越大。
     
     起点终点相同,控制点不同
     */
    func quadRaticBezierView() {
        
        let view = UIView.init(frame: CGRect.init(x: (kScreenWidth-200)/2, y: NavigationBarHeight, width: 200, height: 200))
        view.backgroundColor = UIColor.orange
        self.view.addSubview(view)

        // 绿色二次贝塞尔曲线
        let greenPath = UIBezierPath.init()
        greenPath.move(to: CGPoint.init(x: 0, y: 100))
        let end1Point = CGPoint.init(x: 200, y: 50)
        //二次贝塞尔曲线
        greenPath.addQuadCurve(to: end1Point, controlPoint: CGPoint.init(x: 100, y: 200))
        let layer1 = CAShapeLayer.init()
        layer1.lineWidth = 1
        layer1.strokeColor = UIColor.green.cgColor
        layer1.fillColor = nil
        layer1.path = greenPath.cgPath
        view.layer.addSublayer(layer1)
        
        // 红色二次贝塞尔曲线
        let redPath = UIBezierPath.init()
        redPath.move(to: CGPoint.init(x: 0, y: 100))
        let end2Point = CGPoint.init(x: 100, y: 50)
        // 二次贝塞尔曲线
        redPath.addQuadCurve(to: end2Point, controlPoint: CGPoint.init(x: 100, y: 200))
        let layer2 = CAShapeLayer.init()
        layer2.lineWidth = 1
        layer2.strokeColor = UIColor.red.cgColor
        layer2.fillColor = nil
        layer2.path = redPath.cgPath
        view.layer.addSublayer(layer2)
    }
    /**
     总结:控制点与起点和终点所在直线偏移距离越大,曲线弧度越大。
     */
    func sameEndPointQuadRaticBezierView() {
        let view = UIView.init(frame: CGRect.init(x: (kScreenWidth-200)/2, y: NavigationBarHeight+300, width: 200, height: 200))
        view.backgroundColor = UIColor.orange
        self.view.addSubview(view)
        
        let startPoint = CGPoint.init(x: 0, y: 100)
        let endPoint = CGPoint.init(x: 200, y: 50)
        
        // 绿色二次贝塞尔曲线
        let greenPath = UIBezierPath.init()
        greenPath.move(to: startPoint)
        //二次贝塞尔曲线
        greenPath.addQuadCurve(to: endPoint, controlPoint: CGPoint.init(x: 100, y: 200))
        let layer1 = CAShapeLayer.init()
        layer1.lineWidth = 1
        layer1.strokeColor = UIColor.green.cgColor
        layer1.fillColor = nil
        layer1.path = greenPath.cgPath
        view.layer.addSublayer(layer1)
        
        // 红色二次贝塞尔曲线
        let redPath = UIBezierPath.init()
        redPath.move(to: CGPoint.init(x: 0, y: 100))
        // 二次贝塞尔曲线
        redPath.addQuadCurve(to: endPoint, controlPoint: CGPoint.init(x: 100, y: 150))
        let layer2 = CAShapeLayer.init()
        layer2.lineWidth = 1
        layer2.strokeColor = UIColor.red.cgColor
        layer2.fillColor = nil
        layer2.path = redPath.cgPath
        view.layer.addSublayer(layer2)
    }
}

<十>三次贝塞尔曲线

效果图:
屏幕快照 2018-12-17 上午11.03.08.png
实现
/**
 三次贝塞尔曲线  比较于二次贝塞尔曲线 多了一个控制点
 
 - (void)addCurveToPoint:(CGPoint)endPoint controlPoint1:(CGPoint)controlPoint1 controlPoint2:(CGPoint)controlPoint2
 三次贝赛尔曲线,起点用 moveToPoint方法给出;endPoint:贝赛尔曲线终点;controlPoint1:控制点1;controlPoint2:控制点2;
 曲线是由起点趋向控制点1,之后趋向控制点2,最后到达终点(不会经过控制点)的曲线。在起点和终点所在直线方向上,曲线在起点和控制点1之间,趋向控制点1;在控制点2和终点之间,趋向控制点2.控制点与起点和终点所在直线的偏移影响曲线的偏移程度
 */
class TripleBezierVC: BaseViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let view = UIView.init(frame: CGRect.init(x: (kScreenWidth-200)/2, y: NavigationBarHeight, width: 200, height: 200))
        view.backgroundColor = UIColor.orange
        self.view.addSubview(view)
        
        let startPoint = CGPoint.init(x: 0, y: 100)
        let endPoint = CGPoint.init(x: 200, y: 50)
        
        // 绿色三次贝塞尔曲线
        let path = UIBezierPath.init()
        path.move(to: startPoint)
        //三次贝塞尔曲线
        path.addCurve(to: endPoint, controlPoint1: CGPoint.init(x: 10, y: 0), controlPoint2: CGPoint.init(x: 70, y: 180))
        //在加  可画出n次贝塞尔曲线
//        path.addCurve(to: startPoint, controlPoint1: CGPoint.init(x: 40, y: 40), controlPoint2: CGPoint.init(x: 90, y: 120))
        let layer = CAShapeLayer.init()
        layer.lineWidth = 1
        layer.strokeColor = UIColor.green.cgColor
        layer.fillColor = nil
        layer.path = path.cgPath
        
        
        //动画1
        let animation = CABasicAnimation(keyPath: "strokeEnd")
        animation.fromValue = 0
        animation.toValue = 1
        animation.duration = 2
        layer.add(animation, forKey: "")
        
        view.layer.addSublayer(layer)
    }

}

想下载Demo的小伙伴,Demo下载,觉得有用的点个星吧。有什么错误和不足的地方也欢迎指正。

上一篇下一篇

猜你喜欢

热点阅读