#import "HMView.h"
@implementation HMView
- (void)drawRect:(CGRect)rect
{
// 1.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 矩阵操作
// Current graphics state's Transformation Matrix
// 旋转
// CGContextRotateCTM(ctx, M_PI_4);
// 缩放
// CGContextScaleCTM(ctx, 1, 0.5);
// 平移
// CGContextTranslateCTM(ctx, 150, 150);
// 2.拼接路径 同时 把路径添加到上下文当中
// CGContextAddArc(ctx, 150, 150, 100, 0, 2 * M_PI, 1);
// CGContextMoveToPoint(ctx, 0, 0);
// CGContextAddLineToPoint(ctx, 300, 300);
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddArc(path, NULL, 150, 150, 100, 0, 2 * M_PI, 1);
CGPathMoveToPoint(path, NULL, 0, 0);
CGPathAddLineToPoint(path, NULL, 300, 300);
// 添加路径到上下文当中
CGContextAddPath(ctx, path);
// 设置线宽
CGContextSetLineWidth(ctx, 10);
// 3.渲染
CGContextStrokePath(ctx);
}
@end
0. 图形上下文栈是什么?
每一个“图形上下文”对象都包含一个“栈”结构,这个栈结构用来存储当前图形上下文的状态信息。(每个图形上下文对象中都包含:1>“图形状态”; 2> 路径信息; 3> 输出目标)
1. 为什么要学习图形上下文栈?
1. 第一次画图后修改了颜色, 后面的图形要再恢复到原来的颜色
2. 第一次画图的时候旋转了上下文, 后面的图形不要旋转
3. 第一次画图的时候修改了线宽,线头样式等,后面画图的时候不要这些样式,要默认的样子
总结:前面绘图的时候修改了上下文,后面绘图的时候要再次使用被修改前的上下文对象
1、先绘制图形,设置颜色,线宽,旋转。
2、然后再绘制其他的图形。(其他图形默认也会使用相同的设置(颜色、线宽、旋转等))
解决:
1. 在第一次绘制图形前,保存上下文。
2. 在第二次绘制图形前,恢复上下文。
图形上下文栈的操作
将当前【图形上下文】中的“绘图状态”信息保存到“栈”中
void CGContextSaveGState(CGContextRef c)
将栈顶的“绘图状态”出栈, 替换掉当前的“图形上下文”中的“绘图状态”
void CGContextRestoreGState(CGContextRef c)
使用Path 对象时的内存管理问题:
1> 凡是遇到 retain 、 copy 、 create 出的对象, 都需要进行 release
2> 但是CGPathCreateMutable()不是 OC 方法, 所以不是调用 某个对象的 release方法
3> CGXxxxxCreate 对应的就有 CGXxxxxRelease。
4> 通过 CFRelease(任何类型);可以释放任何类型。
通过 UIKit 框架来绘制
删除背景颜色 可以让阴影的地方显示 文字
将underlineStyleAttributeName 删掉 阴影直接消失 没有下划线参数就没有阴影 参数传为0 就没有下划线
UIImage *imgIcon = [UIImage imageNamed:@"002"];
[imgIcon drawAtPoint:CGPointMake(20, 20)];
[imgIcon drawInRect:rect];
[imgIcon drawAsPatternInRect:rect];
设置 UIView 的背景色为某个图片的屏幕效果。通过[UIColor colorWithXxxxxx:图片]来实现。
ViewController.M
#import "ViewController.h"
#import "HMImageView.h"
@interface ViewController ()
@property (nonatomic, weak) HMImageView* imageView;
//@property (nonatomic, weak) UIImageView* imageView;
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
HMImageView* imageView = [[HMImageView alloc] initWithImage:[UIImage imageNamed:@"me"]];
self.imageView = imageView;
[self.view addSubview:imageView];
// -------
// HMImageView* imageView = [[HMImageView alloc] init];
// imageView.frame = CGRectMake(0, 0, 200, 200);
// imageView.image = [UIImage imageNamed:@"me"];
// self.imageView = imageView;
// [self.view addSubview:imageView];
// *******
// UIImageView* imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"me"]];
// self.imageView = imageView;
// [self.view addSubview:imageView];
// // -------
// UIImageView* imageView = [[UIImageView alloc] init];
// imageView.frame = CGRectMake(0, 0, 200, 200);
// imageView.image = [UIImage imageNamed:@"me"];
// self.imageView = imageView;
// [self.view addSubview:imageView];
}
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event
{
self.imageView.image = [UIImage imageNamed:@"Press"];
}
@end
HMImageView.M
#import "HMImageView.h"
@implementation HMImageView
- (instancetype)initWithImage:(UIImage*)image{
self = [super initWithFrame:CGRectMake(0, 0, image.size.width, image.size.height)];
if (self) {
self.image = image;
}
return self;
}
- (void)setImage:(UIImage*)image{
_image = image;
// 重绘
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect{
// 把传过来的图片绘制到屏幕上
[self.image drawInRect:rect];
}
@end
设置 UIView 的背景为一张图片
1、通过[UIColor colorWithPatternImage:]实现
2、通过绘制一张图片到 UIView 上实现
裁剪 只是裁剪上下文的显示区域 上下文本身依然存在且大小不变
核心代码
void CGContextClip(CGContextRef c)
将当前上下所绘制的路径裁剪出来(超出这个裁剪区域的都不能显示)
图片裁剪思路:
1》 在上下文中绘制一个要裁减的图形
2》调用void CGContextClip(CGContextRef c)进行裁剪
3》在裁剪好的上下文中再把图片绘制上去。(注意绘制图片的时候,必须绘制到已经裁剪出的图形位置,否则不显示)
在 UIView 上显示一个裁剪后的图片
1、获取 UIView 的图形上下文对象
2、在图形上下文对象上绘制一个圆形
3、执行裁剪操作(裁剪的意思是告诉系统,将来只有在被裁减出的区域内绘制的图形才会显示)
4、把图片绘制到上下文上(直接调用 UIImage 对象的绘图方法即可)
直接裁剪图片并保存
图片类型的图形上下文
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView* imageView;
@end
@implementation ViewController
- (void)viewDidLoad{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event{
// 开启图片类型的图形上下文
UIGraphicsBeginImageContext(CGSizeMake(300, 300));
// 获取当前的上下文(图片类型)
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 拼接路径 同时 把路径添加上上下文当中
CGContextMoveToPoint(ctx, 50, 50);
CGContextAddLineToPoint(ctx, 100, 100);
// 渲染
// CGContextStrokePath(ctx);
// 通过图片类型的图形上下文 获取图片对象
UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
// 关闭图片类型的图形上下文
UIGraphicsEndImageContext();
// 把获取到的图片 放到 图片框上
self.imageView.image = image;
}
保存到沙盒
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView* imageView;
@end
@implementation ViewController
- (void)viewDidLoad{
[super viewDidLoad];
}
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event{
// 开启图片类型的图形上下文
UIGraphicsBeginImageContext(CGSizeMake(300, 300));
// 获取当前的上下文(图片类型)
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 拼接路径 同时 把路径添加上上下文当中
CGContextMoveToPoint(ctx, 50, 50);
CGContextAddLineToPoint(ctx, 100, 100);
// 渲染
CGContextStrokePath(ctx);
// 通过图片类型的图形上下文 获取图片对象
UIImage* image = UIGraphicsGetImageFromCurrentImageContext()
// 关闭图片类型的图形上下文
UIGraphicsEndImageContext();
// 把获取到的图片 放到 图片框上
self.imageView.image = image;
// 获取 doc 路径
NSString* docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
// 获取文件路径
NSString* filePath = [docPath stringByAppendingPathComponent:@"xx.png"];
// 1.把 image 对象转化成 nsdata
// NSData* data = UIImagePNGRepresentation(image);
NSData* data = UIImageJPEGRepresentation(image, 0);
// 2.通过 data 的 write to file 写入到沙盒中
[data writeToFile:filePath atomically:YES];
}
@end
开启图片类型的上下文 with option
UIGraphicsBeginImageContextWithOptions(CGSizeMake(300, 300), NO, [UIScreen mainscreen].scale);
< == > UIGraphicsBeginImageContextWithOptions(CGSizeMake(300, 300), NO, 0);
第三个参数最重要 缩放因子 传0表示不缩放 即在不同手机的不同屏幕不缩放
获取裁剪过后的图片
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView* imageView;
@end
@implementation ViewController
- (void)viewDidLoad{
[super viewDidLoad];
}
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event{
// 3.获取图片
UIImage* image = [UIImage imageNamed:@"me"];
// 1.开启图片类型的图形上下文
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
// 5.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 4.画一个裁剪的图片
CGContextAddArc(ctx, image.size.width * 0.5, image.size.height * 0.5, image.size.width * 0.5, 0, 2 * M_PI, 1);
// 6.裁剪
CGContextClip(ctx);
// 7.把图片画上去
[image drawAtPoint:CGPointZero];
// 8.取出来
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
// 2.关闭图片类型的图形上下文
UIGraphicsEndImageContext();
// 测试
self.imageView.image = newImage;
}
@end
获取裁剪后的图片
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIImageView* imageView;
@end
@implementation ViewController
- (void)viewDidLoad{
[super viewDidLoad];
}
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event{
// 3.获取图片
UIImage* image = [UIImage imageNamed:@"me"];
// 1.开启图片类型的图形上下文
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
// 5.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 4.画一个裁剪的图片
CGContextAddArc(ctx, image.size.width * 0.5, image.size.height * 0.5, image.size.width * 0.5, 0, 2 * M_PI, 1);
// 6.裁剪
CGContextClip(ctx);
// 7.把图片画上去
[image drawAtPoint:CGPointZero];
// 8.取出来
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
// 2.关闭图片类型的图形上下文
UIGraphicsEndImageContext();
// 测试
self.imageView.image = newImage;
// 保存到相册
UIImageWriteToSavedPhotosAlbum(newImage, self, @selector(image:didFinishSavingWithError:contextInfo:), @"123123123");
}
// 保存到相册的回调
- (void)image:(UIImage*)image didFinishSavingWithError:(NSError*)error contextInfo:(void*)contextInfo{
NSLog(@"保存完成 - %@", contextInfo);
}
裁剪一个带圆环的图片
/ /确定圆心
// 确定半径
// 开始绘制圆环路径
- (void)viewDidLoad{
[super viewDidLoad];
// 3.获取图片
UIImage* image = [UIImage imageNamed:@"me"];
// 4.margin
CGFloat margin = 20;
// 5.计算图片类型的图形上下文的大小
CGSize ctxSize = CGSizeMake(image.size.width + 2 * margin, image.size.height + 2 * margin);
// 1.开启图片类型的图形上下文
UIGraphicsBeginImageContextWithOptions(ctxSize, NO, 0);
// 6.获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 8.计算圆心
CGPoint arcCenter = CGPointMake(ctxSize.width * 0.5, ctxSize.height * 0.5);
// 9.计算半径
CGFloat radius = (image.size.width + margin) * 0.5;
// 7.画圆环
CGContextAddArc(ctx, arcCenter.x, arcCenter.y, radius, 0, 2 * M_PI, 1);
// 10.设置宽度
CGContextSetLineWidth(ctx, margin);
// 11.渲染圆环
CGContextStrokePath(ctx);
// 12.画头像显示的区域
CGContextAddArc(ctx, arcCenter.x, arcCenter.y, image.size.width * 0.5, 0, 2 * M_PI, 1);
// 13.裁剪显示区域
CGContextClip(ctx);
// 14.画图片
[image drawAtPoint:CGPointMake(margin, margin)];
// 获取图片
image = UIGraphicsGetImageFromCurrentImageContext();
// 2.关闭图片类型的图形上下文
UIGraphicsEndImageContext();
// 保存到相册
UIImageWriteToSavedPhotosAlbum(image, NULL, NULL, NULL);
}
图片水印
水印:在图片上加的防止他人盗图的半透明logo、文字、图标
实现方式:利用Quartz2D,将水印(文字、LOGO)画到图片的右下角
核心代码
开启一个基于位图的图形上下文
void UIGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale)
从上下文中取得图片
(UIImage) UIImage* UIGraphicsGetImageFromCurrentImageContext();
结束基于位图的图形上下文
void UIGraphicsEndImageContext();
图片加水印基本思路
1、添加文字水印
1> 创建位图上下文
2> 把图片画上去
3> 把文字画上去
4> 从上下文中取出图片
2、添加图片水印
1> 创建位图上下文
2> 把图片画上去
3> 加载 logo 图片(水印图片),把水印图片也画上去
4> 从上下文中取出图片
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event{
// 3.获取图片
UIImage* image = [UIImage imageNamed:@"scene"];
// 1.开启图片类型的图形上下文
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
// 6.画大图
[image drawAtPoint:CGPointZero];
// 4.文字
NSString* str = @"黑马13期";
// 5.画文字水印
[str drawAtPoint:CGPointMake(20, 20) withAttributes:@{ NSFontAttributeName : [UIFont systemFontOfSize:20] }];
// 7.图片
UIImage* logo = [UIImage imageNamed:@"logo"];
// 8.画图片水印
[logo drawAtPoint:CGPointMake(image.size.width * 0.6, image.size.height * 0.7)];
// 取图片
image = UIGraphicsGetImageFromCurrentImageContext();
// 保存到相册
UIImageWriteToSavedPhotosAlbum(image, NULL, NULL, NULL);
// 2.关闭图片类型的图形上下文
UIGraphicsEndImageContext();
NSLog(@"test");
}
屏幕截图
核心代码
- (void)renderInContext:(CGContextRef)ctx;
调用某个view的layer的renderInContext:方法即可
截图基本思路:
1、获取控件的 layer 对象
2、调用 layer 对象的 renderInContext:方法渲染到上下文中
**注意:UISegmentedControl 渲染时有问题
- (void)viewDidLoad{
// 开启图片类型的图形上下文
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, 0);
// 获取当前的上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 截图 把view 的内容 放到上下文中 然后 渲染
[self.view.layer renderInContext:ctx];
// 取图片
UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
// 关闭上下文
UIGraphicsEndImageContext();
// 保存到相册
UIImageWriteToSavedPhotosAlbum(image, NULL, NULL, NULL);
}
// --- 旋转缩放平移
// 对图形上下文进行旋转平移缩放的操作
// 缩放
// 第一个参数:需要缩放的上下文
// 第二个参数:x轴缩放的比例
// 第三个参数:y轴缩放的比例
CGContextScaleCTM(ctx, 1, 0.5);
// 平移
// 第一个参数:需要平移的上下文
// 第二个参数:x轴的偏移量
// 第三个参数:y轴的偏移量
CGContextTranslateCTM(ctx, 150, 150);
// 旋转
// 第一个参数:需要旋转的上下文
// 第二个参数:顺时针旋转的角度
CGContextRotateCTM(ctx, M_PI_4);
// --- 图形上下文栈
// 图形上下文栈 只是保存 状态信息(样式)
// 保存状态信息
CGContextSaveGState(ctx);
// 恢复状态信息
CGContextRestoreGState(ctx);
// --- quartz2d 内存管理
// 如果包含 create copy 关键字的时候 记得释放
// 释放 1
CGPathRelease(path);
// 释放 2
CFRelease(path);
// --- 绘制文字
// 绘制 - 从(0,0)点开始画
[str drawAtPoint:CGPointZero withAttributes:dict];
// 绘制 - 绘制到指定的区域
[str drawInRect:rect withAttributes:nil];
// AttributeName - key 在 UIKit下的 NSAttributeName.h 里面
// 个人建议 记其中一个
// shadow
NSShadow* s = [[NSShadow alloc] init];
s.shadowOffset = CGSizeMake(100, 100); // 偏移量
s.shadowBlurRadius = 0; // 越小越不模糊 高斯模糊
s.shadowColor = [UIColor yellowColor]; // 阴影的颜色
// --- 绘制图片
// 绘制 - 从某个点开始绘制
[image drawAtPoint:CGPointZero];
// 绘制 - 绘制到某一个区域
[image drawInRect:rect];
// 绘制 - 平铺
[image drawAsPatternInRect:rect]; // 可以考虑用来设置某个view的背景图片
// --- 裁剪上下文显示的区域
// 前提:画一个图形
// 裁剪图片
CGContextClip(ctx);
// 告诉系统 已这个图形来裁剪图片 渲染 然后显示出来
// 所谓裁剪并不是裁剪掉上下文 只是单纯的 裁剪出来希望显示的区域而已!!!
// --- bitmap上下文
// 创建上下文(图片)
UIGraphicsBeginImageContext(CGSizeMake(300, 300));
// 创建上下文 (大小,不透明,缩放:0)
UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 200), NO, 0);
// 关闭上下文
UIGraphicsEndImageContext();
// 通过图片的图形上下文获取图片
UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
// 保存到沙盒当中
NSString* path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
path = [path stringByAppendingPathComponent:@"xx.png"];
// 把image转化成 NSData类型
NSData* data = UIImagePNGRepresentation(image);
NSLog(@"%ld", data.length);
// 然后在通过data对象 write to file 来写入到沙盒中
[data writeToFile:path atomically:YES];
// --- 裁剪圆形图片
// 保存到相册
// 第一个:图片
// 第二个 第三个 监听 注意:@selector 不能随便写 使用"注释"当中的方法
// 第四个: 可以把它当做一个tag来使用 参数在监听的方法当中会传入
UIImageWriteToSavedPhotosAlbum(newImage, self, @selector(image:didFinishSavingWithError:contextInfo:), @"hello1111");
// --- 屏幕截图
// 对控制器的view的layer属性 进行操作
[self.view.layer renderInContext:ctx];