I have below screen:
When I click Get More Button, it will add new Rounded Button. I have added Pan Gesture on the UIView
where all Rounded Buttons are added which will then draw a UIBezierPath
from Rounded Button only. Up to this point, everything works.
Now, I want the following:
UIView
called playGroundView, which contains all Rounded Button. This will draw a Bezier Path for all those Buttons.Refer to Below code for PlayGround UIView subclass:
@interface PlayGroundView : UIView
@property (nonatomic, weak) PlayGroundViewController *playViewController;
@end
#import "PlayGroundView.h"
#import "RoundedButton.h"
#import "PlayGroundViewController.h"
@interface PlayGroundView () {
UIBezierPath *path;
BOOL isDrawPointInside;
}
@end
@implementation PlayGroundView
#pragma mark - View Methods -
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder])
[self commonInit];
return self;
}
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame])
[self commonInit];
return self;
}
- (void)drawRect:(CGRect)rect {
[[UIColor redColor] setStroke];
[path stroke];
}
#pragma mark - Action Methods -
- (void)pan:(UIPanGestureRecognizer *)pan {
CGPoint currentPoint = [pan locationInView:self];
// NSLog(@"currentPoint: %@", NSStringFromCGPoint(currentPoint));
if (pan.state == UIGestureRecognizerStateBegan) {
NSMutableArray *buttonAry = [NSMutableArray array];
buttonAry = self.playViewController.rBtnOpponentPlayerList;
[buttonAry addObjectsFromArray:self.playViewController.rBtnPlayerList];
for (int i=0; i<buttonAry.count; i++) {
UIButton *btn = [buttonAry objectAtIndex:i];
CGRect nearByRect = CGRectMake(btn.frame.origin.x - 20, btn.frame.origin.y - 20, btn.frame.size.width + 40, btn.frame.size.height + 40);
if (CGRectContainsPoint(nearByRect, currentPoint)) {
isDrawPointInside = YES;
[path moveToPoint:btn.center];
// NSLog(@"point is inside....");
}
}
}
if (pan.state == UIGestureRecognizerStateChanged) {
if (isDrawPointInside) {
[path addLineToPoint:currentPoint];
[self setNeedsDisplay];
}
}
if (pan.state == UIGestureRecognizerStateEnded) {
isDrawPointInside = NO;
// NSLog(@"pan ended");
}
}
#pragma mark - Helper Methods -
- (void)commonInit {
path = [UIBezierPath bezierPath];
path.lineWidth = 3.0;
// Capture touches
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
pan.maximumNumberOfTouches = pan.minimumNumberOfTouches = 1;
[self addGestureRecognizer:pan];
// Erase with long press
[self addGestureRecognizer:[[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(erase)]];
}
- (void)erase {
path = [UIBezierPath bezierPath];
path.lineWidth = 3.0;
[self setNeedsDisplay];
}
@end
I am still not able to manage all Rounded Button state. Let me know the best way to manage all the step of Rounded Button while moving.
I have referred to: http://nachbaur.com/blog/core-animation-part-4
My Full Demo code is available at: https://www.dropbox.com/s/f0ri0bff8ioxtpz/FreeHandDrawingDemo%202.zip?dl=0
I'll assume that, when you say "manage all Rounded Button state", you mean you want to have a way to know the view has started animating, something like a viewHasMovedABitAutomatically
callback :) If that's the case, as far as I know there's no implemented way in the CA framework to do so. However, have a look at this question: Core animation progress callback Here's a way to do a workaround that looks pretty nice, perhaps you can work on something starting from here.
Oh, and one thing. I see you're using a Pan gesture recognizer. I've just skimmed through both your code and tarmes' (the guy in the other thread ;) ) but I think he's using drawInContext
to check the layer is being drawn. This might trigger as well if your view is draggable so, if that's the case, consider using some viewIsBeingDragged
flag on the pan method, and check it on the callback.
EDIT: Sorry, that seems to have nothing to do with your actual problem. I understand that what you meant is to control the animation speed so them all are moving accordingly. Let's elaborate on that.
As far as I know, there's no such a thing as an animation "step". Animating an image to move it 20 pixels to the left doesn't necessarily imply that the image will be redrawn 20 times. The image will be rendered as many times as it's needed, so we can't rely on the render method to calculate that. As far as I know, the only way to control the animation speed is via the duration
parameter of animateWithDuration:
. And you don't know the duration, you want to make the speed constant so the duration is whatever it takes.
Basic physics: duration is equal to distance divided by speed, so if we had the distance we need to cover we could easily calculate the animation duration. At first I thought that maybe UIBezierPath had a method or an attribute to calculate the total length, but I was wrong. However, in this thread Get CGPath total length there's a code that calculates it by numeric integration, you should test it to see if it works. Once you have it, you can do something like this (I didn't test it, I'm just entering pseudocode)
#define SPEED_CALIBRATION 30.0 // or whatever
-(void)animateView:(UIView*)view withBezierPath:(UIBezierPath*)path {
CGFloat distance = [self getBezierPathDistance:path];
CGFloat duration = distance / SPEED_CALIBRATION;
[self animateView:view withDuration:duration andBezierPath:path];
}
Something like this. getBezierPathDistance
is the method from the thread above, assuming it works, and animateView:withDuration:andBezierPath:
is your method to perform the non-linear animation using the CA documentation (I don't quite recall all the details, but I remember it was a pretty long chunk of code ;)).
I'm sorry if the answer is a bit vague, let's hope it helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With