2015-04-06 3 views
5

У меня есть внизу экрана:Множественного UIView с UIBezierPath

enter image description here

Когда я нажимаю кнопку Дополнительно, это добавит новую скругленную кнопку. Я добавил Pan Gesture на UIView, где добавляются все закругленные кнопки, которые затем нарисуют UIBezierPath только с закругленной кнопкой. До этого момента все работает.

Теперь я хочу следующее:

  1. У меня есть родитель UIView под названием playGroundView, который содержит все Rounded Button. Это нарисует путь Безье для всех этих кнопок.
  2. Как только Bezier Path нарисован для всех кнопок, я нажимаю кнопку «Переместить» для одновременной анимации всех закругленных кнопок.
  3. Однажды все кнопки перемещаются по конечной точке пути Безье, затем я снова могу нарисовать дальнейшие шаги «Безье» (добавление дополнительных шагов для закругленной кнопки) к тому же пути для одной и той же кнопки.
  4. После этого кнопка Final Move будет анимирована целиком для одной и той же кнопки одновременно.

См ниже код для PlayGround UIView подкласса:

@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 

Я до сих пор не в состоянии управлять всем скругленным состоянием кнопки. Дайте мне знать, как лучше всего управлять всем шагом Rounded Button во время движения.

я говорил: http://nachbaur.com/blog/core-animation-part-4

Моего Полного кода Демо доступен по адресу: https://www.dropbox.com/s/f0ri0bff8ioxtpz/FreeHandDrawingDemo%202.zip?dl=0

ответ

0

Для достижения этой цели я использовал категорию UIBezierPath + Points. Кроме того, в моем коде ниже классов и логики используется:

В PlayGroundView, я редактировал код в методе DrawRect, код остальное же:

- (void)drawRect:(CGRect)rect { 
    [[UIColor redColor] setStroke]; 
    for (int i=0; i<[self subviews].count; i++) { 
     RoundButton *tmpPlayerButton = [self.subviews objectAtIndex:i]; 
     for (UIBezierPath *bezierPath in tmpPlayerButton.allPathsOfPlayer) { 
      [bezierPath strokeWithBlendMode:kCGBlendModeNormal alpha:1.0]; 
     } 
    } 
    [path stroke]; 
} 

В моем классе ViewController в Get More Buttons код ниже идет:

- (IBAction)getBallPressed:(id)sender { 
    [self moveBalls]; 

// self.playView.viewController = self; 

    int xPos = self.view.frame.size.width-30; 
    int yPos = self.view.frame.size.height-30; 
    int btnXPos = arc4random_uniform(xPos); 
    int btnYPos = arc4random_uniform(yPos); 

    RoundButton *newButton = [[RoundButton alloc] initWithFrame:CGRectMake(btnXPos, btnYPos, 30, 30)]; 
    [newButton setBackgroundColor:[UIColor redColor]]; 
    newButton.layer.cornerRadius = newButton.frame.size.height/2; 
    [newButton setTitle:[self randomStringWithLength:1] forState:UIControlStateNormal]; 

    UILongPressGestureRecognizer *longGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longGestureRecognized:)]; 
    [newButton addGestureRecognizer:longGesture]; 

    [self.playView.playerArray addObject:newButton]; 
    [self.playView addSubview:newButton]; 
} 

- (void)moveBalls { 
    for (int i=0; i<[self.playView subviews].count; i++) { 
     RoundButton *playerButton = [self.playView.subviews objectAtIndex:i]; 
     if (playerButton.isEditPath) { 
      id point = [[[playerButton.allPathsOfPlayer lastObject] points] lastObject]; 
      CGPoint lastPoint = [point CGPointValue]; 
      NSLog(@"lastPoint: %@", NSStringFromCGPoint(lastPoint)); 
      [playerButton setCenter:lastPoint]; 
     } 
    } 
} 

в Баттона Move ниже код идет, что позволяет мне двигаться всю круглую кнопку одновременно, которое мое Exact требование:

- (void)playAnimation { 
    for (int i=0; i<self.playView.subviews.count; i++) { 
     //Prepare the animation - we use keyframe animation for animations of this complexity 
     CAKeyframeAnimation *pathAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"]; 
     //Set some variables on the animation 
     pathAnimation.calculationMode = kCAAnimationPaced; 

     //We want the animation to persist - not so important in this case - but kept for clarity 
     //If we animated something from left to right - and we wanted it to stay in the new position, 
     //then we would need these parameters 
     pathAnimation.fillMode = kCAFillModeForwards; 
     pathAnimation.removedOnCompletion = NO; 
     pathAnimation.duration = 2.0; 
     pathAnimation.repeatCount = 0; 

     RoundButton *tmpRoundButton; 
     UIBezierPath *path; 

     tmpRoundButton = [self.playView.subviews objectAtIndex:i]; 

     for (int j=0; j<tmpRoundButton.allPathsOfPlayer.count; j++) { 
      if (path) 
       [path appendPath:[tmpRoundButton.allPathsOfPlayer objectAtIndex:j]]; 
      else 
       path = [tmpRoundButton.allPathsOfPlayer objectAtIndex:j]; 

      //Now we have the path, we tell the animation we want to use this path - then we release the path 
      pathAnimation.path = [path CGPath]; 
      [tmpRoundButton.layer addAnimation:pathAnimation forKey:@"moveTheSquare"]; 
     } 
     [path moveToPoint:[[[path points] firstObject] CGPointValue]]; 
    } 
    [self.playView setNeedsDisplay]; 
} 
2

Я предполагаю, что, когда вы говорите, «управлять всем скругленным состоянием кнопки», вы имеете в виду вы хотите иметь способ узнать, что представление начало анимацию, что-то вроде обратного вызова viewHasMovedABitAutomatically. Если это так, насколько я знаю, в среде CA нет реализованного способа. Тем не менее, посмотрите на этот вопрос: Core animation progress callback Вот способ обходного пути, который выглядит довольно красиво, возможно, вы можете работать над чем-то, начиная с этого момента.

О, и одно. Я вижу, что вы используете распознаватель жеста Pan. Я просто просматривал как ваш код, так и tarmes (парень в другой теме;)), но я думаю, что он использует drawInContext, чтобы проверить, что слой рисуется. Это может также вызвать, если ваше представление перетаскивается, поэтому, если это так, подумайте об использовании флага viewIsBeingDragged в методе панорамирования и проверьте его на обратном вызове.

EDIT: Извините, это не имеет ничего общего с вашей реальной проблемой. Я понимаю, что вы имели в виду контролировать скорость анимации, чтобы все они двигались соответственно. Давайте подробно остановимся на этом.

Насколько я знаю, нет такой вещи, как «шаг» анимации. Анимация изображения для перемещения на 20 пикселей влево не обязательно означает, что изображение будет перерисовано 20 раз. Изображение будет отображаться столько раз, сколько необходимо, поэтому мы не можем полагаться на метод рендеринга для его вычисления. Насколько я знаю, единственный способ контролировать скорость анимации - через параметр durationanimateWithDuration:. И вы не знаете продолжительность, вы хотите, чтобы скорость была постоянной, поэтому продолжительность - это то, что требуется.

Основная физика: продолжительность равна расстоянию, деленному на скорость, поэтому, если бы мы имели расстояние, которое нам нужно покрыть, мы могли бы легко вычислить продолжительность анимации. Сначала я подумал, что, возможно, у UIBezierPath был метод или атрибут для вычисления общей длины, но я был неправ. Однако в этом потоке Get CGPath total length есть код, который вычисляет его посредством числовой интеграции, вы должны проверить его, чтобы увидеть, работает ли он. После того, как вы есть, вы можете сделать что-то вроде этого (я не проверял, я просто входя псевдокод)

#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]; 
} 

Что-то вроде этого. getBezierPathDistance - это метод из приведенного выше потока, если он работает, и animateView:withDuration:andBezierPath: - это ваш метод выполнения нелинейной анимации с использованием документации CA (я не совсем помню все детали, но я помню, что это был довольно длинный фрагмент код;)).

Прошу прощения, если ответ немного расплывчатый, будем надеяться, что это поможет!

+0

Привет, я не хочу получать обратный вызов для завершения анимации. Мое требование в том, что я хочу перемещать все RoundButtons одновременно с относительным движением. Скажите, если мой первый путь к кнопке составляет 1 см, а второй путь - 2 см, тогда первая кнопка должна достигать первой и второй кнопки, тогда нужно взять двойное время, а затем первую кнопку. Эта анимация должна быть одновременно. – DShah

+0

Плохо, я тебя совсем не понимаю :) У меня есть идея, как это сделать, сейчас я редактирую свой ответ. – Bartserk

Смежные вопросы