La solution donnée par @peerless est un bon début, mais elle ne lance une animation que chaque fois que le glissement commence, sans tenir compte de la vitesse du défilement. Cela se traduit par une expérience plus saccadée que celle que vous obtenez dans l'application Facebook. Pour correspondre au comportement de Facebook, nous devons:
- masquer / afficher la barre de navigation à une vitesse proportionnelle à la vitesse de la traînée
- lancer une animation pour masquer complètement la barre si le défilement s'arrête lorsque la barre est partiellement masquée
- fondu les éléments de la barre de navigation à mesure que la barre se rétrécit.
Tout d'abord, vous aurez besoin de la propriété suivante:
@property (nonatomic) CGFloat previousScrollViewYOffset;
Et voici les UIScrollViewDelegate
méthodes:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGRect frame = self.navigationController.navigationBar.frame;
CGFloat size = frame.size.height - 21;
CGFloat framePercentageHidden = ((20 - frame.origin.y) / (frame.size.height - 1));
CGFloat scrollOffset = scrollView.contentOffset.y;
CGFloat scrollDiff = scrollOffset - self.previousScrollViewYOffset;
CGFloat scrollHeight = scrollView.frame.size.height;
CGFloat scrollContentSizeHeight = scrollView.contentSize.height + scrollView.contentInset.bottom;
if (scrollOffset <= -scrollView.contentInset.top) {
frame.origin.y = 20;
} else if ((scrollOffset + scrollHeight) >= scrollContentSizeHeight) {
frame.origin.y = -size;
} else {
frame.origin.y = MIN(20, MAX(-size, frame.origin.y - scrollDiff));
}
[self.navigationController.navigationBar setFrame:frame];
[self updateBarButtonItems:(1 - framePercentageHidden)];
self.previousScrollViewYOffset = scrollOffset;
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self stoppedScrolling];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView
willDecelerate:(BOOL)decelerate
{
if (!decelerate) {
[self stoppedScrolling];
}
}
Vous aurez également besoin de ces méthodes d'assistance:
- (void)stoppedScrolling
{
CGRect frame = self.navigationController.navigationBar.frame;
if (frame.origin.y < 20) {
[self animateNavBarTo:-(frame.size.height - 21)];
}
}
- (void)updateBarButtonItems:(CGFloat)alpha
{
[self.navigationItem.leftBarButtonItems enumerateObjectsUsingBlock:^(UIBarButtonItem* item, NSUInteger i, BOOL *stop) {
item.customView.alpha = alpha;
}];
[self.navigationItem.rightBarButtonItems enumerateObjectsUsingBlock:^(UIBarButtonItem* item, NSUInteger i, BOOL *stop) {
item.customView.alpha = alpha;
}];
self.navigationItem.titleView.alpha = alpha;
self.navigationController.navigationBar.tintColor = [self.navigationController.navigationBar.tintColor colorWithAlphaComponent:alpha];
}
- (void)animateNavBarTo:(CGFloat)y
{
[UIView animateWithDuration:0.2 animations:^{
CGRect frame = self.navigationController.navigationBar.frame;
CGFloat alpha = (frame.origin.y >= y ? 0 : 1);
frame.origin.y = y;
[self.navigationController.navigationBar setFrame:frame];
[self updateBarButtonItems:alpha];
}];
}
Pour un comportement légèrement différent, remplacez la ligne qui repositionne la barre lors du défilement (le else
bloc entrant scrollViewDidScroll
) par celle-ci:
frame.origin.y = MIN(20,
MAX(-size, frame.origin.y -
(frame.size.height * (scrollDiff / scrollHeight))));
Cela positionne la barre en fonction du dernier pourcentage de défilement, au lieu d'un montant absolu, ce qui entraîne un fondu plus lent. Le comportement original ressemble plus à Facebook, mais j'aime aussi celui-ci.
Remarque: cette solution est uniquement disponible sur iOS 7+. Assurez-vous d'ajouter les vérifications nécessaires si vous prenez en charge les anciennes versions d'iOS.