本篇文章是学习传智播客iOS开发课程的整理笔记。
相关参考链接:iOS7之定制View Controller切换效果之presentViewController:animated:completion:方法:http://ningandjiao.iteye.com/blog/2049105
1>水印简介
* 图片水印作用:防止他人盗取图片,加一些Logo,生成一张新的图片。
* 怎么生成新的图片?和绘图一样的,需要拿到上下文做事情,这里也需要拿到上下文,生成一个新的图片。
* 什么上下文?位图上下文,在这个上下文画东西,就能输出到新的图片上。
* 怎么获取?之前用的都是图层上下文,系统会自动创建,但是我们位图上下文,需要我们手动创建
* 总结:只要不和view有关系的上下文,都需要我们手动创建。
* 在哪获取图像上下文,viewDidLoad,不需要拿到系统创建的图层上下文,没必要在drawRect方法里写,直接viewDidLoad就行了。
* 怎么创建图像上下文了?之前说过跟上下文有关的以什么开头,UIGraphics
* UIGraphicsBeginImageContextWithOptions:看注释,create bitmap,创建一个位图上下文,而且这种方法得到的图片最清晰。
* 解释参数(size:新图片尺寸 opaque:YES:不透明NO:透明 scale:0.0不伸缩)
* 绘制内容(图片,文字)
* 获取图片:把位图上下文的内容生成一个图片给你。
* 关闭上下文,不关闭一直占用着内存。
* 显示UIImageView上
* 保存图片,写到文件,UIImage不能写,需要转换成NSData二进制数据
* UIImageJPEGRepresentation:可以设置图片质量
* UIImagePNGRepresentation:把图片转换成png格式的二进制数据,png格式默认是最高清的。
* 写到桌面
- (void)viewDidLoad
{
[super viewDidLoad];
// 保存源图片
UIImage *oldImage = [UIImage imageNamed:@"img"];
// 1,开启上下文(创建一个上下文,不是获取,是创建)
// size 新的图片大小
// opaque YES 不透明 NO 透明
// scale 缩放比例
UIGraphicsBeginImageContextWithOptions(oldImage.size, NO, 0.0);
// 2,将源图片画进上下文
[oldImage drawAtPoint:CGPointZero];
// 要写进上下文的文字
NSString *text = @"我要高薪 !";
// 写进上下文的文字属性
NSDictionary *dict = @{
NSFontAttributeName : [UIFont systemFontOfSize:15],
NSForegroundColorAttributeName : [UIColor redColor]
};
// 3,将文字写进上下文
// 先画图再写文字
[text drawAtPoint:CGPointMake(120, 170) withAttributes:dict];
// 4,获取新的图片(将上下文中的内容生成新的图片,即,整合了图片和文字后的图片)
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
// 5,关闭上下文(不关闭的话,上写文一直存在,耗内存)
UIGraphicsEndImageContext();
// 在屏幕上显示图片
_imageView.image = newImage;
// 6,把图片转换成png格式的二进制数据(png就是最清晰的)
NSData *data = UIImagePNGRepresentation(newImage);
// 7,写入桌面(保存在桌面)
[data writeToFile:@"/Users/apple/Desktop/newImage.png" atomically:YES];
}
2>图片裁剪简介
* 图片裁剪
* 思路:先设置裁剪区域,把图片画上去,超出裁剪区域的自动裁剪掉。
* 加载旧图片,根据旧图片,获取上下文尺寸。
* 上下文的尺寸 =新图片的尺寸
* 开启一个多大的上下文?:和图片尺寸一样大,避免压缩图片。如果比图片尺寸小,会压缩图片。
* 设置裁剪区域:正切于图片的圆
* 绘制旧图片
* 获取新图片
* 关闭上下文
// 没有圆环的裁剪
- (void)viewDidLoad
{
[super viewDidLoad];
// 1.加载旧的图片
UIImage *oldImage = [UIImage imageNamed:@"阿狸头像"];
// 2.开启上下文
UIGraphicsBeginImageContextWithOptions(oldImage.size, NO, 0.0);
// 3.画圆:正切于上下文
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, oldImage.size.width, oldImage.size.height)];
// 4.设为裁剪区域
[path addClip];
// 5.画图片
[oldImage drawAtPoint:CGPointZero];
// 6.生成一个新的图片
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
// 7.关闭上下文
UIGraphicsEndImageContext();
// 8.显示到View上
_imageView.image = newImage;
}
* 带圆环裁剪:在裁剪的图片外边加个小圆环。
* 分析思路:先画一个大圆,在设置裁剪区域,把图片画上去,超出裁剪区域的自动裁剪掉。
* 加载旧图片,根据旧图片,获取上下文尺寸。
* 确定圆环宽度 borderW
* 上下文的尺寸 =新图片的尺寸
* 确定新的上下文尺寸: newImageW : oldImageW +2 * borderW newImageH : oldImageH +2 * borderW,
* 绘制大圆:
1.获取上下文2.添加路径到上下文3.设置大圆的颜色 =圆环的颜色 4.渲染
* 设置裁剪区域,和图片尺寸一样大,只不过,x,y不一样,x=borderW,y=borderW.
* 绘制旧图片
* 获取新图片
* 关闭上下文
* 抽分类,3个参数,图片名称,圆环宽度,圆环颜色
// 带有圆环的裁剪
- (void)viewDidLoad
{
[super viewDidLoad];
// 圆环的宽度
CGFloat borderW = 5;
// 加载旧的图片
UIImage *oldImage = [UIImage imageNamed:@"阿狸头像"];
// 设置最外的圆环所在矩形的宽高
CGFloat imageW = oldImage.size.width + 2 * borderW;
CGFloat imageH = oldImage.size.height + 2 * borderW;
// 设置最外圆环所在矩形(正方形)的宽高
// 判断是否为长方形,若是长方形,则取最短的那个
CGFloat circirW = imageW > imageH ? imageH : imageW;
// 开启上下文
UIGraphicsBeginImageContextWithOptions(CGSizeMake(circirW, circirW), NO, 0.0);
// 大圆
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, circirW, circirW)];
// 下面要把画的圆添加到上下,所以得获取当前上下文
// 获取当前上下文(获取的是位图上下文,而之前在drawRect:中获取的是layer上下文)
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 添加到上下文
CGContextAddPath(ctx, path.CGPath);
// 设置大圆填充颜色
[[UIColor whiteColor] set];
// 渲染
CGContextFillPath(ctx);
CGRect clipR = CGRectMake(borderW, borderW, oldImage.size.width, oldImage.size.height);
// 内小圆:正切于旧图片的圆
UIBezierPath *clipPath = [UIBezierPath bezierPathWithOvalInRect:clipR];
// 设置裁剪区域
[clipPath addClip];
// 画图片
[oldImage drawAtPoint:CGPointMake(borderW, borderW)];
// 获取新的图片
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
// 关闭上下文
UIGraphicsEndImageContext();
// 显示到View上
_imageView.image = newImage;
}
3> 屏幕截图简介
* 屏幕截图:把屏幕的内容截屏生成一张新的图片
* 通常开发中,都是把控制器的内容截屏,生成新的图片
* 控制器怎么显示?根据view
* view怎么显示?根据layer图层
* 把layer渲染到位图上下文
* 注意:图层只能用渲染,图片和文字可以用draw
* 渲染在哪?新的图片
* 开启图片上下文,和视图一样的尺寸
* 写入桌面
* 抽分类
// 将控制器的view截屏(控制器的view不包含状态栏)
- (void)viewDidLoad
{
[super viewDidLoad];
// 开启上下文
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, 0.0);
// 获取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 渲染控制器view的图层到上下文
// 图层只能用渲染不能用draw,只有图片和文字能用draw
[self.view.layer renderInContext:ctx];
// 获取截屏图片
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
// 关闭上下文
UIGraphicsEndImageContext();
// 转换成二进制的PNG图片
NSData *data = UIImagePNGRepresentation(newImage);
// 保存到指定文件夹
[data writeToFile:@"/Users/apple/Desktop/layer.png" atomically:YES];
}
4>手势解锁
* 演示实例程序
* 分析界面有几个控件:背景:UIImageView白色圆圈:按钮(点击他,会出现另外一种图片,按钮可以设置不同状态下的图片。)单独视图:(画线是有范围的,当超出view就不能画线了)
* HMLoadView:自定义视图(好处:代码可复用的其它项目),在视图一创建的时候,就添加9个按钮。
* 在initWithCoder,initWithFrame方法添加按钮。
* 九宫格布局:
tolcol = 3
计算row,col按钮的x,y跟col,row有关系,col = i % tolcol row = i / tolcol
计算边距 margin = (view.bounds.size.width - tolcol * btnW) / (tolcol +1)
btnX = margin + (btnW + margin) * col
btnY = (btnW + margin) * row
* 圆的选中
* 点击按钮就为选中的图片怎么做?监听按钮点击。
* 不能addTarget:不能及时显示选中图片。
* 监听touchBegin,判断点在不在按钮的frame上。
* touchBegin不调用?原因:事件交给按钮处理,应该把事件交给解锁视图。让按钮不接收事件。
* 设置按钮不允许交互,2个用处:1.不接收事件2.取消高亮效果一举两得
* 遍历所有按钮,看触摸点在哪个按钮上,就选中谁,CGRectContainsPoint传入的参数必须是同一个坐标系
* 实现touchMove方法:因为手指移动的时候,也需要判断点在不在按钮上。
* 抽方法,因为touchMove的方法里,也需要做同样的事情。
1> pointWithTouches根据touches集合取出触摸点
2> buttonWithPoint根据触摸点,获取触摸按钮
* 圆的连线
* 哪些需要连线?被选中按钮之间都需要连线,还有一个多余的线
* 先把选中的按钮全部连线?为什么,因为多余的那根线是从最后一个按钮的圆心开始画,手指移动在哪就画哪。
* 搞个数组保存下所有选中按钮,在drawRect方法中遍历所有按钮,连线
* UIBezierPath画线,不需要上下文。
* 需要多少个UIBezierPath对象?一个,路径都是连续的,不相连的,才需要创建新的UIBezierPath。
* 遍历数组,描述路径
1>起点:第一个按钮的圆心
2>添加一根线到其他按钮的圆心
* 设置路径的颜色和线宽
* 什么时候渲染?把所有路径都描述完就,渲染一次就够了。
* [path stroke] 就能渲染到视图上了。
* 画多余的那根线,记住手指移动的位置。
* setNeedDisplay 没有画出来线?因为drawRect只会调用一次,需要每次手指移动的时候,都需要重绘。
* touchBegin不调用setNeedDisplay?那时候重绘也没用,只有起点。
* lineJoinStyle:有尖尖的东西,线段连接样式的问题,设置为平的。
* 已经选中的按钮,不需要再次选中,和画线
* 手指抬起,取消所有选中按钮,并且清空数组,清空线条
* drawRect判断下没有选中数组,不需要画线。
* 如何判断用户是否输入正确?给选中按钮绑定tag,遍历所有选中按钮,把tag拼接成一个字符串。
// CYPaintView.m
#import "CYLockView.h"
@interface CYLockView ()
@property(nonatomic, strong) NSMutableArray *selectedBtns; // 被选中的按钮
@property(nonatomic, assign) CGPoint curPoint; // 当前触摸点
@end
@implementation CYLockView
// 手动创建视图的时候才会调用
- (instancetype)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {
}
return self;
}
/**
- (id)initWithCoder:(NSCoder *)aDecoder
- (id)initWithFrame:(CGRect)frame
init说明这两个方法都是构造类的方法
在构造类的方法中可以做些初始化子类的事情
*/
- (NSMutableArray *)selectedBtns{
if (_selectedBtns == nil) {
_selectedBtns = [NSMutableArray array];
}
return _selectedBtns;
}
#pragma mark - 初始化按钮
// 解析XIB的时候,加载按钮
- (id)initWithCoder:(NSCoder *)aDecoder{
if (self = [super initWithCoder:aDecoder]) {
[self addBtns];
}
return self;
}
// 添加按钮
- (void)addBtns{
for (int i = 0; i < 9; i++) {
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[btn setBackgroundImage:[UIImage imageNamed:@"gesture_node_normal"] forState:UIControlStateNormal];
[btn setBackgroundImage:[UIImage imageNamed:@"gesture_node_highlighted"] forState:UIControlStateSelected]; // 设置为选中模式
// 不允许按钮交互,好处:1. 点击的时候不会高亮,2. 将点击事件交给self.view来处理
btn.userInteractionEnabled = NO;
btn.tag = i; // 设置tag用于标记按钮
[self addSubview:btn];
}
}
// 设置按钮的位置
- (void)layoutSubviews{
[super layoutSubviews];// 这一句一定要有
CGFloat btnW = 74;
CGFloat btnH = 74;
CGFloat btnX = 0;
CGFloat btnY = 0;
NSUInteger totalCol = 3;
CGFloat margin = (self.bounds.size.width - totalCol * btnW) / (totalCol + 1);
for (int i = 0; i < self.subviews.count; i++) {
UIButton *btn = self.subviews[i];
CGFloat row = i / totalCol;
CGFloat col = i % totalCol;
btnX = margin + (margin + btnW) * col;
btnY = (margin + btnH) * row;
btn.frame = CGRectMake(btnX, btnY, btnW, btnH);
}
}
#pragma mark - 触摸屏幕,相应的按钮响应
// 通过给定触摸点判断该点在不在所有按钮上,在的话则选中该按钮
- (void)pointInButtonWithTouches:(NSSet *)touches{
// 1,获取触摸对象
UITouch *touch = [touches anyObject];
// 2,获取触摸点
CGPoint point = [touch locationInView:self];
_curPoint = point;
// 3,遍历所有按钮,判断点是否在该按钮上
for (UIButton *btn in self.subviews) {
// 判断点是否在按钮上的区域缩小
CGFloat wh = 30;
CGFloat x = btn.center.x - wh * 0.5;
CGFloat y = btn.center.y - wh * 0.5;
CGRect frame = CGRectMake(x, y, wh, wh);
// 判断点在不在CGRect上
// 判断的时候要保证它们在一个坐标系上,btn.frame和point在一个坐标系上,所以这里就不能用btn.bounds
if (CGRectContainsPoint(frame, point) && btn.selected == NO) {// 点在按钮上,并且按钮没被选中
btn.selected = YES; // 选中按钮
[self.selectedBtns addObject:btn]; // 添加选中按钮
}
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
// “选中”触摸点所在的按钮
[self pointInButtonWithTouches:touches];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
// “选中”触摸点所在的按钮
[self pointInButtonWithTouches:touches];
// 重绘
[self setNeedsDisplay];
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
// 1,拼接密码
NSMutableString *pwd = [[NSMutableString alloc] init];
for (UIButton *btn in self.selectedBtns) {
[pwd appendFormat:@"%ld", (long)btn.tag];
}
NSLog(@"%@", pwd);
// 2,取消所有选中按钮的“选中”
// 这个方法是让数组的每个元素都调用@selector的方法,并且还可以传个参数,这里传@NO这个参数
// 就是把按钮中所有的按钮的“选中”都取消了
[self.selectedBtns makeObjectsPerformSelector:@selector(setSelected:) withObject:@NO];
// 3,清除画线,即:把“选中按钮”数组清空
[self.selectedBtns removeAllObjects];
// 4,重绘
[self setNeedsDisplay];
}
// 画线
- (void)drawRect:(CGRect)rect {
if(!self.selectedBtns.count) return;
// 创建路径
UIBezierPath *path = [UIBezierPath bezierPath];
// 把所有选中的按钮之间都连线
for (int i = 0; i < self.selectedBtns.count; i++) {
UIButton *btn = self.selectedBtns[i];
if (i == 0) { // 起点
[path moveToPoint:btn.center];
}else{ // 其它点
[path addLineToPoint:btn.center];
}
}
// 最后一个选中按钮与当前触摸点之间连线
[path addLineToPoint:_curPoint];
// 设置状态信息
[[UIColor greenColor] set]; // 颜色
path.lineWidth = 8; // 路径宽度
path.lineJoinStyle = kCGLineJoinRound; // 圆角
// 渲染到视图
// 之前画线还需要获取上下文,这里直接用这个方法,把那些东西都封装在这个方法里了
// 用之前的画线方法也行(获取上下文的方法)
[path stroke];
}
@end
5>画板
演示程序:
分析控件:ToolBar(使用它的好处:不需要管里面子控件的frame),画板view,自定义工具条(方便屏幕适配,能迅速固定里面子控件的位置)
自动布局
绘图思路:先描述路径,在渲染。
绘图功能:
* touchBegin设置画线起点:开始触摸的点
* 怎么设置起点?创建UIBezierPath,贝塞尔路径才能设置起点
* touchMove:手指移动到哪就画哪,addLine到移动的点
* setNeedDisplay,路径描述完了,就渲染到视图就好了。
* 只能画一条线?drawRect方法每次都会把之前的清掉,重新绘制
* 怎么绘制多条路径?搞个数组保存上一次的
* 设置线宽:每次滑动滑块,就改变下一次路径的宽度
* 监听滑块的值,把值传递给paintView,设置路径的线宽。
* 不能在drawRect写,会导致所有路径都是一个线宽,应该是每个路径都记录自己的线宽,而且线宽只需要设置一次,在路径一创建的时候就设置。
* 设置颜色:
* 自定义UIBezierPath,保存颜色,实现一条路径对应一个颜色
辅助功能:
* 清屏:清空所有路径数组
* 撤销:移除最后一条路径
* 橡皮擦:设置画笔为白色
* 保存:
1>把画板内容截屏
2>把图片保存到相册 UIImageWriteToSavedPhotosAlbum
3>保存相册的回调方法不能乱写,必须按照规定 image:didFinishSavingWithError:contextInfo:
* 照片选择
1>通常都是去相册里去照片
2> UIImagePickerController,就可以去手机相册了
3>用Modal,没有导航控制器,不能push
4>设置代理,获取图片
5>把图片传递给paintView
6>添加到路径,然后重绘。:画图片也需要顺序的
* 照片处理
1> PPT画思路:搞一个和画板一样的透明view,里面搞个UIImageView来显示我们从照片库选择的图片,然后对UIImageView进行旋转,缩放等操作
2> UImageView不能放在layoutSubViews里面设置尺寸,因为要设置他的形变,默认会调用他父类的layoutSubViews,导致一些莫名其妙的原因
3>在传图片的时候设置他的尺寸,和位置,让他和图片一样的尺寸,显示在中间
4>长按操作:在长按结束的时候,做操作
1.默认高亮状态,先变浅在恢复,设置alpha
2.动画结束后,把自己截屏,传给控制器里,在交给paintView显示
3.需要移除父视图,使命完成了,而且不移除,不能绘制东西,永远添加到paintView上面
// HMPaintView.h
#import <UIKit/UIKit.h>
@interface HMPaintView : UIView
@property (nonatomic, assign) CGFloat width; // 路径宽
@property (nonatomic, strong) UIColor *color; // 路径颜色
// 接受由相册中选择的图片,只是中间的一步,后来运用HMHandleImageView后,此属性便失去作用
@property (nonatomic, strong) UIImage *image;
// 清屏
- (void)clearScreen;
// 撤销
- (void)undo;
@end
// HMPaintView.m
#import "HMPaintView.h"
#import "HMPaintPath.h"
@interface HMPaintView ()
@property (nonatomic, strong) UIBezierPath *path;
@property (nonatomic, strong) NSMutableArray *paths;
@end
@implementation HMPaintView
#pragma mark - 初始化线宽
- (void)awakeFromNib
{
_width = 2;
}
#pragma mark - 懒加载路径数组
- (NSMutableArray *)paths
{
if (_paths == nil) {
_paths = [NSMutableArray array];
}
return _paths;
}
#pragma mark - 确定起点
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
// 获取触摸点
CGPoint pos = [self pointWithTouches:touches];
// 创建路径
HMPaintPath *path = [HMPaintPath paintPathWithLineWidth:_width color:_color startPoint:pos];
_path = path;
[self.paths addObject:path];
}
// 获取触摸点
- (CGPoint)pointWithTouches:(NSSet *)touches
{
UITouch *touch = [touches anyObject];
return [touch locationInView:self];
}
#pragma mark - 确定路径终点
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
// 获取触摸点
CGPoint pos = [self pointWithTouches:touches];
// 确定终点
[_path addLineToPoint:pos];
// 重绘
[self setNeedsDisplay];
}
#pragma mark - 绘制路径
- (void)drawRect:(CGRect)rect
{
// 如果路径数组为空则返回,比如清屏之后
if (!self.paths.count) return;
// 遍历路径数组中的所有的路径
for (HMPaintPath *path in self.paths) {
// 判断路径数组元素是否为image,因为image和线段画法不同
if ([path isKindOfClass:[UIImage class]]) { // UIImage
UIImage *image = (UIImage *)path;
[image drawAtPoint:CGPointZero];
}else{ // HMPaintPath
[path.color set];
[path stroke];
}
}
}
#pragma mark - 清屏
- (void)clearScreen
{
// 清空路径数组
[self.paths removeAllObjects];
// 重绘
[self setNeedsDisplay];
}
#pragma mark - 撤销
- (void)undo
{
// 移除最后一次画的路径
[self.paths removeLastObject];
// 重绘
[self setNeedsDisplay];
}
#pragma mark - 设置图片时,就把图片画在画板上
- (void)setImage:(UIImage *)image
{
_image = image;
[self.paths addObject:image];
[self setNeedsDisplay];
}
@end
// HMViewController.m
#import "HMViewController.h"
#import "MBProgressHUD+MJ.h"
#import "HMPaintView.h"
#import "HMHandleImageView.h"
@interface HMViewController ()<UINavigationControllerDelegate,UIImagePickerControllerDelegate>
@property (weak, nonatomic) IBOutlet HMPaintView *paintView;
@end
@implementation HMViewController
#pragma mark - 保存
- (IBAction)save:(id)sender {// 把画板截屏
// 1,开启上下文
UIGraphicsBeginImageContextWithOptions(_paintView.bounds.size, NO, 0.0);
// 2,获取当前上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 3,把画板上的内容渲染到上下文
[_paintView.layer renderInContext:ctx];
// 4,获取新的图片
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
// 5,关闭上下文
UIGraphicsEndImageContext();
// 6,把图片保存到用户的相册里面
// self为回调方法的调用者,@selector后的方法是不能随便调用的,是有规定的:必须调用image:didFinishSavingWithError:contextInfo:
// 这个方法可以在UIImageWriteToSavedPhotosAlbum()头文件中找到
UIImageWriteToSavedPhotosAlbum(newImage, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
}
// 保存相册后必须回调此方法
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
if (error) { // 保存失败
[MBProgressHUD showError:@"保存失败"];
}else{ // 保存成功
[MBProgressHUD showSuccess:@"保存成功"];
}
}
#pragma mark - 选择照片
- (IBAction)selectedPicture:(id)sender {
// 去用户的相册
// 照片选择器
// 去用户的相册必须用这个控制器(保护用户的信息)
// 通过图像选择器使用摄像头和照片库
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
// 图像选择器会将一系列图片或者视频作为他的源。此外,还可指定摄像头作为源
// 数据源:默认是UIImagePickerControllerSourceTypePhotoLibrary
// UIImagePickerControllerSourceTypePhotoLibrary,指定用户将从现有的媒体库重选取照片或视频。照片将被返回到委托
// UIImagePickerControllerSourceTypeCamera,让用户使用内置摄像头拍照或者录像。但是,在使用之前最好用if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]){}判断一下设备是否有内置摄像头,如果所指定的源当前可用,方法isSourceTypeAvailabel:将返回YES。
// UIImagePickerControllerSourceTypeSavedPhotosAlbum,指定用户将从现有照片库重选择照片,蛋选择范围仅限于最近实用的相册。此选项也可以在没有摄像头的设备上运行,虽然用处不大,但是扔可用来选取之前的屏幕快照。
picker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
// 设置delegate
// 如果用户没有按取消,那么用户拍摄的或从库重选择的图像或视频会被传送到委托。无论用户选择了一个媒体文件还是取消了,委托都有责任解除UIImagePickerController,让用户返回到应用。
picker.delegate = self;
// 展示照片选择器(类型,push/pop等,把控制器展示出来)
// 方法并不局限于呈现图像选取器;通过对当前可见视图的视图控制器调用此方法,可以将任何视图控制器以模态(Modal)的方式呈现给用户。
[self presentViewController:picker animated:YES completion:nil];
}
// 代理方法:当用户成功拍摄了照片或视频,或从照片库中选择了相应项之后,调用此方法
// 第一个参数是之前创建的UIImagePickerController的指针
/*
info:{
UIImagePickerControllerMediaType = "public.image";
UIImagePickerControllerOriginalImage = "<UIImage: 0x7ff50c86e990> size {1008, 669} orientation 0 scale 1.000000";
UIImagePickerControllerReferenceURL = "assets-library://asset/asset.JPG?id=2A456415-B1AE-424A-9795-A0625A768EBD&ext=JPG";
}
*/
// 第二个参数包含三个键值,1,如果需要在图像选择器重编辑(并且用户确实对图像或视频进行了编辑),那么包含可选的编辑信息;2.存储在键值UIImagePickerControllerOriginalImage下是,未编辑的原始图像;3.用户所选照片或者当前所选视频的URL;
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{NSLog(@"dict-------info:%@", info);
// 1,获取选中图片
// 可以跳进UIImagePickerControllerOriginalImage的头文件查看其它info的键值信息
UIImage *newImage = info[UIImagePickerControllerOriginalImage];
// 2,创建处理图片的view
HMHandleImageView *handleView = [[HMHandleImageView alloc] initWithFrame:self.paintView.frame];
// 4,定义block:相当于自己的小弟,到时候直接吩咐做事
// 给block赋值
handleView.block = ^(UIImage *image){
_paintView.image = image;
};
// 3,将选择图片传给HMHandleImageView
handleView.image = newImage;
// 5,添加到子视图
[self.view addSubview:handleView];
// 6,取消Modal
[self dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark 橡皮擦
- (IBAction)eraser:(id)sender {
_paintView.color = [UIColor whiteColor];
}
#pragma mark 撤销
- (IBAction)undo:(id)sender {
[_paintView undo];
}
#pragma mark 清屏
- (IBAction)clearScreen:(id)sender {
[_paintView clearScreen];
}
#pragma mark 颜色选择
- (IBAction)colorClick:(UIButton *)sender {
// 颜色传递给画板View
_paintView.color = sender.backgroundColor;
}
#pragma mark 监听滑块的值
- (IBAction)valueChange:(UISlider *)sender {
// 将变化的值传递给画板View
_paintView.width = sender.value;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
// HMPaintPath.h
#import <UIKit/UIKit.h>
@interface HMPaintPath : UIBezierPath
@property (nonatomic, strong) UIColor *color;
+ (instancetype)paintPathWithLineWidth:(CGFloat)width color:(UIColor *)color startPoint:(CGPoint)startP;
@end
// HMPaintPath.m
#import "HMPaintPath.h"
@implementation HMPaintPath
+ (instancetype)paintPathWithLineWidth:(CGFloat)width color:(UIColor *)color startPoint:(CGPoint)startP
{
HMPaintPath *path = [[self alloc] init];
path.lineWidth = width;
path.color = color;
[path moveToPoint:startP];
return path;
}
@end
// HMHandleImageView.h
#import <UIKit/UIKit.h>
// 1,定义block
typedef void(^HMHandleImageViewBlock)(UIImage *image);
@interface HMHandleImageView : UIView
@property (nonatomic, strong) UIImage *image;
// 2,定义属性block
@property (nonatomic, copy) HMHandleImageViewBlock block;
@end
// HMHandleImageView.m
#import "HMHandleImageView.h"
#import "UIImage+Tool.h"
@interface HMHandleImageView()<UIGestureRecognizerDelegate>
@property (nonatomic, weak) UIImageView *imageView;
@end
@implementation HMHandleImageView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// 1,添加UIImageView
[self addImageView];
// 2,添加手势
[self addGestureRecognizers];
}
return self;
}
#pragma mark 添加图片
- (void)addImageView
{
UIImageView *imageView = [[UIImageView alloc] initWithFrame:self.bounds];
imageView.userInteractionEnabled = YES;
_imageView = imageView;
[self addSubview:imageView];
}
#pragma mark 传给一个图片就展示到UIImageView上
- (void)setImage:(UIImage *)image
{
_image = image;
_imageView.image = image;
}
#pragma mark - 添加手势
- (void)addGestureRecognizers
{
// 1.长按
UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];
[_imageView addGestureRecognizer:longPress];
// 2,捏合
[self addPinch];
// 3,旋转
[self addRotation];
}
#pragma mark 长按
- (void)longPress:(UILongPressGestureRecognizer *)longPrss
{
// 长按手势会触发两次,所以先判断一下是哪次触发
if (longPrss.state == UIGestureRecognizerStateBegan) {
[UIView animateWithDuration:0.5 animations:^{
_imageView.alpha = 0.3;
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.5 animations:^{
_imageView.alpha = 1;
} completion:^(BOOL finished) {
// 1.截屏
UIImage *newImage = [UIImage imageWithCaptureView:self];
// 2.把图片传给控制器
// 调用block
_block(newImage);
// 3.把自己移除父控件(不移除则它一直蒙在画板上,进而在画板中无法继续画东西)
[self removeFromSuperview];
}];
}];
}
}
#pragma mark - 捏合
- (void)addPinch
{
UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinch:)];
// 设置代理的原因:想要同时支持多个手势
pinch.delegate = self;
[_imageView addGestureRecognizer:pinch];
}
- (void)pinch:(UIPinchGestureRecognizer *)pinch
{
_imageView.transform = CGAffineTransformScale(_imageView.transform, pinch.scale, pinch.scale);
// 复位
pinch.scale = 1;
}
// Simultaneous:同时
// 默认是不支持多个手势
// 当你使用一个手势的时候就会调用
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
#pragma mark - 旋转
- (void)addRotation
{
// rotation
UIRotationGestureRecognizer *rotation = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotation:)];
rotation.delegate = self;
[_imageView addGestureRecognizer:rotation];
}
- (void)rotation:(UIRotationGestureRecognizer *)rotation
{
// _imagView.transform = CGAffineTransformMakeRotation(rotation.rotation);
_imageView.transform = CGAffineTransformRotate(_imageView.transform, rotation.rotation);
// 复位
rotation.rotation = 0;
}
@end