2015-04-06 143 views
5

動畫我有以下屏幕:多的UIView與UIBezierPath

enter image description here

當我點擊獲取更多按鈕,它會增加新的圓形按鈕。我在UIView中添加了Pan Gesture,其中添加了所有的圓形按鈕,然後只會從Rounded Button繪製一個UIBezierPath。到目前爲止,一切正常。

現在,我想以下幾點:

  1. 我父UIView稱爲playGroundView,它包含了所有圓形按鈕。這將爲所有這些按鈕繪製一條Bezier路徑。
  2. 一旦爲所有按鈕繪製了貝塞爾路徑,然後單擊移動按鈕一次爲所有圓形按鈕設置動畫效果。
  3. 有一次,所有的Button都移動到Bezier路徑的終點,然後我可以再次爲同一個按鈕繪製更多的Bezier路徑(爲Rounded Button添加更多步驟)連接到同一路徑。
  4. 之後,Final Move按鈕將立即爲同一個Button設置動畫。

參考下面的代碼操場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類,以獲取更多的按鈕下面的代碼有云:

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

在移動按鈕,下面的代碼去,這讓我將我的所有輪同步按鈕,這是我的精確要求:

- (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來檢查圖層被繪製。如果你的視圖是可拖動的,這也可能觸發,所以,如果是這種情況,考慮在pan方法上使用一些viewIsBeingDragged標誌,並在回調中檢查它。

編輯:對不起,這似乎與你的實際問題無關。我明白你的意思是控制動畫速度,所以他們都相應地移動。我們來詳細說明一下。

據我所知,沒有動畫「step」這樣的東西。動畫圖像將其向左移動20像素並不一定意味着圖像將被重繪20次。圖像將根據需要渲染多次,所以我們不能依賴渲染方法來計算它。據我所知,控制動畫速度的唯一方法是通過參數animateWithDuration:duration。而且你不知道持續時間,你想讓速度保持不變,所以持續時間是無論如何。基本物理:持續時間等於距離除以速度,所以如果我們有距離我們需要覆蓋,我們可以很容易地計算出動畫的持續時間。起初我以爲也許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

嗨,我不想回調動畫完成。我的要求是,我想通過相對運動同時移動所有RoundButton。假設我的第一個按鈕路徑是1釐米,第二個按鈕路徑是2釐米,那麼第一個按鈕應該到達第一個按鈕,第二個按鈕應該是第一個按鈕的兩倍。這個動畫應該是同時的。 – DShah 2015-04-16 05:06:12

+0

我的不好,我根本不理解你:)我有一個想法,如何做到這一點,我現在正在編輯我的答案。 – Bartserk 2015-04-16 08:03:37