iOS 繪圖學(xué)習(xí)實(shí)戰(zhàn)(一) -- 繪圖基礎(chǔ)

1. 使用Quartz的API 創(chuàng)建Context,并且繪圖

完全使用Quartz創(chuàng)建Context, 并且使用Quartz繪圖的API, 此時(shí), context坐標(biāo)系是left-low原點(diǎn), 并且Quartz繪制方法認(rèn)為圖片的坐標(biāo)系也是LLO.

/**
 CoreGraphic API -> LLO坐標(biāo)系

 CGBitmapContextCreate -> 創(chuàng)建 bitmap Context
 CGBitmapContextCreateImage -> 獲取 bitmap Image CGImageRef -> 轉(zhuǎn)化成UIImage

 用 CoreGraphic CGContextDrawImage    繪圖
 */
-(void)draw1{
    // 1. 創(chuàng)建顏色空間
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    float width = self.bounds.size.width;
    float height = self.bounds.size.height;
    int bitsPerComponent = 8;
    //RGBA(的bytes) * bitsPerComponent *width
    int bytesPerRow = 4 * 8 * bitsPerComponent * width;
    // 2. 創(chuàng)建bitmapContext, 使用Quartz創(chuàng)建的Context,因此坐標(biāo)系是LLO, 坐標(biāo)原點(diǎn)是左下角
    CGContextRef context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast|kCGBitmapByteOrderDefault);

    // 3. 繪制背景
    CGContextFillRect(context, self.bounds);
    CGContextSetRGBFillColor(context, 1, 0, 0, 1);
    CGContextSetRGBStrokeColor(context, 0, 1, 0, 1);
    CGContextFillRect(context, CGRectMake(0, 0, 400, 400));
    CGContextStrokeRect(context, CGRectMake(0, 0, width, height));

    // 4. 獲取UIKit圖片, 并且使用Quartz繪制圖片. 注意使用Quartz相關(guān)的api繪制圖片時(shí),只能傳入U(xiǎn)IImage的底層CGImage.
    //    并且此時(shí)由于繪制圖片API `CGContextDrawImage` 是Quartz API, 因此繪制方法表示圖片的坐標(biāo)原點(diǎn)也是LLO
    UIImage *img=[UIImage imageNamed:@"1.jpg"];
    CGContextDrawImage(context, CGRectMake(0, 0, 100, 100), img.CGImage);

    // 5. 從context中獲取CGImage, 并創(chuàng)建UIImage
    CGImageRef cgimg = CGBitmapContextCreateImage(context);
    UIImage *resultImg = [UIImage imageWithCGImage:cgimg];

    // 6. 清理CoreGraphic 資源
    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);
    CGImageRelease(cgimg);

    self.imageView.image = resultImg;
}

繪制結(jié)果

image.png

2. 使用UIKit的API 創(chuàng)建Context, 并且繪圖

UIKit繪圖時(shí), 通過UIKit的API創(chuàng)建Context, 此時(shí)Context的坐標(biāo)原點(diǎn)是ULO, UIKit繪圖的API認(rèn)為圖像的原點(diǎn)也是ULO. 值得注意的是,與Quartz API不同的是UIKit會(huì)維護(hù)一個(gè)Context Stack, 用于記錄當(dāng)前UIKit中各種狀態(tài)(填充顏色 setFill, 繪線顏色setStroke等等)是作用于哪個(gè)Context的.

/**
 UIKit API -> ULO 原點(diǎn)

 UIGraphicsBeginImageContextWithOptions -> 創(chuàng)建bitmap Context, 并push 入 stack
 UIGraphicsGetImageFromCurrentImageContext -> 獲取 bitmap

 */
-(void)draw2 {
    // 1. 創(chuàng)建 context, 并且將context push到UIKit維護(hù)的Context Stack
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0);
    // 2. 創(chuàng)建Path, 繪制背景和填充顏色
    UIBezierPath *rect = [UIBezierPath bezierPathWithRect:self.bounds];
    [[UIColor redColor] setFill]; // 作用于Context Stack 棧頂stack
    [rect fill];
    [[UIColor greenColor] setStroke]; // 作用于Context Stack 棧頂
    [rect stroke];

    // 3. 使用 UIKit 的drawing method,因此需要坐標(biāo)系是 ULO, 這里使用 UIKit方法創(chuàng)建, 因此繪制的文字都是正的. 坐標(biāo)原點(diǎn)是ULO
    NSString *text= @"文字";
    UIFont *font=[UIFont systemFontOfSize:14];
    [text drawAtPoint:CGPointMake(100, 100) withAttributes:font.fontDescriptor.fontAttributes];

    // 4. 用UIKit 繪制圖像, context是UIKit創(chuàng)建的, 因此是ULO坐標(biāo)系, (0,0,100,100)在左上角,并且圖片是正向
    UIImage *img=[UIImage imageNamed:@"1.jpg"];
    [img drawInRect:CGRectMake(0, 0, 100, 100)];

    // 5. 從當(dāng)前context獲取UIImage
    UIImage *resultImg = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    self.imageView.image = resultImg;
}
image.png

3-1 使用Quartz創(chuàng)建Context, 使用UIKit繪圖

使用Quartz創(chuàng)建的Context坐標(biāo)系是LLO, 而使用UIKit的繪圖API(繪制文字, 繪制圖像) 依賴的坐標(biāo)系是ULO. 隱藏圖像和文字會(huì)倒置.

/**
 CoreGraphic 創(chuàng)建 Context  -> LLO

 繪圖使用 UIKit 繪制
 */
-(void)draw3_1 {
    // 1. 創(chuàng)建CGContext使用的顏色空間
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    float width = self.bounds.size.width;
    float height = self.bounds.size.height;
    int bitsPerComponent = 8;
    //RGBA*8*width
    int bytesPerRow = 4 * 8 * bitsPerComponent * width;
    // 2. 使用Quartz API創(chuàng)建CGContext, 坐標(biāo)系是 LLO
    CGContextRef context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast|kCGBitmapByteOrderDefault);

    // 3. UIKit API的繪圖方法. 需要首先將當(dāng)前context push到 當(dāng)前UIKit維護(hù)的context stack中!!!!!(此時(shí)繪圖方法才知道繪制到哪個(gè)context)
    UIGraphicsPushContext(context);

    // 4. 填充顏色, 配置部分狀態(tài)
    CGContextFillRect(context, self.bounds);
    CGContextSetRGBFillColor(context, 1, 0, 0, 1);
    CGContextSetRGBStrokeColor(context, 0, 1, 0, 1);
    CGContextFillRect(context, CGRectMake(0, 0, 400, 400));
    CGContextStrokeRect(context, CGRectMake(0, 0, width, height));
    [[UIColor redColor] setFill];

    // 5. 使用UIKit方法繪制文字.
    NSString *text= @"文字";
    UIFont *font=[UIFont systemFontOfSize:14];
    [text drawAtPoint:CGPointMake(100, 100) withAttributes:font.fontDescriptor.fontAttributes];

    // 6. 使用UIKit方法繪制圖像
    UIImage *img=[UIImage imageNamed:@"1.jpg"];
    [img drawInRect:CGRectMake(0, 0, 100, 100)];

    // 7. 從當(dāng)前context中獲取UIImage
    CGImageRef cgimg = CGBitmapContextCreateImage(context);
    UIImage *resultImg = [UIImage imageWithCGImage:cgimg];
    UIGraphicsPopContext();

    // 8. 清理資源
    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);
    CGImageRelease(cgimg);

    self.imageView.image = resultImg;
}
3-1.png

3-2 使用Quartz創(chuàng)建Context, 使用UIKit繪圖(修正坐標(biāo)系)

3-1中由于Quartz創(chuàng)建的Context坐標(biāo)系是LLO, 而UIKit繪制坐標(biāo)系要求ULO, 只要兩者不匹配. 因此在繪制之前要保證. 當(dāng)前Context的坐標(biāo)系與繪制方法依賴的坐標(biāo)系相同. 因此在調(diào)用UIKit時(shí),需要將Context坐標(biāo)系轉(zhuǎn)換成ULO, 繪制完成以后. 最好將該坐標(biāo)系恢復(fù)原始坐標(biāo)系.


-(void)draw3_2 {
    // 1. 創(chuàng)建CGContext使用的顏色空間
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    float width = self.bounds.size.width;
    float height = self.bounds.size.height;
    int bitsPerComponent = 8;
    //RGBA*8*width
    int bytesPerRow = 4 * 8 * bitsPerComponent * width;
    // 2. 使用Quartz API創(chuàng)建CGContext, 坐標(biāo)系是 LLO
    CGContextRef context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast|kCGBitmapByteOrderDefault);

    // 3. UIKit API的繪圖方法. 需要首先將當(dāng)前context push到 當(dāng)前UIKit維護(hù)的context stack中!!!!!(此時(shí)繪圖方法才知道繪制到哪個(gè)context)
    UIGraphicsPushContext(context);

    // 4. 填充顏色, 配置部分狀態(tài)
    CGContextFillRect(context, self.bounds);
    CGContextSetRGBFillColor(context, 1, 0, 0, 1);
    CGContextSetRGBStrokeColor(context, 0, 1, 0, 1);
    CGContextFillRect(context, CGRectMake(0, 0, 400, 400));
    CGContextStrokeRect(context, CGRectMake(0, 0, width, height));
    [[UIColor redColor] setFill];

    // 5. 在使用UIKit繪制方法進(jìn)行繪制之前,先將當(dāng)前context的坐標(biāo)系調(diào)整成ULO, 具體步驟如下: 翻轉(zhuǎn)畫布 -> 先將畫布向上移動(dòng) height, 然后將沿著y翻轉(zhuǎn)坐標(biāo)軸
    CGContextTranslateCTM(context, 0, height);
    CGContextScaleCTM(context, 1.0, -1.0);

    // 6.1 使用UIKit方法繪制文字, 由于context的坐標(biāo)系統(tǒng)已經(jīng)是ULO.此時(shí)繪制的內(nèi)容是通過ULO為坐標(biāo)系的
    NSString *text= @"文字1";
    UIFont *font=[UIFont systemFontOfSize:14];
    [text drawAtPoint:CGPointMake(100, 100) withAttributes:font.fontDescriptor.fontAttributes];

    // 6.2. 使用UIKit方法繪制圖像 ,同上
    UIImage *img=[UIImage imageNamed:@"1.jpg"];
    [img drawInRect:CGRectMake(0, 0, 100, 100)];

    // 6.3 還原之前的context的坐標(biāo)系, 轉(zhuǎn)化成 LLO
    CGContextTranslateCTM(context, 0, height);
    CGContextScaleCTM(context, 1.0, -1.0);

    // 6.4 用UIKit 繪制文字和圖片. 此時(shí)看的出來 坐標(biāo)系是LLO, 圖片和蚊子位置正確. 具體表現(xiàn)同 3-1
    text= @"文字2";
    [text drawAtPoint:CGPointMake(100, 100) withAttributes:font.fontDescriptor.fontAttributes];
    [img drawInRect:CGRectMake(0, 0, 100, 100)];

    // 7. 從當(dāng)前context中獲取UIImage
    CGImageRef cgimg = CGBitmapContextCreateImage(context);
    UIImage *resultImg = [UIImage imageWithCGImage:cgimg];
    UIGraphicsPopContext();

    // 8. 清理資源
    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);
    CGImageRelease(cgimg);

    self.imageView.image = resultImg;
}
3-2.png

4-1 使用UIKit創(chuàng)建Context, 使用Quartz 繪制API

-(void)draw4_1{

    // 1. UIKit方法會(huì)創(chuàng)建Context, 并且將該Context push到UIKit維護(hù)的Context stack, 并且UIKit會(huì)自動(dòng)將Context的坐標(biāo)調(diào)整為ULO,
    /*
     UIGraphicsBeginImageContextWithOptions() 相當(dāng)于 Quartz 以下API:
        1. CGBitmapContextCreate -> 創(chuàng)建 CGContext
        2. UIGraphicsPushContext(context) -> 將context push進(jìn)入U(xiǎn)IKit的 context stack
        3. CGContextTranslateCTM(context, 0, height); CGContextScaleCTM(context, 1.0, -1.0);  -> 調(diào)整context的坐標(biāo)系為ULO
     */
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0);

    // 2. UIKit 的相關(guān)方法填充背景. 繪制邊界
    UIBezierPath *rect = [UIBezierPath bezierPathWithRect:self.bounds];
    [[UIColor redColor] setFill];
    [rect fill];
    [[UIColor greenColor] setStroke];
    [rect stroke];

    // 3. UIKit通過`UIGraphicsGetCurrentContext`獲取 CGContext 對象(Quartz API使用)
    CGContextRef context = UIGraphicsGetCurrentContext();

    // 4. Quartz 的繪制方法, 要求Context坐標(biāo)系是LLO, 此時(shí)和當(dāng)前context不匹配. 繪制是倒置的圖像
    UIImage *img = [UIImage imageNamed:@"1.jpg"];
    CGContextDrawImage(context, CGRectMake(0, 0, 100, 100), img.CGImage);

    UIImage *resultImg = UIGraphicsGetImageFromCurrentImageContext();

  // 5 清理CGContext
    UIGraphicsEndImageContext();

    self.imageView.image = resultImg;
}
4-1.png

4-2 使用UIKit創(chuàng)建Context, 使用Quartz 繪制API(坐標(biāo)系校正)

-(void)draw4_2{

    // 1. UIKit方法會(huì)創(chuàng)建Context, 并且將該Context push到UIKit維護(hù)的Context stack, 并且UIKit會(huì)自動(dòng)將Context的坐標(biāo)調(diào)整為ULO,
    /*
     UIGraphicsBeginImageContextWithOptions() 相當(dāng)于 Quartz 以下API:
     1. CGBitmapContextCreate -> 創(chuàng)建 CGContext
     2. UIGraphicsPushContext(context) -> 將context push進(jìn)入U(xiǎn)IKit的 context stack
     3. CGContextTranslateCTM(context, 0, height); CGContextScaleCTM(context, 1.0, -1.0);  -> 調(diào)整context的坐標(biāo)系為ULO
     */
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0);

    // 2. UIKit 的相關(guān)方法填充背景. 繪制邊界
    UIBezierPath *rect = [UIBezierPath bezierPathWithRect:self.bounds];
    [[UIColor redColor] setFill];
    [rect fill];
    [[UIColor greenColor] setStroke];
    [rect stroke];

    // 3. UIKit通過`UIGraphicsGetCurrentContext`獲取 CGContext 對象(Quartz API使用)
    CGContextRef context = UIGraphicsGetCurrentContext();


    // 4. 在調(diào)用Quartz方法繪制之前,需要將 坐標(biāo)系滿足 LLO
    CGContextTranslateCTM(context, 0, self.bounds.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);

    // 5.1 具體的Quartz 繪制方法, 此時(shí)繪制的圖像位于左下角
    // CGContextDrawXXXX 方法 -> 會(huì)以 Context 的LLO為坐標(biāo)系
    UIImage *img=[UIImage imageNamed:@"1.jpg"];
    CGContextDrawImage(context, CGRectMake(0, 0, 100, 100), img.CGImage);


    // 5.2 將context坐標(biāo)重新轉(zhuǎn)化成ULO, 然后繼續(xù)繪制. 繪制的圖像位于左上角
    CGContextTranslateCTM(context, 0, self.bounds.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
    [img drawInRect:CGRectMake(0, 0, 100, 100)];

    UIImage *resultImg = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    self.imageView.image = resultImg;
}
4-2.png

5. drawRect: 或者 drawLayer:inContext中繪圖

這里使用自定義view的drawRect舉例,

/**
 drawRect 會(huì)幫我們做如下事情(類似于UIGraphicsBeginImageContextWithOptions,但產(chǎn)生的context并非 bitmapContext):
    1. 創(chuàng)建一個(gè)context(注意這個(gè)context 并非 BitmapContext, 也就是說繪制的目的不是得到繪制圖片, 而是直接渲染在view底層的layer 的content上)
    2. 會(huì)隱士調(diào)用UIGraphicsPushContext(context), 將這個(gè)創(chuàng)建的 context push 進(jìn)入U(xiǎn)IKit context stack
    3. 自動(dòng)幫我們調(diào)整context坐標(biāo)系是ULO

 */
- (void)drawRect:(CGRect)rect {
    // 1. 直接通過UIKit API 獲取當(dāng)前的繪制所在的context
    CGContextRef context = UIGraphicsGetCurrentContext();

    // 2. 設(shè)置背景等等
    CGContextSetRGBFillColor(context, 1, 0, 0, 1);
    CGContextFillRect(context, rect);

    // 3. 使用UIKit API 進(jìn)行繪制
    UIImage *img = [UIImage imageNamed:@"1.jpg"];
    [img drawInRect:CGRectMake(0, 0, 100, 100)];
    NSString *text = @"文字";
    UIFont *font = [UIFont systemFontOfSize:14];
    [text drawAtPoint:CGPointMake(100, 100) withAttributes:font.fontDescriptor.fontAttributes];

    // ps: 無需清理
}
image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容