图片水印
图片水印就是在图片的基础上绘制一些文字或logo,最终生成一张新的图片
-
在viewDidLoad方法中生成图片水印的步骤:
-
加载图片
UIImage *image = [UIImage imageNamed:@"小黄人"];
手动获取上下文,之前的上下文都是在view的drawRect方法中获取的(跟View相关联的layer上下文),目前我们需要绘制图片到新的图片上,所以需要用到位图上下文
-
怎样获取位图上下文呢?注意,位图上下文的获取方式跟layer上下文不一样,位图上下文需要我们手动创建
// 开启一个位图上下文,注意位图上下文跟view无关联,所以不需要在drawRect. // size:位图上下文的尺寸(新图片的尺寸) // opaque: 不透明度 YES:不透明 NO:透明,通常我们一般都弄透明的上下文 // scale:通常不需要缩放上下文,取值为0,表示不缩放 UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
-
绘制图片水印
// 1.绘制原生的图片 [image drawAtPoint:CGPointZero]; // 2.给原生的图片添加文字 NSString *str = @"水印文字"; // 创建字典属性 NSMutableDictionary *dict = [NSMutableDictionary dictionary]; dict[NSForegroundColorAttributeName] = [UIColor redColor]; dict[NSFontAttributeName] = [UIFont systemFontOfSize:20]; [str drawAtPoint:CGPointMake(200, 528) withAttributes:dict];
-
-
绘制贝瑟尔路径
UIBezierPath *path =[UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 300, 300)]; [[UIColor redColor] set]; [path stroke];
-
底层实现
// 1.获取上下文(位图上下文) CGContextRef ctx = UIGraphicsGetCurrentContext(); // 2.描述路径 CGContextMoveToPoint(ctx, 50, 50); CGContextAddLineToPoint(ctx, 200, 200); [[UIColor redColor] set]; // 3.渲染上下文 CGContextStrokePath(ctx);
-
生成一张图片给我们,从上下文中获取图片,并将其显示到UIImageView上
UIImage *imageWater = UIGraphicsGetImageFromCurrentImageContext(); self.imageView.image = imageWater;
-
关闭上下文
UIGraphicsEndImageContext();
图片裁剪
- 图片裁剪是把正方形图片进行裁剪,并生成一张圆形的图片
- 裁剪无边框的图片
- (void)clipImage
{
// 0.加载图片
UIImage *image = [UIImage imageNamed:@"阿狸头像"];
// 1.开启位图上下文,跟图片尺寸一样大
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
// 2.设置圆形裁剪区域,正切与图片
// 2.1创建圆形的路径
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
// 2.2把路径设置为裁剪区域
[path addClip];
// 3.绘制图片
[image drawAtPoint:CGPointZero];
// 4.从上下文中获取图片
UIImage *clipImage = UIGraphicsGetImageFromCurrentImageContext();
// 5.关闭上下文
UIGraphicsEndImageContext();
_imageView.image = clipImage;
}
- 裁剪有边框的图片,并将裁剪方法放到分类中去,以便他人的使用
+ (UIImage *)imageWithClipImage:(UIImage *)image borderWidth:(CGFloat)borderWidth borderColor:(UIColor *)color
{
// 图片的宽度和高度
CGFloat imageWH = image.size.width;
// 设置圆环的宽度
CGFloat border = borderWidth;
// 圆形的宽度和高度
CGFloat ovalWH = imageWH + 2 * border;
// 1.开启上下文
UIGraphicsBeginImageContextWithOptions(CGSizeMake(ovalWH, ovalWH), NO, 0);
// 2.画大圆
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, ovalWH, ovalWH)];
[color set];
[path fill];
// 3.设置裁剪区域
UIBezierPath *clipPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(border, border, imageWH, imageWH)];
[clipPath addClip];
// 4.绘制图片
[image drawAtPoint:CGPointMake(border, border)];
// 5.获取图片
UIImage *clipImage = UIGraphicsGetImageFromCurrentImageContext();
// 6.关闭上下文
UIGraphicsEndImageContext();
return clipImage;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// 图片裁剪:把正方形图片重新生产一张圆形的图片
// 图片裁剪
UIImage *image = [UIImage imageWithClipImage:[UIImage imageNamed:@"阿狸头像"] borderWidth:1 borderColor:[UIColor redColor]];
_imageView.image = image;
}
屏幕截屏
- 屏幕截屏的代码可以封装到分类当中
+ (UIImage *)imageWithCaputureView:(UIView *)view
{
// 开启位图上下文
UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0);
// 获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 把控件上的图层渲染到上下文,layer只能渲染,不能绘制
[view.layer renderInContext:ctx];
// 生成一张图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 关闭上下文
UIGraphicsEndImageContext();
return image;
}
- 在viewDidLoad方法中调用分类,返回一个UIImage对象
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// 生成一张新的图片
UIImage *image = [UIImage imageWithCaputureView:self.view];
// image转data
// compressionQuality: 图片质量 1:最高质量
NSData *data = UIImageJPEGRepresentation(image,1);
[data writeToFile:@"/Users/xiaomage/Desktop/view.png" atomically:YES];
}
图片截取
- 在viewDidLoad方法中给控制器View添加一个pan手势
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// 给控制器的view添加一个pan手势
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
[self.view addGestureRecognizer:pan];
}
- 在pan:方法中对手势进行处理
- (void)pan:(UIPanGestureRecognizer *)pan
{
CGPoint endA = CGPointZero;
if (pan.state == UIGestureRecognizerStateBegan) { // 一开始拖动的时候
// 获取一开始触摸点
_startP = [pan locationInView:self.view];
}else if(pan.state == UIGestureRecognizerStateChanged){ // 一直拖动
// 获取结束点
endA = [pan locationInView:self.view];
CGFloat w = endA.x - _startP.x;
CGFloat h = endA.y - _startP.y;
// 获取截取范围
CGRect clipRect = CGRectMake(_startP.x, _startP.y, w, h);
// 生成截屏的view
self.clipView.frame = clipRect;
}else if (pan.state == UIGestureRecognizerStateEnded){
// 图片裁剪,生成一张新的图片
// 开启上下文
// 如果不透明,默认超出裁剪区域会变成黑色,通常都是透明
UIGraphicsBeginImageContextWithOptions(_imageV.bounds.size, NO, 0);
// 使用贝瑟尔路径,设置裁剪区域
UIBezierPath *path = [UIBezierPath bezierPathWithRect:_clipView.frame];
[path addClip];
// 获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 把控件上的内容渲染到上下文
[_imageV.layer renderInContext:ctx];
// 生成一张新的图片
_imageV.image = UIGraphicsGetImageFromCurrentImageContext();
// 关闭上下文
UIGraphicsEndImageContext();
// 先移除
[_clipView removeFromSuperview];
// 截取的view设置为nil
_clipView = nil;
}
// 获取手指的偏移量
// pan translationInView:(UIView *)
}
- clipView的懒加载,仅当clipView为空的时候才去创建,否则直接返回
- (UIView *)clipView{
if (_clipView == nil) {
UIView *view = [[UIView alloc] init];
_clipView = view;
view.backgroundColor = [UIColor blackColor];
view.alpha = 0.5;
[self.view addSubview:view];
}
return _clipView;
}
图片擦除
- 需要用到手势操作
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
[self.view addGestureRecognizer:pan];
}
- pan方法根据用户手指的位置进行图片擦除
- (void)pan:(UIPanGestureRecognizer *)pan
{
// 获取当前点
CGPoint curP = [pan locationInView:self.view];
// 获取擦除的矩形范围
CGFloat wh = 100;
CGFloat x = curP.x - wh * 0.5;
CGFloat y = curP.y - wh * 0.5;
CGRect rect = CGRectMake(x, y, wh, wh);
// 开启上下文
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, 0);
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 控件的layer渲染上去
[_imageView.layer renderInContext:ctx];
// 擦除图片
CGContextClearRect(ctx, rect);
// 生成一张图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
_imageView.image = image;
// 关闭上下文
UIGraphicsEndImageContext();
}
手势解锁的界面布局
- 首先,在awakeFromNib中创建9个按钮对象
// 加载完xib的时候调用
- (void)awakeFromNib
{
// 创建9个按钮
for ( int i = 0; i < 9; i++) {
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[btn setImage:[UIImage imageNamed:@"gesture_node_normal"] forState:UIControlStateNormal];
[btn setImage:[UIImage imageNamed:@"gesture_node_highlighted"] forState:UIControlStateSelected];
[self addSubview:btn];
}
}
- 然后,在layoutSubviews方法中对创建的按钮对象进行布局操作
// 为什么要在这个方法布局子控件,因为只要一调用这个方法,就表示父控件的尺寸确定
- (void)layoutSubviews
{
[super layoutSubviews];
NSUInteger count = self.subviews.count;
int cols = 3;
CGFloat x = 0;
CGFloat y = 0;
CGFloat w = 74;
CGFloat h = 74;
CGFloat margin = (self.bounds.size.width - cols * w) / (cols + 1);
CGFloat col = 0;
CGFloat row = 0;
for (NSUInteger i = 0; i < count; i++) {
UIButton *btn = self.subviews[i];
// 获取当前按钮的列数
col = i % cols;
row = i / cols;
x = margin + col * (margin + w);
y = row * (margin + w);
btn.frame = CGRectMake(x, y, w, h);
}
}
手势解锁的业务逻辑实现
- 使用可变数组来存储已经选中的按钮
@property (nonatomic, strong) NSMutableArray *selectedsBtn;
// 重写get方法,对数组进行懒加载
- (NSMutableArray *)selectedsBtn
{
if (_selectedsBtn == nil) {
_selectedsBtn = [NSMutableArray array];
}
return _selectedsBtn;
}
- 重写pan方法,对用户的手势进行判断,首先判断触摸点是否在按钮上,然后当用户手指离开屏幕时还原界面
- (IBAction)pan:(UIPanGestureRecognizer *)pan
{
// 获取触摸点
_curP = [pan locationInView:self];
// 判断触摸点在不在按钮上
for (UIButton *btn in self.subviews) {
// 点在不在某个范围内,并且按钮没有被选中
if (CGRectContainsPoint(btn.frame, _curP) && btn.selected == NO) {
// 点在按钮上
btn.selected = YES;
// 保存到数组中
[self.selectedsBtn addObject:btn];
}
}
// 重绘
[self setNeedsDisplay];
if (pan.state == UIGestureRecognizerStateEnded) {
// 创建可变字符串
NSMutableString *strM = [NSMutableString string];
// 保存输入密码
for (UIButton *btn in self.selectedsBtn) {
[strM appendFormat:@"%ld",btn.tag];
}
NSLog(@"%@",strM);
// 还原界面
// 取消所有按钮的选中
[self.selectedsBtn makeObjectsPerformSelector:@selector(setSelected:) withObject:@(NO)];
// 清除画线,把选中按钮清空
[self.selectedsBtn removeAllObjects];
}
}
- 根据被选中按钮的数组,对界面进行重绘操作
// 只要调用这个方法,就会把之前绘制的东西全部清掉,重新绘制
- (void)drawRect:(CGRect)rect
{
// 没有选中按钮,不需要连线
if (self.selectedsBtn.count == 0) return;
// 把所有选中按钮中心点连线
UIBezierPath *path = [UIBezierPath bezierPath];
NSUInteger count = self.selectedsBtn.count;
// 把所有选中按钮之间都连好线
for (int i = 0; i < count; i++) {
UIButton *btn = self.selectedsBtn[i];
if (i == 0) {
// 设置起点
[path moveToPoint:btn.center];
}else{
[path addLineToPoint:btn.center];
}
}
// 连线到手指的触摸点
[path addLineToPoint:_curP];
[[UIColor greenColor] set];
path.lineWidth = 10;
path.lineJoinStyle = kCGLineJoinRound;
[path stroke];
}
画板
- 根据数组中数据的类型,绘制图片和线条
// 绘制图形
// 只要调用drawRect方法就会把之前的内容全部清空
- (void)drawRect:(CGRect)rect
{
for (DrawPath *path in self.paths) {
if ([path isKindOfClass:[UIImage class]]) {
// 绘制图片
UIImage *image = (UIImage *)path;
[image drawInRect:rect];
}else{
// 画线
[path.pathColor set];
[path stroke];
}
}
}
- 选择照片,并实现代理方法,将选中的照片绘制到画板上
#pragma mark - 选择照片
- (IBAction)pickerPhoto:(id)sender {
// 弹出系统的相册
// 选择控制器(系统相册)
UIImagePickerController *picekerVc = [[UIImagePickerController alloc] init];
// 设置选择控制器的来源
// UIImagePickerControllerSourceTypePhotoLibrary 相册集
// UIImagePickerControllerSourceTypeSavedPhotosAlbum:照片库
picekerVc.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
// 设置代理
picekerVc.delegate = self;
// modal
[self presentViewController:picekerVc animated:YES completion:nil];
}
#pragma mark - UIImagePickerControllerDelegate
// 当用户选择一张图片的时候调用
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
// 获取选中的照片
UIImage *image = info[UIImagePickerControllerOriginalImage];
// 把选中的照片画到画板上
_drawView.image = image;
// dismiss
[self dismissViewControllerAnimated:YES completion:nil];
}
- 保存修改后的照片,相当于截屏操作
#pragma mark - 保存
- (IBAction)save:(id)sender {
// 截屏
// 开启上下文
UIGraphicsBeginImageContextWithOptions(_drawView.bounds.size, NO, 0);
// 获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 渲染图层
[_drawView.layer renderInContext:ctx];
// 获取上下文中的图片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 关闭上下文
UIGraphicsEndImageContext();
// 保存画板的内容放入相册
// image:写入的图片
// completionTarget图片保存监听者
// 注意:以后写入相册方法中,想要监听图片有没有保存完成,保存完成的方法不能随意乱写
UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
}
// 监听保存完成,必须实现这个方法
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
NSLog(@"保存图片成功");
}