123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110 |
- //
- // RSKImageCropViewController.m
- //
- // Copyright (c) 2014-present Ruslan Skorb, https://ruslanskorb.com
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to deal
- // in the Software without restriction, including without limitation the rights
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- // copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- // THE SOFTWARE.
- //
- #import "RSKImageCropViewController.h"
- #import "RSKTouchView.h"
- #import "RSKImageScrollView.h"
- #import "RSKImageScrollViewDelegate.h"
- #import "RSKInternalUtility.h"
- #import "UIImage+RSKImageCropper.h"
- #import "CGGeometry+RSKImageCropper.h"
- static const CGFloat kResetAnimationDuration = 0.4;
- static const CGFloat kLayoutImageScrollViewAnimationDuration = 0.25;
- @interface RSKImageCropViewController () <RSKImageScrollViewDelegate, UIGestureRecognizerDelegate>
- @property (assign, nonatomic) BOOL originalNavigationControllerNavigationBarHidden;
- @property (strong, nonatomic) UIImage *originalNavigationControllerNavigationBarShadowImage;
- @property (copy, nonatomic) UIColor *originalNavigationControllerViewBackgroundColor;
- @property (strong, nonatomic) RSKImageScrollView *imageScrollView;
- @property (strong, nonatomic) RSKTouchView *overlayView;
- @property (strong, nonatomic) CAShapeLayer *maskLayer;
- @property (assign, nonatomic) CGRect maskRect;
- @property (copy, nonatomic) UIBezierPath *maskPath;
- @property (readonly, nonatomic) CGRect rectForMaskPath;
- @property (readonly, nonatomic) CGRect rectForClipPath;
- @property (readonly, nonatomic) CGRect imageRect;
- @property (strong, nonatomic) UILabel *moveAndScaleLabel;
- @property (strong, nonatomic) UIButton *cancelButton;
- @property (strong, nonatomic) UIButton *chooseButton;
- @property (strong, nonatomic) UITapGestureRecognizer *doubleTapGestureRecognizer;
- @property (strong, nonatomic) UIRotationGestureRecognizer *rotationGestureRecognizer;
- @property (assign, nonatomic) BOOL didSetupConstraints;
- @property (strong, nonatomic) NSLayoutConstraint *moveAndScaleLabelTopConstraint;
- @property (strong, nonatomic) NSLayoutConstraint *cancelButtonBottomConstraint;
- @property (strong, nonatomic) NSLayoutConstraint *cancelButtonLeadingConstraint;
- @property (strong, nonatomic) NSLayoutConstraint *chooseButtonBottomConstraint;
- @property (strong, nonatomic) NSLayoutConstraint *chooseButtonTrailingConstraint;
- @end
- @implementation RSKImageCropViewController
- #pragma mark - Lifecycle
- - (instancetype)init
- {
- self = [super init];
- if (self) {
- _avoidEmptySpaceAroundImage = NO;
- _alwaysBounceVertical = NO;
- _alwaysBounceHorizontal = NO;
- _applyMaskToCroppedImage = NO;
- _bounces = YES;
- _bouncesZoom = YES;
- _maskLayerLineWidth = 1.0;
- _rotationEnabled = NO;
- _cropMode = RSKImageCropModeCircle;
-
- _portraitCircleMaskRectInnerEdgeInset = 15.0f;
- _portraitSquareMaskRectInnerEdgeInset = 20.0f;
- _portraitMoveAndScaleLabelTopAndCropViewSafeAreaTopVerticalSpace = 44.0f;
- _portraitCropViewSafeAreaBottomAndCancelButtonBottomVerticalSpace = 21.0f;
- _portraitCropViewSafeAreaBottomAndChooseButtonBottomVerticalSpace = 21.0f;
- _portraitCancelButtonLeadingAndCropViewSafeAreaLeadingHorizontalSpace = 13.0f;
- _portraitCropViewSafeAreaTrailingAndChooseButtonTrailingHorizontalSpace = 13.0;
-
- _landscapeCircleMaskRectInnerEdgeInset = 45.0f;
- _landscapeSquareMaskRectInnerEdgeInset = 45.0f;
- _landscapeMoveAndScaleLabelTopAndCropViewSafeAreaTopVerticalSpace = 12.0f;
- _landscapeCropViewSafeAreaBottomAndCancelButtonBottomVerticalSpace = 12.0f;
- _landscapeCropViewSafeAreaBottomAndChooseButtonBottomVerticalSpace = 12.0f;
- _landscapeCancelButtonLeadingAndCropViewSafeAreaLeadingHorizontalSpace = 13.0;
- _landscapeCropViewSafeAreaTrailingAndChooseButtonTrailingHorizontalSpace = 13.0;
- }
- return self;
- }
- - (instancetype)initWithImage:(UIImage *)originalImage
- {
- self = [self init];
- if (self) {
- _originalImage = originalImage;
- }
- return self;
- }
- - (instancetype)initWithImage:(UIImage *)originalImage cropMode:(RSKImageCropMode)cropMode
- {
- self = [self initWithImage:originalImage];
- if (self) {
- _cropMode = cropMode;
- }
- return self;
- }
- - (BOOL)prefersStatusBarHidden
- {
- return YES;
- }
- - (void)viewDidLoad
- {
- [super viewDidLoad];
-
- if ([self respondsToSelector:@selector(edgesForExtendedLayout)]) {
-
- self.edgesForExtendedLayout = UIRectEdgeNone;
- }
-
- self.imageScrollView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
-
- self.view.backgroundColor = [UIColor blackColor];
- self.view.clipsToBounds = YES;
-
- [self.view addSubview:self.imageScrollView];
- [self.view addSubview:self.overlayView];
- [self.view addSubview:self.moveAndScaleLabel];
- [self.view addSubview:self.cancelButton];
- [self.view addSubview:self.chooseButton];
-
- [self.view addGestureRecognizer:self.doubleTapGestureRecognizer];
- [self.view addGestureRecognizer:self.rotationGestureRecognizer];
- }
- - (void)viewWillAppear:(BOOL)animated
- {
- [super viewWillAppear:animated];
-
- self.originalNavigationControllerNavigationBarHidden = self.navigationController.navigationBarHidden;
- [self.navigationController setNavigationBarHidden:YES animated:NO];
-
- self.originalNavigationControllerNavigationBarShadowImage = self.navigationController.navigationBar.shadowImage;
- self.navigationController.navigationBar.shadowImage = nil;
- }
- - (void)viewDidAppear:(BOOL)animated
- {
- [super viewDidAppear:animated];
-
- self.originalNavigationControllerViewBackgroundColor = self.navigationController.view.backgroundColor;
- self.navigationController.view.backgroundColor = [UIColor blackColor];
- }
- - (void)viewWillDisappear:(BOOL)animated
- {
- [super viewWillDisappear:animated];
-
- [self.navigationController setNavigationBarHidden:self.originalNavigationControllerNavigationBarHidden animated:animated];
- self.navigationController.navigationBar.shadowImage = self.originalNavigationControllerNavigationBarShadowImage;
- self.navigationController.view.backgroundColor = self.originalNavigationControllerViewBackgroundColor;
- }
- - (void)viewWillLayoutSubviews
- {
- [super viewWillLayoutSubviews];
-
- [self updateMaskRect];
- [self layoutImageScrollView];
- [self layoutOverlayView];
- [self updateMaskPath];
- [self.view setNeedsUpdateConstraints];
- }
- - (void)viewDidLayoutSubviews
- {
- [super viewDidLayoutSubviews];
-
- if (!self.imageScrollView.image) {
- [self displayImage];
- }
- }
- - (void)updateViewConstraints
- {
- [super updateViewConstraints];
-
- if (!self.didSetupConstraints) {
- // ---------------------------
- // The label "Move and Scale".
- // ---------------------------
-
- NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:self.moveAndScaleLabel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual
- toItem:self.view.safeAreaLayoutGuide attribute:NSLayoutAttributeCenterX multiplier:1.0f
- constant:0.0f];
- [self.view addConstraint:constraint];
-
- CGFloat constant = self.portraitMoveAndScaleLabelTopAndCropViewSafeAreaTopVerticalSpace;
- self.moveAndScaleLabelTopConstraint = [NSLayoutConstraint constraintWithItem:self.moveAndScaleLabel attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
- toItem:self.view.safeAreaLayoutGuide attribute:NSLayoutAttributeTop multiplier:1.0f
- constant:constant];
- [self.view addConstraint:self.moveAndScaleLabelTopConstraint];
-
- // --------------------
- // The button "Cancel".
- // --------------------
-
- constant = self.portraitCancelButtonLeadingAndCropViewSafeAreaLeadingHorizontalSpace;
- self.cancelButtonLeadingConstraint = [NSLayoutConstraint constraintWithItem:self.cancelButton attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual
- toItem:self.view.safeAreaLayoutGuide attribute:NSLayoutAttributeLeading multiplier:1.0f
- constant:constant];
- [self.view addConstraint:self.cancelButtonLeadingConstraint];
-
- constant = self.portraitCropViewSafeAreaBottomAndCancelButtonBottomVerticalSpace;
- self.cancelButtonBottomConstraint = [NSLayoutConstraint constraintWithItem:self.view.safeAreaLayoutGuide attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual
- toItem:self.cancelButton attribute:NSLayoutAttributeBottom multiplier:1.0f
- constant:constant];
- [self.view addConstraint:self.cancelButtonBottomConstraint];
-
- // --------------------
- // The button "Choose".
- // --------------------
-
- constant = self.portraitCropViewSafeAreaTrailingAndChooseButtonTrailingHorizontalSpace;
- self.chooseButtonTrailingConstraint = [NSLayoutConstraint constraintWithItem:self.view.safeAreaLayoutGuide attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual
- toItem:self.chooseButton attribute:NSLayoutAttributeTrailing multiplier:1.0f
- constant:constant];
- [self.view addConstraint:self.chooseButtonTrailingConstraint];
-
- constant = self.portraitCropViewSafeAreaBottomAndChooseButtonBottomVerticalSpace;
- self.chooseButtonBottomConstraint = [NSLayoutConstraint constraintWithItem:self.view.safeAreaLayoutGuide attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual
- toItem:self.chooseButton attribute:NSLayoutAttributeBottom multiplier:1.0f
- constant:constant];
- [self.view addConstraint:self.chooseButtonBottomConstraint];
-
- self.didSetupConstraints = YES;
- } else {
- if ([self isPortraitInterfaceOrientation]) {
- self.moveAndScaleLabelTopConstraint.constant = self.portraitMoveAndScaleLabelTopAndCropViewSafeAreaTopVerticalSpace;
- self.cancelButtonBottomConstraint.constant = self.portraitCropViewSafeAreaBottomAndCancelButtonBottomVerticalSpace;
- self.cancelButtonLeadingConstraint.constant = self.portraitCancelButtonLeadingAndCropViewSafeAreaLeadingHorizontalSpace;
- self.chooseButtonBottomConstraint.constant = self.portraitCropViewSafeAreaBottomAndChooseButtonBottomVerticalSpace;
- self.chooseButtonTrailingConstraint.constant = self.portraitCropViewSafeAreaTrailingAndChooseButtonTrailingHorizontalSpace;
- } else {
- self.moveAndScaleLabelTopConstraint.constant = self.landscapeMoveAndScaleLabelTopAndCropViewSafeAreaTopVerticalSpace;
- self.cancelButtonBottomConstraint.constant = self.landscapeCropViewSafeAreaBottomAndCancelButtonBottomVerticalSpace;
- self.cancelButtonLeadingConstraint.constant = self.landscapeCancelButtonLeadingAndCropViewSafeAreaLeadingHorizontalSpace;
- self.chooseButtonBottomConstraint.constant = self.landscapeCropViewSafeAreaBottomAndChooseButtonBottomVerticalSpace;
- self.chooseButtonTrailingConstraint.constant = self.landscapeCropViewSafeAreaTrailingAndChooseButtonTrailingHorizontalSpace;
- }
- }
- }
- #pragma mark - Custom Accessors
- - (RSKImageScrollView *)imageScrollView
- {
- if (!_imageScrollView) {
- _imageScrollView = [[RSKImageScrollView alloc] init];
- _imageScrollView.clipsToBounds = NO;
- _imageScrollView.aspectFill = self.avoidEmptySpaceAroundImage;
- _imageScrollView.alwaysBounceHorizontal = self.alwaysBounceHorizontal;
- _imageScrollView.alwaysBounceVertical = self.alwaysBounceVertical;
- _imageScrollView.bounces = self.bounces;
- _imageScrollView.bouncesZoom = self.bouncesZoom;
- _imageScrollView.imageScrollViewDelegate = self;
- }
- return _imageScrollView;
- }
- - (RSKTouchView *)overlayView
- {
- if (!_overlayView) {
- _overlayView = [[RSKTouchView alloc] init];
- _overlayView.receiver = self.imageScrollView;
- [_overlayView.layer addSublayer:self.maskLayer];
- }
- return _overlayView;
- }
- - (CAShapeLayer *)maskLayer
- {
- if (!_maskLayer) {
- _maskLayer = [CAShapeLayer layer];
- _maskLayer.fillRule = kCAFillRuleEvenOdd;
- _maskLayer.fillColor = self.maskLayerColor.CGColor;
- _maskLayer.lineWidth = self.maskLayerLineWidth;
- _maskLayer.strokeColor = self.maskLayerStrokeColor.CGColor;
- }
- return _maskLayer;
- }
- - (UIColor *)maskLayerColor
- {
- if (!_maskLayerColor) {
- _maskLayerColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.7f];
- }
- return _maskLayerColor;
- }
- - (UILabel *)moveAndScaleLabel
- {
- if (!_moveAndScaleLabel) {
- _moveAndScaleLabel = [[UILabel alloc] init];
- _moveAndScaleLabel.translatesAutoresizingMaskIntoConstraints = NO;
- _moveAndScaleLabel.backgroundColor = [UIColor clearColor];
- _moveAndScaleLabel.text = RSKLocalizedString(@"Move and Scale", @"Move and Scale label");
- _moveAndScaleLabel.textColor = [UIColor whiteColor];
- _moveAndScaleLabel.opaque = NO;
- }
- return _moveAndScaleLabel;
- }
- - (UIButton *)cancelButton
- {
- if (!_cancelButton) {
- _cancelButton = [[UIButton alloc] init];
- _cancelButton.translatesAutoresizingMaskIntoConstraints = NO;
- [_cancelButton setTitle:RSKLocalizedString(@"Cancel", @"Cancel button") forState:UIControlStateNormal];
- [_cancelButton addTarget:self action:@selector(onCancelButtonTouch:) forControlEvents:UIControlEventTouchUpInside];
- _cancelButton.opaque = NO;
- }
- return _cancelButton;
- }
- - (UIButton *)chooseButton
- {
- if (!_chooseButton) {
- _chooseButton = [[UIButton alloc] init];
- _chooseButton.translatesAutoresizingMaskIntoConstraints = NO;
- [_chooseButton setTitle:RSKLocalizedString(@"Choose", @"Choose button") forState:UIControlStateNormal];
- [_chooseButton addTarget:self action:@selector(onChooseButtonTouch:) forControlEvents:UIControlEventTouchUpInside];
- _chooseButton.opaque = NO;
- }
- return _chooseButton;
- }
- - (UITapGestureRecognizer *)doubleTapGestureRecognizer
- {
- if (!_doubleTapGestureRecognizer) {
- _doubleTapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleDoubleTap:)];
- _doubleTapGestureRecognizer.delaysTouchesEnded = NO;
- _doubleTapGestureRecognizer.numberOfTapsRequired = 2;
- _doubleTapGestureRecognizer.delegate = self;
- }
- return _doubleTapGestureRecognizer;
- }
- - (UIRotationGestureRecognizer *)rotationGestureRecognizer
- {
- if (!_rotationGestureRecognizer) {
- _rotationGestureRecognizer = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(handleRotation:)];
- _rotationGestureRecognizer.delaysTouchesEnded = NO;
- _rotationGestureRecognizer.delegate = self;
- _rotationGestureRecognizer.enabled = self.isRotationEnabled;
- }
- return _rotationGestureRecognizer;
- }
- - (CGRect)imageRect
- {
- float zoomScale = 1.0 / self.imageScrollView.zoomScale;
-
- CGRect imageRect = CGRectZero;
-
- imageRect.origin.x = self.imageScrollView.contentOffset.x * zoomScale;
- imageRect.origin.y = self.imageScrollView.contentOffset.y * zoomScale;
- imageRect.size.width = CGRectGetWidth(self.imageScrollView.bounds) * zoomScale;
- imageRect.size.height = CGRectGetHeight(self.imageScrollView.bounds) * zoomScale;
-
- imageRect = RSKRectNormalize(imageRect);
-
- CGSize imageSize = self.originalImage.size;
- CGFloat x = CGRectGetMinX(imageRect);
- CGFloat y = CGRectGetMinY(imageRect);
- CGFloat width = CGRectGetWidth(imageRect);
- CGFloat height = CGRectGetHeight(imageRect);
-
- UIImageOrientation imageOrientation = self.originalImage.imageOrientation;
- if (imageOrientation == UIImageOrientationRight || imageOrientation == UIImageOrientationRightMirrored) {
- imageRect.origin.x = y;
- imageRect.origin.y = floor(imageSize.width - CGRectGetWidth(imageRect) - x);
- imageRect.size.width = height;
- imageRect.size.height = width;
- } else if (imageOrientation == UIImageOrientationLeft || imageOrientation == UIImageOrientationLeftMirrored) {
- imageRect.origin.x = floor(imageSize.height - CGRectGetHeight(imageRect) - y);
- imageRect.origin.y = x;
- imageRect.size.width = height;
- imageRect.size.height = width;
- } else if (imageOrientation == UIImageOrientationDown || imageOrientation == UIImageOrientationDownMirrored) {
- imageRect.origin.x = floor(imageSize.width - CGRectGetWidth(imageRect) - x);
- imageRect.origin.y = floor(imageSize.height - CGRectGetHeight(imageRect) - y);
- }
-
- CGFloat imageScale = self.originalImage.scale;
- imageRect = CGRectApplyAffineTransform(imageRect, CGAffineTransformMakeScale(imageScale, imageScale));
-
- return imageRect;
- }
- - (CGRect)cropRect
- {
- CGRect maskRect = self.maskRect;
- CGFloat rotationAngle = self.rotationAngle;
- CGRect rotatedImageScrollViewFrame = self.imageScrollView.frame;
- float zoomScale = 1.0 / self.imageScrollView.zoomScale;
-
- CGAffineTransform imageScrollViewTransform = self.imageScrollView.transform;
- self.imageScrollView.transform = CGAffineTransformIdentity;
-
- CGPoint imageScrollViewContentOffset = self.imageScrollView.contentOffset;
- CGRect imageScrollViewFrame = self.imageScrollView.frame;
- self.imageScrollView.frame = self.maskRect;
-
- CGRect imageFrame = CGRectZero;
- imageFrame.origin.x = CGRectGetMinX(maskRect) - self.imageScrollView.contentOffset.x;
- imageFrame.origin.y = CGRectGetMinY(maskRect) - self.imageScrollView.contentOffset.y;
- imageFrame.size = self.imageScrollView.contentSize;
-
- CGFloat tx = CGRectGetMinX(imageFrame) + self.imageScrollView.contentOffset.x + CGRectGetWidth(maskRect) * 0.5f;
- CGFloat ty = CGRectGetMinY(imageFrame) + self.imageScrollView.contentOffset.y + CGRectGetHeight(maskRect) * 0.5f;
-
- CGFloat sx = CGRectGetWidth(rotatedImageScrollViewFrame) / CGRectGetWidth(imageScrollViewFrame);
- CGFloat sy = CGRectGetHeight(rotatedImageScrollViewFrame) / CGRectGetHeight(imageScrollViewFrame);
-
- CGAffineTransform t1 = CGAffineTransformMakeTranslation(-tx, -ty);
- CGAffineTransform t2 = CGAffineTransformMakeRotation(rotationAngle);
- CGAffineTransform t3 = CGAffineTransformMakeScale(sx, sy);
- CGAffineTransform t4 = CGAffineTransformMakeTranslation(tx, ty);
- CGAffineTransform t1t2 = CGAffineTransformConcat(t1, t2);
- CGAffineTransform t1t2t3 = CGAffineTransformConcat(t1t2, t3);
- CGAffineTransform t1t2t3t4 = CGAffineTransformConcat(t1t2t3, t4);
-
- imageFrame = CGRectApplyAffineTransform(imageFrame, t1t2t3t4);
-
- CGRect cropRect = CGRectMake(0.0, 0.0, CGRectGetWidth(maskRect), CGRectGetHeight(maskRect));
-
- cropRect.origin.x = -CGRectGetMinX(imageFrame) + CGRectGetMinX(maskRect);
- cropRect.origin.y = -CGRectGetMinY(imageFrame) + CGRectGetMinY(maskRect);
-
- cropRect = CGRectApplyAffineTransform(cropRect, CGAffineTransformMakeScale(zoomScale, zoomScale));
-
- cropRect = RSKRectNormalize(cropRect);
-
- CGFloat imageScale = self.originalImage.scale;
- cropRect = CGRectApplyAffineTransform(cropRect, CGAffineTransformMakeScale(imageScale, imageScale));
-
- self.imageScrollView.frame = imageScrollViewFrame;
- self.imageScrollView.contentOffset = imageScrollViewContentOffset;
- self.imageScrollView.transform = imageScrollViewTransform;
-
- return cropRect;
- }
- - (CGRect)rectForClipPath
- {
- if (!self.maskLayerStrokeColor) {
- return self.overlayView.frame;
- } else {
- CGFloat maskLayerLineHalfWidth = self.maskLayerLineWidth / 2.0;
- return CGRectInset(self.overlayView.frame, -maskLayerLineHalfWidth, -maskLayerLineHalfWidth);
- }
- }
- - (CGRect)rectForMaskPath
- {
- if (!self.maskLayerStrokeColor) {
- return self.maskRect;
- } else {
- CGFloat maskLayerLineHalfWidth = self.maskLayerLineWidth / 2.0;
- return CGRectInset(self.maskRect, maskLayerLineHalfWidth, maskLayerLineHalfWidth);
- }
- }
- - (CGFloat)rotationAngle
- {
- CGAffineTransform transform = self.imageScrollView.transform;
- CGFloat rotationAngle = atan2(transform.b, transform.a);
- return rotationAngle;
- }
- - (CGFloat)zoomScale
- {
- return self.imageScrollView.zoomScale;
- }
- - (void)setAvoidEmptySpaceAroundImage:(BOOL)avoidEmptySpaceAroundImage
- {
- if (_avoidEmptySpaceAroundImage != avoidEmptySpaceAroundImage) {
- _avoidEmptySpaceAroundImage = avoidEmptySpaceAroundImage;
-
- self.imageScrollView.aspectFill = avoidEmptySpaceAroundImage;
- }
- }
- - (void)setAlwaysBounceVertical:(BOOL)alwaysBounceVertical
- {
- if (_alwaysBounceVertical != alwaysBounceVertical) {
- _alwaysBounceVertical = alwaysBounceVertical;
-
- self.imageScrollView.alwaysBounceVertical = alwaysBounceVertical;
- }
- }
- - (void)setAlwaysBounceHorizontal:(BOOL)alwaysBounceHorizontal
- {
- if (_alwaysBounceHorizontal != alwaysBounceHorizontal) {
- _alwaysBounceHorizontal = alwaysBounceHorizontal;
-
- self.imageScrollView.alwaysBounceHorizontal = alwaysBounceHorizontal;
- }
- }
- - (void)setBounces:(BOOL)bounces
- {
- if (_bounces != bounces) {
- _bounces = bounces;
-
- self.imageScrollView.bounces = bounces;
- }
- }
- - (void)setBouncesZoom:(BOOL)bouncesZoom
- {
- if (_bouncesZoom != bouncesZoom) {
- _bouncesZoom = bouncesZoom;
-
- self.imageScrollView.bouncesZoom = bouncesZoom;
- }
- }
- - (void)setCropMode:(RSKImageCropMode)cropMode
- {
- if (_cropMode != cropMode) {
- _cropMode = cropMode;
-
- if (self.imageScrollView.image) {
- [self reset:NO];
- }
- }
- }
- - (void)setOriginalImage:(UIImage *)originalImage
- {
- if (![_originalImage isEqual:originalImage]) {
- _originalImage = originalImage;
- if (self.isViewLoaded && self.view.window) {
- [self displayImage];
- }
- }
- }
- - (void)setMaskPath:(UIBezierPath *)maskPath
- {
- if (![_maskPath isEqual:maskPath]) {
- _maskPath = maskPath;
-
- UIBezierPath *clipPath = [UIBezierPath bezierPathWithRect:self.rectForClipPath];
- [clipPath appendPath:maskPath];
- clipPath.usesEvenOddFillRule = YES;
-
- CAAnimation *animation = (CAAnimation *)[self.overlayView actionForLayer:self.overlayView.layer forKey:@"backgroundColor"];
- if ([animation isKindOfClass:[CAAnimation class]]) {
- CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
- pathAnimation.duration = animation.duration;
- pathAnimation.timingFunction = animation.timingFunction;
-
- [self.maskLayer addAnimation:pathAnimation forKey:@"path"];
- }
-
- self.maskLayer.path = [clipPath CGPath];
- }
- }
- - (void)setRotationAngle:(CGFloat)rotationAngle
- {
- if (self.rotationAngle != rotationAngle) {
- CGFloat rotation = (rotationAngle - self.rotationAngle);
- CGAffineTransform transform = CGAffineTransformRotate(self.imageScrollView.transform, rotation);
- self.imageScrollView.transform = transform;
- [self layoutImageScrollView];
- }
- }
- - (void)setRotationEnabled:(BOOL)rotationEnabled
- {
- if (_rotationEnabled != rotationEnabled) {
- _rotationEnabled = rotationEnabled;
-
- self.rotationGestureRecognizer.enabled = rotationEnabled;
- }
- }
- - (void)setZoomScale:(CGFloat)zoomScale
- {
- self.imageScrollView.zoomScale = zoomScale;
- }
- #pragma mark - Action handling
- - (void)onCancelButtonTouch:(UIBarButtonItem *)sender
- {
- [self cancelCrop];
- }
- - (void)onChooseButtonTouch:(UIBarButtonItem *)sender
- {
- [self cropImage];
- }
- - (void)handleDoubleTap:(UITapGestureRecognizer *)gestureRecognizer
- {
- if (self.imageScrollView.zoomScale == self.zoomScaleDefaultValue) {
- [self.imageScrollView zoomToLocation:[gestureRecognizer locationInView:self.imageScrollView] animated:YES];
- } else {
- [self reset:YES];
- }
- }
- - (void)handleRotation:(UIRotationGestureRecognizer *)gestureRecognizer
- {
- CGFloat rotation = gestureRecognizer.rotation;
- CGAffineTransform transform = CGAffineTransformRotate(self.imageScrollView.transform, rotation);
- self.imageScrollView.transform = transform;
-
- gestureRecognizer.rotation = 0;
-
- if (gestureRecognizer.state == UIGestureRecognizerStateEnded) {
- [UIView animateWithDuration:kLayoutImageScrollViewAnimationDuration
- delay:0.0
- options:UIViewAnimationOptionBeginFromCurrentState
- animations:^{
- [self layoutImageScrollView];
- }
- completion:nil];
- }
- }
- - (void)zoomToRect:(CGRect)rect animated:(BOOL)animated
- {
- rect = [self.imageScrollView convertRect:rect fromView:self.view];
- rect = [self.imageScrollView convertRect:rect toCoordinateSpace:self.imageScrollView.imageViewCoordinateSpace];
- [self.imageScrollView zoomToRect:rect animated:animated];
- }
- #pragma mark - Public
- - (BOOL)isPortraitInterfaceOrientation
- {
- return CGRectGetHeight(self.view.bounds) > CGRectGetWidth(self.view.bounds);
- }
- #pragma mark - Private
- - (void)reset:(BOOL)animated
- {
- if (animated) {
- [UIView beginAnimations:@"rsk_reset" context:NULL];
- [UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
- [UIView setAnimationDuration:kResetAnimationDuration];
- [UIView setAnimationBeginsFromCurrentState:YES];
- }
-
- [self resetRotation];
- [self resetZoomScale];
- [self resetContentOffset];
- [self centerImage];
-
- if (animated) {
- [UIView commitAnimations];
- }
- }
- - (void)resetContentOffset
- {
- CGSize boundsSize = self.imageScrollView.bounds.size;
- CGRect frameToCenter = self.imageScrollView.imageViewFrame;
-
- CGPoint contentOffset;
- if (CGRectGetWidth(frameToCenter) > boundsSize.width) {
- contentOffset.x = (CGRectGetWidth(frameToCenter) - boundsSize.width) * 0.5f;
- } else {
- contentOffset.x = 0;
- }
- if (CGRectGetHeight(frameToCenter) > boundsSize.height) {
- contentOffset.y = (CGRectGetHeight(frameToCenter) - boundsSize.height) * 0.5f;
- } else {
- contentOffset.y = 0;
- }
-
- self.imageScrollView.contentOffset = contentOffset;
- }
- - (void)resetRotation
- {
- [self setRotationAngle:0.0];
- }
- - (void)resetZoomScale
- {
- self.imageScrollView.zoomScale = self.zoomScaleDefaultValue;
- }
- - (CGFloat)zoomScaleDefaultValue
- {
- CGFloat zoomScale;
- if (CGRectGetWidth(self.view.bounds) > CGRectGetHeight(self.view.bounds)) {
- zoomScale = CGRectGetHeight(self.view.bounds) / self.originalImage.size.height;
- } else {
- zoomScale = CGRectGetWidth(self.view.bounds) / self.originalImage.size.width;
- }
- return zoomScale;
- }
- - (NSArray *)intersectionPointsOfLineSegment:(RSKLineSegment)lineSegment withRect:(CGRect)rect
- {
- RSKLineSegment top = RSKLineSegmentMake(CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect)),
- CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect)));
-
- RSKLineSegment right = RSKLineSegmentMake(CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect)),
- CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect)));
-
- RSKLineSegment bottom = RSKLineSegmentMake(CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect)),
- CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect)));
-
- RSKLineSegment left = RSKLineSegmentMake(CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect)),
- CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect)));
-
- CGPoint p0 = RSKLineSegmentIntersection(top, lineSegment);
- CGPoint p1 = RSKLineSegmentIntersection(right, lineSegment);
- CGPoint p2 = RSKLineSegmentIntersection(bottom, lineSegment);
- CGPoint p3 = RSKLineSegmentIntersection(left, lineSegment);
-
- NSMutableArray *intersectionPoints = [@[] mutableCopy];
- if (!RSKPointIsNull(p0)) {
- [intersectionPoints addObject:[NSValue valueWithCGPoint:p0]];
- }
- if (!RSKPointIsNull(p1)) {
- [intersectionPoints addObject:[NSValue valueWithCGPoint:p1]];
- }
- if (!RSKPointIsNull(p2)) {
- [intersectionPoints addObject:[NSValue valueWithCGPoint:p2]];
- }
- if (!RSKPointIsNull(p3)) {
- [intersectionPoints addObject:[NSValue valueWithCGPoint:p3]];
- }
-
- return [intersectionPoints copy];
- }
- - (void)displayImage
- {
- if (self.originalImage) {
- self.imageScrollView.image = self.originalImage;
- [self reset:NO];
- if ([self.delegate respondsToSelector:@selector(imageCropViewControllerDidDisplayImage:)]) {
- [self.delegate imageCropViewControllerDidDisplayImage:self];
- }
- }
- }
- - (void)centerImage
- {
- // center the image view of the imageScrollView as it becomes smaller than the size of the imageScrollView
-
- CGPoint contentOffset = self.imageScrollView.contentOffset;
-
- // center vertically
- if (self.imageScrollView.contentSize.height < CGRectGetHeight(self.imageScrollView.bounds)) {
- contentOffset.y = -(CGRectGetHeight(self.imageScrollView.bounds) - self.imageScrollView.contentSize.height) * 0.5f;
- }
-
- // center horizontally
- if (self.imageScrollView.contentSize.width < CGRectGetWidth(self.imageScrollView.bounds)) {
- contentOffset.x = -(CGRectGetWidth(self.imageScrollView.bounds) - self.imageScrollView.contentSize.width) * 0.5f;;
- }
-
- self.imageScrollView.contentOffset = contentOffset;
- }
- - (void)layoutImageScrollView
- {
- CGRect frame = CGRectZero;
-
- // The bounds of the image scroll view should always fill the mask area.
- switch (self.cropMode) {
- case RSKImageCropModeSquare: {
- if (self.rotationAngle == 0.0) {
- frame = self.maskRect;
- } else {
- // Step 1: Rotate the left edge of the initial rect of the image scroll view clockwise around the center by `rotationAngle`.
- CGRect initialRect = self.maskRect;
- CGFloat rotationAngle = self.rotationAngle;
-
- CGPoint leftTopPoint = CGPointMake(initialRect.origin.x, initialRect.origin.y);
- CGPoint leftBottomPoint = CGPointMake(initialRect.origin.x, initialRect.origin.y + initialRect.size.height);
- RSKLineSegment leftLineSegment = RSKLineSegmentMake(leftTopPoint, leftBottomPoint);
-
- CGPoint pivot = RSKRectCenterPoint(initialRect);
-
- CGFloat alpha = fabs(rotationAngle);
- RSKLineSegment rotatedLeftLineSegment = RSKLineSegmentRotateAroundPoint(leftLineSegment, pivot, alpha);
-
- // Step 2: Find the points of intersection of the rotated edge with the initial rect.
- NSArray *points = [self intersectionPointsOfLineSegment:rotatedLeftLineSegment withRect:initialRect];
-
- // Step 3: If the number of intersection points more than one
- // then the bounds of the rotated image scroll view does not completely fill the mask area.
- // Therefore, we need to update the frame of the image scroll view.
- // Otherwise, we can use the initial rect.
- if (points.count > 1) {
- // We have a right triangle.
-
- // Step 4: Calculate the altitude of the right triangle.
- if ((alpha > M_PI_2) && (alpha < M_PI)) {
- alpha = alpha - M_PI_2;
- } else if ((alpha > (M_PI + M_PI_2)) && (alpha < (M_PI + M_PI))) {
- alpha = alpha - (M_PI + M_PI_2);
- }
- CGFloat sinAlpha = sin(alpha);
- CGFloat cosAlpha = cos(alpha);
- CGFloat hypotenuse = RSKPointDistance([points[0] CGPointValue], [points[1] CGPointValue]);
- CGFloat altitude = hypotenuse * sinAlpha * cosAlpha;
-
- // Step 5: Calculate the target width.
- CGFloat initialWidth = CGRectGetWidth(initialRect);
- CGFloat targetWidth = initialWidth + altitude * 2;
-
- // Step 6: Calculate the target frame.
- CGFloat scale = targetWidth / initialWidth;
- CGPoint center = RSKRectCenterPoint(initialRect);
- frame = RSKRectScaleAroundPoint(initialRect, center, scale, scale);
-
- // Step 7: Avoid floats.
- frame.origin.x = floor(CGRectGetMinX(frame));
- frame.origin.y = floor(CGRectGetMinY(frame));
- frame = CGRectIntegral(frame);
- } else {
- // Step 4: Use the initial rect.
- frame = initialRect;
- }
- }
- break;
- }
- case RSKImageCropModeCircle: {
- frame = self.maskRect;
- break;
- }
- case RSKImageCropModeCustom: {
- frame = [self.dataSource imageCropViewControllerCustomMovementRect:self];
- break;
- }
- }
-
- CGAffineTransform transform = self.imageScrollView.transform;
- self.imageScrollView.transform = CGAffineTransformIdentity;
-
- self.imageScrollView.frame = frame;
- [self centerImage];
-
- self.imageScrollView.transform = transform;
- }
- - (void)layoutOverlayView
- {
- CGRect frame = CGRectMake(0, 0, CGRectGetWidth(self.view.bounds) * 2, CGRectGetHeight(self.view.bounds) * 2);
- self.overlayView.frame = frame;
- }
- - (void)updateMaskRect
- {
- switch (self.cropMode) {
- case RSKImageCropModeCircle: {
- CGFloat viewWidth = CGRectGetWidth(self.view.bounds);
- CGFloat viewHeight = CGRectGetHeight(self.view.bounds);
-
- CGFloat diameter;
- if ([self isPortraitInterfaceOrientation]) {
- diameter = MIN(viewWidth, viewHeight) - self.portraitCircleMaskRectInnerEdgeInset * 2;
- } else {
- diameter = MIN(viewWidth, viewHeight) - self.landscapeCircleMaskRectInnerEdgeInset * 2;
- }
-
- CGSize maskSize = CGSizeMake(diameter, diameter);
-
- CGRect maskRect = CGRectMake(floor((viewWidth - maskSize.width) * 0.5f),
- floor((viewHeight - maskSize.height) * 0.5f),
- maskSize.width,
- maskSize.height);
- self.maskRect = CGRectIntegral(maskRect);
- break;
- }
- case RSKImageCropModeSquare: {
- CGFloat viewWidth = CGRectGetWidth(self.view.bounds);
- CGFloat viewHeight = CGRectGetHeight(self.view.bounds);
-
- CGFloat length;
- if ([self isPortraitInterfaceOrientation]) {
- length = MIN(viewWidth, viewHeight) - self.portraitSquareMaskRectInnerEdgeInset * 2;
- } else {
- length = MIN(viewWidth, viewHeight) - self.landscapeSquareMaskRectInnerEdgeInset * 2;
- }
-
- CGSize maskSize = CGSizeMake(length, length);
-
- CGRect maskRect = CGRectMake(floor((viewWidth - maskSize.width) * 0.5f),
- floor((viewHeight - maskSize.height) * 0.5f),
- maskSize.width,
- maskSize.height);
- self.maskRect = CGRectIntegral(maskRect);
- break;
- }
- case RSKImageCropModeCustom: {
- self.maskRect = [self.dataSource imageCropViewControllerCustomMaskRect:self];
- break;
- }
- }
- }
- - (void)updateMaskPath
- {
- switch (self.cropMode) {
- case RSKImageCropModeCircle: {
- self.maskPath = [UIBezierPath bezierPathWithOvalInRect:self.rectForMaskPath];
- break;
- }
- case RSKImageCropModeSquare: {
- self.maskPath = [UIBezierPath bezierPathWithRect:self.rectForMaskPath];
- break;
- }
- case RSKImageCropModeCustom: {
- self.maskPath = [self.dataSource imageCropViewControllerCustomMaskPath:self];
- break;
- }
- }
- }
- - (UIImage *)imageWithImage:(UIImage *)image inRect:(CGRect)rect scale:(CGFloat)scale imageOrientation:(UIImageOrientation)imageOrientation
- {
- if (!image.images) {
- CGImageRef cgImage = CGImageCreateWithImageInRect(image.CGImage, rect);
- UIImage *image = [UIImage imageWithCGImage:cgImage scale:scale orientation:imageOrientation];
- CGImageRelease(cgImage);
- return image;
- } else {
- UIImage *animatedImage = image;
- NSMutableArray *images = [NSMutableArray array];
- for (UIImage *animatedImageImage in animatedImage.images) {
- UIImage *image = [self imageWithImage:animatedImageImage inRect:rect scale:scale imageOrientation:imageOrientation];
- [images addObject:image];
- }
- return [UIImage animatedImageWithImages:images duration:image.duration];
- }
- }
- - (UIImage *)croppedImage:(UIImage *)originalImage cropMode:(RSKImageCropMode)cropMode cropRect:(CGRect)cropRect imageRect:(CGRect)imageRect rotationAngle:(CGFloat)rotationAngle zoomScale:(CGFloat)zoomScale maskPath:(UIBezierPath *)maskPath applyMaskToCroppedImage:(BOOL)applyMaskToCroppedImage
- {
- // Step 1: create an image using the data contained within the specified rect.
- UIImage *image = [self imageWithImage:originalImage inRect:imageRect scale:originalImage.scale imageOrientation:originalImage.imageOrientation];
-
- // Step 2: fix orientation of the image.
- image = [image fixOrientation];
-
- // Step 3: If current mode is `RSKImageCropModeSquare` and the original image is not rotated
- // or mask should not be applied to the image after cropping and the original image is not rotated,
- // we can return the image immediately.
- // Otherwise, we must further process the image.
- if ((cropMode == RSKImageCropModeSquare || !applyMaskToCroppedImage) && rotationAngle == 0.0) {
- // Step 4: return the image immediately.
- return image;
- } else {
- // Step 4: create a new context.
- CGSize contextSize = cropRect.size;
- UIGraphicsBeginImageContextWithOptions(contextSize, NO, originalImage.scale);
-
- // Step 5: apply the mask if needed.
- if (applyMaskToCroppedImage) {
- // 5a: scale the mask to the size of the crop rect.
- UIBezierPath *maskPathCopy = [maskPath copy];
- CGFloat scale = 1.0 / zoomScale;
- [maskPathCopy applyTransform:CGAffineTransformMakeScale(scale, scale)];
-
- // 5b: center the mask.
- CGPoint translation = CGPointMake(-CGRectGetMinX(maskPathCopy.bounds) + (CGRectGetWidth(cropRect) - CGRectGetWidth(maskPathCopy.bounds)) * 0.5f,
- -CGRectGetMinY(maskPathCopy.bounds) + (CGRectGetHeight(cropRect) - CGRectGetHeight(maskPathCopy.bounds)) * 0.5f);
- [maskPathCopy applyTransform:CGAffineTransformMakeTranslation(translation.x, translation.y)];
-
- // 5c: apply the mask.
- [maskPathCopy addClip];
- }
-
- // Step 6: rotate the image if needed.
- if (rotationAngle != 0) {
- image = [image rotateByAngle:rotationAngle];
- }
-
- // Step 7: draw the image.
- CGPoint point = CGPointMake(floor((contextSize.width - image.size.width) * 0.5f),
- floor((contextSize.height - image.size.height) * 0.5f));
- [image drawAtPoint:point];
-
- // Step 8: get the cropped image affter processing from the context.
- UIImage *croppedImage = UIGraphicsGetImageFromCurrentImageContext();
-
- // Step 9: remove the context.
- UIGraphicsEndImageContext();
-
- croppedImage = [UIImage imageWithCGImage:croppedImage.CGImage scale:originalImage.scale orientation:image.imageOrientation];
-
- // Step 10: return the cropped image affter processing.
- return croppedImage;
- }
- }
- - (void)cropImage
- {
- if ([self.delegate respondsToSelector:@selector(imageCropViewController:willCropImage:)]) {
- [self.delegate imageCropViewController:self willCropImage:self.originalImage];
- }
-
- UIImage *originalImage = self.originalImage;
- RSKImageCropMode cropMode = self.cropMode;
- CGRect cropRect = self.cropRect;
- CGRect imageRect = self.imageRect;
- CGFloat rotationAngle = self.rotationAngle;
- CGFloat zoomScale = self.imageScrollView.zoomScale;
- UIBezierPath *maskPath = self.maskPath;
- BOOL applyMaskToCroppedImage = self.applyMaskToCroppedImage;
-
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
-
- UIImage *croppedImage = [self croppedImage:originalImage cropMode:cropMode cropRect:cropRect imageRect:imageRect rotationAngle:rotationAngle zoomScale:zoomScale maskPath:maskPath applyMaskToCroppedImage:applyMaskToCroppedImage];
-
- dispatch_async(dispatch_get_main_queue(), ^{
- [self.delegate imageCropViewController:self didCropImage:croppedImage usingCropRect:cropRect rotationAngle:rotationAngle];
- });
- });
- }
- - (void)cancelCrop
- {
- if ([self.delegate respondsToSelector:@selector(imageCropViewControllerDidCancelCrop:)]) {
- [self.delegate imageCropViewControllerDidCancelCrop:self];
- }
- }
- #pragma mark - RSKImageScrollViewDelegate
- - (void)imageScrollViewWillBeginDragging
- {
- [self updateIsUserInteractionEnabledOfCancelAndChooseButtons];
- }
- - (void)imageScrollViewDidEndDragging:(BOOL)willDecelerate
- {
- if (willDecelerate == NO) {
- [self updateIsUserInteractionEnabledOfCancelAndChooseButtons];
- }
- }
- - (void)imageScrollViewDidEndDecelerating
- {
- [self updateIsUserInteractionEnabledOfCancelAndChooseButtons];
- }
- - (void)imageScrollViewWillBeginZooming
- {
- [self updateIsUserInteractionEnabledOfCancelAndChooseButtons];
- }
- - (void)imageScrollViewDidEndZooming
- {
- [self updateIsUserInteractionEnabledOfCancelAndChooseButtons];
- }
- - (void)updateIsUserInteractionEnabledOfCancelAndChooseButtons
- {
- BOOL isUserInteractionEnabled = (self.imageScrollView.isDragging || self.imageScrollView.isDecelerating || self.imageScrollView.isZooming) == NO;
-
- [self.cancelButton setUserInteractionEnabled:isUserInteractionEnabled];
- [self.chooseButton setUserInteractionEnabled:isUserInteractionEnabled];
- }
- #pragma mark - UIGestureRecognizerDelegate
- - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
- {
- return ([gestureRecognizer isEqual:self.doubleTapGestureRecognizer] || [otherGestureRecognizer isEqual:self.doubleTapGestureRecognizer]) == NO;
- }
- @end
|