From e6e3ed43393402daf1f7a153edf3b5c7339dfaf6 Mon Sep 17 00:00:00 2001 From: Anny Date: Mon, 11 Apr 2016 17:32:32 +0530 Subject: [PATCH] Create UIView+MDCSwipeToChoose.m Please replace this file for swift project for enable manually like dislike animation in proper way --- UIView+MDCSwipeToChoose.m | 254 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100644 UIView+MDCSwipeToChoose.m diff --git a/UIView+MDCSwipeToChoose.m b/UIView+MDCSwipeToChoose.m new file mode 100644 index 0000000..a9ea1d5 --- /dev/null +++ b/UIView+MDCSwipeToChoose.m @@ -0,0 +1,254 @@ +// +// UIView+MDCSwipeToChoose.m +// +// Copyright (c) 2014 to present, Brian Gesiak @modocache +// +// 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 "UIView+MDCSwipeToChoose.h" +#import "MDCSwipeToChoose.h" +#import "MDCViewState.h" +#import "MDCGeometry.h" +#import + +const void * const MDCSwipeOptionsKey = &MDCSwipeOptionsKey; +const void * const MDCViewStateKey = &MDCViewStateKey; + +@implementation UIView (MDCSwipeToChoose) + +#pragma mark - Public Interface + +- (void)mdc_swipeToChooseSetup:(MDCSwipeOptions *)options { + self.mdc_options = options ? options : [MDCSwipeOptions new]; + self.mdc_viewState = [MDCViewState new]; + self.mdc_viewState.originalCenter = self.center; + + [self mdc_setupPanGestureRecognizer]; +} + +- (void)mdc_swipe:(MDCSwipeDirection)direction { + [self mdc_swipeToChooseSetupIfNecessary]; + + // A swipe in no particular direction "finalizes" the swipe. + if (direction == MDCSwipeDirectionNone) { + [self mdc_finalizePosition]; + return; + } + + // Moves the view to the minimum point exceeding the threshold. + // Transforms and executes pan callbacks as well. + void (^animations)(void) = ^{ + CGPoint translation = [self mdc_translationExceedingThreshold:self.mdc_options.threshold + direction:direction]; + self.center = MDCCGPointAdd(self.center, translation); + [self mdc_rotateForTranslation:translation + rotationDirection:MDCRotationAwayFromCenter]; + [self mdc_executeOnPanBlockForTranslation:translation]; + }; + + // Finalize upon completion of the animations. + void (^completion)(BOOL) = ^(BOOL finished) { + if (finished) { [self mdc_finalizePosition]; } + }; + + [UIView animateWithDuration:self.mdc_options.swipeAnimationDuration + delay:0.0 + options:self.mdc_options.swipeAnimationOptions + animations:animations + completion:completion]; +} + +#pragma mark - Internal Methods + +- (void)setMdc_options:(MDCSwipeOptions *)options { + objc_setAssociatedObject(self, MDCSwipeOptionsKey, options, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (MDCSwipeOptions *)mdc_options { + return objc_getAssociatedObject(self, MDCSwipeOptionsKey); +} + +- (void)setMdc_viewState:(MDCViewState *)state { + objc_setAssociatedObject(self, MDCViewStateKey, state, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (MDCViewState *)mdc_viewState { + return objc_getAssociatedObject(self, MDCViewStateKey); +} + +#pragma mark Setup + +- (void)mdc_swipeToChooseSetupIfNecessary { + if (!self.mdc_options || !self.mdc_viewState) { + [self mdc_swipeToChooseSetup:nil]; + } +} + +- (void)mdc_setupPanGestureRecognizer { + SEL action = @selector(mdc_onSwipeToChoosePanGestureRecognizer:); + UIPanGestureRecognizer *panGestureRecognizer = + [[UIPanGestureRecognizer alloc] initWithTarget:self + action:action]; + [self addGestureRecognizer:panGestureRecognizer]; +} + +#pragma mark Translation + +- (void)mdc_finalizePosition { + MDCSwipeDirection direction = [self mdc_directionOfExceededThreshold]; + switch (direction) { + case MDCSwipeDirectionRight: + case MDCSwipeDirectionLeft: { + CGPoint translation = MDCCGPointSubtract(self.center, + self.mdc_viewState.originalCenter); + [self mdc_exitSuperviewFromTranslation:translation]; + break; + } + case MDCSwipeDirectionNone: + [self mdc_returnToOriginalCenter]; + [self mdc_executeOnPanBlockForTranslation:CGPointZero]; + break; + } +} + +- (void)mdc_returnToOriginalCenter { + [UIView animateWithDuration:self.mdc_options.swipeCancelledAnimationDuration + delay:0.0 + options:self.mdc_options.swipeCancelledAnimationOptions + animations:^{ + self.transform = CGAffineTransformIdentity; + self.center = self.mdc_viewState.originalCenter; + } completion:^(BOOL finished) { + id delegate = self.mdc_options.delegate; + if ([delegate respondsToSelector:@selector(viewDidCancelSwipe:)]) { + [delegate viewDidCancelSwipe:self]; + } + }]; +} + +- (void)mdc_exitSuperviewFromTranslation:(CGPoint)translation { + MDCSwipeDirection direction = [self mdc_directionOfExceededThreshold]; + id delegate = self.mdc_options.delegate; + if ([delegate respondsToSelector:@selector(view:shouldBeChosenWithDirection:)]) { + BOOL should = [delegate view:self shouldBeChosenWithDirection:direction]; + if (!should) { + return; + } + } + + MDCSwipeResult *state = [MDCSwipeResult new]; + state.view = self; + state.translation = translation; + state.direction = direction; + state.onCompletion = ^{ + if ([delegate respondsToSelector:@selector(view:wasChosenWithDirection:)]) { + [delegate view:self wasChosenWithDirection:direction]; + } + }; + self.mdc_options.onChosen(state); +} + +- (void)mdc_executeOnPanBlockForTranslation:(CGPoint)translation { + if (self.mdc_options.onPan) { + CGFloat thresholdRatio = MIN(1.f, fabsf(translation.x)/self.mdc_options.threshold); + + MDCSwipeDirection direction = MDCSwipeDirectionNone; + if (translation.x > 0.f) { + direction = MDCSwipeDirectionRight; + } else if (translation.x < 0.f) { + direction = MDCSwipeDirectionLeft; + } + + MDCPanState *state = [MDCPanState new]; + state.view = self; + state.direction = direction; + state.thresholdRatio = thresholdRatio; + self.mdc_options.onPan(state); + } +} + +#pragma mark Rotation + +- (void)mdc_rotateForTranslation:(CGPoint)translation + rotationDirection:(MDCRotationDirection)rotationDirection { + CGFloat rotation = MDCDegreesToRadians(translation.x/100 * self.mdc_options.rotationFactor); + self.transform = CGAffineTransformRotate(CGAffineTransformIdentity, + rotationDirection * rotation); +} + +#pragma mark Threshold + +- (CGPoint)mdc_translationExceedingThreshold:(CGFloat)threshold + direction:(MDCSwipeDirection)direction { + NSParameterAssert(direction != MDCSwipeDirectionNone); + + CGFloat offset = threshold + 1.f; + switch (direction) { + case MDCSwipeDirectionLeft: + return CGPointMake(-offset, 0); + case MDCSwipeDirectionRight: + return CGPointMake(offset, 0); + default: + [NSException raise:NSInternalInconsistencyException + format:@"Invallid direction argument."]; + return CGPointZero; + } +} + +- (MDCSwipeDirection)mdc_directionOfExceededThreshold { + if (self.center.x > self.mdc_viewState.originalCenter.x + self.mdc_options.threshold) { + return MDCSwipeDirectionRight; + } else if (self.center.x < self.mdc_viewState.originalCenter.x - self.mdc_options.threshold) { + return MDCSwipeDirectionLeft; + } else { + return MDCSwipeDirectionNone; + } +} + +#pragma mark Gesture Recognizer Events + +- (void)mdc_onSwipeToChoosePanGestureRecognizer:(UIPanGestureRecognizer *)panGestureRecognizer { + UIView *view = panGestureRecognizer.view; + + if (panGestureRecognizer.state == UIGestureRecognizerStateBegan) { + self.mdc_viewState.originalCenter = view.center; + + // If the pan gesture originated at the top half of the view, rotate the view + // away from the center. Otherwise, rotate towards the center. + if ([panGestureRecognizer locationInView:view].y < view.center.y) { + self.mdc_viewState.rotationDirection = MDCRotationAwayFromCenter; + } else { + self.mdc_viewState.rotationDirection = MDCRotationTowardsCenter; + } + } else if (panGestureRecognizer.state == UIGestureRecognizerStateEnded) { + // Either move the view back to its original position or move it off screen. + [self mdc_finalizePosition]; + } else { + // Update the position and transform. Then, notify any listeners of + // the updates via the pan block. + CGPoint translation = [panGestureRecognizer translationInView:view]; + view.center = MDCCGPointAdd(self.mdc_viewState.originalCenter, translation); + [self mdc_rotateForTranslation:translation + rotationDirection:self.mdc_viewState.rotationDirection]; + [self mdc_executeOnPanBlockForTranslation:translation]; + } +} + +@end