Reputation: 11607
I've got a bit of a challenging thing I am trying to build.
Basically, I got this scenario here:
I am able to draw the dotted line thanks to this Stackoverflow answer:
Draw dotted (not dashed!) line, with IBDesignable in 2017
The dotted line drawing is done inside:
-(void)drawRect:(CGRect)rect
{
[self drawDottedLineFromStartingPoint:self.mainPhoto.center ToEndPoint:self.photo1.center];
[self drawDottedLineFromStartingPoint:self.mainPhoto.center ToEndPoint:self.photo2.center];
[self drawDottedLineFromStartingPoint:self.mainPhoto.center ToEndPoint:self.photo3.center];
[self drawDottedLineFromStartingPoint:self.mainPhoto.center ToEndPoint:self.photo4.center];
[self drawDottedLineFromStartingPoint:self.mainPhoto.center ToEndPoint:self.photo5.center];
}
-(void)drawDottedLineFromStartingPoint:(CGPoint)startPoint ToEndPoint:(CGPoint)endPoint
{
UIBezierPath *path = [[UIBezierPath alloc] init];
[path moveToPoint:startPoint];
[path addLineToPoint:endPoint];
path.lineWidth = 4;
CGFloat dashes[] = {path.lineWidth * 0, path.lineWidth * 2};
[path setLineDash:dashes count:2 phase:0];
path.lineCapStyle = kCGLineCapRound;
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(ctx, [ThemeManager mediumTextColor].CGColor);
[path stroke];
}
The problem I am facing is that each smaller circles animate in by falling down into place.
The way I am animating the smaller circle is by using something like:
CGAffineTransformMakeTranslation(0, 50);
When I do that, the dotted lines is drawn from the big circle to the final resting position of the smaller circle, the dotted lines doesn't "follow" the smaller circle as it's being animated and falling down.
So what you see is this:
Is there any easy solution to what I am trying to achieve ? I've tried thinking of using NSTimer and calculating the hypotenuse length, divide by a duration, but I didn't get very far :D
Upvotes: 0
Views: 779
Reputation: 11607
I want to thank rdelmar for enlightening me as well as this link informing me about a view's presentationLayer frame
ios observing change in frame of a UIView during animation
I didn't use KVO in this case but I did solved my problem using NSTimer and redrawing my screen.
Basically I after I start my animations for the circles falling down, I also schedule a NSTimer that triggers every 0.01 seconds and the callback method checks to see if the final circle photo has finished animating, if yes, then invalidate timer:
-(void)showIntroAnimations
{
self.hasBegunAnimating = YES;
// start animating circle photos falling down
[self setupObservers];
}
-(void)setupObservers
{
[NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(redrawLines:) userInfo:nil repeats:YES];
}
-(void)redrawLines:(NSTimer *)timer
{
if([self.photo5.layer animationKeys].count > 0)
{
[self setNeedsDisplay];
}
else
{
[timer invalidate];
timer = nil;
}
}
Upvotes: 0
Reputation: 104082
I made an app a while ago that did something similar to what you want, so I modified it do what you're trying to do. I have a class called NodeView, that's just a UIView subclass with one property, called line, which is a UIBezierPath (you could subclass UIImageView to do the same thing). I use a timer to move the nodes, and observe the node's center property so I can call drawRect to stroke the node's line. This version moves the nodes one by one. I set the controller's view to a subclass of UIView, and here's the code in that subclass,
#import "RDView.h"
#import "NodeView.h"
@interface RDView ()
@property (weak, nonatomic) IBOutlet UIView *bigView; // this is equivalent to your large image view from which the other image views drop
@property (strong,nonatomic) NSMutableArray *paths;
@end
@implementation RDView
-(void)didMoveToWindow {
self.bigView.layer.cornerRadius = self.bigView.frame.size.width/2.0;
self.paths = [NSMutableArray new];
self.nodes = [NSMutableArray new];
for (int i = 0; i<5; i++) {
NodeView *aNode = [NodeView new];
CGFloat hue = arc4random_uniform(1000)/1000.0;
aNode.backgroundColor = [UIColor colorWithHue:hue saturation:1 brightness:1 alpha:1.0];
aNode.frame = CGRectMake(0, 0, 40, 40);
[self.bigView layoutIfNeeded];
aNode.center = self.bigView.center;
[self addSubview:aNode];
[self.nodes addObject:aNode];
aNode.line = [UIBezierPath bezierPath];
CGFloat dashes[] = {0, aNode.line.lineWidth * 4};
[aNode.line setLineDash:dashes count:2 phase:0];
aNode.line.lineCapStyle = kCGLineCapRound;
[aNode.line moveToPoint:[self.bigView center]];
}
[self bringSubviewToFront:self.bigView];
for (NodeView *aNode in self.nodes) {
[aNode addObserver:self forKeyPath:@"center" options:NSKeyValueObservingOptionNew context:nil];
}
[self performSelector:@selector(dropNodes) withObject:nil afterDelay:1];
}
-(void)dropNodes {
static int i = 0;
[NSTimer scheduledTimerWithTimeInterval:.005 target:self selector:@selector(dropNode:) userInfo:@{@"nodeIndex":@(i)} repeats:YES];
i++;
}
-(void)dropNode:(NSTimer *) timer {
NSInteger i = [timer.userInfo[@"nodeIndex"] integerValue];
NodeView *node = self.nodes[i];
[node setCenter: CGPointMake(node.center.x + (i-2)*.25, node.center.y + 1)]; // spread the nodes out horizontally and move down
[node.line addLineToPoint:node.center];
if (node.center.y > 400) {
[timer invalidate];
if (i < self.nodes.count-1) {
[self dropNodes];
}
}
}
-(void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:@"center"] ) {
[self setNeedsDisplay];
}
}
- (void)drawRect:(CGRect)rect {
for (NodeView *aNode in self.nodes) {
[aNode.line stroke];
}
}
You can find a link to my project here, http://jmp.sh/lIl5MXp
Upvotes: 2