Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions ImageSwiper/CardView.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ typedef enum {
@interface CardView : UIView

- (void)returnCardViewToStartPointAnimated:(BOOL)animated;
- (id)initWithFrontView:(UIView *)frontView back:(UIView *)backView;
- (void)goOffscreenWithAngle:(CGFloat)angle;
- (void)flip:(UIButton *)sender;

// Views
@property (strong, nonatomic) UIView *front; // Add subviews to the front UIView to customize front of card
@property (strong, nonatomic) UIView *back; // Add subviews to the back UIView to customize back of card
@property (strong, nonatomic) UIButton *infoButton;
@property (strong, nonatomic) UIView IBOutlet *front; // Add subviews to the front UIView to customize front of card
@property (strong, nonatomic) UIView IBOutlet *back; // Add subviews to the back UIView to customize back of card
@property (strong, nonatomic) UILabel *label;

// Attributes
Expand All @@ -41,6 +43,8 @@ typedef enum {
@property (readonly, nonatomic) BOOL isOffScreen;
@property (readonly, nonatomic) BOOL isShowingFront;
@property (assign, nonatomic) CGFloat rotationAngle;
@property (nonatomic) BOOL swipeEnabled;

// Delegates
@property (assign, nonatomic) id<CardViewDelegate> delegate;

Expand Down
142 changes: 74 additions & 68 deletions ImageSwiper/CardView.m
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#define DEGREE_TO_RADIAN(x) x*M_PI/180

// Defaults
#define DEFAULT_SWIPE_DISTANCE 80
#define DEFAULT_SWIPE_DISTANCE 60
#define DEFAULT_BORDER_WIDTH 5
#define DEFAULT_ROTATION_ANGLE 10

Expand All @@ -36,39 +36,34 @@ - (id)initWithFrame:(CGRect)frame
return self;
}

- (id)initWithFrontView:(UIView *)frontView back:(UIView *)backView {
self = [super initWithFrame:frontView.frame];
if(self){
self.front = frontView;
self.back = backView;
self.swipeEnabled = YES;
[self setupViews];
[self setupAttributes];
[self setupGestures];
}
return self;
}

- (void)setupViews {
// Add front card
self.front = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
self.front.layer.backgroundColor = [UIColor lightGrayColor].CGColor;
self.front.layer.shouldRasterize = YES;

// Add back card
self.back = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
self.back.layer.backgroundColor = [UIColor lightGrayColor].CGColor;
self.back.layer.shouldRasterize = YES;

// Add white background to front card
UIView *background = [[UIView alloc] initWithFrame:CGRectMake(DEFAULT_BORDER_WIDTH,
DEFAULT_BORDER_WIDTH,
self.frame.size.width-2*DEFAULT_BORDER_WIDTH,
self.frame.size.height-2*DEFAULT_BORDER_WIDTH)];
background.layer.backgroundColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.7].CGColor;
[self.front addSubview:background];

// Add info button to container
self.infoButton = [UIButton buttonWithType:UIButtonTypeInfoDark];
self.infoButton.center = CGPointMake(self.frame.size.width-20, self.frame.size.height-20);
[self.infoButton addTarget:self action:@selector(flip:) forControlEvents:UIControlEventTouchUpInside];

self.front.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
self.back.frame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);

self.clipsToBounds = YES;
self.front.layer.cornerRadius = 16.0f;
self.back.layer.cornerRadius = 16.0f;

// Add "Yep" "Nope" labels
self.label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
self.label.alpha = 0;
self.label.textAlignment = NSTextAlignmentCenter;

[self.front addSubview:self.label];
[self addSubview:self.back];
[self addSubview:self.front];
[self addSubview:self.infoButton];
}

- (void)setupAttributes {
Expand All @@ -82,23 +77,22 @@ - (void)setupAttributes {
- (void)setupGestures {
self.panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panHandle:)];
[self addGestureRecognizer:self.panGesture];

self.tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapHandle:)];
[self addGestureRecognizer:self.tapGesture];
}

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/

#pragma mark - Gesture Handlers

- (void)setSwipeEnabled:(BOOL)swipeEnabled {
_swipeEnabled = swipeEnabled;
if (!swipeEnabled) {
[self returnCardViewToStartPointAnimated:YES];
}
}

- (IBAction)panHandle:(UIPanGestureRecognizer *)gesture {
if(!self.swipeEnabled){
return;
}

CGPoint newLocation = [gesture locationInView:self.superview];

if(gesture.state==UIGestureRecognizerStateBegan) {
Expand Down Expand Up @@ -151,38 +145,50 @@ - (IBAction)panHandle:(UIPanGestureRecognizer *)gesture {
[UIView animateWithDuration:slideFactor*2 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
gesture.view.layer.position = finalPoint;
} completion:^(BOOL finished) {
// Calculate final change in x-position that was made
CGFloat swipeDistance = self.startPointInSuperview.x - newLocation.x;
CGFloat absSwipeDistance = labs(swipeDistance);
// Calculate final change that was made
CGFloat dx = newLocation.x - self.startPointInSuperview.x;
CGFloat dy = newLocation.y - self.startPointInSuperview.y;
CGFloat swipeDistance = sqrt(dx*dx+dy*dy);

if (absSwipeDistance < self.neededSwipeDistance) {
if (swipeDistance < self.neededSwipeDistance) {
if ([self.delegate respondsToSelector:@selector(cardView:willReturnToCenterFrom:)]) {
[self.delegate cardView:self willReturnToCenterFrom:cardViewLocation];
}

[self returnCardViewToStartPointAnimated:YES];
}
else {
if ([self.delegate respondsToSelector:@selector(cardView:willGoOffscreenFrom:)]) {
[self.delegate cardView:self willGoOffscreenFrom:cardViewLocation];
}

// Animate off screen
[UIView animateWithDuration:0.4 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
CGFloat offscreenX = (swipeDistance > 0 ? -self.superview.frame.origin.x-self.bounds.size.width : self.superview.frame.size.width+self.bounds.size.width);
gesture.view.layer.position = CGPointMake(offscreenX, gesture.view.layer.position.y);
} completion:^(BOOL finished) {
if ([self.delegate respondsToSelector:@selector(cardView:didGoOffscreenFrom:)]) {
[self.delegate cardView:self didGoOffscreenFrom:cardViewLocation];
}
}];
[self goOffscreenWithAngle:atan2(dy, dx)];
}
}];
}
}

- (void)goOffscreenWithAngle:(CGFloat)angle {
CardViewLocation cardViewLocation = cosf(angle) > 0 ? (sinf(angle) > 0 ? CardViewBottomRight : CardViewTopRight) : (sinf(angle) > 0 ? CardViewBottomLeft : CardViewTopLeft);
if ([self.delegate respondsToSelector:@selector(cardView:willGoOffscreenFrom:)]) {
[self.delegate cardView:self willGoOffscreenFrom:cardViewLocation];
}

// Animate off screen
[UIView animateWithDuration:0.8 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
CGRect screenBounds = [UIScreen mainScreen].bounds;
CGFloat screenDiagonal = sqrt(pow(screenBounds.size.width, 2)+pow(screenBounds.size.height, 2));
CGFloat distance = sqrt(pow(self.bounds.size.width, 2)+pow(self.bounds.size.height, 2)) + screenDiagonal;

CGFloat offscreenX = distance * cosf(angle);
CGFloat offscreenY = distance * sinf(angle);

self.layer.position = CGPointMake(offscreenX, offscreenY);
} completion:^(BOOL finished) {
if (self.delegate && [self.delegate respondsToSelector:@selector(cardView:didGoOffscreenFrom:)]) {
[self.delegate cardView:self didGoOffscreenFrom:cardViewLocation];
}
}];
}

- (void)tapHandle:(UITapGestureRecognizer *)gesture {
[self flip:nil];
// [self flip:nil];
}

#pragma mark - Public Methods
Expand Down Expand Up @@ -212,18 +218,18 @@ - (void)returnCardViewToStartPointAnimated:(BOOL)animated {
#pragma mark - Helper methods

- (void)flip:(UIButton *)sender {
if (self.isShowingFront) {
[UIView transitionFromView:self.front toView:self.back duration:0.5 options:UIViewAnimationOptionTransitionFlipFromLeft completion:^(BOOL finished) {
[self bringSubviewToFront:self.infoButton];
}];
}
else {
[UIView transitionFromView:self.back toView:self.front duration:0.5 options:UIViewAnimationOptionTransitionFlipFromLeft completion:^(BOOL finished) {
[self bringSubviewToFront:self.infoButton];
}];
}
self.isShowingFront = !self.isShowingFront;
if(self.back){
if (self.isShowingFront) {
[UIView transitionFromView:self.front toView:self.back duration:0.5 options:UIViewAnimationOptionTransitionFlipFromLeft completion:^(BOOL finished) {
}];
}
else {
[UIView transitionFromView:self.back toView:self.front duration:0.5 options:UIViewAnimationOptionTransitionFlipFromLeft completion:^(BOOL finished) {
}];
}
self.isShowingFront = !self.isShowingFront;
}
}

-(void)setAnchorPoint:(CGPoint)anchorPoint forView:(UIView *)view
Expand Down