QuartZ 2D personal summary

In the past two days, I have concentrated on learning the knowledge of using QuartZ 2D drawing, and now I will sort out my notes:

Original address: http://blog.csdn.net/Le_Wrynn/article/details/50734231

QuartZ 2D is a set of APIs developed by Apple and is part of the Core Graphics Framework. All operations of Core Graphics API are performed in context. Therefore, before drawing, you need to obtain the context and pass it into the function that performs rendering. Obtaining a graphics context is the first step to complete the drawing task. The graphics context can be understood as a canvas. If you don't get the canvas, you can't do any drawing. There are two ways to obtain a graphics context:

The first method is to create an image type context. Call the UIGraphicsBeginImageContextWithOptions function to obtain the graphics context used to process images. Using this context, you can draw on it and generate images. Call theUIGraphicsGetImageFromCurrentImageContext function to get a UIImage object from the current context. Remember to call the UIGraphicsEndImageContext function to close the graphics context after all your drawing operations.

The second method is to subclass a UIView and override the drawRect: method. OncedrawRect: When the method is called, Cocoa will create a graphics context for you. At this time, all your drawing operations on the graphics will be displayed in on this UIView. Supplement: The context obtained in the UIview's drawRect: method is the context of the layer

When is the drawRect: method called?
When the view is displayed on the screen for the first time (it is added to the UIWindow and displayed)
When the setNeedsDisplay or setNeedsDisplayInRect: of the view is called

Another method is to implement the drawing task in the drawLayer:inContext: method of the UIView subclass.

There are several points to note when determining whether a context is the current graphics context:

  • The UIGraphicsBeginImageContextWithOptions function not only creates a context suitable for graphics operations, but the context also belongs to the current context.
  • WhendrawRect method is called, the drawing context of UIView belongs to the current graphics context.
  • The context: parameter held by the callback method does not make any context the current graphics context. This parameter is simply a reference to a graphics context.
Above code:
The first method is to draw a circle:
        UIGraphicsBeginImageContextWithOptions(CGSizeMake(100,100), false, 0)
        let context = UIGraphicsGetCurrentContext();
        CGContextAddEllipseInRect(context, CGRectMake(0,0,100,100));
        CGContextSetFillColorWithColor(context, UIColor.blueColor().CGColor);
        CGContextFillPath(context);
        let image = UIGraphicsGetImageFromCurrentImageContext()
        let imageV = UIImageView(image: image)
        imageV.frame = CGRectMake(0, 0, 100, 100)
        self.view.addSubview(imageV)
        UIGraphicsEndImageContext();
The second method:
    override func drawRect(rect: CGRect) {
        let context = UIGraphicsGetCurrentContext()
        CGContextSetLineWidth(context, 2)
        CGContextAddRect(context, CGRectMake(50, 50, 50, 50))
        CGContextSetStrokeColorWithColor(context, UIColor.yellowColor().CGColor)
        CGContextDrawPath(context, CGPathDrawingMode.Stroke)
        CGContextStrokeRect(context,CGRectMake(100, 120, 10, 10))
    }
Third method:
    override func drawLayer(layer: CALayer, inContext ctx: CGContext) {
        UIGraphicsPushContext(ctx)
        let path = UIBezierPath.init(ovalInRect: CGRectMake(0, 0, 100, 100))
        UIColor.blueColor().setFill()
        path.fill()
        UIGraphicsPopContext()
    }
Note: (1). When using the first method, there must be both begin and end. UIGraphicsBeginImageContextWithOptions The meaning of the function parameters: the first parameter indicates the size of the image to be created; the second parameter is used to specify whether the background of the generated image is Opaque, as above we use YES instead of NO, then the background of the image we get will be black, which is obviously not what we want; the third parameter specifies the scaling factor of the generated image, which is the same as the scale attribute of UIImage. The meaning is consistent. Passing in 0 means that the scaling factor of the image changes according to the resolution of the screen, so the image we get will look good whether it is on a single resolution or a retina screen.
       (2). Using the third method already has a context parameter. Before using the method provided by UIKit, the context parameter must be converted into the current context. Calling the UIGraphicsPushContext function can conveniently convert the context: parameter into the current context. Remember not to forget to call the UIGraphicsPopContext function Restore the context.
       (3). In personal testing, after calling the customized View, the
drawLayer(layer: CALayer, inContext ctx: CGContext)方法后,不会再走drawRect方法。
Summary of settings for drawing graphics properties:

       The width of the line

  CGContextSetLineWidth

       The dashed style of the line

       CGContextSetLineDash

  Line cap and line connection point styles

  CGContextSetLineCap (line cap), CGContextSetLineJoin (connecting corners), CGContextSetMiterLimit

  Line color and line mode

  CGContextSetRGBStrokeColor、CGContextSetGrayStrokeColor、CGContextSetStrokeColorWithColor、CGContextSetStrokePattern

  The corresponding fill color and mode are

  CGContextSetRGBFillColor,CGContextSetGrayFillColor,CGContextSetFillColorWithColor, CGContextSetFillPattern

  Shadow

  CGContextSetShadow、CGContextSetShadowWithColor

  Mixed mode

  CGContextSetBlendMode (Determines how the graphics you are currently drawing and existing graphics are combined)

  Overall transparency

  CGContextSetAlpha (some colors also have alpha components)

  Text properties

  CGContextSelectFont、CGContextSetFont、CGContextSetFontSize、CGContextSetTextDrawingMode、CGContextSetCharacterSpacing

  Whether to turn on anti-aliasing and font smoothing

  CGContextSetShouldAntialias、CGContextSetShouldSmoothFonts

Some other property settings:

  Cropping area: Drawing outside the cropping area will not be actually drawn.

  Transformation (or "CTM", meaning Current Transformation Matrix): Changes how points in subsequent drawing commands you specify are mapped into the physical space of the canvas.

  Many of these property settings will be explained below with examples.

Path and drawing methods:

    Locate the current point

    CGContextMoveToPoint

    Draw a line

    CGContextAddLineToPoint、CGContextAddLines

    Draw a rectangle

    CGContextAddRect、CGContextAddRects

    描画一个椭圆或圆形

    CGContextAddEllipseInRect

    描画一段圆弧

    CGContextAddArcToPoint、CGContextAddArc(参数解读:1、上下文,2、x,y圆弧圆心,3、半径,4、开始弧度5、结束弧度6、方向:0或1对应顺时针、逆时针)

    通过一到两个控制点描画一段贝赛尔曲线

    CGContextAddQuadCurveToPoint(cpx、cpy:控制点 x、y:结束点)、CGContextAddCurveToPoint

    关闭当前路径

    CGContextClosePath 这将从路径的终点到起点追加一条线。如果你打算填充一段路径,那么就不需要使用该命令,因为该命令会被自动调用。

    描边或填充当前路径

    CGContextStrokePath、CGContextFillPath、CGContextEOFillPath、CGContextDrawPath对当前路径描边或填充会清除掉路径。如果你只想使用一条命令完成描边和填充任务,可以使用CGContextDrawPath命令,因为如果你只是使用CGContextStrokePath对路径描边,路径就会被清除掉,你就不能再对它进行填充了。

    创建路径并描边路径或填充路径只需一条命令就可完成的函数:CGContextStrokeLineSegments、CGContextStrokeRect、CGContextStrokeRectWithWidth、CGContextFillRect、CGContextFillRects、CGContextStrokeEllipseInRect、CGContextFillEllipseInRect。

    一段路径是被合成的,意思是它是由多条独立的路径组成。举个例子,一条单独的路径可能由两个独立的闭合形状组成:一个矩形和一个圆形。当你在构造一条路径的中间过程(意思是在描画了一条路径后没有调用描边或填充命令,或调用CGContextBeginPath函数来清除路径)调用CGContextMoveToPoint函数,就像是你拾起画笔,并将画笔移动到一个新的位置,如此来准备开始一段独立的相同路径。如果你担心当你开始描画一条路径的时候,已经存在的路径和新的路径会被认为是已存在路径的一个合成部分,你可以调用CGContextBeginPath函数指定你绘制的路径是一条独立的路径;苹果的许多例子都是这样做的,但在实际开发中我发现这是非必要的。

    CGContextClearRect函数的功能是擦除一个区域。这个函数会擦除一个矩形内的所有已存在的绘图;并对该区域执行裁剪。结果像是打了一个贯穿所有已存在绘图的孔。

    CGContextClearRect函数的行为依赖于上下文是透明还是不透明。当在图形上下文中绘图时,这会尤为明显和直观。如果图片上下文是透明的(UIGraphicsBeginImageContextWithOptions第二个参数为NO),那么CGContextClearRect函数执行擦除后的颜色为透明,反之则为黑色。

CGContextRef con = UIGraphicsGetCurrentContext();

// 绘制一个黑色的垂直黑色线,作为箭头的杆子

CGContextMoveToPoint(context, 100, 100)

CGContextAddLineToPoint(context, 100, 19)

CGContextSetLineWidth(context, 20)

CGContextStrokePath(context)

// 绘制一个红色三角形箭头

CGContextSetFillColorWithColor(context, UIColor.redColor().CGColor

CGContextMoveToPoint(context, 80, 25)

CGContextAddLineToPoint(context, 100, 0)

CGContextAddLineToPoint(context, 120, 25)

CGContextFillPath(context)

// 从箭头杆子上裁掉一个三角形,使用清除混合模式

CGContextMoveToPoint(context, 90, 101)

CGContextAddLineToPoint(context, 100, 90)

CGContextAddLineToPoint(context, 110, 101);

CGContextSetBlendMode(context, CGBlendMode.Clear);

CGContextFillPath(context);

为了以防万一,我们应该在绘图代码周围使用CGContextSaveGState CGContextRestoreGState 函数。但对于一些情况并不会有区别。

如果一段路径需要重用或共享,你可以将路径封装为CGPath(具体类型是CGPathRef)。你可以创建一个新的CGMutablePathRef对象并使用多个类似于图形的路径函数的CGPath函数构造路径,或者使用CGContextCopyPath函数复制图形上下文的当前路径。有许多CGPath函数可用于创建基于简单几何形状的路径(CGPathCreateWithRect、CGPathCreateWithEllipseInRect)或基于已存在路径(CGPathCreateCopyByStrokingPath、CGPathCreateCopyDashingPath、CGPathCreateCopyByTransformingPath)。

  UIKit的UIBezierPath类包装了CGPath。它提供了用于绘制某种形状路径的方法,以及用于描边、填充、存取某些当前上下文状态的设置方法。类似地,UIColor提供了用于设置当前上下文描边与填充的颜色。因此我们可以重写我们之前绘制箭头的代码(自己写的坐标没对o(╯□╰)o):

        let path = UIBezierPath()
        path.moveToPoint(CGPointMake(120, 120))
        path.addLineToPoint(CGPointMake(120, 200))
        path.lineWidth = 5
        path.stroke()
        UIColor.blueColor().set()
        path.removeAllPoints()
        path.moveToPoint(CGPointMake(80, 80))
        path.addLineToPoint(CGPointMake(80, 160))
        path.addLineToPoint(CGPointMake(120, 20))
        path.fill()
        path.removeAllPoints()
        path.moveToPoint(CGPointMake(90, 70))
        path.addLineToPoint(CGPointMake(90, 110))
        path.addLineToPoint(CGPointMake(110, 110))
        path.fillWithBlendMode(CGBlendMode.Clear, alpha: 1)

  在这种特殊情况下,完成同样的工作并没有节省多少代码,但是UIBezierPath仍然还是有用的。如果你需要对象特性,UIBezierPath提供了一个便利方法:bezierPathWithRoundedRect:cornerRadius:,它可用于绘制带有圆角的矩形,如果是使用Core Graphics就相当冗长乏味了。还可以只让圆角出现在左上角和右上角。

- (void)drawRect:(CGRect)rect {

  CGContextRef ctx = UIGraphicsGetCurrentContext();

  CGContextSetStrokeColorWithColor(ctx, [UIColor blackColor].CGColor);

  CGContextSetLineWidth(ctx, 3);

  UIBezierPath *path;

  path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(100, 100, 100, 100) byRoundingCorners:(UIRectCornerTopLeft |UIRectCornerTopRight) cornerRadii:CGSizeMake(10, 10)];

  [path stroke];

裁剪(有点懵 w(゚Д゚)w)

  路径的另一用处是遮蔽区域,以防对遮蔽区域进一步绘图。这种用法被称为裁剪。裁剪区域外的图形不会被绘制到。默认情况下,一个图形上下文的裁剪区域是整个图形上下文。你可在上下文中的任何地方绘图。

  总的来说,裁剪区域是上下文的一个特性。与已存在的裁剪区域相交会出现新的裁剪区域。所以如果你应用了你自己的裁剪区域,稍后将它从图形上下文中移除的做法是使用CGContextSaveGStateCGContextRestoreGState函数将代码包装起来。

    为了便于说明这一点,我使用裁剪而不是使用混合模式在箭头杆子上打孔的方法重写了生成箭头的代码。这样做有点小复杂,因为我们想要裁剪区域不在三角形内而在三角形外部。为了表明这一点,我们使用了一个三角形和一个矩形组成了一个组合路径。

    当填充一个组合路径并使用它表示一个裁剪区域时,系统遵循以下两规则之一:

  环绕规则(Winding rule)

  如果边界是顺时针绘制,那么在其内部逆时针绘制的边界所包含的内容为空。如果边界是逆时针绘制,那么在其内部顺时针绘制的边界所包含的内容为空。

  奇偶规则

  最外层的边界代表内部都有效,都要填充;之后向内第二个边界代表它的内部无效,不需填充;如此规则继续向内寻找边界线。我们的情况非常简单,所以使用奇偶规则就很容易了。这里我们使用CGContextEOCllip设置裁剪区域然后进行绘图。(如果不是很明白,可以参见这篇文章:五种方法绘制有孔的2d形状

        // 在上下文裁剪区域中挖一个三角形状的孔
        CGContextMoveToPoint(context, 90, 100)
        CGContextAddLineToPoint(context, 100, 90)
        CGContextAddLineToPoint(context, 110, 100)
        CGContextClosePath(context)
        CGContextAddRect(context, CGContextGetClipBoundingBox(context))
        // 使用奇偶规则,裁剪区域为矩形减去三角形区域
        CGContextEOClip(context)
        // 绘制垂线
        CGContextMoveToPoint(context, 100, 100)
        CGContextAddLineToPoint(context, 100, 19)
        CGContextSetLineWidth(context, 20)
        CGContextStrokePath(context)
        // 画红色箭头
        CGContextSetFillColorWithColor(context, UIColor.redColor().CGColor)
        CGContextMoveToPoint(context, 80, 25)
        CGContextAddLineToPoint(context, 100, 0)
        CGContextAddLineToPoint(context, 120, 25)
        CGContextFillPath(context)

渐变

渐变可以很简单也可以很复杂。一个简单的渐变(接下来要讨论的)由一端点的颜色与另一端点的颜色决定,如果在中间点加入颜色(可选),那么渐变会在上下文的两个点之间线性的绘制或在上下文的两个圆之间放射状的绘制。不能使用渐变作为路径的填充色,但可使用裁剪限制对路径形状的渐变。

  以下代码重写了绘制箭头的代码,箭杆使用了线性渐变。

        CGContextSaveGState(context)
        // 在上下文裁剪区域挖一个三角形孔
        CGContextMoveToPoint(context, 90, 100)
        CGContextAddLineToPoint(context, 100, 90)
        CGContextAddLineToPoint(context, 110, 100)
        CGContextClosePath(context)
        CGContextAddRect(context, CGContextGetClipBoundingBox(context))
        CGContextEOClip(context)
        //绘制一个垂线,让它的轮廓形状成为裁剪区域
        CGContextMoveToPoint(context, 100, 100)
        CGContextAddLineToPoint(context, 100, 19)
        CGContextSetLineWidth(context, 20)
        // 使用路径的描边版本替换图形上下文的路径
        CGContextReplacePathWithStrokedPath(context)
        // 对路径的描边版本实施裁剪
        CGContextClip(context)
        // 绘制渐变
        let locations:[CGFloat] = [ 0.0, 0.5, 1.0 ]
        let colors:[CGFloat] = [
            0.3,0.3,0.3,0.8, // 开始颜色,透明灰
            0.0,0.0,0.0,1.0, // 中间颜色,黑色
            0.3,0.3,0.3,0.8 // 末尾颜色,透明灰
        ]
        let colorSpace = CGColorSpaceCreateDeviceGray()
        let gradient = CGGradientCreateWithColorComponents(colorSpace,colors, locations, 3)
        CGContextDrawLinearGradient(context, gradient, CGPointMake(89,0), CGPointMake(111,0), .DrawsAfterEndLocation)
        CGContextRestoreGState(context) // 完成裁剪
        // 绘制红色箭头
        CGContextSetFillColorWithColor(context, UIColor.redColor().CGColor)
        CGContextMoveToPoint(context, 80, 25)
        CGContextAddLineToPoint(context, 100, 0)
        CGContextAddLineToPoint(context, 120, 25)
        CGContextFillPath(context)

待续。。。
绘制文字:




Guess you like

Origin blog.csdn.net/qizd0802/article/details/51003016