Intermediate Core Graphics(Swift)-Transforms、Shadows

本文介绍如何使用Core Graphics进行视图变换,包括平移、旋转等操作,并展示如何正确绘制扇形图。此外,还详细解释了如何设置阴影效果及使用透明图层实现单一阴影。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文内容来自Intermediate Core Graphics

Intermediate Core Graphics(Swift)-Transforms、Shadows

Transforms

所有的viewcontext都有一个current transformation matrix,或者叫做CTMmatrix表示的当前的positionscalerotation
UIView有一个CGAffineTransform类型的transform属性来表示matrix
context通过CGContextGetCTM来获取当前状态的CTM

例如,需要绘制如下的饼状图:
饼状图

首先绘制红色的扇形图:
红色的扇形图

绘制绿色的扇形,把context旋转红色的扇形大小的角度,由于context的默认旋转点是在左上角,所以此时绿色的扇形是绘制在屏幕之外的地方

绘制绿色的扇形

所以,正确的做法是,在旋转context之前,平移context,这样context的左上角就处于旋转的正确的位置:

平移context

然后再旋转

旋转

再把context平移到原来的位置

context平移

这时就可以绘制绿色的扇形

绘制绿色的扇形

所以,整个的绘制的过程是:Translate->Rotate->Translate Back->Draw

本例的绘制chart过程如下:

    override func drawRect(rect: CGRect) {


        let context = UIGraphicsGetCurrentContext()
        let totalSpent = categories.reduce(0, combine: {$0 + $1.spent})
        guard totalSpent > 0 else { return }

        let diameter = min(bounds.width, bounds.height)
        let margin: CGFloat = 20
        //绘制Circle,平移到中心点
        let circle = UIBezierPath(ovalInRect: CGRect(x: 0, y: 0, width: diameter, height: diameter).insetBy(dx: margin, dy: margin))
        let transform = CGAffineTransformMakeTranslation(bounds.width/2-diameter/2, 0)
        circle.applyTransform(transform)

        let workingCategories = categories.filter({$0.spent > 0})
        for(index, _) in workingCategories.enumerate() {

            //扇形的百分比和角度
            let percent = CGFloat(workingCategories[index].spent / totalSpent)
            let angle = percent * 2 * π;

            print(workingCategories[index].name, workingCategories[index].spent, round(percent * 100))
            //半径和中心点
            let radius = diameter / 2 - margin
            let centerPoint = center
            //扇形路径
            let slice = UIBezierPath()
            slice.moveToPoint(centerPoint);
            slice.addLineToPoint(CGPoint(x: centerPoint.x+radius, y: centerPoint.y))
            slice.addArcWithCenter(centerPoint, radius: radius, startAngle: 0, endAngle: angle, clockwise: true)
            slice.closePath()
            //填充
            chartColors[index].setFill()
            slice.fill()
            //CTM变换
            CGContextTranslateCTM(context, centerPoint.x, centerPoint.y)
            CGContextRotateCTM(context, angle)
            CGContextTranslateCTM(context, -centerPoint.x, -centerPoint.y)

        }


        circle.stroke()

    }

结果如下:

chart

不光Core Graphic可以应用Transform,view和layer也可以应用Transform

Shadows

设置阴影的非UIKit的方法:

CGContextSetShadowWithColor(_:_:_:_:)

指定color,shadow的偏移量和blur radius

如下所示的例子:

shadow例子

Shadow with Stroke
下图是上图的circle添加了stroke之后的阴影效果,会发现其有两个阴影

  • fill的阴影
  • stroke的阴影

两个阴影

如果要实现如下的只有一个阴影该怎么做呢?
一个阴影

答案就是要使用transparency layer

transparent layer

在stroke和fill之前,开始了一个transparency layer。它所做的是,创建了一个单独的drawing buffer ,所有的shape都被绘制到这个buffer中,在这个
transparency layer结束后,然后buffer作为一个整体被绘制到context中,所以就只有一个阴影

本例的内容是为上一节的chart,创建一个阴影
如果单独添加如下代码:

//阴影
let shadowColor = UIColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 0.4)
CGContextSetShadowWithColor(context, CGSize(width: 10,height: 10), 1, shadowColor.CGColor)

此时的效果如下,会发现每个扇形区域都一个阴影:
每个扇形区域都一个阴影

当添加CGContextBeginTransparencyLayer(context, nil)
这里写图片描述

还要注意的是当改变当前CTM,最好使用CGContextSaveGState来保存状态

完整代码如下:

    CGContextSaveGState(context)
    //阴影
    let shadowColor = UIColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 0.4)
    CGContextSetShadowWithColor(context, CGSize(width: 10,height: 10), 1, shadowColor.CGColor)
    CGContextBeginTransparencyLayer(context, nil)


    let workingCategories = categories.filter({ $0.spent > 0 })

    for (index, _) in workingCategories.enumerate() {

      ......

    }


    circle.stroke()
    //结束TransparencyLayer
    CGContextEndTransparencyLayer(context)
    CGContextRestoreGState(context)

可以对context进行scale和translate,使chart看起来有3d的效果

    CGContextScaleCTM(context, 1, 0.6)
    CGContextTranslateCTM(context, 0, 100)

效果如下:

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值