1.带有边框的圆形图片裁剪(实用)
一般我们会抽取一个分类,等用到的时候就直接调用.其实自己写一个也挺方便的.主要的思路是用到位图上下文的原理(一般需要用到图片的地方,如果你想要动它的话,一般都要开).然后就是设置路径(绘制圆形),然后就是裁剪.把图片绘制到上下文中.生成一张新的图片,然后直接用就ok乐.多余就不说了,直接上代码.
//0.设置边框宽度
//1.加载图片
//2.开启一个位图上下文(W = imageW + 2 * border H = imageH + 2 * border)
CGSizesize =CGSizeMake(image.size.width+2* borderWH, image.size.height+2* borderWH);
UIGraphicsBeginImageContextWithOptions(size,NO,0);
//3.绘制一个圆形的形状.
UIBezierPath*path = [UIBezierPathbezierPathWithOvalInRect:CGRectMake(0,0, size.width, size.height)];
[colorset];
[pathfill];
//4.设置一个小圆,并设置成裁剪区域
path = [UIBezierPathbezierPathWithOvalInRect:CGRectMake(borderWH, borderWH, image.size.width, image.size.height)];
//把路径设置成裁剪区域
[pathaddClip];
//5.把图片绘制到上下文当中.
[imagedrawAtPoint:CGPointMake(borderWH, borderWH)];
//6.从上下文当中生成一张图片
UIImage*newImage =UIGraphicsGetImageFromCurrentImageContext();
//7.关闭上下文.
UIGraphicsEndImageContext();
2.全屏截图(这个比较简单)
思路:当我们手指点击的时候,那么它就开启一张位图上下文,然后直接把VIew里面的内容用渲染的方式绘制到上下文中(不能直接绘制).然后就是生成一张新的图片,关闭上下文等.当然生成的图片怎么传输?这里只说了保存到桌面,用到的是二进流来进行传输
//1.开启位图上下文.
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size,NO,0);
//2.把控制器View的内容绘制到上下文当中.
//layer是不能够直接绘制的.要用渲染的方法才能够让它绘制到上下文当中.
CGContextRefctx =UIGraphicsGetCurrentContext();
[self.view.layerrenderInContext:ctx];
//3.生成一张图片
UIImage*newImage =UIGraphicsGetImageFromCurrentImageContext();
//4.关闭上下文
UIGraphicsEndImageContext();
//把生成的图片写入到桌面.都是以二进流进行传输.
//怎么样把图片转成二进制流.
//image:要把哪一张图片转成二进制流
//compressionQuality:压缩质量.
NSData*data =UIImagePNGRepresentation(newImage);
[datawriteToFile:@"/Users/xmg/Desktop/newImage.png"atomically:YES];
3.图片截屏
其实跟全屏截图差不多,只是这里多了一个手势,而且我们要根据手势三个方法(开始,移动,结束)来进行相对应的操作.当然阴影部分制造是懒加载形式加载.当然也是开始的时候,我们就要获取开始的点,当移动的时候我们计算整个截取范围的尺寸(遮盖的frame),当结束的时候我们会生成一张新的图片(开启位图,裁剪等等).当然还要记得移除遮盖
@property(nonatomic,assign)CGPointstartP;
/**遮盖*/
@property(nonatomic,weak)UIView*coverView;
-(UIView*)coverView{
if(_coverView==nil) {
UIView*coverView = [[UIViewalloc]init];
coverView.backgroundColor= [UIColorblackColor];
coverView.alpha=0.7;
_coverView= coverView;
[self.viewaddSubview:coverView];
}
return_coverView;
}
- (IBAction)pan:(UIPanGestureRecognizer*)pan {
CGPointcurP = [panlocationInView:pan.view];
if(pan.state==UIGestureRecognizerStateBegan) {
//获取当前手指的点.
self.startP= curP;
}elseif(pan.state==UIGestureRecognizerStateChanged){
CGFloatoffsetX = curP.x-self.startP.x;
CGFloatoffsetY = curP.y-self.startP.y;
CGRectrect =CGRectMake(self.startP.x,self.startP.y, offsetX, offsetY);
self.coverView.frame= rect;
}elseif(pan.state==UIGestureRecognizerStateEnded){
//开启一个跟UIimageView相同大小的上下文.
UIGraphicsBeginImageContextWithOptions(self.imageV.bounds.size,NO,0);
//self.coverView.frame
UIBezierPath*path = [UIBezierPathbezierPathWithRect:self.coverView.frame];
//把路径设置成裁剪区域
[pathaddClip];
//把UIImageView的内容渲染到上下文当中.
//获取上下文
CGContextRefctx =UIGraphicsGetCurrentContext();
[self.imageV.layerrenderInContext:ctx];
//[self.imageV.image drawAtPoint:CGPointZero];
//从上下文当中生成一张图片
UIImage*newImage =UIGraphicsGetImageFromCurrentImageContext();
//关闭上下文
UIGraphicsEndImageContext();
self.imageV.image= newImage;
//移除遮盖
[self.coverViewremoveFromSuperview];
}
}
4.图片擦擦擦(类似妄撮)
咳咳,回到正题上,不要被上面的图片所迷惑,其实它很简单,它只是2张图片重叠(一张是穿衣服,另一张是没穿衣服).放在最上面的当然是穿衣服的.然后就是他的方法很简单,只是用到一个手势和用到上下文里德擦除方法(当然也就是我们用到上下文的方法).当然手势的话也是拖动手势,而且我们要获取其中的擦除区域,也就是指定一个尺寸范围.直接上代码.
- (void)pan:(UIPanGestureRecognizer*)pan{
//确定的擦除的宽高
CGFloatrectWH =30;
//获取当前手指所在的点
CGPointcurP = [panlocationInView:self.imageV];
//确定的擦除的区域(-0.5是为了居中,想想你的擦除的原点)
CGFloatx = curP.x- rectWH *0.5;
CGFloaty = curP.y- rectWH *0.5;
CGRectrect =CGRectMake(x, y, rectWH, rectWH);
//最终生成一张图片
//开启一个位图上下文
UIGraphicsBeginImageContextWithOptions(self.imageV.bounds.size,NO,0);
//获取上下文
CGContextRefctx =UIGraphicsGetCurrentContext();
//把UIImageView的内容渲染到上下文当中.
[self.imageV.layerrenderInContext:ctx];
//擦除指定的区域.
CGContextClearRect(ctx, rect);
//生成一张新的图片
UIImage*newImage =UIGraphicsGetImageFromCurrentImageContext();
//关闭上下文
UIGraphicsEndImageContext();
}
5.手势解锁(重要,这里会教你如何添加手势路径,而且获得其中的密码)
1.这里用到的素材只有3个,一个是背景图片,其他2个分别圆形按钮(未选中状态)和圆形按钮(选中状态)
2.当然这里我们也是创建2个类,一个是全屏View(设置背景图片).一个是中间的大图片(设置按钮).
3.先说一下全屏View,也没多少难度.你只需把它绘制上去就ok了.当然也是从零点开始绘制.
-(void)drawRect:(CGRect)rect{
//1.加载图片
UIImage*image = [UIImageimageNamed:@"Home_refresh_bg"];
[imagedrawInRect:rect];
}
4.中间大图片(这个慢慢来)
首先,我们先设置好按钮的位置,先把按钮摆上去,当然这里设置它们各自的frame也是有一定的技巧的,先计算好它们的间距,再去计算好它们的行和列.然后根据各自所处于的行和列计算它们的x和y.
4.1.1我们先弄好借口,哈哈.如果是独立开发的话,这里可以忽略.(setup是初始化方法).
-(void)awakeFromNib{
//初始化
[selfsetUp];
}
-(instancetype)initWithFrame:(CGRect)frame{
if(self= [superinitWithFrame:frame]) {
//初始化
[selfsetUp];
}
returnself;
}
4.1.2接下来,我们当然是创建按钮了,里面我们需要用到按钮里面的一个属性tag,我们可以用它给按钮来排号,方便我们在后面根据它的标识来给它设置frame.当然我们还要取消按钮接收事件,不然的话后面做不了拖线什么的.所以我们就让事件处理交给父控件来处理.
//初始化
- (void)setUp{
for(inti =0; i <9; i++) {
//创建按钮
UIButton*btn = [UIButtonbuttonWithType:UIButtonTypeCustom];
btn.tag= i;
//让按钮取消事件,让事件传给它的父控件.
btn.userInteractionEnabled=NO;
//设置正常状态图片
[btnsetImage:[UIImageimageNamed:@"gesture_node_normal"]forState:UIControlStateNormal];
//设置选中状态图片
[btnsetImage:[UIImageimageNamed:@"gesture_node_highlighted"]forState:UIControlStateSelected];
//添加按钮
[selfaddSubview:btn];
}
}
4.1.3然后就是设置它的frame.一般我们设置它的frame都是layoutsubviews里设置,当然所有按钮都是分开设置,由于按钮是存于View里面,我们只需根据子控件的数量和取出来里面的子控件,这里就多亏前面我们设置的tag.找到相对应的按钮.然后就是设置它们的frame.我们也是设置9次.我们用到也是间距的方法来计算,(简单的数学计算)直接看代码.
-(void)layoutSubviews{
[superlayoutSubviews];
CGFloatx =0;
CGFloaty =0;
CGFloatbtnWh =74;
intcloumn =3;
CGFloatmargin = (self.bounds.size.width- cloumn * btnWh) / (cloumn +1);
intcurCloumn =0;
intcurRow =0;
//取出所有的子控件,设置尺寸大小.
for(inti =0; i
//取出每一个按钮
UIButton*btn =self.subviews[i];
//当前所在的列
curCloumn = i % cloumn;
//当前所在的行
curRow = i / cloumn;
x = margin + curCloumn * (btnWh + margin);
y = curRow * (btnWh + margin);
//设置按钮尺寸
btn.frame=CGRectMake(x, y, btnWh, btnWh);
}
}
4.2.1手势方法,首先来到这里,我们要自定义2个方法(封装),一个是获取当前手指所在的点方法,一个是判断现在你手指的点在不在按钮身上,如果在的话,就返回按钮,如果不是的话就返回空值.
//根据一个touches集合获取当前手指所在的点
- (CGPoint)getCurrentPoint:(NSSet*)touches{
//获取当前手指所在的点
UITouch*touch = [touchesanyObject];
return[touchlocationInView:self];
}
//根据传入的点,判断传入的点在不在按钮身上
- (UIButton*)btnRectContainsPoint:(CGPoint)point{
for(UIButton*btninself.subviews) {
if(CGRectContainsPoint(btn.frame, point)) {
returnbtn;
}
}
returnnil;
}
4.2.2然后我们就开始设置手势的方法,首先我们先定义一个属性来保存选中的按钮.然后后就是手势的运用,分别也就是开始,移动,结束.但我们手指开始点击的时候,那么就获取当前手指的点,然后就是判断当前手指的点在不在按钮身上,如果在的话,那么就设置按钮的selected属性.而移动的话,也是获取当前手指的点,然后就是判断在不在按钮身上,在的话就点亮按钮.当然这里要用到重绘方法(后面会说的).而结束的时候,就是我们取消全部选中状态,这里我们就用到之前定义的数组,直接把它遍历出来,然后取消它选中状态,最后也要把那个数组清空一下,方便后面使用然后再重绘.这里我们可以看到密码哦,也就是我们绘制手势的密码,用到按钮的tag属性,用string来给它拼接就可以了.我们直接打印出来就可以了.
//开始点击时,按钮成为选中状态
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent*)event{
//获取当前手指所在的点
//UITouch *touch = [touches anyObject];
//CGPoint curP = [touch locationInView:self];
CGPointcurP = [selfgetCurrentPoint:touches];
//判断当前点在不在按钮身上.
UIButton*btn = [selfbtnRectContainsPoint:curP];
if(btn && btn.selected==NO) {
//当前点在这个按钮上面.
//让按钮成为选中状态
btn.selected=YES;
//保存选中的按钮
[self.selectBtnArrayaddObject:btn];
}
}
//移动过程当中,如果发现当前的点在按钮身上,让按钮成为选中状态.
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent*)event{
CGPointcurP = [selfgetCurrentPoint:touches];
self.curP= curP;
//判断当前点在不在按钮身上.
UIButton*btn = [selfbtnRectContainsPoint:curP];
if(btn && btn.selected==NO) {
//当前点在这个按钮上面.
//让按钮成为选中状态
btn.selected=YES;
//保存选中的按钮
[self.selectBtnArrayaddObject:btn];
}
//重绘
[selfsetNeedsDisplay];
}
//当手指松开时,所有的选中按钮取消选中状态.
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent*)event{
//1.取消所有选中的按钮
NSMutableString*str = [NSMutableStringstring];
for(UIButton*btninself.selectBtnArray) {
btn.selected=NO;
[strappendFormat:@"%ld",btn.tag];
}
NSLog(@"%@",str);
//2.把所有的路径给清空.
[self.selectBtnArrayremoveAllObjects];
[selfsetNeedsDisplay];
}
4.2.3绘制方法,我们可以做一个小判断,如果数组里有值的话,我们就绘制路径,连线什么的,但没有值的直接恢复原样.而数组有值得话,我们用一个循环根据数组的数量;而且里面做一个判断,如果i=0,也就是第一个,那么它就作为起点,其他都是直接连到它们各自的中心点.
-(void)drawRect:(CGRect)rect{
if(self.selectBtnArray.count) {
//创建路径.添加线
UIBezierPath*path = [UIBezierPathbezierPath];
//判断选中的按钮是不是第一个按钮.如果是第一个按钮,就把第一个按钮的中心点,设置成路径的起点.
for(inti =0; i
//取出选中的每一个按钮
UIButton*btn =self.selectBtnArray[i];
//判断选中的按钮是不是第一个按钮
if(i ==0) {
//就把第一个按钮的中心点,设置成路径的起点.
[pathmoveToPoint:btn.center];
}else{
//直接添加一根线到按钮的中心点
[pathaddLineToPoint:btn.center];
}
}
//添加一根线到当前手指所在的点.
[pathaddLineToPoint:self.curP];
//设置线宽度
[pathsetLineWidth:10];
//设置y颜色
[[UIColorredColor]set];
//设置连接样式
[pathsetLineJoinStyle:kCGLineJoinRound];
[pathstroke];
}
5.画板功能
1.先说一下界面的搭建吧,首先最上面,我们可以用到toolBar,这个是帮我们自动布局的,我们只需添加item上去就可以,最后的item我们需要添加一个弹簧属性(flexible Space).而下面的需要我们手动自动布局了,需要有点麻烦,但你懂得,青年,慢慢来.这里就不多说了.
2.然后就是拖线,首先我们就是拖线拖到控制器里,然后再调用中间空白View类里面的方法就可以了.(先说基本的属性设置)
//清屏
- (IBAction)clear:(id)sender {
[self.drawViewclear];
}
//撤销
- (IBAction)undo:(id)sender {
[self.drawViewundo];
}
//橡皮擦
- (IBAction)erase:(id)sender {
[self.drawViewerase];
}
//设置线的宽度
- (IBAction)setLineWidth:(UISlider*)sender {
[self.drawViewsetLineWidth:sender.value];
}
//设置线的颜色
- (IBAction)setLineColor:(UIButton*)sender {
[self.drawViewsetLineColor:sender.backgroundColor];
}
3.然后我们在我们自定义的类里面实现它们的方法.
3.1首先我们先定义需要保存的属性(当前绘制的路径,保存所有绘制路径的数组,宽度,颜色)
/**当前绘制的路径*/
@property(nonatomic,strong)UIBezierPath*path;
/**保存的是所有绘制的路径*/
@property(nonatomic,strong)NSMutableArray*pathArray;
@property(nonatomic,assign)CGFloatwidth;
@property(nonatomic,strong)UIColor*color;
3.2当然又用到数组,我们会毫不犹豫用到的是懒加载,然后就是添加它们的移动手势,而且会调用手势的方法.
-(NSMutableArray*)pathArray{
if(_pathArray==nil) {
_pathArray= [NSMutableArrayarray];
}
return_pathArray;
}
-(void)awakeFromNib{
//添加移动手势
UIPanGestureRecognizer*pan = [[UIPanGestureRecognizeralloc]initWithTarget:selfaction:@selector(pan:)];
[selfaddGestureRecognizer:pan];
self.width=1;
self.color= [UIColorblackColor];
}
3.3移动手势的方法;首先我们是获取当前手指的点,然后根据手指移动的状态,先是设置它的起点,而且我们也要一开始设置线条的宽度和颜色.由于线条并没有颜色这个属性,所以需要我们自定义类,然后再给它添加颜色的属性.同时我们要把当前的路径添加数组里,而且保存现在的路径,方便后面连线.然后在移动过程中连接到当前手指所指的位置.最后就是要重绘一下.
- (void)pan:(UIPanGestureRecognizer*)pan{
//获取当前手指的点
CGPointcurP = [panlocationInView:self];
//手指开始的点是路径的起点
if(pan.state==UIGestureRecognizerStateBegan){
MyBezierPath*path = [[MyBezierPathalloc]init];
//设置线的宽度
[pathsetLineWidth:self.width];
//设置颜色.当发现系统的类,瞒足不了要求时,想到自定义类.
path.color=self.color;
//把当前路径给保存起来
[self.pathArrayaddObject:path];
self.path= path;
[pathmoveToPoint:curP];
}elseif(pan.state==UIGestureRecognizerStateChanged){
//添加一根线到当前手指的点
[self.pathaddLineToPoint:curP];
//重绘
[selfsetNeedsDisplay];
}
}
-(void)drawRect:(CGRect)rect{
//[self.path stroke];
//取出所有的路径,绘制出来
for(MyBezierPath*pathinself.pathArray) {
[path.colorset];
[pathstroke];
}
}
3.4然后就是功能实现:
//清屏
- (void)clear{
//清空所有的路径
[self.pathArrayremoveAllObjects];
//重绘
[selfsetNeedsDisplay];
}
//撤销
- (void)undo{
//清空最后一条路径
[self.pathArrayremoveLastObject];
[selfsetNeedsDisplay];
}
//设置线的宽度
- (void)setLineWidth:(CGFloat)lineWidth{
self.width= lineWidth;
}
//设置线的颜色
- (void)setLineColor:(UIColor*)color{
self.color= color;
}
//橡皮擦
- (void)erase{
[selfsetLineColor:[UIColorwhiteColor]];
}
4.保存照片(也是截全屏功能一样,获取上下文,然后就是渲染.生成一张新的图片.最后就是把生成的图片写入到系统相册里面.)
//把生成的图片写入到系统相册
//注意:保存完毕执行的这方法必须得要是
//- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
UIImageWriteToSavedPhotosAlbum(newImage,self,@selector(image:didFinishSavingWithError:contextInfo:),nil);
5.照片功能(当我们点击照片按钮的时候,系统会跳到系统相册里面,而我们从系统相册里选取照片).
这里也是用到代理和设置它的来源(遵守协议),当然这是系统的,我们只需创建一个UIImagePickerController,然后就是把modal出来.当然我们选择了一张照片后,系统会调用一个方法.
协议*
//照片(从系统相册当中选择一张照片)
- (IBAction)photo:(id)sender {
UIImagePickerController*pickVC = [[UIImagePickerControlleralloc]init];
//设置来源
pickVC.sourceType=UIImagePickerControllerSourceTypeSavedPhotosAlbum;
//UIImagePickerController如果实现代理方法,需要手动去关掉它.
pickVC.delegate=self;
//modal
[selfpresentViewController:pickVCanimated:YEScompletion:nil];
}
//当选择一张照片时调用
-(void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
NSLog(@"%@",info);
UIImage*image = info[UIImagePickerControllerOriginalImage];
NSData*data =UIImagePNGRepresentation(image);
[datawriteToFile:@"/Users/xmg/Desktop/newImage.jpg"atomically:YES];
[selfdismissViewControllerAnimated:YEScompletion:nil];
}
6.涂鸦功能
咳咳,这个还没做,主要现在比较忙,可能这个是借口吧.我之前对着代码敲了一遍,但还是没实现出来.然后后面的任务比较繁琐.所以先搁置一边.等之后用得时候或者有空余时间就重新敲一遍,试试.请谅解.