| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360 |
- //
- // VPImageCropperViewController.m
- // VPolor
- //
- // Created by Vinson.D.Warm on 12/30/13.
- // Copyright (c) 2013 Huang Vinson. All rights reserved.
- //
- #import "VPImageCropperViewController.h"
- #define SCALE_FRAME_Y 100.0f
- #define BOUNDCE_DURATION 0.3f
- @interface VPImageCropperViewController ()
- @property (nonatomic, retain) UIImage *originalImage;
- @property (nonatomic, retain) UIImage *editedImage;
- @property (nonatomic, retain) UIImageView *showImgView;
- @property (nonatomic, retain) UIView *overlayView;
- @property (nonatomic, retain) UIView *ratioView;
- @property (nonatomic, assign) CGRect oldFrame;
- @property (nonatomic, assign) CGRect largeFrame;
- @property (nonatomic, assign) CGFloat limitRatio;
- @property (nonatomic, assign) CGRect latestFrame;
- @end
- @implementation VPImageCropperViewController
- - (void)dealloc {
- self.originalImage = nil;
- self.showImgView = nil;
- self.editedImage = nil;
- self.overlayView = nil;
- self.ratioView = nil;
- }
- - (id)initWithImage:(UIImage *)originalImage cropFrame:(CGRect)cropFrame limitScaleRatio:(NSInteger)limitRatio {
- self = [super init];
- if (self) {
- self.cropFrame = cropFrame;
- self.limitRatio = limitRatio;
- self.originalImage = [self fixOrientation:originalImage];
- }
- return self;
- }
- - (void)viewDidLoad
- {
- [super viewDidLoad];
- [self initView];
- [self initControlBtn];
- }
- - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
- return NO;
- }
- - (void)initView {
- self.view.backgroundColor = [UIColor blackColor];
-
- self.showImgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)];
- [self.showImgView setMultipleTouchEnabled:YES];
- [self.showImgView setUserInteractionEnabled:YES];
- [self.showImgView setImage:self.originalImage];
- [self.showImgView setUserInteractionEnabled:YES];
- [self.showImgView setMultipleTouchEnabled:YES];
-
- // scale to fit the screen
- CGFloat oriWidth = self.cropFrame.size.width;
- CGFloat oriHeight = self.originalImage.size.height * (oriWidth / self.originalImage.size.width);
- CGFloat oriX = self.cropFrame.origin.x + (self.cropFrame.size.width - oriWidth) / 2;
- CGFloat oriY = self.cropFrame.origin.y + (self.cropFrame.size.height - oriHeight) / 2;
- self.oldFrame = CGRectMake(oriX, oriY, oriWidth, oriHeight);
- self.latestFrame = self.oldFrame;
- self.showImgView.frame = self.oldFrame;
-
- self.largeFrame = CGRectMake(0, 0, self.limitRatio * self.oldFrame.size.width, self.limitRatio * self.oldFrame.size.height);
-
- [self addGestureRecognizers];
- [self.view addSubview:self.showImgView];
-
- self.overlayView = [[UIView alloc] initWithFrame:self.view.bounds];
- self.overlayView.alpha = .5f;
- self.overlayView.backgroundColor = [UIColor blackColor];
- self.overlayView.userInteractionEnabled = NO;
- self.overlayView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
- [self.view addSubview:self.overlayView];
-
- self.ratioView = [[UIView alloc] initWithFrame:self.cropFrame];
- self.ratioView.layer.borderColor = [UIColor yellowColor].CGColor;
- self.ratioView.layer.borderWidth = 1.0f;
- self.ratioView.autoresizingMask = UIViewAutoresizingNone;
- [self.view addSubview:self.ratioView];
-
- [self overlayClipping];
- }
- - (void)initControlBtn {
- UIButton *cancelBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height - 50.0f, 100, 50)];
- cancelBtn.backgroundColor = [UIColor blackColor];
- cancelBtn.titleLabel.textColor = [UIColor whiteColor];
- [cancelBtn setTitle:@"Cancel" forState:UIControlStateNormal];
- [cancelBtn.titleLabel setFont:[UIFont boldSystemFontOfSize:18.0f]];
- [cancelBtn.titleLabel setTextAlignment:NSTextAlignmentCenter];
- [cancelBtn.titleLabel setLineBreakMode:NSLineBreakByWordWrapping];
- [cancelBtn.titleLabel setNumberOfLines:0];
- [cancelBtn setTitleEdgeInsets:UIEdgeInsetsMake(5.0f, 5.0f, 5.0f, 5.0f)];
- [cancelBtn addTarget:self action:@selector(cancel:) forControlEvents:UIControlEventTouchUpInside];
- [self.view addSubview:cancelBtn];
-
- UIButton *confirmBtn = [[UIButton alloc] initWithFrame:CGRectMake(self.view.frame.size.width - 100.0f, self.view.frame.size.height - 50.0f, 100, 50)];
- confirmBtn.backgroundColor = [UIColor blackColor];
- confirmBtn.titleLabel.textColor = [UIColor whiteColor];
- [confirmBtn setTitle:@"OK" forState:UIControlStateNormal];
- [confirmBtn.titleLabel setFont:[UIFont boldSystemFontOfSize:18.0f]];
- [confirmBtn.titleLabel setTextAlignment:NSTextAlignmentCenter];
- confirmBtn.titleLabel.textColor = [UIColor whiteColor];
- [confirmBtn.titleLabel setLineBreakMode:NSLineBreakByWordWrapping];
- [confirmBtn.titleLabel setNumberOfLines:0];
- [confirmBtn setTitleEdgeInsets:UIEdgeInsetsMake(5.0f, 5.0f, 5.0f, 5.0f)];
- [confirmBtn addTarget:self action:@selector(confirm:) forControlEvents:UIControlEventTouchUpInside];
- [self.view addSubview:confirmBtn];
- }
- - (void)cancel:(id)sender {
- if (self.delegate && [self.delegate conformsToProtocol:@protocol(VPImageCropperDelegate)]) {
- [self.delegate imageCropperDidCancel:self];
- }
- }
- - (void)confirm:(id)sender {
- if (self.delegate && [self.delegate conformsToProtocol:@protocol(VPImageCropperDelegate)]) {
- [self.delegate imageCropper:self didFinished:[self getSubImage]];
- }
- }
- - (void)overlayClipping
- {
- CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
- CGMutablePathRef path = CGPathCreateMutable();
- // Left side of the ratio view
- CGPathAddRect(path, nil, CGRectMake(0, 0,
- self.ratioView.frame.origin.x,
- self.overlayView.frame.size.height));
- // Right side of the ratio view
- CGPathAddRect(path, nil, CGRectMake(
- self.ratioView.frame.origin.x + self.ratioView.frame.size.width,
- 0,
- self.overlayView.frame.size.width - self.ratioView.frame.origin.x - self.ratioView.frame.size.width,
- self.overlayView.frame.size.height));
- // Top side of the ratio view
- CGPathAddRect(path, nil, CGRectMake(0, 0,
- self.overlayView.frame.size.width,
- self.ratioView.frame.origin.y));
- // Bottom side of the ratio view
- CGPathAddRect(path, nil, CGRectMake(0,
- self.ratioView.frame.origin.y + self.ratioView.frame.size.height,
- self.overlayView.frame.size.width,
- self.overlayView.frame.size.height - self.ratioView.frame.origin.y + self.ratioView.frame.size.height));
- maskLayer.path = path;
- self.overlayView.layer.mask = maskLayer;
- CGPathRelease(path);
- }
- // register all gestures
- - (void) addGestureRecognizers
- {
- // add pinch gesture
- UIPinchGestureRecognizer *pinchGestureRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchView:)];
- [self.view addGestureRecognizer:pinchGestureRecognizer];
-
- // add pan gesture
- UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panView:)];
- [self.view addGestureRecognizer:panGestureRecognizer];
- }
- // pinch gesture handler
- - (void) pinchView:(UIPinchGestureRecognizer *)pinchGestureRecognizer
- {
- UIView *view = self.showImgView;
- if (pinchGestureRecognizer.state == UIGestureRecognizerStateBegan || pinchGestureRecognizer.state == UIGestureRecognizerStateChanged) {
- view.transform = CGAffineTransformScale(view.transform, pinchGestureRecognizer.scale, pinchGestureRecognizer.scale);
- pinchGestureRecognizer.scale = 1;
- }
- else if (pinchGestureRecognizer.state == UIGestureRecognizerStateEnded) {
- CGRect newFrame = self.showImgView.frame;
- newFrame = [self handleScaleOverflow:newFrame];
- newFrame = [self handleBorderOverflow:newFrame];
- [UIView animateWithDuration:BOUNDCE_DURATION animations:^{
- self.showImgView.frame = newFrame;
- self.latestFrame = newFrame;
- }];
- }
- }
- // pan gesture handler
- - (void) panView:(UIPanGestureRecognizer *)panGestureRecognizer
- {
- UIView *view = self.showImgView;
- if (panGestureRecognizer.state == UIGestureRecognizerStateBegan || panGestureRecognizer.state == UIGestureRecognizerStateChanged) {
- // calculate accelerator
- CGFloat absCenterX = self.cropFrame.origin.x + self.cropFrame.size.width / 2;
- CGFloat absCenterY = self.cropFrame.origin.y + self.cropFrame.size.height / 2;
- CGFloat scaleRatio = self.showImgView.frame.size.width / self.cropFrame.size.width;
- CGFloat acceleratorX = 1 - ABS(absCenterX - view.center.x) / (scaleRatio * absCenterX);
- CGFloat acceleratorY = 1 - ABS(absCenterY - view.center.y) / (scaleRatio * absCenterY);
- CGPoint translation = [panGestureRecognizer translationInView:view.superview];
- [view setCenter:(CGPoint){view.center.x + translation.x * acceleratorX, view.center.y + translation.y * acceleratorY}];
- [panGestureRecognizer setTranslation:CGPointZero inView:view.superview];
- }
- else if (panGestureRecognizer.state == UIGestureRecognizerStateEnded) {
- // bounce to original frame
- CGRect newFrame = self.showImgView.frame;
- newFrame = [self handleBorderOverflow:newFrame];
- [UIView animateWithDuration:BOUNDCE_DURATION animations:^{
- self.showImgView.frame = newFrame;
- self.latestFrame = newFrame;
- }];
- }
- }
- - (CGRect)handleScaleOverflow:(CGRect)newFrame {
- // bounce to original frame
- CGPoint oriCenter = CGPointMake(newFrame.origin.x + newFrame.size.width/2, newFrame.origin.y + newFrame.size.height/2);
- if (newFrame.size.width < self.oldFrame.size.width) {
- newFrame = self.oldFrame;
- }
- if (newFrame.size.width > self.largeFrame.size.width) {
- newFrame = self.largeFrame;
- }
- newFrame.origin.x = oriCenter.x - newFrame.size.width/2;
- newFrame.origin.y = oriCenter.y - newFrame.size.height/2;
- return newFrame;
- }
- - (CGRect)handleBorderOverflow:(CGRect)newFrame {
- // horizontally
- if (newFrame.origin.x > self.cropFrame.origin.x) newFrame.origin.x = self.cropFrame.origin.x;
- if (CGRectGetMaxX(newFrame) < self.cropFrame.size.width) newFrame.origin.x = self.cropFrame.size.width - newFrame.size.width;
- // vertically
- if (newFrame.origin.y > self.cropFrame.origin.y) newFrame.origin.y = self.cropFrame.origin.y;
- if (CGRectGetMaxY(newFrame) < self.cropFrame.origin.y + self.cropFrame.size.height) {
- newFrame.origin.y = self.cropFrame.origin.y + self.cropFrame.size.height - newFrame.size.height;
- }
- // adapt horizontally rectangle
- if (self.showImgView.frame.size.width > self.showImgView.frame.size.height && newFrame.size.height <= self.cropFrame.size.height) {
- newFrame.origin.y = self.cropFrame.origin.y + (self.cropFrame.size.height - newFrame.size.height) / 2;
- }
- return newFrame;
- }
- -(UIImage *)getSubImage{
- CGRect squareFrame = self.cropFrame;
- CGFloat scaleRatio = self.latestFrame.size.width / self.originalImage.size.width;
- CGFloat x = (squareFrame.origin.x - self.latestFrame.origin.x) / scaleRatio;
- CGFloat y = (squareFrame.origin.y - self.latestFrame.origin.y) / scaleRatio;
- CGFloat w = squareFrame.size.width / scaleRatio;
- CGFloat h = squareFrame.size.height / scaleRatio;
- if (self.latestFrame.size.width < self.cropFrame.size.width) {
- CGFloat newW = self.originalImage.size.width;
- CGFloat newH = newW * (self.cropFrame.size.height / self.cropFrame.size.width);
- x = 0; y = y + (h - newH) / 2;
- w = newH; h = newH;
- }
- if (self.latestFrame.size.height < self.cropFrame.size.height) {
- CGFloat newH = self.originalImage.size.height;
- CGFloat newW = newH * (self.cropFrame.size.width / self.cropFrame.size.height);
- x = x + (w - newW) / 2; y = 0;
- w = newH; h = newH;
- }
- CGRect myImageRect = CGRectMake(x, y, w, h);
- CGImageRef imageRef = self.originalImage.CGImage;
- CGImageRef subImageRef = CGImageCreateWithImageInRect(imageRef, myImageRect);
- CGSize size;
- size.width = myImageRect.size.width;
- size.height = myImageRect.size.height;
- UIGraphicsBeginImageContext(size);
- CGContextRef context = UIGraphicsGetCurrentContext();
- CGContextDrawImage(context, myImageRect, subImageRef);
- UIImage* smallImage = [UIImage imageWithCGImage:subImageRef];
- CGImageRelease(subImageRef);
- UIGraphicsEndImageContext();
- return smallImage;
- }
- - (UIImage *)fixOrientation:(UIImage *)srcImg {
- if (srcImg.imageOrientation == UIImageOrientationUp) return srcImg;
- CGAffineTransform transform = CGAffineTransformIdentity;
- switch (srcImg.imageOrientation) {
- case UIImageOrientationDown:
- case UIImageOrientationDownMirrored:
- transform = CGAffineTransformTranslate(transform, srcImg.size.width, srcImg.size.height);
- transform = CGAffineTransformRotate(transform, M_PI);
- break;
-
- case UIImageOrientationLeft:
- case UIImageOrientationLeftMirrored:
- transform = CGAffineTransformTranslate(transform, srcImg.size.width, 0);
- transform = CGAffineTransformRotate(transform, M_PI_2);
- break;
-
- case UIImageOrientationRight:
- case UIImageOrientationRightMirrored:
- transform = CGAffineTransformTranslate(transform, 0, srcImg.size.height);
- transform = CGAffineTransformRotate(transform, -M_PI_2);
- break;
- case UIImageOrientationUp:
- case UIImageOrientationUpMirrored:
- break;
- }
-
- switch (srcImg.imageOrientation) {
- case UIImageOrientationUpMirrored:
- case UIImageOrientationDownMirrored:
- transform = CGAffineTransformTranslate(transform, srcImg.size.width, 0);
- transform = CGAffineTransformScale(transform, -1, 1);
- break;
-
- case UIImageOrientationLeftMirrored:
- case UIImageOrientationRightMirrored:
- transform = CGAffineTransformTranslate(transform, srcImg.size.height, 0);
- transform = CGAffineTransformScale(transform, -1, 1);
- break;
- case UIImageOrientationUp:
- case UIImageOrientationDown:
- case UIImageOrientationLeft:
- case UIImageOrientationRight:
- break;
- }
-
- CGContextRef ctx = CGBitmapContextCreate(NULL, srcImg.size.width, srcImg.size.height,
- CGImageGetBitsPerComponent(srcImg.CGImage), 0,
- CGImageGetColorSpace(srcImg.CGImage),
- CGImageGetBitmapInfo(srcImg.CGImage));
- CGContextConcatCTM(ctx, transform);
- switch (srcImg.imageOrientation) {
- case UIImageOrientationLeft:
- case UIImageOrientationLeftMirrored:
- case UIImageOrientationRight:
- case UIImageOrientationRightMirrored:
- CGContextDrawImage(ctx, CGRectMake(0,0,srcImg.size.height,srcImg.size.width), srcImg.CGImage);
- break;
-
- default:
- CGContextDrawImage(ctx, CGRectMake(0,0,srcImg.size.width,srcImg.size.height), srcImg.CGImage);
- break;
- }
-
- CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
- UIImage *img = [UIImage imageWithCGImage:cgimg];
- CGContextRelease(ctx);
- CGImageRelease(cgimg);
- return img;
- }
- @end
|