Quartz 2D绘图 (2)再遇
图片裁剪
直接裁剪图片
-
裁剪思路:
- 绘制一个已经裁剪好的圆形的图形上下文
- 将图片绘制上去就可以了
-
代码
// 1.获取图形上下文
CGContextRef cxtRef = UIGraphicsGetCurrentContext();
// 2.圆形路径对象
UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(150, 150) radius:50 startAngle:0 endAngle:M_PI * 2 clockwise:YES];
// 3.将路径对象添加到上下文
CGContextAddPath(cxtRef, path.CGPath);
// 3.2裁剪图形上下文,注意:必须是在添加完路径对象后,并且是渲染之前进行裁剪,否则没有意义。
CGContextClip(cxtRef);
// 4.渲染
CGContextDrawPath(cxtRef, kCGPathStroke);
// 5.绘制图片绘制图片需要在最后进行绘制
UIImage *image = [UIImage imageNamed:@"me"];
[image drawAtPoint:CGPointMake(100, 100)];
保存到相册和沙盒
> 相册
- 保存到相册
// 添加图片到相册中. 可选的监听方法格式如下:
// - (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo;
UIKIT_EXTERN void UIImageWriteToSavedPhotosAlbum(UIImage *image, __nullable id completionTarget, __nullable SEL completionSelector, void * __nullable contextInfo);
- 保存裁剪的图片
// 保存裁剪的图片到相册
UIImageWriteToSavedPhotosAlbum(imgCliped, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
#pragma mark - 监听保存图片的成功与失败
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
if (error) {
NSLog(@"保存失败");
} else {
NSLog(@"保存成功");
}
}
> 沙盒
-
保存到沙盒
- 获取文件路径
- 将图片文件转为二进制数据
- 将二进制数据写入到文件
-
参考代码
// 保存到沙盒
// 文件路径
NSString *filePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject] stringByAppendingPathComponent:@"abc.png"];
NSLog(@"%@", filePath);
// 转为二进制文件
NSData *imgData = UIImagePNGRepresentation(imgCliped);
// 写入文件
[imgData writeToFile:filePath atomically:YES];
裁剪带圆环的图片
-
裁剪思路:
- 先绘制一个圆环
- 再绘制要裁剪的图形
- 最后在进行裁剪
-
参考代码
#pragma mark - 裁剪带圆环的图形 // 加载图片显示 UIImage *image = [UIImage imageNamed:@"me"]; self.imgView.image = image; // 1.开启图片的图形上下文 CGFloat margin = 10; CGSize size = CGSizeMake(image.size.width + 2 * margin, image.size.height + 2 * margin); UIGraphicsBeginImageContextWithOptions(size, NO, 0.0); // 2.获取当前的图形上下文 CGContextRef cxtRef = UIGraphicsGetCurrentContext(); // 圆心 CGPoint center = CGPointMake(size.width * 0.5, size.height * 0.5); // 2.1绘制圆环 // 半径 需要让圆环的半径刚好可以正其先换在最外面 CGFloat radius = MIN(size.width, size.height) * 0.5 - margin * 0.5; // 圆环的路径 UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:0 endAngle:M_PI * 2 clockwise:YES]; CGContextSetLineWidth(cxtRef, margin); [[UIColor redColor] set]; // 2.1.2添加 CGContextAddPath(cxtRef, path.CGPath); // 2.2.3将圆环部分渲染 CGContextDrawPath(cxtRef, kCGPathFill); // 2.2绘制要裁剪的图形 CGFloat radius2 = MIN(image.size.width, image.size.height) * 0.5; UIBezierPath *path2 = [UIBezierPath bezierPathWithArcCenter:center radius:radius2 startAngle:0 endAngle:M_PI * 2 clockwise:YES]; // 2.2.2添加 CGContextAddPath(cxtRef, path2.CGPath); // 5.裁剪 CGContextClip(cxtRef); // 6.绘制图片 [image drawAtPoint:CGPointMake(margin, margin)]; // 7.从当前图形上下文中获取图片 UIImage *imgClicped = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); self.imgView.image = imgClicped;
裁剪带圆环的图片
-
裁剪思路:
- 先绘制一个圆环
- 再绘制要裁剪的图形
- 最后在进行裁剪
-
参考代码
#pragma mark - 裁剪带圆环的图形 // 加载图片显示 UIImage *image = [UIImage imageNamed:@"me"]; self.imgView.image = image; // 1.开启图片的图形上下文 CGFloat margin = 10; CGSize size = CGSizeMake(image.size.width + 2 * margin, image.size.height + 2 * margin); UIGraphicsBeginImageContextWithOptions(size, NO, 0.0); // 2.获取当前的图形上下文 CGContextRef cxtRef = UIGraphicsGetCurrentContext(); // 圆心 CGPoint center = CGPointMake(size.width * 0.5, size.height * 0.5); // 2.1绘制圆环 // 半径 需要让圆环的半径刚好可以正其先换在最外面 CGFloat radius = MIN(size.width, size.height) * 0.5 - margin * 0.5; // 圆环的路径 UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:0 endAngle:M_PI * 2 clockwise:YES]; CGContextSetLineWidth(cxtRef, margin); [[UIColor redColor] set]; // 2.1.2添加 CGContextAddPath(cxtRef, path.CGPath); // 2.2.3将圆环部分渲染 CGContextDrawPath(cxtRef, kCGPathFill); // 2.2绘制要裁剪的图形 CGFloat radius2 = MIN(image.size.width, image.size.height) * 0.5; UIBezierPath *path2 = [UIBezierPath bezierPathWithArcCenter:center radius:radius2 startAngle:0 endAngle:M_PI * 2 clockwise:YES]; // 2.2.2添加 CGContextAddPath(cxtRef, path2.CGPath); // 5.裁剪 CGContextClip(cxtRef); // 6.绘制图片 [image drawAtPoint:CGPointMake(margin, margin)]; // 7.从当前图形上下文中获取图片 UIImage *imgClicped = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); self.imgView.image = imgClicped;
屏幕截图
- 屏幕截图过程</br>
- 开启图片的图形上下文
- 获取当前的图形上下文
- 将view的layer渲染到图形上下文中
- 从当前的图形上下文中获取图片
- 参考代码
// 1.开启图片的图形上下文 UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, 0.0); // 2.获取当前的图形上下文 CGContextRef cxtRef = UIGraphicsGetCurrentContext(); // 3.将view的layer渲染到图形上下文中 [self.view.layer renderInContext:cxtRef]; // 4.从当前的图形上下文中获取图片 UIImage *img = UIGraphicsGetImageFromCurrentImageContext(); // 5.关闭图形上下文 UIGraphicsEndImageContext(); // 6.将图片保存到相册中 UIImageWriteToSavedPhotosAlbum(img, nil, nil, nil);
触摸事件介绍
- iOS中事件大体分为三类:触摸事件、加速计事件、远程控制事件
- 需要掌握4中触摸事件
- 不接受用户触摸事件的几种情况
- 了解响应者链条
- 手势识别
4种触摸事件
-
什么类型的对象是响应者对象呢?
- 只要这个对象的类型是直接或者间接的继承自UIResponder,那么这个对象就是响应者对象。
-
4种触摸事件
#pragma mark - 当手指按下的时候调用 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event; #pragma mark - 当手指移动的时候调用 - (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event; #pragma mark - 当手指抬起的时候调用 - (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event; #pragma mark - 当有一些系统事件的时候调用,会打断对手指的跟踪行为 - (void)touchesCancelled:(nullable NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event;
-
touches参数是NSSet类型
- 特点是:1.无序 2.集合内的元素不重复
- 遍历:可以通过forIn遍历
- 取值:通过anyObject来获取对应的值
- 在当前集合参数中装的都是UITouch类型的触摸对象
-
event参数是UIEvent类型
- 用来区分是那种情况的触摸事件
- 可以根据类型type和子类型subtype来确定是那种触摸事件。
触摸事件的响应顺序和不接受触摸事件的几种情况
-
触摸事件的响应顺序
- 首先是最顶层的view被触发[第一响应者]
- 然后是向下传,所有嵌套的view依次被触发
- 控制器的view里面也是会被触发的。
-
不接受触摸事件的几种情况
- 1> 控件的hidden为YES的时候,不会触发
- 2> 控件的User Interaction Enable 取消,不会触发
- 3> 控件的父控件不能进行用户交互的时候,不会触发
- 4> 控件的透明度小于等于0.01的时候,不会触发
- 5> 控件不在父控件的有效范围内的时候,不会触发
- 6> 图片框默认是不能接受用户交互的
- 6> 对于按钮来说,按钮接收到用户事件后,会将用户响应事件切断,所以它的父控件就不会再去响应事件了。
触摸事件的产生和传递过程
-
触摸事件的产生
- 当用户点击屏幕的时候就会产生UITouch类型的触摸对象,进而产生一系列的触摸事件。
-
触摸事件的传递过程
-
1> 应用程序启动完毕后,内部就有一个运行循环,始终监听用户的触摸事件及其他的事件
-
2> 首先,iOS监听到触摸点后,会把这个消息交给UIApplication对象,说"现在又触摸对象了,看一下按的是谁",
-
3> UIApplication就直接告诉UIWindow"现在有用户触摸点了,看一下点的谁",
-
4> 然后UIWindow就告诉控制器,现在有用户手指触摸屏幕,看一下是按到谁了,如果有导航控制器的及根控制器的话,会继续往下传递这个事件,传给最终在显示的控制器
-
5> 最终显示的控制器拿到事件再传给它的view,控制器的view再搜索他自己的子控件,从后往前问,找到对应被点击的view。
-
6> 找到被点击的view后,将消息一次返回说明找到了对应的view,应用程序再找对应view的注册事件,然后就执行对应的事件。前提是得基于触摸事件。
-
7> 从用户触摸事件产生,然后从上往下传递事件。
-
事件传递过程中涉及的方法有:
-
hitTest:当事件从上往下传的时候,递归查找那个view被点了。内部算法的原因会重复调用了两次
-
控制器也是知道这个事件的,事件时通过控制器传递过来的
-
pointInside:WithEvent方法,确定事件到底在不在view内部。
-
hitTest就是用来测试控件是否可以响应事件。
-
-
手势识别介绍
-
在以前如果想监听一个view上的触摸事件,之前的做法是
-
自定义一个view
-
实现view的touches方法,在方法内部实现具体处理代码
-
通过touches方法监听view的触摸事件,有很明显的几个缺点
- 必须得自定义view
- 由于是在view内部的touches方法中监听触摸事件,因此默认情况下,无法让其他外界对象监听view的触摸事件。
- 不容易具体区分用户的具体手势行为
-
-
在iOS3.2 之后,苹果退出了手势识别功能(Gesture Recognizer),在触摸事件处理方面,大大简化了开发者的开发难度。
-
6种常用的手势识别器
-
UITapGestureRecognizer :轻按手势
-
UIPinchGestureRecognizer :缩放手势
-
UIRotationGestureRecognizer :旋转手势
-
UISwipeGestureRecognizer :清扫手势
-
UIPanGestureRecognizer :拖拽手势
-
UILongPressGestureRecognizer :长按手势
-
-
手势识别的使用方法。
实例化,指定监听方法
添加到view上
实现对应的方法。