2015-04-06 14 views
5

mam poniżej ekranu:Multiple UIView jest z UIBezierPath

enter image description here

Kiedy klikam przycisk Pobierz więcej, będzie dodać nową Zaokrąglone przycisk. Dodałem Pan Gesture na UIView, gdzie dodano wszystkie zaokrąglone przyciski, które następnie narysują UIBezierPath tylko zaokrąglony przycisk. Do tego momentu wszystko działa.

Teraz chcę następujące:

  1. mam rodzica UIView nazwie playGroundView, który zawiera wszystkie Zaokrąglone przycisk. To narysuje ścieżkę Beziera dla wszystkich tych Przycisków.
  2. Po wybraniu ścieżki Beziera dla wszystkich przycisków, klikam przycisk Przenieś, aby animować cały przycisk zaokrąglenia jednocześnie.
  3. Kiedy wszystkie przyciski przesuwają się w punkcie końcowym ścieżki Beziera, mogę ponownie narysować kolejne ścieżki Beziera (dodawanie kolejnych kroków dla zaokrąglonego przycisku) do tej samej ścieżki dla tego samego przycisku.
  4. Po tym, przycisk Końcowy ruch będzie animował całość dla tego samego przycisku naraz.

Patrz kod poniżej PlayGround UIView podklasy:

@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 

ja nadal nie jestem w stanie zarządzać wszystkimi Zaokrąglone stanu przycisku. Daj mi znać najlepszy sposób na zarządzanie wszystkimi krokami zaokrąglonego przycisku podczas przenoszenia.

mam, o których mowa: http://nachbaur.com/blog/core-animation-part-4

mój pełny kod Demo jest dostępne na stronie: https://www.dropbox.com/s/f0ri0bff8ioxtpz/FreeHandDrawingDemo%202.zip?dl=0

Odpowiedz

0

Aby to osiągnąć, użyłem kategorii UIBezierPath + Points. Oprócz tego, w moim kodu następujących klas i logiki jest stosowany:

W PlayGroundView, I edycja kodu w metodzie drawRect, kod reszta jest taka sama:

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

W mojej klasie ViewController, Get więcej przycisków poniższy kod idzie:

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

W Move Buttona poniżej kod idzie, co pozwala mi przenieść wszystkie moje okrągły przycisk, który jest jednocześnie moim Dokładny wymóg:

- (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

będę zakładać, że kiedy mówisz „zarządzać wszystkimi Zaokrąglone stan Buttona”, to znaczy, że chce mieć sposób, aby dowiedzieć się, że widok zaczął się animować, coś w rodzaju callback viewHasMovedABitAutomatically :) Jeśli tak jest, o ile wiem, nie ma zaimplementowanego sposób w strukturze CA, aby to zrobić. Jednak popatrz na to pytanie: Core animation progress callback Oto sposób na obejście problemu, które wygląda całkiem nieźle, być może możesz pracować nad czymś zaczynającym się od tego miejsca.

Aha, i jedno. Widzę, że używasz rozpoznawania gestów Pan. Właśnie przejrzałem zarówno twój kod, jak i pasmo (facet w drugim wątku;)), ale myślę, że używa on drawInContext do sprawdzenia, czy warstwa jest rysowana. Może to również wywołać, jeśli widok jest przeciągalny, więc jeśli tak jest, rozważ użycie flagi viewIsBeingDragged w metodzie pan i sprawdź to na wywołaniu zwrotnym.

EDYCJA: Przepraszam, to wydaje się nie mieć nic wspólnego z rzeczywistym problemem. Rozumiem, że chodziło o kontrolowanie szybkości animacji, aby wszyscy odpowiednio się poruszali. Omówmy to.

O ile mi wiadomo, nie ma czegoś takiego jak "krok" animacji. Animowanie obrazu w celu przesunięcia go o 20 pikseli w lewo niekoniecznie oznacza, że ​​obraz zostanie przerysowany 20 razy. Obraz będzie renderowany tyle razy, ile potrzeba, więc nie możemy polegać na metodzie renderowania, aby to obliczyć. O ile mi wiadomo, jedynym sposobem kontrolowania prędkości animacji jest parametr duration z. I nie znasz czasu trwania, chcesz, aby prędkość była stała, więc czas jest dowolny.

Podstawowa fizyka: czas trwania jest równy odległości dzielonej przez prędkość, więc jeśli mamy odległość, którą musimy pokonać, możemy łatwo obliczyć czas trwania animacji. Na początku pomyślałem, że być może UIBezierPath ma metodę lub atrybut do obliczenia całkowitej długości, ale myliłem się. Jednak w tym wątku Get CGPath total length istnieje kod, który oblicza go przez integrację numeryczną, należy przetestować go, aby sprawdzić, czy działa. Po to, można zrobić coś takiego (nie testowałem go, ja tylko wejściem pseudokod)

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

coś takiego. getBezierPathDistance to metoda z powyższego wątku, zakładając, że działa, a animateView:withDuration:andBezierPath: to twoja metoda wykonywania nieliniowej animacji za pomocą dokumentacji CA (nie pamiętam dokładnie wszystkich szczegółów, ale pamiętam, że był to całkiem długi fragment kod ;)).

Przykro mi, jeśli odpowiedź jest nieco niejasna, miejmy nadzieję, że pomoże!

+0

Witam, nie chcę otrzymywać oddzwonienia w celu zakończenia animacji. Moim wymaganiem jest to, że chcę poruszać wszystkimi okrągłymi przyciskami jednocześnie ze względnym ruchem. Powiedz, czy moja pierwsza ścieżka przycisku ma 1 cm, a druga ścieżka przycisku ma 2 cm, następnie pierwszy przycisk powinien sięgać pierwszy, a drugi przycisk powinien zająć podwójny czas, a następnie pierwszy przycisk. Ta animacja powinna być jednocześnie. – DShah

+0

Moje złe, w ogóle cię nie rozumiałem :) Mam pomysł, jak to zrobić, teraz edytuję swoją odpowiedź. – Bartserk