2012-06-15 36 views
6

Mam widok, który ma dwie etykiety. Przesuwając palcem w lewo, wypełniam następną treść tekstem etykiety. Podobnie przesuwając prawo ładuje poprzednią zawartość. Chcę nadać efekt etykietom podobnym do przewijania z lewej lub prawej strony. Użyłem przewijania wcześniej, ale miał problem z pamięcią. Używam jednego widoku, a gest przesunięcia ładuje następną lub poprzednią treść. Chcę dodać przesuwny efekt scrollview do etykiet. Jak mogę to zrobić?Efekt przewijania z gestem przesuń w iOS

+0

Przy okazji, podczas gdy pokazuję sposób animacji etykiet poniżej, nie rozumiem, dlaczego miałbyś "problem z pamięcią" z widokiem przewijania. Jeśli umieścisz ten kod, pomożemy ci znaleźć tam problem z pamięcią, jeśli chcesz. – Rob

+0

użyj funkcji Cover Flow, aby uzyskać najlepszy efekt i nadaj mu ładny wygląd. – parag

Odpowiedz

15

Nie jestem do końca pewien, jakiego efektu szukasz, ale możesz zrobić coś takiego, co tworzy nową, tymczasową etykietę, wyłącza ekran, animuje przesuwanie go po etykiecie, którą masz na ekranie, a następnie po zakończeniu resetuje stary i usuwa tymczasową etykietę. To, co może wyglądać implementacja nie autolayout jak:

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    // Do any additional setup after loading the view. 

    UISwipeGestureRecognizer *left = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(leftSwipe:)]; 
    [left setDirection:UISwipeGestureRecognizerDirectionLeft]; 
    [self.view addGestureRecognizer:left]; 
    // if non-ARC, release it 
    // [release left]; 

    self.label1.text = @"Mo"; 
} 

- (void)leftSwipe:(UISwipeGestureRecognizer *)gesture 
{ 
    NSString *newText; 
    UILabel *existingLabel = self.label1; 

    // in my example, I'm just going to toggle the value between Mo and Curly 

    if ([existingLabel.text isEqualToString:@"Curly"]) 
     newText = @"Mo"; 
    else 
     newText = @"Curly"; 

    // create new label 

    UILabel *tempLabel = [[UILabel alloc] initWithFrame:existingLabel.frame]; 
    [existingLabel.superview addSubview:tempLabel]; 
    tempLabel.text = newText; 

    // move the new label off-frame to the right 

    tempLabel.transform = CGAffineTransformMakeTranslation(tempLabel.superview.bounds.size.width, 0); 

    // animate the sliding of them into place 

    [UIView animateWithDuration:0.5 
        animations:^{ 
         tempLabel.transform = CGAffineTransformIdentity; 
         existingLabel.transform = CGAffineTransformMakeTranslation(-existingLabel.superview.bounds.size.width, 0); 
        } 
        completion:^(BOOL finished) { 
         existingLabel.text = newText; 
         existingLabel.transform = CGAffineTransformIdentity; 
         [tempLabel removeFromSuperview]; 
        }]; 

    // if non-ARC, release it 
    // [release tempLabel]; 
} 

Ta animacja animuje etykiety w odniesieniu do jej SuperView. Możesz chcieć upewnić się, że superview jest ustawione na "klip subviews". W ten sposób animacja będzie ograniczana do granic tego superview, co daje nieco bardziej dopracowany wygląd.

Uwaga, jeśli korzystasz z automatycznego układu, pomysł jest taki sam (chociaż wykonanie jest bardziej skomplikowane). Zasadniczo skonfiguruj swoje ograniczenia, aby nowy widok był przesunięty w prawo, a następnie w bloku animacji zaktualizuj/zastąp wiązania, aby oryginalna etykieta była wyłączona po lewej stronie, a nowa była w miejscu oryginalnej etykiety, a na koniec blok zakończenia resetuje wiązania oryginalnej etykiety i usuwa tymczasową etykietę.


Nawiasem mówiąc, to wszystko jest nieskończenie łatwiej, jeśli jesteś dobrze z jednym z wbudowanego w przejściach:

- (void)leftSwipe:(UISwipeGestureRecognizer *)gesture 
{ 
    NSString *newText; 
    UILabel *existingLabel = self.label1; 

    // in my example, I'm just going to toggle the value between Mo and Curly 

    if ([existingLabel.text isEqualToString:@"Curly"]) 
     newText = @"Mo"; 
    else 
     newText = @"Curly"; 

    [UIView transitionWithView:existingLabel // or try `existingLabel.superview` 
         duration:0.5 
         options:UIViewAnimationOptionTransitionFlipFromRight 
        animations:^{ 
         existingLabel.text = newText; 
        } 
        completion:nil]; 
} 
+0

Dziękuję bardzo za szczegółową odpowiedź. Animacja jest teraz OK. Ale animacja przewijania jest lepsza. Problem z przewijaniem: wewnątrz przewijania znajduje się inny widok. Za każdym razem, gdy użytkownik przewinie nowy widok, zostanie dodany do przewijania. Ale po 50 odsłonach aplikacja ulega awarii z ostrzeżeniem o pamięci. Próbowałem czegoś, ale nie znalazłem rozwiązania. Zdecydowano więc usunąć widok przewijania. – Oktay

+0

@Oktay brzmi, jakbyś miał prosty błąd/przeciek w swoim kodzie przewijania. Wiele osób korzysta z scrollview bez żadnego problemu. Ponownie, jeśli napiszesz swój kod, podejrzewam, że można go łatwo usunąć. – Rob

+0

@Oktay Mówisz, że lubisz animację widoku przewijania lepiej, ale nie jestem pewien, co ci się podoba lepiej. Ponieważ używa gestu panoramy (tzn. Jest ciągłym gestem, który zaczyna się poruszać, gdy tylko zaczniesz przesuwać palec po ekranie)? Wykonałem gest machnięcia, a nie gest panowania, ponieważ twój tytuł sugerował, że tego chcesz, ale ciągły gest pan jest bardzo łatwy. A może wolisz coś innego w przewijanym widoku? – Rob

6

Jeśli wolisz animację, która zachowuje się bardziej jak myślą przewijania (czyli z ciągłej informacji zwrotnej na gest), może to wyglądać mniej więcej tak:

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 

    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panHandler:)]; 
    [self.view addGestureRecognizer:pan]; 

    self.label1.text = @"Mo"; 
} 

- (void)panHandler:(UIPanGestureRecognizer *)sender 
{ 
    if (sender.state == UIGestureRecognizerStateBegan) 
    { 
     _panLabel = [[UILabel alloc] init]; 

     // in my example, I'm just going to toggle the value between Mo and Curly 
     // you'll presumably set the label contents based upon the direction of the 
     // pan (if positive, swiping to the right, grab the "previous" label, if negative 
     // pan, grab the "next" label) 

     if ([self.label1.text isEqualToString:@"Curly"]) 
      _newText = @"Mo"; 
     else 
      _newText = @"Curly"; 

     // set the text 

     _panLabel.text = _newText; 

     // set the frame to just be off screen 

     _panLabel.frame = CGRectMake(self.label1.frame.origin.x + self.containerView.frame.size.width, 
            self.label1.frame.origin.y, 
            self.label1.frame.size.width, 
            self.label1.frame.size.height); 

     [self.containerView addSubview:_panLabel]; 

     _originalCenter = self.label1.center; // save where the original label originally was 
    } 
    else if (sender.state == UIGestureRecognizerStateChanged) 
    { 
     CGPoint translate = [sender translationInView:self.containerView]; 

     if (translate.x > 0) 
     { 
      _panLabel.center = CGPointMake(_originalCenter.x - self.containerView.frame.size.width + translate.x, _originalCenter.y); 
      self.label1.center = CGPointMake(_originalCenter.x + translate.x, _originalCenter.y); 
     } 
     else 
     { 
      _panLabel.center = CGPointMake(_originalCenter.x + self.containerView.frame.size.width + translate.x, _originalCenter.y); 
      self.label1.center = CGPointMake(_originalCenter.x + translate.x, _originalCenter.y); 
     } 
    } 
    else if (sender.state == UIGestureRecognizerStateEnded || sender.state == UIGestureRecognizerStateFailed || sender.state == UIGestureRecognizerStateCancelled) 
    { 
     CGPoint translate = [sender translationInView:self.containerView]; 
     CGPoint finalNewFieldLocation; 
     CGPoint finalOriginalFieldLocation; 
     BOOL panSucceeded; 

     if (sender.state == UIGestureRecognizerStateFailed || 
      sender.state == UIGestureRecognizerStateCancelled) 
     { 
      panSucceeded = NO; 
     } 
     else 
     { 
      // by factoring in the velocity, we can capture a flick more accurately 
      // 
      // (by the way, I don't like iOS's velocity, because if you stop moving, it records the velocity 
      // prior to stopping the move rather than noting that you actually stopped, so I usually calculate my own, 
      // but I'll leave this as is for purposes of this example) 

      CGPoint velocity = [sender velocityInView:self.containerView]; 

      if (translate.x < 0) 
       panSucceeded = ((translate.x + velocity.x * 0.5) < -(self.containerView.frame.size.width/2)); 
      else 
       panSucceeded = ((translate.x + velocity.x * 0.5) > (self.containerView.frame.size.width/2)); 
     } 

     if (panSucceeded) 
     { 
      // if we succeeded, finish moving the stuff 

      finalNewFieldLocation = _originalCenter; 
      if (translate.x < 0) 
       finalOriginalFieldLocation = CGPointMake(_originalCenter.x - self.containerView.frame.size.width, _originalCenter.y); 
      else 
       finalOriginalFieldLocation = CGPointMake(_originalCenter.x + self.containerView.frame.size.width, _originalCenter.y); 
     } 
     else 
     { 
      // if we didn't, then just return everything to where it was 

      finalOriginalFieldLocation = _originalCenter; 

      if (translate.x < 0) 
       finalNewFieldLocation = CGPointMake(_originalCenter.x + self.containerView.frame.size.width, _originalCenter.y); 
      else 
       finalNewFieldLocation = CGPointMake(_originalCenter.x - self.containerView.frame.size.width, _originalCenter.y); 
     } 

     // animate the moving of stuff to their final locations, and on completion, clean everything up 

     [UIView animateWithDuration:0.3 
           delay:0.0 
          options:UIViewAnimationOptionCurveEaseOut 
         animations:^{ 
          _panLabel.center = finalNewFieldLocation; 
          self.label1.center = finalOriginalFieldLocation; 
         } 
         completion:^(BOOL finished) { 
          if (panSucceeded) 
           self.label1.text = _newText; 
          self.label1.center = _originalCenter; 
          [_panLabel removeFromSuperview]; 
          _panLabel = nil; // in non-ARC, release instead 
         } 
     ]; 
    } 
} 

Uwaga, mam umieścić zarówno oryginalną etykietę, a także nowa etykieta jest cukrem na, w UIVie pojemnika w (zwany containerView, o dziwo), dzięki czemu mogę przypiąć animację do tego kontenera.