美文网首页
CoreGraphics那些事

CoreGraphics那些事

作者: 盖小聂 | 来源:发表于2019-06-21 14:50 被阅读0次

    目录
    1、CoreGraphics在绘图系统中的地位
    2、CoreGraphics的坐标系
    3、Context的获取以及状态
    4、圆弧和曲线的绘制

    1、CoreGraphics在绘图系统中的地位

    屏幕快照 2019-06-22 上午8.12.20.png

    2、CoreGraphics的坐标系
    UIKit的坐标系为左上角原点的坐标系,CoreGraphics的坐标系为左下角为原点的坐标系。那为什么我们在drawRect方法中使用CoreGraphics方法绘制内容的时候可以使用UIKit的坐标系?因为iOS系统在drawRect返回CGContext的时候,默认帮我们进行了一次变换,以方便开发者直接用UIKit坐标系进行渲染。

    坐标系翻转函数

    /*
    平移坐标系统。该方法相当于把原来位于 (0, 0) 位置的坐标原点平移到 (tx, ty) 点。
    在平移后的坐标系统上绘制图形时,所有坐标点的 X 坐标都相当于增加了 tx,
    所有点的 Y 坐标都相当于增加了 ty。
    */
    CGContextTranslateCTM(CGContextRef  _Nullable c, CGFloat tx, CGFloat ty)
    
    /*
    缩放坐标系统。该方法控制坐标系统水平方向上缩放 sx,垂直方向上缩放 sy。
    在缩放后的坐标系统上绘制图形时,所有点的 X 坐标都相当于乘以 sx 因子,
    所有点的 Y 坐标都相当于乘以 sy 因子。
    */
    CGContextScaleCTM(CGContextRef  _Nullable c, CGFloat sx, CGFloat sy)
    
    /*
    旋转坐标系统。该方法控制坐标系统旋转 angle 弧度。
    在缩放后的坐标系统上绘制图形时,所有坐标点的 X、Y 坐标都相当于旋转了 angle弧度之后的坐标。
    */
    CGContextRotateCTM(CGContextRef  _Nullable c, CGFloat angle)
    

    举个例子:CoreGraphics坐标系-->UIKit坐标系

        CGContextSaveGState(context);
        CGContextTranslateCTM(context, 0, rect.size.height);
        CGContextScaleCTM(context, 1.0, -1.0);
        ……
        CGContextRestoreGState(context);
    

    3、Context的获取以及状态
    3.1、获取context的三种方式
    方式1:在drawRect:方法中采用UIGraphicsGetCurrentContext()获取context

    - (void) drawRect: (CGRect) rect {
        [super drawRect:rect];
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextAddEllipseInRect(context, CGRectMake(0,0,100,100));
        CGContextSetFillColorWithColor(context, [UIColor blueColor].CGColor);
        CGContextFillPath(context);
    }
    

    方式2:在drawLayer: inContext:方法中采用UIGraphicsPushContext(CGContextRef _Nonnull context)方法将传入的context设置为current context,因为传入的context可能不是current context,然后使用UIGraphicsPopContext()方法还原current context。

    - (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
        [super drawLayer:layer inContext:ctx];
        [[UIColor redColor] setFill];
        //context压入栈中,并将context设置为当前绘图上下文
        UIGraphicsPushContext(ctx);
        [[UIColor blackColor] setFill];
        //将栈顶的上下文弹出,恢复先前的上下文,但是绘图状态不变
        UIGraphicsPopContext();
        UIRectFill(CGRectMake(90, 340, 100, 100)); // black color
    }
    

    方式3:UIGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale)与方法UIGraphicsEndImageContext()方法配对使用。在这两个方法中可以使用UIGraphicsGetCurrentContext()方法获取他们创建的context。然后在context中作画,通过UIGraphicsGetImageFromCurrentImageContext()方法获取作图image。这对方法可以在应用程序的任何方法中使用,不像上面两种方式只能在特定的方法中使用。

        UIGraphicsBeginImageContextWithOptions(CGSizeMake(100,100), NO, 0);
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextAddEllipseInRect(context, CGRectMake(0,0,100,100));
        CGContextSetFillColorWithColor(context, [UIColor blueColor].CGColor);
        CGContextFillPath(context);
        UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    

    3.2、context状态的保存与恢复

    //成对使用,save暂存context的状态,restore恢复之前暂存的context的状态
    CGContextSaveGState(CGContextRef  _Nullable c)
    CGContextRestoreGState(CGContextRef  _Nullable c)
    

    具体的应用场景:
    CoreText的坐标系原点是左下角,使用CoreText渲染文字必须翻转坐标系,才能使CoreText渲染出来的文字不会出现上下颠倒的现象。但此时修改之后的坐标系UIKIt无法正常使用。所以可以使用save暂存当前context,然后翻转坐标系,处理CoreText的绘制,处理完之后,在调用restore方法恢复context,继续UIKit的绘制工作。

    - (void)drawRect:(CGRect)rect {
        [super drawRect:rect];
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextSaveGState(context);
        CGContextTranslateCTM(context, 0, self.bounds.size.height);
        CGContextScaleCTM(context, 1.0, -1.0);
        //CoreText的绘制
        ……
        CGContextRestoreGState(context);
        
        //继续UIKit的绘制
        ……
    }
    

    4、圆弧和曲线的绘制
    4.1、画圆弧

    方法一

    /*
    参数clockwise 0表示顺时针,1表示逆时针
    (这个顺时针和逆时针应该和context的坐标系有关,
    如果翻转了context的坐标系,这个对应关系应该就不成立了吧)
    */
    CGContextAddArc(CGContextRef  _Nullable c,
    CGFloat x, CGFloat y, 
    CGFloat radius,  
    CGFloat startAngle, 
    CGFloat endAngle, 
    int clockwise)
    

    方法二

    /*
    current point为p0,p0与p1(x1,y1)的连线,p1(x1,y1)与p2(x2,y2)的连线,
    与指定半径并且和这两条相交线相切形成的圆弧就是我们要绘制的圆弧,
    绘制的时候先连接p0到圆弧的起点线段,然后在绘制圆弧。
    也就是说参数中p1和p2这两个点可能不在绘制的圆弧之上,只是绘制圆弧的辅助点。
    (不知道这种画圆弧的方式有什么实际的应用场景?)
    */
    CGContextAddArcToPoint(CGContextRef  _Nullable c, 
    CGFloat x1, CGFloat y1, 
    CGFloat x2, CGFloat y2, 
    CGFloat radius)
    
    屏幕快照 2019-06-22 上午8.11.55.png

    4.2、画曲线

    绘制2阶贝塞尔曲线

    //点cp(cpx,cpy)是控制点,不在绘制的曲线上,是绘制曲线的辅助点;
    //点p(x,y)是终点
    CGContextAddQuadCurveToPoint(CGContextRef  _Nullable c, 
    CGFloat cpx, CGFloat cpy,  
    CGFloat x, CGFloat y)
    

    绘制3阶贝塞尔曲线

    //点cp1(cp1x,cp1y),点cp2(cp2x,cp2y)是控制点,不在绘制的曲线上,是绘制曲线的辅助点;
    //点p(x,y)是终点
    CGContextAddCurveToPoint(CGContextRef  _Nullable c, 
    CGFloat cp1x, CGFloat cp1y, 
    CGFloat cp2x, CGFloat cp2y, 
    CGFloat x, CGFloat y)
    

    相关文章

      网友评论

          本文标题:CoreGraphics那些事

          本文链接:https://www.haomeiwen.com/subject/untyqctx.html