J'ai la sous-classe UIScrollView. Son contenu est réutilisable - environ 4 ou 5 vues sont utilisées pour afficher des centaines d’éléments (lors du défilement des objets masqués réutilisés et des sauts vers une autre position quand il faut les voir)
Ce dont j'ai besoin: possibilité de faire défiler automatiquement l'affichage de défilement vers n'importe quelle position. Par exemple, ma vue de défilement affiche les 4ème, 5ème et 6ème éléments et lorsque je tape sur un bouton, il doit défiler jusqu'au 30ème élément. En d'autres termes, j'ai besoin du comportement standard de UIScrollView.
Cela fonctionne bien:
[self setContentOffset:CGPointMake(index*elementWidth, 0) animated:YES];
mais j'ai besoin de personnalisation. Par exemple, modifiez la durée de l'animation, ajoutez du code à exécuter en fin d'animation.
Décision évidente:
[UIView animateWithDuration:3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
[self setContentOffset:CGPointMake(index*elementWidth, 0)];
} completion:^(BOOL finished) {
//some code
}];
mais j'ai quelques actions liées à scroll event, et maintenant elles sont toutes dans un bloc d'animation et cela provoque l'animation de toutes les images de la sous-vue (grâce à quelques éléments réutilisables, toutes n'animent pas ce que je veux)
La question est: Comment puis-je créer une animation personnalisée (en fait, j'ai besoin d'une durée personnalisée, d'actions à la fin et de l'option BeginFromCurrentState) pour le contenu décalé SANS QUE n'anime tout le code, lié à l'événement scrollViewDidScroll
?
UPD: Merci à Réponse d'Andrew (première partie) J'ai résolu le problème d'animation dans scrollViewDidScroll
:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
[UIView performWithoutAnimation:^{
[self refreshTiles];
}];
}
Mais scrollViewDidScroll
doit (pour mes besoins) exécuter chaque image de l'animation comme c'était le cas en cas de
[self setContentOffset:CGPointMake(index*elementWidth, 0) animated:YES];
Cependant, maintenant, il ne s'exécute qu'une fois au début de l'animation.
Comment puis-je résoudre ça?
Avez-vous essayé la même approche, mais avec une animation désactivée dans scrollViewDidScroll
?
Sur iOS 7, vous pouvez essayer d’envelopper votre code dans scrollViewDidScroll
dans
[UIView performWithoutAnimation:^{
//Your code here
}];
sur les versions précédentes d'iOS, vous pouvez essayer:
[CATransaction begin];
[CATransaction setDisableActions:YES];
//Your code here
[CATransaction commit];
Mettre à jour:
Malheureusement, c'est là que vous avez frappé la partie la plus difficile de tout. setContentOffset:
appelle le délégué une seule fois, ce qui équivaut à setContentOffset:animated:NO
, qui l'appelle encore une fois.
setContentOffset:animated:YES
appelle le délégué lorsque l'animation modifie les limites de la vue de défilement et vous le souhaitez, mais vous ne souhaitez pas l'animation fournie. Le seul moyen de contourner ce problème est de modifier progressivement la contentOffset
de la vue de défilement, de sorte que le système d'animation ne saute pas à la valeur finale, comme c'est le cas pour le moment.
Pour ce faire, vous pouvez consulter les animations d'images clés, comme pour iOS 7:
[UIView animateKeyframesWithDuration:duration delay:delay options:options animations:^{
[UIView addKeyframeWithRelativeStartTime:0.0 relativeDuration:0.5 animations:^{
[self setContentOffset:CGPointMake(floorf(index/2) * elementWidth, 0)];
}];
[UIView addKeyframeWithRelativeStartTime:0.5 relativeDuration:0.5 animations:^{
[self setContentOffset:CGPointMake(index*elementWidth, 0)];
}];
} completion:^(BOOL finished) {
//Completion Block
}];
Cela vous donnera deux mises à jour et vous pouvez bien sûr utiliser quelques maths et une boucle pour en ajouter beaucoup plus avec les timings appropriés.
Sur les versions précédentes d'iOS, vous devrez passer à CoreAnimation pour les animations d'images clés, mais c'est fondamentalement la même chose avec une syntaxe légèrement différente.
Méthode 2: Vous pouvez essayer d'interroger la presentationLayer de la vue de défilement pour connaître les modifications effectuées avec une minuterie que vous commencez au début de l'animation, car malheureusement, les propriétés de la couche de présentation ne sont pas observables. Vous pouvez également utiliser needsDisplayForKey
dans une sous-classe de la couche pour être averti lorsque les limites changent, mais cela nécessitera du travail à la configuration et cela entraînera un redessinage, ce qui pourrait affecter les performances.
Méthode 3: .__ serait de disséquer exactement ce qu'il advient du scrollView lorsqu'il est animé d'essayer OUI d'essayer d'intercepter l'animation définie dans le scrollview et de modifier ses paramètres, mais comme ce serait le cas le plus difficile, cassable en raison des modifications apportées par Apple et méthode la plus délicate, je ne vais pas y aller.
Pour ce faire, utilisez la bibliothèque AnimationEngine . C'est une très petite bibliothèque: six fichiers, avec trois autres si vous voulez un comportement de ressort amorti.
En coulisse, il utilise une CADisplayLink
pour exécuter votre bloc d'animation une fois par image. Vous obtenez une syntaxe basée sur des blocs propres, facile à utiliser, ainsi que de nombreuses fonctions interpolation et easing qui vous font gagner du temps.
Pour animer contentOffset
:
startOffset = scrollView.contentOffset;
endOffset = ..
// Constant speed looks good...
const CGFloat kTimelineAnimationSpeed = 300;
CGFloat timelineAnimationDuration = fabs(deltaToDesiredX) / kTimelineAnimationSpeed;
[INTUAnimationEngine animateWithDuration:timelineAnimationDuration
delay:0
easing:INTULinear
animations:^(CGFloat progress) {
self.videoTimelineView.contentOffset =
INTUInterpolateCGPoint(startOffset, endOffset, progress);
}
completion:^(BOOL finished) {
autoscrollEnabled = YES;
}];
Essaye ça:
UIView.animate(withDuration: 0.6, animations: {
self.view.collectionView.contentOffset = newOffset
self.view.layoutIfNeeded()
}, completion: nil)