I'm actually Frontback co-founder and developer who made this.
This is how I did it, it's in Rubymotion but it can easily be ported back to Obj-C (or even easier to Swift), you get the idea.
def setupCaptureAnimation
w = view.bounds.size.width
h = view.bounds.size.height
location = CGPointMake(w/2, h/2)
shapeLayer = CAShapeLayer.layer
outerRadius = 0.5 * Math.sqrt(w**2 + h**2) # circle around the view bounds
@animStartPath = makeCircleAtLocation(location, radius:outerRadius).CGPath
@animEndPath = makeCircleAtLocation(location, radius:0).CGPath
shapeLayer.path = @animStartPath
shapeLayer.fillColor = UIColor.blackColor.CGColor
@animLayer = shapeLayer
end
def playCaptureStartAnimation
view.layer.addSublayer(@animLayer)
pathAnimation = CABasicAnimation.animationWithKeyPath("path")
pathAnimation.setValue("start", forKey:"id")
pathAnimation.duration = 0.1
pathAnimation.fromValue = @animStartPath
pathAnimation.toValue = @animEndPath
pathAnimation.delegate = self
@animLayer.addAnimation(pathAnimation, forKey:nil)
@animLayer.path = @animEndPath
end
def playCaptureEndAnimation
pathAnimation = CABasicAnimation.animationWithKeyPath("path")
pathAnimation.setValue("end", forKey:"id")
pathAnimation.duration = 0.2
pathAnimation.fromValue = @animEndPath
pathAnimation.toValue = @animStartPath
pathAnimation.delegate = self
@animLayer.addAnimation(pathAnimation, forKey:nil)
@animLayer.path = @animStartPath
end
def makeCircleAtLocation(location, radius:radius)
path = UIBezierPath.bezierPath
# fill rectangle
rect = view.bounds
path.moveToPoint(CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect)))
path.addLineToPoint(CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect)))
path.addLineToPoint(CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect)))
path.addLineToPoint(CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect)))
path.addLineToPoint(CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect)))
# remove circle
path.addArcWithCenter(location, radius:radius, startAngle:0.0, endAngle:2 * Math::PI, clockwise:true)
path.closePath
path
end
def animationDidStop(anim, finished:finished)
if anim.valueForKey("id") == "start"
# trigger capture and after that playCaptureEndAnimation
else
@animLayer.removeFromSuperlayer
# done
end
end
The drawing trick resides in makeCircleAtLocation:radius:
: when you draw a path, if you superimpose something (like a circle) on top of something else (like a rectangle), the intersection is actually removed from the path. So by drawing the circle in the rectangle, the circle is removed from the rectangle.
You first have to call setupCaptureAnimation
at initialization. Then when the user taps the camera button you have to call playCaptureStartAnimation
that will play the shrinking animation, then the delegate method animationDidStop:finished
will be called. At that moment the screen is all black, you have to capture the image from the camera session. When it's taken, call playCaptureEndAnimation
and it will play the reverse animation.