//--------------------------------- 图片裁剪案例 -----------------------------------------
//图片裁剪案例
- (void)drawRect:(CGRect)rect {
// Drawing code
//1 获取图形上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//2 在图形上下文中绘制一个要裁剪的圆形
UIBezierPath * path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 100, 100)];
//2.1把路径添加到上下文对象中
CGContextAddPath(ctx, path.CGPath);
//3 执行裁剪
CGContextClip(ctx);
//4 把图片绘制到图形上下文中
//4.1 加载图片
UIImage * image = [UIImage imageNamed:@"me"];
//4.2 绘制图片
[image drawInRect:CGRectMake(0, 0, 100, 100)];
}
//--------------------------------- 图片裁剪案例 -----------------------------------------
//-------------------裁剪一个圆形图片 并保存到相册和沙盒中 -----------------------------------
- (void) test01
{
// 1、加载要裁减的图片
UIImage * image = [UIImage imageNamed:@"me"];
// 2、根据要裁剪的图片大小开启一个Bitmap 的图形上下文
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0.0);
//获取刚刚开启的上下文对象
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 3、在这个图形上下文上绘制一个圆形(这个圆的圆心应为这个图形上下文的中心点,半径应为这个图形上下文的最短的边的一半)
//圆心
CGPoint centerP = CGPointMake(image.size.width/2, image.size.height/2);
//半径
CGFloat radius = MIN(image.size.width, image.size.height)/2;
UIBezierPath * path = [UIBezierPath bezierPathWithArcCenter:centerP radius:radius startAngle:0 endAngle:M_PI * 2 clockwise:YES];
//把这个路径对象添加到上下文对象中
CGContextAddPath(ctx, path.CGPath);
// 4、执行裁剪操作
CGContextClip(ctx);
// 5、把图片绘制到当前的“图形上下文”中(因为这个图形上下文的大小是按照要裁减的图片的大小来创建的,所以绘图的时候直接从(0,0)开始绘制即可)
[image drawAtPoint:CGPointZero];
// 6、从图形上下文中获取图片对象
UIImage * getImage = UIGraphicsGetImageFromCurrentImageContext();
CGFloat scale = [UIScreen mainScreen].scale;
CGImageRef imgRef = CGImageCreateWithImageInRect(getImage.CGImage, CGRectMake(0, (getImage.size.height - getImage.size.width)/2 * scale, getImage.size.width*scale, getImage.size.width*scale));
getImage = [UIImage imageWithCGImage:imgRef];
self.imageView.image = getImage;
UIGraphicsEndImageContext();
// 7、将图片对象转换为 NSData 类型UIImagePNGRepresentation(img)
NSData * imageData = UIImagePNGRepresentation(getImage);
// 8、保存图片对象到目标位置(沙盒或者相册)
// UIImageWriteToSavedPhotosAlbum(getImage, nil, nil, nil);
UIImageWriteToSavedPhotosAlbum(getImage, self, @selector(image:didFinishSavingWithError:contextInfo:), @"hehe");
//获取沙盒路径
NSString * imagePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString * filePath = [imagePath stringByAppendingPathComponent:@"001.png"];
[imageData writeToFile:filePath atomically:YES];
}
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
NSLog(@"保存完毕。。。");
}
//-------------------裁剪一个圆形图片 并保存到相册和沙盒中 -----------------------------------
//--------------------- 裁剪一个带圆环圆形图片 并保存到相册和沙盒中 ---------------------------
- (IBAction)buttonClick:(id)sender
{
// 加载要裁剪的图片
UIImage * image = [UIImage imageNamed:@"me"];
// 创建一个比原始图片略大的基于 Bitmap 的“图形上下文”
CGFloat margin = 5;
CGSize ctxSize = CGSizeMake(image.size.width + 2*margin, image.size.height + 2*margin);
UIGraphicsBeginImageContextWithOptions(ctxSize, NO, 0.0);
// 获取当前上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// ------------------------------------ 开始绘制一个圆环
// 确定圆心
CGPoint centerP = CGPointMake(ctxSize.width/2, ctxSize.height/2);
// 确定半径
CGFloat radius = MIN(image.size.width, image.size.height)/2;
// 开始绘制圆环路径
UIBezierPath * path = [UIBezierPath bezierPathWithArcCenter:centerP radius:radius startAngle:0 endAngle:M_PI * 2 clockwise:YES];
//把路径添加到上下文中
CGContextAddPath(ctx, path.CGPath);
//设置圆环的颜色和线宽
[[UIColor redColor] setStroke];
CGContextSetLineWidth(ctx, 8);
// 渲染
CGContextDrawPath(ctx, kCGPathStroke);
//-------------------------------- 开始裁剪一个圆
UIBezierPath * path2 = [UIBezierPath bezierPathWithArcCenter:centerP radius:radius startAngle:0 endAngle:M_PI * 2 clockwise:YES];
//把这个路径添加到上下文中
CGContextAddPath(ctx, path2.CGPath);
CGContextClip(ctx);
// ------------------------------------ 开始绘制图片
[image drawAtPoint:CGPointMake(margin,margin)];
// 从绘图上下文中获取图片
UIImage * getImage = UIGraphicsGetImageFromCurrentImageContext();
self.imageView.image = getImage;
// 结束图形上下文
UIGraphicsEndImageContext();
// 保存
// 保存到沙盒
//获取沙盒路径
NSData * imageData = UIImagePNGRepresentation(getImage);
NSString * imagePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString * filePath = [imagePath stringByAppendingPathComponent:@"002.png"];
[imageData writeToFile:filePath atomically:YES];
// 保存图片
UIImageWriteToSavedPhotosAlbum(getImage, nil, nil, nil);
}
//--------------------- 裁剪一个带圆环圆形图片 并保存到相册和沙盒中 ---------------------------
//------------------------- 添加文字、图片水印案例 ---------------------------------------
- (IBAction)buttonClick:(id)sender
{
//1. 加载图片
UIImage * image = [UIImage imageNamed:@"img01"];
//2. 创建一个和图片一样大小的上下文
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0.0);
//3. 把原图绘制到上下文中
[image drawAtPoint:CGPointZero];
//4. 添加文字水印
NSString * str = @"这是一位德艺双馨的好老师";
NSDictionary * att = @{
NSFontAttributeName : [UIFont systemFontOfSize:20.f],
NSForegroundColorAttributeName : [UIColor redColor]
};
[str drawAtPoint:CGPointMake(30, 50) withAttributes:att];
//5. 添加图片水印
UIImage * myImage = [UIImage imageNamed:@"logo"];
[myImage drawAtPoint:CGPointMake(150, 200)];
//6. 从上下文中获取图片
UIImage * getImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//7. 保存图片到相册
UIImageWriteToSavedPhotosAlbum(getImage, nil, nil, nil);
}
//------------------------- 添加文字、图片水印案例 ---------------------------------------
//------------------------- 屏幕截图 --------------------------------------------
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//1. 开启一个图片上下文
UIGraphicsBeginImageContextWithOptions(self.view.frame.size, NO, 0.0);
//2. 获取刚刚开启的图片上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
//3、获取控件的 layer 对象 调用 layer 对象的 renderInContext:方法渲染到上下文中
[self.view.layer renderInContext:ctx];
//4. 从上下文中获取图片
UIImage * getImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//5. 保存图片到相册
UIImageWriteToSavedPhotosAlbum(getImage, nil, nil, nil);
});
//------------------------- 屏幕截图 --------------------------------------------
1. 复习事件处理
1.1 UIEvent 对象介绍(了解)【UIEvent是一个事件对象】
- 每产生一个事件,就会产生一个UIEvent对象
- UIEvent: 称为事件对象,记录事件产生的时刻和类型
- 一次完整的触摸过程中,只会产生一个事件对象,4个触摸方法都是同一个event参数
- 常见属性:(打开看枚举类型, 远程控制事件中又分很多子类型)
@property(nonatomic,readonly) UIEventType type;
@property(nonatomic,readonly) UIEventSubtype subtype;
1.2 UIResponder 对象
- 响应者对象, 能与用户交互的就是响应者对象
- UIApplication、UIViewController、UIView都继承自UIResponder,因此它们都是响应者对象,都能够接收并处理事件
1.3 注意:
- 如果两根手指同时触摸一个view,那么view只会调用一次touchesBegan:withEvent:方法,touches参数中装着2个UITouch对象
- 如果这两根手指一前一后分开触摸同一个view,那么view会分别调用2次touchesBegan:withEvent:方法,并且每次调用时的touches参数中只包含一个UITouch对象
- 根据touches中UITouch的个数可以判断出是单点触摸还是多点触摸
* 如果一根手指触摸屏幕叫做单点触摸
* 如果多根手指同时触摸屏幕叫做多点触摸
1.4 总结:
- 4个触摸事件处理方法中, 都有NSSet *touches和UIEvent *event两个参数, 一次完整的触摸过程中,只会产生一个事件对象,4个触摸方法都是同一个event参数。在一次完整的触摸事件中 touches 集合中的 UITouch对象也是在各个阶段共享的。
2. 多点触摸(案例)
- 思路:
0> 把控制器 view的背景色变成黑色
1> 重写控制器的 touchesBegan 方法和 touchesMoved 方法
2> 在控制器中添加 images 属性, 在该属性中保存2张 UIImage 图片
3> 在 touchesBegan 方法中,:
* 获取每一个触摸对象, 以及每个触摸对象的 location
* 对于每个触摸对象创建一个 UIImageView, 并向其中添加一个 UIImage对象
* 最后把这个 UIImageView 添加到 self.view 中
4> 在 touchesMoved 方法中执行与 touchesBegan 方法中同样的代码
5> 在 touchesEnded 方法中输出当前 self.view 中的 UIImageView 的个数
6> 在每次添加完毕一个 UIImageView 后, 执行一个 alpha = 0的动画, 动画执行完毕从 self.view 中移除这个控件
** 注意: 一般默认情况下, 控件都不支持多点触摸(为了提高性能), 所以需要手动设置一个 UIView 允许多点触摸
/** 参考代码:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
int i = 0;
for (UITouch *touch in touches) {
CGPoint loc = [touch locationInView:self.view];
UIImageView *imgView = [[UIImageView alloc] initWithImage:self.images[i]];
imgView.center = loc;
[self.view addSubview:imgView];
i++;
// 慢慢消失
[UIView animateWithDuration:2.0 animations:^{
imgView.alpha = 0;
} completion:^(BOOL finished) {
[imgView removeFromSuperview];
}];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
int i = 0;
for (UITouch *touch in touches) {
CGPoint loc = [touch locationInView:self.view];
UIImageView *imgView = [[UIImageView alloc] initWithImage:self.images[i]];
imgView.center = loc;
[self.view addSubview:imgView];
i++;
// 慢慢消失
[UIView animateWithDuration:2.0 animations:^{
imgView.alpha = 0;
} completion:^(BOOL finished) {
[imgView removeFromSuperview];
}];
}
}
*/
3. 介绍控件不能接受用户交互的情况
* 演示要求:在控制器 view 中创建多个 UIView, 并且为每个 UIView 指定一个自定义 View, 实现 touchesBegan 方法.
1> 控件的userInteractionEnabled = NO 的情况下不能与用户交互
** 注意: 如果父容器不能与用户交互, 那么在该容器中的所有子控件也不能与用户交互(例如: 添加在 UIImageView 中的按钮)
2> 透明度小于等于0.01, alpha = 0.01
3> 控件被隐藏的时候, hidden = YES
4> 如果子视图的位置超出了父视图的有效范围, 那么子视图也是无法与用户交互的, 即使设置了父视图的 clipsToBounds = NO, 可以看懂, 但是也是无法与用户交互的
5> 默认情况下, 从控件库中拖拽的 UIImageView 是无法接受用户的触摸事件的
** 演示向 UIImageView 中添加一个按钮, 监听按钮的点击事件。
** UIImageView 默认是不支持多点触摸, 也不响应用户事件的。
补充: 直接从媒体库中把图片拖拽进来(通过这种方式拖进来的UIImageView, 默认即支持多点触摸也支持用户交互, 并且图片框大小就是图片的实际大小)
4. 事件响应链条
4.1 先看现象(查看 test 项目(预03-UIView不接受用户交互的情况))
- 触摸谁, 谁的触摸事件被执行(父容器的触摸事件也有可能被执行), 系统是如何找到被触摸的控件的?
- 按钮的单击事件是如何被触发的?
4.2 再看原理
- 什么是响应者? 能与用户交互就是响应者。所有继承自 UIResponder 的类型.
- 监听事件的基本流程:
1> 当应用程序启动以后创建 UIApplication 对象
2> 然后启动“消息循环”监听所有的事件
3> 当用户触摸屏幕的时候, "消息循环"监听到这个触摸事件
4> "消息循环" 首先把监听到的触摸事件传递了 UIApplication 对象
5> UIApplication 对象再传递给 UIWindow 对象
6> UIWindow 对象再传递给 UIWindow 的根控制器(rootViewController)
7> 控制器再传递给控制器所管理的 view
8> 控制器所管理的 View 在其内部搜索看本次触摸的点在哪个控件的范围内
9> 找到某个控件以后(调用这个控件的 touchesXxx 方法), 再一次向上返回, 最终返回给"消息循环"
10> "消息循环"知道哪个按钮被点击后, 在搜索这个按钮是否注册了对应的事件, 如果注册了, 那么就调用这个"事件处理"程序。(一般就是执行控制器中的"事件处理"方法)
** 解释触摸事件和单击事件的关系: 为按钮注册单击事件, 其实都是通过触摸事件找到对应的控件, 然后调用控件的单击事件的处理程序来执行的。
** 注意: 如果父控件不能处理"触摸事件", 那么子控件是不可能接收到"触摸事件"的, 事件链条断了。
hitTest方法的执行(系统是通过 hitTest 方法来进行递归搜索的)
1> - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event; 这个方法是 UIView 中的一个方法
2> 参数 point 表示是相对于当前 UIView 的点位置。用来判断这个触摸点是否在当前视图的"有效范围"内。
3> 这个方法的作用, 当控制器 view 接收到"触摸事件"后, 调用自己的 hitTest 方法, 递归搜索子控件, 看看是否有更合适的子控件来处理这个事件(如果这个触摸点在具体的某个子控件的有效范围内, 那么这个子控件就是"更合适的控件"), 找到某个子控件以后, 这个子控件再调用自己的 hitTest 方法依次类推继续查找"更合适的子控件", 直到没有子控件了或者没有包含这个点的子控件了
4> 如果当前控件的 userInteractionEnabled = NO, 那么就不再搜索它的子控件了。
5> 如果在 hitTest 方法中, 没有调用 super 的 hitTest 方法, 只是直接返回 self, 那么这样就终止了这个搜索过程, 系统会认为当前处理这个触摸的控件就是这个控件。
6. 手势识别
- 触摸事件只有4个:
1> 按下 touchesBegan
2> 移动 touchesMoved
3> 抬起 touchesEnded
4> 取消 touchesCanceled
- iOS3.2之后, 把触摸事件做了封装, 对常用的手势进行了处理, 封装了6种常见的手势
UITapGestureRecognizer(敲击)
UILongPressGestureRecognizer(长按)
UISwipeGestureRecognizer(轻扫)
UIRotationGestureRecognizer(旋转)
UIPinchGestureRecognizer(捏合,用于缩放)
UIPanGestureRecognizer(拖拽)
- 手势识别是单独添加到某个视图上的。
- 点按、点击、敲击手势
/** 参考:
- (void)viewDidLoad {
[super viewDidLoad];
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tabGestureHandler)];
// 需要的最小点击数
tapGesture.numberOfTapsRequired = 2;
// 需要的最少触摸点
tapGesture.numberOfTouchesRequired = 1;
[self.imgView addGestureRecognizer:tapGesture];
}
- (void)tabGestureHandler
{
NSLog(@"tab..");
}
*/
- 长按手势
/** 参考代码:
- (void)viewDidLoad {
[super viewDidLoad];
UILongPressGestureRecognizer *longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPressGestureHandler:)];
// 最小的长按时间
//longPressGesture.minimumPressDuration = 1.5;
[self.imgView addGestureRecognizer:longPressGesture];
}
- (void)longPressGestureHandler:(UILongPressGestureRecognizer *)recognizer
{
// 父类中的 state 属性来判断当前的状态
if (recognizer.state == UIGestureRecognizerStateBegan) {
NSLog(@"长按手势被识别。。。");
[UIView animateWithDuration:1.0 animations:^{
self.imgView.alpha = 0.3;
} completion:^(BOOL finished) {
[UIView animateWithDuration:1.0 animations:^{
self.imgView.alpha = 1.0;
}];
}];
}
}
*/
- 轻扫手势
/** 参考:
- (void)viewDidLoad {
[super viewDidLoad];
// 注意, 要想支持同时向多个方向的轻扫, 需要创建2个 SwipeGesture, 一个向左, 一个向右
UISwipeGestureRecognizer *swipeGestureLeft = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGestureHandler:)];
// 指定轻扫方向
swipeGestureLeft.direction = UISwipeGestureRecognizerDirectionLeft;
UISwipeGestureRecognizer *swipeGestureRight = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(swipeGestureHandler:)];
// 不设置, 默认也是向右
swipeGestureRight.direction = UISwipeGestureRecognizerDirectionRight;
[self.imgView addGestureRecognizer:swipeGestureLeft];
[self.imgView addGestureRecognizer:swipeGestureRight];
}
- (void)swipeGestureHandler:(UISwipeGestureRecognizer *)recognizer
{
NSLog(@"轻扫了。。");
CGPoint from = recognizer.view.center;
CGPoint to;
// 判断轻扫的方向
if (recognizer.direction == UISwipeGestureRecognizerDirectionLeft) {
to = CGPointMake(-2 * from.x , from.y);
} else {
to = CGPointMake(3 * from.x, from.y);
}
[UIView animateWithDuration:1.0 animations:^{
recognizer.view.center = to;
} completion:^(BOOL finished) {
[UIView animateWithDuration:1.0 animations:^{
recognizer.view.center = from;
}];
}];
}
*/
- 旋转手势
* 说明:
1> recognizer.rotation 默认表示的是当前手势旋转了的度数, 如果持续旋转了2圈, 那么这个 rotaion 就是4*π了
2> 每次出发一次旋转手势, 将 rotation 复位到0, 那么每次就是当前本次的旋转度数, 不是从手势一开始选在到现在的总的度数
/** 参考:
- (void)viewDidLoad {
[super viewDidLoad];
// 旋转手势
UIRotationGestureRecognizer *rotationGesture = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotationGestureHandler:)];
[self.imgView addGestureRecognizer:rotationGesture];
}
- (void)rotationGestureHandler:(UIRotationGestureRecognizer *)recognizer
{
//NSLog(@"旋转。。。。");
NSLog(@"%f", recognizer.rotation);
//recognizer.view.transform = CGAffineTransformMakeRotation(recognizer.rotation);
recognizer.view.transform = CGAffineTransformRotate(recognizer.view.transform, recognizer.rotation);
recognizer.rotation = 0;
}
*/
- 捏合手势(缩放)
/** 参考:
- (void)viewDidLoad {
[super viewDidLoad];
UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchGestureHandler:)];
[self.imgView addGestureRecognizer:pinchGesture];
}
- (void)pinchGestureHandler:(UIPinchGestureRecognizer *)recognizer
{
//recognizer.view.transform = CGAffineTransformMakeScale(recognizer.scale, recognizer.scale);
recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, recognizer.scale);
recognizer.scale = 1.0;
}
//------------------ 同时应用2个手势---------------------
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
- (void)viewDidLoad {
[super viewDidLoad];
// 缩放
UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchGestureHandler:)];
//pinchGesture.delegate = self;
[self.imgView addGestureRecognizer:pinchGesture];
// 旋转手势
UIRotationGestureRecognizer *rotationGesture = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotationGestureHandler:)];
rotationGesture.delegate = self;
[self.imgView addGestureRecognizer:rotationGesture];
}
- (void)pinchGestureHandler:(UIPinchGestureRecognizer *)recognizer
{
//recognizer.view.transform = CGAffineTransformMakeScale(recognizer.scale, recognizer.scale);
recognizer.view.transform = CGAffineTransformScale(recognizer.view.transform, recognizer.scale, recognizer.scale);
recognizer.scale = 1.0;
}
*/
//------------------ 同时应用2个手势---------------------
- 拖动、拖拽手势: UIPanGestureRecognizer(拖拽)
/** 参考:
- (void)viewDidLoad {
[super viewDidLoad];
//拖动手势
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureHandler:)];
[self.imgView addGestureRecognizer:panGesture];
}
- (void)panGestureHandler:(UIPanGestureRecognizer *)recognizer
{
CGPoint translation = [recognizer translationInView:recognizer.view];
recognizer.view.transform = CGAffineTransformTranslate(recognizer.view.transform, translation.x, translation.y);
// 复位
[recognizer setTranslation:CGPointZero inView:recognizer.view];
}
*/