最近做圖片文字識(shí)別項(xiàng)目中有對(duì)圖片裁剪的需求斥废,本來使用的是TKImageView做裁剪功能饱亿,但產(chǎn)品說需要對(duì)圖片自由裁剪蚜退,可以在TKImageView的基礎(chǔ)上進(jìn)行修改闰靴,但太耗時(shí),于是只能自己寫一個(gè)圖片裁剪的工具钻注,其中主要的想法還是借鑒TKImageView蚂且,感謝大神!
先上效果圖
主要功能:
1.四個(gè)頂點(diǎn)自由拖動(dòng)
2.四個(gè)中心點(diǎn)按水平或垂直拖動(dòng)
Let's do IT!
1.圖片蒙層和裁剪區(qū)域線條設(shè)置
/// 設(shè)置蒙層路徑
- (void)resetMaskPath {
CAShapeLayer *maskLayer = (CAShapeLayer *)self.maskView.layer.mask;
if (!maskLayer) {
maskLayer = [CAShapeLayer layer];
self.maskView.layer.mask = maskLayer;
}
UIBezierPath *clearPath = [UIBezierPath bezierPath];
[clearPath moveToPoint:self.topLeftAreaView.center];
[clearPath addLineToPoint:self.topCenterAreaView.center];
[clearPath addLineToPoint:self.topRightAreaView.center];
[clearPath addLineToPoint:self.rightCenterAreaView.center];
[clearPath addLineToPoint:self.bottomRightAreaView.center];
[clearPath addLineToPoint:self.bottomCenterAreaView.center];
[clearPath addLineToPoint:self.bottomLeftAreaView.center];
[clearPath addLineToPoint:self.leftCenterAreaView.center];
[clearPath closePath];
UIBezierPath *maskPath = [[UIBezierPath bezierPathWithRect:self.maskView.bounds] bezierPathByReversingPath];
[maskPath appendPath:clearPath];
maskLayer.path = maskPath.CGPath;
self.lineLayer.path = clearPath.CGPath;
}
2.拖動(dòng)四個(gè)頂點(diǎn)的方法
/// 拖動(dòng)頂點(diǎn)裁剪范圍
- (void)movePoint:(UIPanGestureRecognizer *)pan {
AreaView *view = (AreaView *)pan.view;
if (pan.state == UIGestureRecognizerStateChanged) {
CGPoint location = [pan locationInView:self.areaView];
CGFloat x = location.x;
CGFloat y = location.y;
// 將裁剪范圍限制在圖片范圍內(nèi)
if (!CGRectContainsPoint(self.areaView.frame, location)) {
if (location.x < CGRectGetMinX(self.areaView.frame)) {
x = CGRectGetMinX(self.areaView.frame);
}
if (location.x > CGRectGetMaxX(self.areaView.frame)) {
x = CGRectGetMaxX(self.areaView.frame);
}
if (location.y < CGRectGetMinX(self.areaView.frame)) {
y = CGRectGetMinY(self.areaView.frame);
}
if (y > CGRectGetMaxY(self.areaView.frame)) {
y = CGRectGetMaxY(self.areaView.frame);
}
}
if (view == self.topLeftAreaView) {
x = MIN(x, self.topCenterAreaView.center.x - self.minClipWidthAndHeight/2);
y = MIN(y, self.leftCenterAreaView.center.y - self.minClipWidthAndHeight/2);
} else if (view == self.bottomLeftAreaView) {
x = MIN(x, self.topCenterAreaView.center.x - self.minClipWidthAndHeight/2);
y = MAX(y, self.leftCenterAreaView.center.y + self.minClipWidthAndHeight/2);
} else if (view == self.topRightAreaView) {
x = MAX(x, self.topCenterAreaView.center.x + self.minClipWidthAndHeight/2);
y = MIN(y, self.rightCenterAreaView.center.y - self.minClipWidthAndHeight/2);
} else if (view == self.bottomRightAreaView) {
x = MAX(x, self.bottomCenterAreaView.center.x + self.minClipWidthAndHeight/2);
y = MAX(y, self.rightCenterAreaView.center.y + self.minClipWidthAndHeight/2);
}
view.center = CGPointMake(x, y);
[self resetMaskPath];
}
}
3.拖動(dòng)中心點(diǎn)方法
/// 拖動(dòng)中心點(diǎn)裁剪范圍
- (void)moveCenterPoint:(UIPanGestureRecognizer *)pan {
CenterAreaView *view = (CenterAreaView *)pan.view;
if (pan.state == UIGestureRecognizerStateChanged) {
CGPoint point = [pan locationInView:self.areaView];
if (view == self.leftCenterAreaView ||
view == self.rightCenterAreaView) {
CGFloat x = point.x;
if (x < CGRectGetMinX(self.areaView.frame)) {
x = CGRectGetMinX(self.areaView.frame);
}
if (x > CGRectGetMaxX(self.areaView.frame)) {
x = CGRectGetMaxX(self.areaView.frame);
}
if (view == self.leftCenterAreaView) {
x = MIN(x, self.topCenterAreaView.center.x - self.minClipWidthAndHeight/2);
} else {
x = MAX(x, self.topCenterAreaView.center.x + self.minClipWidthAndHeight/2);
}
CGPoint center = view.center;
CGPoint center1 = view.relationView1.center;
CGPoint center2 = view.relationView2.center;
view.center = CGPointMake(x, center.y);
view.relationView1.center = CGPointMake(x, center1.y);
view.relationView2.center = CGPointMake(x, center2.y);
} else {
CGFloat y = point.y;
if (y < CGRectGetMinY(self.areaView.frame)) {
y = CGRectGetMinY(self.areaView.frame);
}
if (y > CGRectGetMaxY(self.areaView.frame)) {
y = CGRectGetMaxY(self.areaView.frame);
}
if (view == self.topCenterAreaView) {
y = MIN(y, self.leftCenterAreaView.center.y - self.minClipWidthAndHeight/2);
} else {
y = MAX(y, self.leftCenterAreaView.center.y + self.minClipWidthAndHeight/2);
}
CGPoint center = view.center;
CGPoint center1 = view.relationView1.center;
CGPoint center2 = view.relationView2.center;
view.center = CGPointMake(center.x, y);
view.relationView1.center = CGPointMake(center1.x, y);
view.relationView2.center = CGPointMake(center2.x, y);
}
[self resetMaskPath];
}
}
4.獲取裁剪后圖片(主要是根據(jù)圖片大小轉(zhuǎn)換貝塞爾曲線)
/// 獲取裁剪后的圖片
- (UIImage *)currentImage {
CGFloat hScale = self.originImage.size.width/self.imageView.bounds.size.width;
CGFloat vScale = self.originImage.size.height/self.imageView.bounds.size.height;
/*
將imageview的路徑轉(zhuǎn)換為圖片的路徑
這樣可以使圖片在切割時(shí)不用縮放幅恋,防止圖片失真
*/
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:CGPointMake(self.topLeftAreaView.center.x*hScale, self.topLeftAreaView.center.y*vScale)];
[path addLineToPoint:CGPointMake(self.topCenterAreaView.center.x*hScale, self.topCenterAreaView.center.y*vScale)];
[path addLineToPoint:CGPointMake(self.topRightAreaView.center.x*hScale, self.topRightAreaView.center.y*vScale)];
[path addLineToPoint:CGPointMake(self.rightCenterAreaView.center.x*hScale, self.rightCenterAreaView.center.y*vScale)];
[path addLineToPoint:CGPointMake(self.bottomRightAreaView.center.x*hScale, self.bottomRightAreaView.center.y*vScale)];
[path addLineToPoint:CGPointMake(self.bottomCenterAreaView.center.x*hScale, self.bottomCenterAreaView.center.y*vScale)];
[path addLineToPoint:CGPointMake(self.bottomLeftAreaView.center.x*hScale, self.bottomLeftAreaView.center.y*vScale)];
[path addLineToPoint:CGPointMake(self.leftCenterAreaView.center.x*hScale, self.leftCenterAreaView.center.y*vScale)];
[path closePath];
return [self.originImage clipWithPath:[path copy]];
}
5.使用貝塞爾曲線對(duì)圖片進(jìn)行裁剪杏死,方法如下
注:從相冊(cè)獲取文中效果圖的圖片裁剪后出現(xiàn)上下顛倒的狀態(tài),這是因?yàn)閳D片的imageOrientation屬性不是UIImageOrientationUp捆交,所以拿到圖片后需要先判斷圖片是不是UIImageOrientationUp狀態(tài)淑翼,如果不是需要做相應(yīng)處理:1.將圖片的狀態(tài)修改為UIImageOrientationUp狀態(tài);2.將圖片旋轉(zhuǎn)到UIImageOrientationUp狀態(tài)品追;我采用的是第一種方案(因?yàn)榇a少??)
// 根據(jù)path切割圖片
- (UIImage*)clipWithPath:(UIBezierPath*)path {
@autoreleasepool {
UIImage * image = [self copy];
// 解決圖片不是朝上的問題 - 重置為UIImageOrientationUp
if (image.imageOrientation != UIImageOrientationUp) {
UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);
[image drawInRect:(CGRect){0, 0, image.size}];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
//開始繪制圖片
UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);
CGContextRef contextRef = UIGraphicsGetCurrentContext();
UIBezierPath * clipPath = path;
CGContextAddPath(contextRef, clipPath.CGPath);
CGContextClosePath(contextRef);
CGContextClip(contextRef);
//坐標(biāo)系轉(zhuǎn)換
CGContextTranslateCTM(contextRef, 0, image.size.height);
CGContextScaleCTM(contextRef, image.scale, -image.scale);
CGRect drawRect = CGRectMake(0, 0, image.size.width, image.size.height);
CGContextDrawImage(contextRef, drawRect, [image CGImage]);
UIImage *destImg = UIGraphicsGetImageFromCurrentImageContext();
//結(jié)束繪畫
UIGraphicsEndImageContext();
//轉(zhuǎn)成png格式 會(huì)保留透明
NSData * data = UIImageJPEGRepresentation(destImg, .5);
UIImage * dImage = [UIImage imageWithData:data];
return dImage;
}
}
注意:每次調(diào)用UIGraphicsBeginImageContextWithOptions方法獲取上下文玄括,圖片編輯結(jié)束后一定不要忘記調(diào)用UIGraphicsEndImageContext方法關(guān)閉上下文,否則內(nèi)存會(huì)不斷增加肉瓦,直至溢出遭京!
遇到的問題:
相機(jī)或相冊(cè)獲取圖片過大在運(yùn)行時(shí)內(nèi)存會(huì)瞬間提高很多(60M左右,可能會(huì)更大)风宁,我的解決方案是在獲取圖片時(shí)對(duì)圖片進(jìn)行了裁剪(對(duì)圖片質(zhì)量要求高的不適用)
附方法:具體裁剪的大小根據(jù)自己的需求設(shè)置
- (UIImage *)clipImage:(UIImage *)image {
if (image.size.width > 500 || image.size.height > 500) {
CGFloat scale = image.size.height/image.size.width;
CGFloat width = image.size.width;
if (image.size.width > 500) {
width = 500;
}
CGSize size = CGSizeMake(width, width*scale);
UIGraphicsBeginImageContextWithOptions(size, NO, image.scale);
[image drawInRect:(CGRect){0, 0, size}];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
return image;
}
可擴(kuò)展的功能:(有興趣的可以嘗試一下)
1.圖片放大、縮小
2.整體拖動(dòng)裁剪區(qū)域
更新:
1蛹疯、優(yōu)化錨點(diǎn)拖動(dòng) - 只有四個(gè)頂點(diǎn)可以自由移動(dòng)戒财,中間的錨點(diǎn)只能上下或左右移動(dòng),這樣切的圖片不會(huì)太不規(guī)則(其實(shí)是因?yàn)楫a(chǎn)品要求的)捺弦,且中間錨點(diǎn)一直處于中間狀態(tài)饮寞。
2、可移動(dòng)裁剪區(qū)域
3列吼、支持圖片縮放手勢(shì)幽崩,但有個(gè)問題就是放大圖片后無法拖動(dòng)裁剪區(qū)域