Imiter Facebook cacher / montrer l'expansion / la contraction de la barre de navigation

Dans la nouvelle application iPhone iOS7 de Facebook, lorsque l'user fait défiler la barre de navigationBar se cache progressivement à un point où il disparaît complètement. Ensuite, lorsque l'user fait défiler la navigationBar s'affiche progressivement.

Comment implémenteriez-vous ce comportement vous-même? Je suis conscient de la solution suivante, mais elle disparaît tout de suite et n'est pas liée à la vitesse du geste de défilement de l'user.

 [navigationController setNavigationBarHidden: YES animated:YES]; 

J'espère que ce n'est pas un doublon, car je ne suis pas sûr de la meilleure façon de décrire le comportement «expansion / contraction».

La solution donnée par @peerless est un bon début, mais elle ne lance une animation que lorsque le glisser commence, sans tenir count de la vitesse du défilement. Cela se traduit par une expérience plus choppier que vous obtenez dans l'application Facebook. Pour correspondre au comportement de Facebook, nous devons:

  • cacher / afficher la barre de navigation à un taux proportionnel au taux de glissement
  • lancer une animation pour masquer complètement la barre si le défilement s'arrête lorsque la barre est partiellement cachée
  • fondu les éléments de la barre de navigation lorsque la barre rétrécit.

D'abord, vous aurez besoin de la propriété suivante:

 @property (nonatomic) CGFloat previousScrollViewYOffset; 

Et voici les methods UIScrollViewDelegate :

 - (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 methods 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 replace la barre lors du défilement (le bloc else dans scrollViewDidScroll ) avec celui-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 nombre absolu, ce qui entraîne un fondu plus lent. Le comportement original est plus proche de Facebook, mais j'aime aussi celui-ci.

Remarque: Cette solution est uniquement pour iOS 7+. Assurez-vous d'append les vérifications nécessaires si vous prenez en charge les anciennes versions d'iOS.

EDIT: Seulement pour iOS 8 et supérieur.

Vous pouvez essayer d'utiliser

 self.navigationController.hidesBarsOnSwipe = YES; 

Travaille pour moi.

Si votre encoding dans swift vous devez utiliser de cette façon ( https://stackoverflow.com/a/27662702/2283308 )

 navigationController?.hidesBarsOnSwipe = true 

Voici une implémentation de plus: TLYShyNavBar v1.0.0 publié!

J'ai décidé de faire mon propre après avoir essayé les solutions fournies, et pour moi, ils étaient soit mal performants, avaient une haute barrière d'input et un code de plaque de chaudière, ou manquaient de la vue d'extension sous la barre de navigation. Pour utiliser ce composant, tout ce que vous avez à faire est:

 self.shyNavBarManager.scrollView = self.scrollView; 

Oh, et il est testé dans notre propre application.

Vous pouvez jeter un oeil à mon GTScrollNavigationBar . J'ai sous-classé UINavigationBar pour le faire défiler en fonction du défilement d'un UIScrollView.

Remarque: Si vous disposez d'une barre de navigation OPAQUE, la barre de défilement doit EXPAND lorsque la barre de navigation est CACHÉE. C'est exactement ce que fait GTScrollNavigationBar. (Tout comme dans Safari sur iOS par exemple.)

iOS8 inclut des propriétés pour cacher la barre de navigation gratuitement. Il y a une video WWDC qui le démontre, searchz "View Controller Advancements in iOS 8".

Exemple :

 class QuotesTableViewController: UITableViewController { override func viewDidAppear(animated: Bool) { super.viewDidAppear(animated) navigationController?.hidesBarsOnSwipe = true } 

}

Autres propriétés:

 class UINavigationController : UIViewController { //... truncated /// When the keyboard appears, the navigation controller's navigationBar toolbar will be hidden. The bars will remain hidden when the keyboard dismisses, but a tap in the content area will show them. @availability(iOS, introduced=8.0) var hidesBarsWhenKeyboardAppears: Bool /// When the user swipes, the navigation controller's navigationBar & toolbar will be hidden (on a swipe up) or shown (on a swipe down). The toolbar only participates if it has items. @availability(iOS, introduced=8.0) var hidesBarsOnSwipe: Bool /// The gesture recognizer that sortingggers if the bars will hide or show due to a swipe. Do not change the delegate or attempt to replace this gesture by overriding this method. @availability(iOS, introduced=8.0) var barHideOnSwipeGestureRecognizer: UIPanGestureRecognizer { get } /// When the UINavigationController's vertical size class is compact, hide the UINavigationBar and UIToolbar. Unhandled taps in the regions that would normally be occupied by these bars will reveal the bars. @availability(iOS, introduced=8.0) var hidesBarsWhenVerticallyCompact: Bool /// When the user taps, the navigation controller's navigationBar & toolbar will be hidden or shown, depending on the hidden state of the navigationBar. The toolbar will only be shown if it has items to display. @availability(iOS, introduced=8.0) var hidesBarsOnTap: Bool /// The gesture recognizer used to recognize if the bars will hide or show due to a tap in content. Do not change the delegate or attempt to replace this gesture by overriding this method. @availability(iOS, introduced=8.0) unowned(unsafe) var barHideOnTapGestureRecognizer: UITapGestureRecognizer { get } } 

Trouvé via http://natashatherobot.com/navigation-bar-interactions-ios8/

J'ai une sorte de solution rapide et sale pour ça. N'a pas fait de tests approfondis mais voici l'idée:

Cette propriété conservera tous les éléments de la barre de navigation pour ma class UITableViewController

 @property (strong, nonatomic) NSArray *navBarItems; 

Dans la même class UITableViewController j'ai:

 -(void)scrollViewDidScrollToTop:(UIScrollView *)scrollView { if([[[UIDevice currentDevice] systemVersion] floatValue] < 7.0f){ return; } CGRect frame = self.navigationController.navigationBar.frame; frame.origin.y = 20; if(self.navBarItems.count > 0){ [self.navigationController.navigationBar setItems:self.navBarItems]; } [self.navigationController.navigationBar setFrame:frame]; } -(void)scrollViewDidScroll:(UIScrollView *)scrollView { if([[[UIDevice currentDevice] systemVersion] floatValue] < 7.0f){ return; } CGRect frame = self.navigationController.navigationBar.frame; CGFloat size = frame.size.height - 21; if([scrollView.panGestureRecognizer translationInView:self.view].y < 0) { frame.origin.y = -size; if(self.navigationController.navigationBar.items.count > 0){ self.navBarItems = [self.navigationController.navigationBar.items copy]; [self.navigationController.navigationBar setItems:nil]; } } else if([scrollView.panGestureRecognizer translationInView:self.view].y > 0) { frame.origin.y = 20; if(self.navBarItems.count > 0){ [self.navigationController.navigationBar setItems:self.navBarItems]; } } [UIView beginAnimations:@"toggleNavBar" context:nil]; [UIView setAnimationDuration:0.2]; [self.navigationController.navigationBar setFrame:frame]; [UIView commitAnimations]; } 

C'est seulement pour ios> = 7, c'est moche je sais mais un moyen rapide d'y parvenir. Tous les commentaires / suggestions sont les bienvenus 🙂

Voici ma mise en œuvre: SherginScrollableNavigationBar .

Dans mon approche, j'utilise KVO pour observer l'état de UIScrollView , il n'est donc pas nécessaire d'utiliser un délégué (et vous pouvez utiliser ce délégué pour tout ce dont vous avez besoin).

Cela fonctionne pour iOS 8 et supérieur et garantit que la barre d'état conserve son arrière-plan

 self.navigationController.hidesBarsOnSwipe = YES; CGRect statuBarFrame = [UIApplication sharedApplication].statusBarFrame; UIView *statusbarBg = [[UIView alloc] initWithFrame:statuBarFrame]; statusbarBg.backgroundColor = [UIColor blackColor]; [self.navigationController.view addSubview:statusbarBg]; 

Et si vous voulez afficher la barre de navigation lorsque vous appuyez sur la barre d'état, vous pouvez le faire:

 - (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView { self.navigationController.navigationBarHidden = NO; } 

S'il vous plaît essayez cette solution et laissez-moi savoir pourquoi ce n'est pas aussi bon que les réponses précédentes.

 - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset { if (fabs(velocity.y) > 1) [self hideTopBar:(velocity.y > 0)]; } - (void)hideTopBar:(BOOL)hide { [self.navigationController setNavigationBarHidden:hide animated:YES]; [[UIApplication sharedApplication] setStatusBarHidden:hide withAnimation:UIStatusBarAnimationSlide]; } 

Une façon que j'ai accompli ceci est la suivante.

Enregistrez votre controller de vue comme UIScrollViewDelegate de votre UITableView par exemple.

 - (void)scrollViewDidScroll:(UIScrollView *)scrollView; - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView; - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate; 

Depuis les methods de UIScrollViewDelegate , vous pouvez get le nouveau contentOffset et traduire votre UINavigationBar en conséquence.

La définition de l'alpha des sous-vues peut également être effectuée en fonction de certaines valeurs de seuil et facteurs que vous pouvez définir et calculer.

J'espère que cela aide!

En plus de la réponse d'Iwburk, j'ai ajouté ce qui suit pour corriger le problème alpha sur les barres de navigation non personnalisées et pour réinitialiser la barre de navigation dans la méthode viewWillDisappear:

 - (void)updateBarButtonItems:(CGFloat)alpha { for (UIView *view in self.navigationController.navigationBar.subviews) { NSSsortingng *className = NSSsortingngFromClass([view class]); if ( ![className isEqualToSsortingng:@"_UINavigationBarBackground"] ) { view.alpha = alpha; } } } - (void)resetNavigationBar { CGRect frame = self.navigationController.navigationBar.frame; frame.origin.y = 20; [self.navigationController.navigationBar setFrame:frame]; [self updateBarButtonItems:1.0f]; } 

Je cherchais une solution qui permette tout style et tout comportement. Vous remarquerez que le comportement de condensation de barres est différent dans de nombreuses applications différentes. Et bien sûr, la façon dont la barre ressemble est totalement différente entre les applications.

J'ai créé une solution pour ce problème avec https://github.com/bryankeller/BLKFlexibleHeightBar/

Vous pouvez définir vos propres règles de comportement pour contrôler comment et quand la barre se rétrécit et se développe, et vous pouvez définir exactement comment vous voulez que les sous-vues de la barre réagissent à la barre se condensant ou grandissant.

Jetez un oeil à mon projet si vous voulez beaucoup de flexibilité pour faire n'importe quel type de barre d'en-tête que vous pouvez imaginer.

J'ai essayé d'implémenter GTScrollNavigationBar mais mon application m'a demandé de modifier les contraintes de disposition automatique. J'ai décidé de mettre un exemple de ma mise en œuvre sur GitHub au cas où quelqu'un d'autre devrait le faire avec la layout automatique. L'autre problème que j'ai eu avec la plupart des autres implémentations est que les gens ne fixent pas les limites de la vue défilement pour éviter l'effet de défilement de parallaxe que vous créez pendant que vous faites défiler et ajustez la taille de la scrollview simultanément.

Découvrez JSCollapsingNavBarViewController si vous avez besoin de le faire avec la layout automatique. J'ai inclus deux versions, l'une avec la barre de navigation seulement et l'autre avec une sous-barre au-dessous de la barre de navigation qui s'effondre avant de réduire la barre de navigation.

J'essayais d'émuler ce comportement dans une situation où j'avais besoin d'un en-tête personnalisé assis sur un UITableView. J'ai roulé ma propre barre de "navigation" parce que cela se trouve au-dessous d'un tas d'autres choses sur la page et je voulais que les en-têtes de section suivent le comportement "docking" par défaut. Je pense que j'ai trouvé une façon assez intelligente et succincte d'ajuster un UITableView / UIScrollView avec un autre object dans un style similaire à celui de Facebook / Instagram / Chrome / etc. applications.

Dans mon file .xib, mes composants sont chargés dans une vue de forme libre: http://imgur.com/0z9yebJ (désolé, ne pas avoir le rep pour intégrer les images)

Notez que, dans la barre latérale gauche, la table est placée derrière la vue d'en-tête principale. Vous ne pouvez pas le savoir à partir de la capture d'écran, mais il a également la même position y que la vue d'en-tête principale. Comme il s'étend hors de vue, la propriété contentInset sur UITableView est définie sur 76 (hauteur de la vue d'en-tête principale).

Pour faire glisser la vue d'en-tête principale à l'unisson avec UIScrollView, j'utilise les methods scrollViewDidScroll de UIScrollViewDelegate pour effectuer des calculs et modifier le contentInset d'UIScrollView ainsi que l'image de la vue d'en-tête principale.

 - (void)scrollViewDidScroll:(UIScrollView *)scrollView { UIEdgeInsets insets = scrollView.contentInset; //tableViewInsetDelta and tableViewOriginalInsetValue are NSInteger variables that I set to 0 and 76, respectively, in viewDidLoad tableViewInsetDelta = tableViewOriginalInsetValue + scrollView.contentOffset.y; insets.top = tableViewOriginalInsetValue - tableViewInsetDelta; if (scrollView.contentOffset.y > -76 && scrollView.contentOffset.y < 0) { [scrollView setContentInset:insets]; self.pathTitleContainer.frame = CGRectMake(self.pathTitleContainer.frame.origin.x, 44 - tableViewInsetDelta, self.pathTitleContainer.frame.size.width, self.pathTitleContainer.frame.size.height); } else if (scrollView.contentOffset.y > 0) { insets.top = 0; [scrollView setContentInset:insets]; self.pathTitleContainer.frame = CGRectMake(self.pathTitleContainer.frame.origin.x, -32, self.pathTitleContainer.frame.size.width, self.pathTitleContainer.frame.size.height); } else if (scrollView.contentOffset.y < -76) { insets.top = 76; [scrollView setContentInset:insets]; self.pathTitleContainer.frame = CGRectMake(self.pathTitleContainer.frame.origin.x, 44, self.pathTitleContainer.frame.size.width, self.pathTitleContainer.frame.size.height); } } 

La première instruction if fait le gros du travail, mais j'ai dû inclure les deux autres pour gérer les situations où l'user glisse avec force et les valeurs initiales contentOffset envoyées à scrollViewDidScroll sont en dehors de la plage de la première instruction if .

En fin de count, cela fonctionne très bien pour moi. Je déteste charger mes projets avec un tas de sous-classs gonflées. Je ne peux pas dire si c'est la meilleure solution en termes de performances (j'ai toujours hésité à mettre du code dans scrollViewDidScroll car il est appelé tout le time), mais l'empreinte du code est la plus petite que j'ai vu solution pour ce problème et il ne s'agit pas d'imbriquer un UITableView dans un UIScrollView (Apple le déconseille dans la documentation et les events tactiles finissent un peu funky sur le UITableView). J'espère que cela aide quelqu'un!

HidingNavigationBar un excellent projet qui cache la barre de navigation et la barre d'tabs si vous le souhaitez.

HidingNavigationBar prend en charge le masquage / l'affichage des éléments d'affichage suivants:

UINavigationBar

UINavigationBar et une extension UIView

UINavigationBar et un UIToolbar

UINavigationBar et un UITabBar

https://github.com/sortingstanhimmelman/HidingNavigationBar

Je l'ai essayé de cette façon, j'espère que ça va aider. implémentez simplement le code dans la méthode déléguée et définissez sur la vue / sous-vue souhaitée

 -(void)scrollViewDidScroll:(UIScrollView *)scrollView{ CGRect frame=self.view.frame; CGRect resultFrame=CGRectZero; if(scrollView.contentOffset.y==0 || scrollView.contentOffset.y<0){ self.lastContentOffset=0; self.offset=0; resultFrame=CGRectMake(0, frame.size.height-(40-self.offset.intValue), frame.size.width, 40-self.offset.intValue); // Pass the resultFrame [self showHide:YES withFrame:resultFrame]; }else if (self.lastContentOffset > scrollView.contentOffset.y){ NSNumber *temp=[NSNumber numberWithDouble:self.lastContentOffset-scrollView.contentOffset.y]; if(temp.intValue>40 || self.offset.intValue<temp.intValue){ self.offset=[NSNumber numberWithInt:0]; resultFrame=CGRectMake(0, frame.size.height-(40-self.offset.intValue), frame.size.width, 40-self.offset.intValue); // Pass the resultFrame [self showHide:YES withFrame:resultFrame]; }else{ if(temp.intValue>0){ self.offset=[NSNumber numberWithInt:self.offset.intValue-temp.intValue]; resultFrame=CGRectMake(0, frame.size.height-(40-self.offset.intValue), frame.size.width, 40-self.offset.intValue); // Pass the resultFrame [self showHide:YES withFrame:resultFrame]; } } }else if (self.lastContentOffset < scrollView.contentOffset.y){ NSNumber *temp=[NSNumber numberWithDouble:scrollView.contentOffset.y-self.lastContentOffset]; if(self.offset.intValue>40 || (self.offset.intValue+temp.intValue)>40){ self.offset=[NSNumber numberWithInt:40]; // Pass the resultFrame [self showHide:NO withFrame:resultFrame]; }else{ self.offset=[NSNumber numberWithInt:self.offset.intValue+temp.intValue]; resultFrame=CGRectMake(0, frame.size.height-(40-self.offset.intValue), frame.size.width, 40-self.offset.intValue); // Pass the resultFrame [self showHide:YES withFrame:resultFrame]; } } self.lastContentOffset = scrollView.contentOffset.y; } -(void)showHide:(Boolean)boolView withFrame:(CGRect)frame{ if(showSRPFilter){ //Assign value of "frame"to any view on which you wan to to perform animation }else{ //Assign value of "frame"to any view on which you wan to to perform animation } } 

Une extension de la réponse de @Iwburk … Au lieu de changer l'origine de la barre de navigation, je devais agrandir / réduire la taille de la barre de navigation.

 - (void)scrollViewDidScroll:(UIScrollView *)scrollView { CGRect frame = self.previousRect; // a property set in the init method to hold the initial size of the uinavigationbar CGFloat size = frame.size.height; CGFloat framePercentageHidden = ((MINIMUMNAVBARHEIGHT - 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 = -MINIMUMNAVBARHEIGHT; } else if ((scrollOffset + scrollHeight) >= scrollContentSizeHeight) { frame.origin.y = -size; } else { frame.origin.y = MIN(-MINIMUMNAVBARHEIGHT, MAX(-size, frame.origin.y - scrollDiff)); } self.previousRect = CGRectMake(0, frame.origin.y, self.jsExtendedBarView.frame.size.width, 155); self.layoutConstraintExtendedViewHeight.constant = MAXIMUMNAVBARHEIGHT + frame.origin.y + MINIMUMNAVBARHEIGHT; [self updateBarButtonItems:(1 - framePercentageHidden)]; self.previousScrollViewYOffset = scrollOffset; } 

Il ne fonctionne pas encore avec la méthode stoppedScrolling , mais affiche une mise à jour quand je l'ai

Toutes ces approches semblent trop compliquées … Alors naturellement, j'ai construit le mien:

 class ViewController: UIViewController, UIScrollViewDelegate { var originalNavbarHeight:CGFloat = 0.0 var minimumNavbarHeight:CGFloat = 0 weak var scrollView:UIScrollView! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. // setup delegates scrollView.delegate = self // save the original nav bar height originalNavbarHeight = navigationController!.navigationBar.height } func scrollViewDidScroll(scrollView: UIScrollView) { // will relayout subviews view.setNeedsLayout() // calls viewDidLayoutSubviews } override func viewDidLayoutSubviews() { var percentageScrolled = min(scrollView.contentOffset.y / originalNavbarHeight, 1) navigationController?.navigationBar.height = min(max((1 - percentageScrolled) * originalNavbarHeight, minimumNavbarHeight), originalNavbarHeight) // re-position and scale scrollview scrollView.y = navigationController!.navigationBar.height + UIApplication.sharedApplication().statusBarFrame.height scrollView.height = view.height - scrollView.y } override func viewWillDisappear(animated: Bool) { navigationController?.navigationBar.height = originalNavbarHeight } } 

J'ai trouvé toutes les réponses données dans Objective-C. C'est ma réponse dans Swift 3. C'est un code très générique et peut être utilisé directement. Cela fonctionne avec UIScrollView et UITableView.

 var lastContentOffset: CGPoint? = nil var maxMinus: CGFloat = -24.0 var maxPlus: CGFloat = 20.0 var initial: CGFloat = 0.0 override func viewDidLoad() { super.viewDidLoad() self.title = "Alarm Details" self.lastContentOffset = self.alarmDetailsTableView.contentOffset initial = maxPlus } func scrollViewDidScroll(_ scrollView: UIScrollView) { var navigationBarFrame: CGRect = self.navigationController!.navigationBar.frame let currentOffset = scrollView.contentOffset if (currentOffset.y > (self.lastContentOffset?.y)!) { if currentOffset.y > 0 { initial = initial - fabs(CGFloat(currentOffset.y - self.lastContentOffset!.y)) } else if scrollView.contentSize.height < scrollView.frame.size.height { initial = initial + fabs(CGFloat(currentOffset.y - self.lastContentOffset!.y)) } } else { if currentOffset.y < scrollView.contentSize.height - scrollView.frame.size.height { initial = initial + fabs(CGFloat(currentOffset.y - self.lastContentOffset!.y)) } else if scrollView.contentSize.height < scrollView.frame.size.height && initial < maxPlus { initial = initial - fabs(CGFloat(currentOffset.y - self.lastContentOffset!.y)) } } initial = (initial <= maxMinus) ? maxMinus : initial initial = (initial >= maxPlus) ? maxPlus : initial navigationBarFrame.origin.y = initial self.navigationController!.navigationBar.frame = navigationBarFrame scrollView.frame = CGRect(x: 0.0, y: initial + navigationBarFrame.size.height , width: navigationBarFrame.size.width, height: self.view.frame.size.height - (initial + navigationBarFrame.size.height)) let framePercentageHidden: CGFloat = ((20 - navigationBarFrame.origin.y) / (navigationBarFrame.size.height)); self.lastContentOffset = currentOffset; self.updateBarButtonItems(alpha: 1 - framePercentageHidden) } func updateBarButtonItems(alpha: CGFloat) { self.navigationController?.navigationBar.titleTextAtsortingbutes = [NSForegroundColorAtsortingbuteName: UIColor.darkGray.withAlphaComponent(alpha)] self.navigationController?.navigationBar.isUserInteractionEnabled = (alpha < 1) ? false: true guard (self.navigationItem.leftBarButtonItems?.count) != nil else { return } for (_, value) in self.navigationItem.leftBarButtonItems!.enumerated() { value.customView?.alpha = alpha } guard (self.navigationItem.rightBarButtonItems?.count) != nil else { return } for (_, value) in (self.navigationItem.rightBarButtonItems?.enumerated())! { value.customView?.alpha = alpha } } 

La logique de la définition d'alpha pour les éléments de navigation est copiée à partir de @ WayneBurkett réponse et réécrit dans Swift 3.