项目中要用到,选取图库或者拍照的图片,然后截取固定的尺寸作为头像,当时是使用第三方库GKImagePicker,但是其中的bug,弄了半天也修不了,并且感觉它的这个功能的代码做的太复杂了,所有自己写了一个类似功能的。
这里主要记录过程中发现的一些东西:
scrollview缩放后,其frame不变,sourceImage的frame会被缩放到需要的大小,scrollview的contentsize不论之前是多少,在缩放后,会和sourceImage的frame一样大小。
截取内容尺寸大小,需要乘以一个比例,这个比例是image的frame大小和image实际图像大小的比例。
scrollview如果contentoffset是处于边界了,就无法移动,不论加在上面的sourceImage有多大,都会被弹回,而contentsize的默认坐标也只能是(0,0)。
@interface CropImageView : UIView<UIScrollViewDelegate> {
UIImageView* _sourceImage;
UIScrollView* _scrollView;
UIView* _maskView;
CGFloat _addWidth;
CGFloat _addHeight;
}
@property(nonatomic, strong) UIImage* inputImage;
@property(nonatomic, strong) UIImage* outputImage;
// 裁剪需要的尺寸,会居中显示
@property(nonatomic, assign) CGSize cropSize;
#define SCREEN_WIDTH [[UIScreen mainScreen] bounds].size.width
#define SCREEN_HEIGHT [[UIScreen mainScreen] bounds].size.height
#import "CropImageView.h"
@implementation CropImageView
- (UIImage *)outputImage {
CGFloat w_faktor = _sourceImage.image.size.width / _sourceImage.frame.size.width;
CGFloat h_faktor = _sourceImage.image.size.height / _sourceImage.frame.size.height;
CGFloat x = _scrollView.contentOffset.x * w_faktor;
CGFloat y = _scrollView.contentOffset.y * h_faktor;
CGRect myImageRect = CGRectMake(x, y, _cropSize.width * w_faktor, _cropSize.height * h_faktor);
CGImageRef sourceImageRef = [_sourceImage.image CGImage];
CGImageRef newImageRef = CGImageCreateWithImageInRect(sourceImageRef, myImageRect);
UIImage *newImage = [UIImage imageWithCGImage:newImageRef];
CGImageRelease(newImageRef);
return newImage;
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
return _scrollView;
}
- (void)layoutSubviews {
[super layoutSubviews];
_scrollView = [[UIScrollView alloc] init];
_scrollView.backgroundColor = [UIColor greenColor];
// scrollview的frame这里只是暂时设置
_scrollView.frame = CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
_scrollView.clipsToBounds = NO;
_scrollView.delegate = self;
_scrollView.minimumZoomScale = 1.0f;
_scrollView.maximumZoomScale = 20.0f;
[_scrollView setZoomScale:1.0f];
[self addSubview:_scrollView];
CGFloat resultHeight, resultWidth;
// 把图片进行压缩,但要比例不变
if (_inputImage.size.width > SCREEN_WIDTH) {
if (_inputImage.size.width > _inputImage.size.height) {
CGFloat faktor = _inputImage.size.width / SCREEN_WIDTH;
resultWidth = SCREEN_WIDTH;
resultHeight = _inputImage.size.height / faktor;
}
} else if (_inputImage.size.height > SCREEN_HEIGHT) {
if (_inputImage.size.height > _inputImage.size.width) {
CGFloat faktor = _inputImage.size.height / SCREEN_HEIGHT;
resultHeight = SCREEN_HEIGHT;
resultWidth = _inputImage.size.width / faktor;
}
} else {
resultWidth = _inputImage.size.width;
resultHeight = _inputImage.size.height;
}
// 计算位置,也要居中
CGFloat x = (_scrollView.frame.size.width - resultWidth) / 2;
CGFloat y = (_scrollView.frame.size.height - resultHeight) / 2;
// 补差,因为裁剪的显示位置也会居中,那如何让image的各个部分都可以滚动到裁剪位置内呢,这就需要补差
CGFloat addWidth = 0, addHeight = 0;
if (resultWidth > _cropSize.width) {
addWidth = (resultWidth - _cropSize.width) / 2;
}
if (resultHeight > _cropSize.height) {
addHeight = (resultHeight - _cropSize.height) / 2;
}
_addWidth = addWidth;
_addHeight = addHeight;
_scrollView.frame = CGRectMake(x, y, resultWidth, resultHeight);
_scrollView.contentSize = CGSizeMake(resultWidth + 2 * addWidth, resultHeight + 2 * addHeight);
_sourceImage = [[UIImageView alloc] initWithImage:_inputImage];
_sourceImage.backgroundColor = [UIColor brownColor];
_sourceImage.frame = CGRectMake(addWidth, addHeight, resultWidth, resultHeight);
[_scrollView addSubview:_sourceImage];
_scrollView.contentOffset = CGPointMake(addWidth, addHeight);
// 最上层的view,用来作为遮罩
_maskView = [[UIView alloc] initWithFrame:self.bounds];
_maskView.backgroundColor = [UIColor blackColor];
_maskView.alpha = 0.6;
_maskView.userInteractionEnabled = NO;
[self addSubview:_maskView];
x = (SCREEN_WIDTH - _cropSize.width) / 2;
y = (SCREEN_HEIGHT - _cropSize.height) / 2;
// 显示出裁剪位置尺寸
UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)];
// 这个方法在ios7下有问题,会变成三角形,具体原因不知,替代方法是换成圆角矩形,把圆角角度设为0
// [path appendPath:[[UIBezierPath bezierPathWithRoundedRect:CGRectMake(x, y, _cropSize.width, _cropSize.height) byRoundingCorners:0 cornerRadii:CGSizeZero] bezierPathByReversingPath]];
[path appendPath:[[UIBezierPath bezierPathWithRect:CGRectMake(x, y, _cropSize.width, _cropSize.height)] bezierPathByReversingPath]];
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = path.CGPath;
_maskView.layer.mask = shapeLayer;
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return _sourceImage;
}
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale {
CGSize recontentSize = CGSizeMake(_scrollView.contentSize.width + _addWidth * 2, _scrollView.contentSize.height + _addHeight * 2);
_scrollView.contentSize = recontentSize;
}