In mid 2013 we developed the native profile picture upload for our iOS application. Since iOS6 was current, we loved to have a skeumorphic animation, if a user cancelled to upload the picture or deletes it from his own profile.
It seemed obvious to us, to tear the image in this very situation. As we found no Open Source code, that would’ve helped us to get the thing done, we built this animation on our own.
That’s, how it looked in the end:
Reusability is key
When we craft our software, a core focus is the reusability of our code. So, we wrote an Objective-c category, that can be easily dropped in and which enables everyone to tear any UIImageView with just one call:
- (void)tearWithDuration:(NSTimeInterval)duration
animations:(void (^)(void))animations
completion:(void (^)(BOOL finished))completion;
How does the category UIImageView+tear work?
To explain the animation it is best to break it into 3 parts:
- Random tear path creation
- Creating masked copies of the original image and delete the original image reference
- Animate the masked images
Let’s tear an image of me, Sascha, the writer of this article:
1. Random tear path creation
The starting point for the path is the center top of the imageView. Starting there, we are going down 1 pixel and randomly between 0 and 3 pixels left or right, since we reach the bottom of the image:
float frameWidth = self.frame.size.width;
float midX = frameWidth / 2.0;
CGPathRef tear = [self randomTear:3 startAtX:midX];
Example for CGPathRef tear, after creation with randomTear:startAtX: method
- (CGPathRef)randomTear:(int)maxDistance startAtX:(float)x
{
CGMutablePathRef tear = CGPathCreateMutable();
CGPathMoveToPoint(tear, NULL, 0, 0);
for (NSUInteger y = 0; y < self.frame.size.height; ++y)
{
CGPathAddLineToPoint(tear, NULL, x, y);
int randomDirection = [self randomSign];
int randomDistance = arc4random_uniform(maxDistance);
x += (randomDirection * randomDistance);
}
return tear;
}
2. Creating masked copies of the original image and delete the original image reference
We decided, that it would be easier to handle, if we keep the original size of the image and create masked copies, so positioning becomes no issue.
CALayer *leftMaskPath = [self shapeWithTear:tear cornerX:0 tearStartAtX:midX];
CALayer *rightMaskPath = [self shapeWithTear:tear cornerX:frameWidth tearStartAtX:midX];
Example for CALayer *leftMaskPath and CALayer *rightMaskPath after applying shapeWithTear:cornerX:tearStartAtX: method
UIImageView *leftImageHalf = [self copyViewWithMask:leftMaskPath];
[self addSubview:leftImageHalf];
UIImageView *rightImageHalf = [self copyViewWithMask:rightMaskPath];
[self addSubview:rightImageHalf];
self.image = nil;
Example for UIImageView *leftImageHalf and UIImageView *rightImageHalf after applying copyViewWithMask: method
Helper methods for creating masked copies
- (UIImageView *)copyViewWithMask:(CALayer *)mask
{
UIImageView *imageViewCopy = [[UIImageView alloc] initWithImage:self.image];
imageViewCopy.frame = self.bounds;
imageViewCopy.layer.masksToBounds = YES;
imageViewCopy.contentMode = self.contentMode;
imageViewCopy.layer.mask = mask;
return imageViewCopy;
}
- (CAShapeLayer *)shapeWithTear:(CGPathRef)tear
cornerX:(float)cornerX
tearStartAtX:(float)midX
{
CGRect rect = CGPathGetBoundingBox(tear);
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, cornerX, 0);
CGPathAddLineToPoint(path, NULL, midX, 0);
CGPathAddPath(path, NULL, tear);
CGPathAddLineToPoint(path, NULL, cornerX, rect.size.height);
CGPathAddLineToPoint(path, NULL, cornerX, 0);
CAShapeLayer *shapeLayer = [CAShapeLayer new];
shapeLayer.borderColor = [UIColor blackColor].CGColor;
shapeLayer.borderWidth = 3;
shapeLayer.path = path;
CGPathRelease(path);
return shapeLayer;
}
3. Animate the masked images
The first idea was, that in the moment both sides get torn apart an animation starts that shows both sides of the image moving away from each other. During implementation though, we saw the basic animation, that just animates and rotates the two views outside the viewport and really liked it. After only smaller refinements we decided to stick with this more basic version.
[UIView animateWithDuration:duration
animations:^{
float targetX = [UIScreen mainScreen].bounds.size.width + midX;
leftImageHalf.frame = CGRectOffset(leftImageHalf.frame, -targetX, 0);
leftImageHalf.transform = CGAffineTransformMakeRotation(-M_PI/2);
rightImageHalf.frame = CGRectOffset(rightImageHalf.frame, targetX, 0);
rightImageHalf.transform = CGAffineTransformMakeRotation(M_PI/2);
animations();
}
completion:^(BOOL finished)
{
[leftImageHalf removeFromSuperview];
[rightImageHalf removeFromSuperview];
completion(finished);
}];
Behavior of the animation
We like to share our experience with you
There should be no need to reinvent the wheel. So we decided to give the UIImageView tear animation as is it Open Source to use and modify in commercial and non-commercial products. Surely, we would love to see you using the category. We would be even happier, if you leave a comment and tell us where you used it.
Ein Gedanke zu “UIImageView – Tear animation”