
As you can see, the animation consists of two parts: the drawing of the framing circle and the disclosure of the image. Please note that the image itself does not change its size during the animation, but only its visible part changes - that is, the mask is animated in this case, which closes our image. In addition, these two animations do not start at the same time: the second part, the one with the opening of the mask, waits until the circle is drawn in half, and only then reveals the image to us.@property (nonatomic, weak) IBOutlet UIView *viewToDrawOn; @property (nonatomic, weak) IBOutlet UIView *imageToReveal;
... where imageToReveal is the subview of viewToDrawOn. If you are familiar with English, you should have already guessed that viewToDrawOn is a UIView object on which we will draw, and imageToReveal is an UIView object (or even a UIImageView) that we will mask. Here they are both declared with the IBOutlet keyword, because I'm a lazy guy and won't show you how to create these objects - I added them to the storyboard right in the Interface Builder visual editor. For the same reason, both of them are declared weak - there is no need for “strong” (strong) references to UIView objects, which in any case will be added to the hierarchy of higher-level UIViews, which already ensures that these objects will not be deleted. If my words about who for whom in this tandem is a feat are not enough - here is a picture for you. // — CAShapeLayer *circle = [CAShapeLayer layer]; // UIView CGFloat radius = self.viewToDrawOn.frame.size.width / 2.0f; circle.position = CGPointZero; circle.path = [UIBezierPath bezierPathWithRoundedRect:self.viewToDrawOn.bounds cornerRadius:radius].CGPath; // , circle.fillColor = [UIColor clearColor].CGColor; // — UIColor Hex, circle.strokeColor = [UIColor colorWithHex:@"ffd800"].CGColor; circle.lineWidth = 1.0f; [self.viewToDrawOn.layer addSublayer:circle]; // , , 0 1 ( 1 — ) CABasicAnimation *drawAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"]; drawAnimation.duration = kAnimationDuration; drawAnimation.repeatCount = 1.0; drawAnimation.fromValue = [NSNumber numberWithFloat:0.0f]; drawAnimation.toValue = [NSNumber numberWithFloat:1.0f]; drawAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; [circle addAnimation:drawAnimation forKey:@"drawOutlineAnimation"]; // CGFloat initialRadius = 1.0f; CGFloat finalRadius = self.imageToReveal.bounds.size.width / 2.0f; // , CAShapeLayer *revealShape = [CAShapeLayer layer]; revealShape.bounds = self.imageToReveal.bounds; // — , revealShape.fillColor = [UIColor blackColor].CGColor; // , : UIBezierPath *startPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(CGRectGetMidX(self.imageToReveal.bounds) - initialRadius, CGRectGetMidY(self.imageToReveal.bounds) - initialRadius, initialRadius * 2, initialRadius * 2) cornerRadius:initialRadius]; UIBezierPath *endPath = [UIBezierPath bezierPathWithRoundedRect:self.imageToReveal.bounds cornerRadius:finalRadius]; revealShape.path = startPath.CGPath; revealShape.position = CGPointMake(CGRectGetMidX(self.imageToReveal.bounds) - initialRadius, CGRectGetMidY(self.imageToReveal.bounds) - initialRadius); //, , , self.imageToReveal.layer.mask = revealShape; // . path — , , . CABasicAnimation *revealAnimationPath = [CABasicAnimation animationWithKeyPath:@"path"]; revealAnimationPath.fromValue = (__bridge id)(startPath.CGPath); revealAnimationPath.toValue = (__bridge id)(endPath.CGPath); revealAnimationPath.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]; revealAnimationPath.duration = kAnimationDuration/2.0f; revealAnimationPath.repeatCount = 1.0f; // , revealAnimationPath.beginTime = CACurrentMediaTime() + kAnimationDuration/2.0f; revealAnimationPath.delegate = self; // , , hidden , .. dispatch_time_t timeToShow = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(kAnimationDuration/2.0f * NSEC_PER_SEC)); dispatch_after(timeToShow, dispatch_get_main_queue(), ^{ self.imageToReveal.hidden = NO; }); revealShape.path = endPath.CGPath; [revealShape addAnimation:revealAnimationPath forKey:@"revealAnimation"]; 
Source: https://habr.com/ru/post/258107/
All Articles