Ho una vista con due etichette. Quando faccio scorrere verso sinistra, riempio il contenuto successivo al testo dell'etichetta. Allo stesso modo strisciare a destra carica il contenuto precedente. Voglio dare un effetto alle etichette come stanno scorrendo da sinistra o destra. Ho usato una scrollview prima ma aveva un problema di memoria. Quindi sto usando una vista e il gesto di scorrimento carica il contenuto successivo o precedente. Voglio aggiungere l'effetto di scorrimento della scrollview alle etichette. Come lo posso fare?Effetto di scorrimento con il gesto di scorrimento in iOS
risposta
Non sono proprio sicuro di quale effetto stiate cercando, ma potreste fare qualcosa del genere, che crea una nuova etichetta temporanea, la mette fuori dallo schermo, lo anima spostandolo sull'etichetta che avete su schermo e, al termine, reimposta il vecchio ed elimina l'etichetta temporanea. Questo è ciò che un'implementazione non autolayout potrebbe essere simile:
- (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];
}
Questa animazione anima l'etichetta rispetto alla sua superview. Si consiglia di verificare che superview
sia impostato su "sottoview clip". In questo modo, l'animazione sarà vincolata ai limiti di quello superview
, che produce un aspetto leggermente più lucido.
Nota: se si utilizza il layout automatico, l'idea è la stessa (sebbene l'esecuzione sia più complicata). In pratica, configura i tuoi vincoli in modo che la nuova vista sia sulla destra, quindi, nel blocco di animazione, aggiorna/sostituisci i vincoli in modo che l'etichetta originale si trovi a sinistra e la nuova sia nel punto dell'etichetta originale e, infine, in il blocco di completamento ripristina i vincoli dell'etichetta originale e rimuove l'etichetta temporanea.
A proposito, questo è tutto infinitamente più facile se sei a tuo agio con una delle costruito in transizioni:
- (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];
}
Grazie mille per la risposta dettagliata. L'animazione è OK ora. Ma l'animazione della scrollview è migliore. Il problema con la vista di scorrimento: all'interno di scrollview c'è un'altra vista. Ogni volta che l'utente scorre una nuova vista viene aggiunta all'interno della scrollview. Ma dopo 50 visualizzazioni, l'app si arresta in modo anomalo con l'avviso di memoria. Ho provato qualcosa ma non ho trovato una soluzione. Quindi decise di rimuovere la vista di scorrimento. – Oktay
@Oktay sembra che tu abbia un semplice bug/perdita nel tuo codice scrollview. Molte persone usano le immagini a scorrimento senza problemi. Di nuovo, se pubblichi il tuo codice, ho il sospetto che sia facilmente risolvibile. – Rob
@Oktay Hai detto che ti piace l'animazione della vista pergamena, ma non sono sicuro di cosa ti piaccia di più. Perché usa un gesto pan (vale a dire è un gesto continuo che inizia a muoversi non appena inizi a muovere il dito sullo schermo)? Ho fatto un gesto di scorrimento piuttosto che un gesto di panoramica perché il titolo ti suggeriva di volerlo, ma un gesto di panoramica continua è molto semplice. O c'è qualcos'altro riguardo la scrollview che preferisci? – Rob
Se si preferisce l'animazione che si comporta più come una vista di scorrimento (vale a dire, con un feedback continuo sul gesto), che potrebbe essere simile alla seguente:
- (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
}
];
}
}
nota, ho messo sia l'etichetta originale, così come la nuova etichetta di essere stroncato su, in un contenitore di UIVie w (chiamato containerView, abbastanza sorprendentemente), in modo che io possa ritagliare l'animazione su quel contenitore.
A proposito, mentre mostro un modo per animare le etichette di seguito, non capisco perché si dovrebbe avere un "problema di memoria" con una vista di scorrimento. Se pubblichi quel codice, possiamo aiutarti a trovare il tuo problema di memoria lì, se vuoi. – Rob
utilizzare il flusso di copertura è meglio e dare un aspetto gradevole – parag