1.矩陣操作
1.1.平移
- (void)drawRect:(CGRect)rect {
//1.獲得上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//2.矩陣操作
//X軸偏移量,Y軸偏移量
CGContextTranslateCTM(ctx, 100, 100);
//3.繪制圖形,并添加到上下文
//繪制圓
CGContextAddArc(ctx, rect.size.width*0.5, rect.size.height*0.5, 100, 0, M_PI*2, 1);
//繪制直線
CGContextMoveToPoint(ctx, 0, 0);
CGContextAddLineToPoint(ctx, rect.size.width, rect.size.height);
//設(shè)置屬性
CGContextSetLineWidth(ctx, 10);
//4.渲染
CGContextStrokePath(ctx);
}
1.2.旋轉(zhuǎn)
#define angle2Arc(angle) (angle * M_PI / 180)
- (void)drawRect:(CGRect)rect {
//1.獲得上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//2.矩陣操作
//旋轉(zhuǎn)弧度
CGContextRotateCTM(ctx, angle2Arc(10));
//3.繪制圖形,并添加到上下文
//繪制圓
CGContextAddArc(ctx, rect.size.width*0.5, rect.size.height*0.5, 100, 0, M_PI*2, 1);
//繪制直線
CGContextMoveToPoint(ctx, 0, 0);
CGContextAddLineToPoint(ctx, rect.size.width, rect.size.height);
//設(shè)置屬性
CGContextSetLineWidth(ctx, 10);
//4.渲染
CGContextStrokePath(ctx);
}
1.3.縮放
- (void)drawRect:(CGRect)rect {
//1.獲得上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//2.矩陣操作
//X軸縮放比例,Y軸縮放比例
CGContextScaleCTM(ctx, 0.5, 0.5);
//3.繪制圖形,并添加到上下文
//繪制圓
CGContextAddArc(ctx, rect.size.width*0.5, rect.size.height*0.5, 100, 0, M_PI*2, 1);
//繪制直線
CGContextMoveToPoint(ctx, 0, 0);
CGContextAddLineToPoint(ctx, rect.size.width, rect.size.height);
//設(shè)置屬性
CGContextSetLineWidth(ctx, 10);
//4.渲染
CGContextStrokePath(ctx);
}
1.4.注意
1.一定要先做矩陣操作,然后再繪制圖形.(也就是矩陣操作在添加路徑之前有效)
2.只會(huì)對矩陣操作之后并且在渲染之前繪制的圖形有效.
2.圖形上下文棧
2.1.通過繪圖原理來理解圖形上下文棧
當(dāng)我們把上下文中的圖形渲染到UIView上,其實(shí)就是把上下文中的圖形搬到了UIView中,
上下文中沒有了圖形,但是上下文還在,上下文中的圖形狀態(tài)(顏色,線寬,樣式)還在.
那我們還是可以繼續(xù)畫線的.
那圖形上下文棧呢用來干什么呢劫流?
其實(shí)就是我們想在操作圖形上下文中之前,先把原始的圖形上下文備份一下.
然后再操作,再渲染后,再把圖形上下文恢復(fù),恢復(fù)之后繼續(xù)繪圖就可以了.
2.2.棧的特性
先進(jìn)后出
2.3.圖形上下文棧操作
//入棧
void CGContextSaveGState(CGContextRef c)
1.圖形上下文棧中保存的僅僅是當(dāng)前的繪圖狀態(tài)信息(顏色,線寬,樣式,矩陣).
2.跟上下文中的路徑?jīng)]有關(guān)系
//出棧
CGContextRestoreGState(ctx);
1.將棧頂?shù)睦L圖狀態(tài)取出,替換掉當(dāng)前的圖形上下文中的繪圖狀態(tài).
(也就是說出棧操作就是在設(shè)置繪圖狀態(tài)而已)
2.恢復(fù)的位置在渲染之前都是可以的.
2.4.解析代碼
- (void)drawRect:(CGRect)rect {
//1.獲得上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//備份-入棧
CGContextSaveGState(ctx);
//3.繪制圖形,并添加到上下文
//繪制直線
CGContextMoveToPoint(ctx, 0, 0);
CGContextAddLineToPoint(ctx, rect.size.width, rect.size.height);
//設(shè)置屬性
CGContextSetLineWidth(ctx, 10);
[[UIColor redColor] set];
//4.渲染
CGContextStrokePath(ctx);
//繪制圓
CGContextAddArc(ctx, rect.size.width*0.5, rect.size.height*0.5, 100, 0, M_PI*2, 1);
//恢復(fù)-出棧
CGContextRestoreGState(ctx);
//渲染
CGContextStrokePath(ctx);
}
解析:
1.獲得上下文就是準(zhǔn)備了草稿紙
2.入棧:把當(dāng)前上下文的狀態(tài)拷貝一份保存到棧中
3.添加路徑:往草稿紙中繪制路徑
4.設(shè)置屬性:修改當(dāng)前上下文的狀態(tài)信息
5.渲染:把上下文中的路徑在設(shè)置狀態(tài)后搬到UIView中顯示.
6.再往上下文中繪制圓
7.出棧:把當(dāng)前上下文中的狀態(tài)替換成出棧的屬性,并且刪除棧頂提出的狀態(tài).
8.渲染:繼續(xù)把上下文中的路徑在設(shè)置狀態(tài)后搬到UIView中顯示.
3.Quartz2D內(nèi)存管理
3.1.CGMutablePathRef路徑方式問題
這段代碼是存在內(nèi)存泄露的問題昼汗。
- (void)drawRect:(CGRect)rect {
//1.獲得上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//2.繪制路徑
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, 100, 100);
CGPathAddLineToPoint(path, NULL, 200, 200);
//3將路徑添加到上下文中
CGContextAddPath(ctx, path);
//4.渲染
CGContextStrokePath(ctx);
}
3.2.C語言內(nèi)存分析
3.2.1分析
Xcode --> Product --> Analyze
3.2.2查看問題
3.3.內(nèi)存泄露解決方式
在代碼中如果有用到C語言的方法獲得一個(gè)對象。例如使用方法中帶有 create,retain,copy這些詞的方法獲得的對象糠惫,我們一定要在不使用這個(gè)對象后進(jìn)行release操作实撒。
解決上邊問題的方式
- (void)drawRect:(CGRect)rect {
//1.獲得上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//2.繪制路徑
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, 100, 100);
CGPathAddLineToPoint(path, NULL, 200, 200);
//3將路徑添加到上下文中
CGContextAddPath(ctx, path);
//4.渲染
CGContextStrokePath(ctx);
//5.釋放內(nèi)存
CGPathRelease(path);//第一種方式
// CFRelease(path);//第二種方式
}
解析:
1.CGXxxCreate 對應(yīng)就有 CGXxxRelease
2.通過CFRelease(任何類型),可以釋放任何類型.
3.4.Analyze其它功能
可以分析出項(xiàng)目中類的生命周期的super調(diào)用的漏寫姊途。
例如:
4.繪制文字
4.1繪制文字
// 從某個(gè)點(diǎn)開始繪制,并且只繪制一行,超出區(qū)域不顯示
- (void)drawRect:(CGRect)rect {
//1.創(chuàng)建文字
NSString *str = @"傳智播客";
//2.繪制文字
//point:從哪個(gè)點(diǎn)開始繪制
//attribute:文字屬性知态,可以傳nil(就是默認(rèn)的屬性)
[str drawAtPoint:CGPointZero withAttributes:nil];
}
//在某個(gè)區(qū)域內(nèi)繪制,會(huì)換行,超出區(qū)域不顯示
- (void)drawRect:(CGRect)rect {
//1.創(chuàng)建文字
NSString *str = @"傳智播客";
//2.繪制文字
//rect:在哪個(gè)區(qū)域內(nèi)繪制
//attribute:文字屬性捷兰,可以傳nil(就是默認(rèn)的屬性)
[str drawInRect:CGRectMake(100, 100, 40, 60) withAttributes:nil];
}
4.2文字屬性
文字屬性在 UIKit 框架的 NSAttributedString.h 中
NSFontAttributeName 字體
NSParagraphStyleAttributeName 設(shè)置段落樣式
NSForegroundColorAttributeName 字體顏色
NSBackgroundColorAttributeName 背景色
NSLigatureAttributeName
NSKernAttributeName 調(diào)整字句 kerning 字句調(diào)整
NSStrikethroughStyleAttributeName 刪除線
NSUnderlineStyleAttributeName 下劃線
NSStrokeColorAttributeName 設(shè)置文字描邊顏色,需要和NSStrokeWidthAttributeName設(shè)置描邊寬度负敏,這樣就能使文字空心.
NSStrokeWidthAttributeName 設(shè)置描邊寬度
NSShadowAttributeName 設(shè)置陰影贡茅,單獨(dú)設(shè)置不好使,必須和其他屬性搭配才好使。和這三個(gè)任一個(gè)都好使顶考,NSVerticalGlyphFormAttributeName彤叉,NSObliquenessAttributeName,NSExpansionAttributeName
NSTextEffectAttributeName
NSAttachmentAttributeName
NSLinkAttributeName
NSBaselineOffsetAttributeName
NSUnderlineColorAttributeName 下劃線顏色
NSStrikethroughColorAttributeName 刪除線顏色
NSObliquenessAttributeName 設(shè)置字體傾斜
NSExpansionAttributeName 設(shè)置文本扁平化
NSWritingDirectionAttributeName
NSVerticalGlyphFormAttributeName 該屬性所對應(yīng)的值是一個(gè) NSNumber 對象(整數(shù))村怪。0 表示橫排文本秽浇。1 表示豎排文本。在 iOS 中甚负,總是使用橫排文本柬焕,0 以外的值都未定義。
5.繪制圖片
5.1繪制圖片-OC方式
//從某個(gè)點(diǎn)開始繪制-圖片會(huì)按照原比例繪制
-(void)drawImage{
//1.創(chuàng)建圖片
UIImage *image = [UIImage imageNamed:@"hero"];
//2.繪制圖片
[image drawAtPoint:CGPointZero];
}
//在某個(gè)區(qū)域內(nèi)進(jìn)行繪制-圖片會(huì)被拉伸或者壓縮
-(void)drawImage:(CGRect)rect{
//1.創(chuàng)建圖片
UIImage *image = [UIImage imageNamed:@"hero"];
//2.繪制圖片
[image drawInRect:rect];
}
//會(huì)平鋪在某個(gè)區(qū)域內(nèi).
-(void)drawImage:(CGRect)rect{
//1.創(chuàng)建圖片
UIImage *image = [UIImage imageNamed:@"hero"];
//2.繪制圖片
[image drawAsPatternInRect:rect];
}
-(void)drawImage:(CGRect)rect{
//1.創(chuàng)建圖片
UIImage *image = [UIImage imageNamed:@"hero"];
//注意: blendMode 是繪制圖片的參數(shù)
//2.繪制圖片--從某個(gè)點(diǎn)開始繪制--原比例繪制
[image drawAtPoint:CGPointZero blendMode:kCGBlendModeNormal alpha:0.3];
//2.繪制圖片--在某塊區(qū)域內(nèi)進(jìn)行繪制--圖片會(huì)被拉伸或者壓縮
// [image drawInRect:rect blendMode:kCGBlendModeNormal alpha:0.3];
}
5.2圖片屬性
kCGBlendModeNormal, 正常梭域;也是默認(rèn)的模式斑举。前景圖會(huì)覆蓋背景圖
kCGBlendModeMultiply, 正片疊底;混合了前景和背景的顏色病涨,最終顏色比原先的都暗
kCGBlendModeScreen, 濾色富玷;把前景和背景圖的顏色先反過來,然后混合
kCGBlendModeOverlay, 覆蓋既穆;能保留灰度信息赎懦,結(jié)合kCGBlendModeSaturation能保留透明度信息,在imageWithBlendMode方法中兩次執(zhí)行drawInRect方法實(shí)現(xiàn)我們基本需求
kCGBlendModeDarken, 變暗
kCGBlendModeLighten, 變亮
kCGBlendModeColorDodge, 顏色變淡
kCGBlendModeColorBurn, 顏色加深
kCGBlendModeSoftLight, 柔光
kCGBlendModeHardLight, 強(qiáng)光
kCGBlendModeDifference, 插值
kCGBlendModeExclusion, 排除
kCGBlendModeHue, 色調(diào)
kCGBlendModeSaturation, 飽和度
kCGBlendModeColor, 顏色
kCGBlendModeLuminosity, 亮度
//Apple額外定義的枚舉
//R: premultiplied result, 表示混合結(jié)果
//S: Source, 表示源顏色(Sa對應(yīng)透明度值: 0.0-1.0)
//D: destination colors with alpha, 表示帶透明度的目標(biāo)顏色(Da對應(yīng)透明度值: 0.0-1.0)
kCGBlendModeClear, R = 0
kCGBlendModeCopy, R = S
kCGBlendModeSourceIn, R = S*Da
kCGBlendModeSourceOut, R = S*(1 - Da)
kCGBlendModeSourceAtop, R = S*Da + D*(1 - Sa)
kCGBlendModeDestinationOver, R = S*(1 - Da) + D
kCGBlendModeDestinationIn, R = D*Sa幻工;能保留透明度信息
kCGBlendModeDestinationOut, R = D*(1 - Sa)
kCGBlendModeDestinationAtop, R = S*(1 - Da) + D*Sa
kCGBlendModeXOR, R = S*(1 - Da) + D*(1 - Sa)
kCGBlendModePlusDarker, R = MAX(0, (1 - D) + (1 - S))
kCGBlendModePlusLighter R = MIN(1, S + D)(最后一種混合模式)
5.3繪制圖片--C方式
注意:C的方式繪圖會(huì)按照Quartz2D的坐標(biāo)系來繪制.所以顯示的圖片都是反向的励两。
-(void)drawImageWithC:(CGRect)rect{
//1.獲得上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//2.加載圖片
UIImage *image = [UIImage imageNamed:@"dog"];
//3.繪制圖片
// 在指定的區(qū)域繪制圖片
// 以填充方式繪制圖片-圖片會(huì)被拉伸或者壓縮
CGContextDrawImage(ctx, rect, image.CGImage);
}
-(void)drawImageWithC:(CGRect)rect{
//1.獲得上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//2.加載圖片
UIImage *image = [UIImage imageNamed:@"dog"];
//3.繪制圖片
// 以平鋪方式繪圖
//注意:這個(gè)rect 是設(shè)置圖片的大小,我們一般給原比例就可以了
CGContextDrawTiledImage(ctx, CGRectMake(0, 0, 100, 100), image.CGImage);
}
6.模擬ImageView
6.1.系統(tǒng)UIImageView的init 和 initWithImage的區(qū)別
init 方法創(chuàng)建的對象默認(rèn)沒有大小
initWithImage 方法創(chuàng)建的對象默認(rèn)有大小
6.2.模擬ImageView
1.自定義一個(gè)UIView,起名CZImageView
2.頭文件中聲明image屬性
@property(nonatomic,strong)UIImage *image;
3.將傳入的圖片繪制到設(shè)備上
- (void)drawRect:(CGRect)rect{
//將圖片繪制到屏幕上
[self.image drawInRect:rect];
}
4.重寫image的set方法,當(dāng)傳入圖片時(shí)重繪界面
- (void)setImage:(UIImage *)image{
_image = image;
//重繪界面
[self setNeedsDisplay];
}
5.自定義 initWithImage 方法
- (instancetype)initWithImage:(UIImage *)image{
self = [super initWithFrame:CGRectMake(0, 0, image.size.width, image.size.height)];
if (self) {
self.image = image;
}
return self;
}
7.裁剪圖片
7.1.裁剪上下文顯示區(qū)域
- (void)drawRect:(CGRect)rect{
//1.獲得圖片對象
UIImage *image = [UIImage imageNamed:@"me"];
//2.獲得上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//3.繪制上下文顯示區(qū)域
CGContextAddArc(ctx, rect.size.width*0.5, rect.size.height*0.5, 150, 0, M_PI*2, 1);
//4.裁剪
CGContextClip(ctx);
//5.繪制圖片
[image drawInRect:rect];
}
7.2注意:
裁剪的是上下文的顯示區(qū)域,不是說裁剪的上下文,上下文該是多大就還是多大,就是哪一塊需要顯示改變了.
8.圖片類型上下文
8.1基本使用
1.需求
繪制一個(gè)UIimage圖片
2.使用方式
- (void)imageContext{
//1.開啟一個(gè)圖片類型的上下文
UIGraphicsBeginImageContext(CGSizeMake(300, 300));
/*
開啟一個(gè)圖片類型的上下文
size : 上下文大小 -- 像素
opaque : 不透明(上下文這張草稿紙是否透明) YES表示不透明 NO表示透明
scale : 縮放 會(huì)縮放上下文 (重要參數(shù)) -- 可以根據(jù)屏幕像素比例生成想要的圖片 0表示當(dāng)前設(shè)置(方法內(nèi)部做了判斷)
*/
// UIGraphicsBeginImageContextWithOptions(CGSizeMake(300, 300), YES, 1);
//2.獲得當(dāng)前的圖片類型上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//3.繪制圖形
CGContextMoveToPoint(ctx, 0, 0);
CGContextAddLineToPoint(ctx, 300, 300);
//4.渲染
CGContextStrokePath(ctx);
//5.獲得圖片對象
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
//6.關(guān)閉上下文
UIGraphicsEndImageContext();
//使用我們繪制圖片
self.imageView.image = newImage;
}
8.2把圖片存入沙盒
- (void)writeToFile:(UIImage *)image{
//1.創(chuàng)建路徑
NSString *filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
filePath = [filePath stringByAppendingPathComponent:@"xx.png"];
//2.把image保存為png圖片
NSData *pngdata = UIImagePNGRepresentation(image);
/*
把image保存為jpeg圖片
compressionQuality是質(zhì)量,jpeg圖片是可以壓縮的
*/
// NSData *jpegdata = UIImageJPEGRepresentation(image, 1);
//3.通過NSData寫入沙盒
[pngdata writeToFile:filePath atomically:YES];
}
8.2裁剪圖片
- (UIImage *)imageContext:(UIImage *)image{
//1.開啟一個(gè)圖片類型的上下文
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
//2.獲得當(dāng)前的圖片類型上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//3.繪制裁剪圖形
CGContextAddArc(ctx, image.size.width*0.5, image.size.height*0.5, image.size.width*0.5, 0, M_PI*2, 1);
//4.裁剪上下文
CGContextClip(ctx);
//5.渲染圖片
[image drawAtPoint:CGPointZero];
//6.獲得圖片對象
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
//7.關(guān)閉上下文
UIGraphicsEndImageContext();
//8.返回裁剪后的圖片
return newImage;
}
8把圖片保到相冊
8.1使用
- (void)imageWriteToFile:(UIImage *)image{
/*
image : 要保存的圖片
completionTarget : 監(jiān)聽對象
completionSelector : 監(jiān)聽回調(diào)方法
contextInfo : 上下文
*/
UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
}
/*
保存到相冊的回調(diào)
image : 保存的圖片
error : 保存信息
contextInfo : 上下文 - 上邊傳什么這里就返回什么
*/
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo{
if(error){
NSLog(@"保存失敗");
}else{
NSLog(@"保存成功");
}
}
8.2訪問相冊權(quán)限問題
1彈出
2如果選擇了NO,如何修改權(quán)限