web-dev-qa-db-fra.com

Imiter Facebook cacher/montrer en expansion/sous-traitance barre de navigation

Dans la nouvelle application iPhone Facebook iOS7, lorsque l'utilisateur fait défiler l'écran, la variable navigationBar se cache progressivement et disparaît complètement. Ensuite, lorsque l'utilisateur fait défiler la navigationBar se montre progressivement. 

Comment implémenteriez-vous ce comportement vous-même? Je connais la solution suivante, mais elle disparaît immédiatement et n’est pas du tout liée à la vitesse de défilement de l’utilisateur. 

[navigationController setNavigationBarHidden: YES animated:YES];

J'espère que ce n'est pas un doublon, car je ne sais pas comment décrire au mieux le comportement "expansion/contraction".

125
El Mocoso

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

  • masquer/afficher la barre de navigation à un taux proportionnel au taux de 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
  • effacer les éléments de la barre de navigation lorsque la barre se rétrécit.

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

@property (nonatomic) CGFloat previousScrollViewYOffset;

Et voici les méthodes 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 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 bloc else dans 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 initial ressemble davantage à celui de Facebook, mais j'aime aussi celui-ci.

Remarque: cette solution est iOS 7+ uniquement. Assurez-vous d’ajouter les vérifications nécessaires si vous prenez en charge les anciennes versions d’iOS.

161
Wayne Burkett

EDIT: uniquement pour iOS 8 et supérieur.

Vous pouvez essayer d'utiliser 

self.navigationController.hidesBarsOnSwipe = YES;

Travaille pour moi.

Si vous codez dans Swift, vous devez utiliser cette méthode (à partir de https://stackoverflow.com/a/27662702/2283308 )

navigationController?.hidesBarsOnSwipe = true
52
Pedro Romão

Voici une autre implémentation: TLYShyNavBar v1.0.0 disponible!

J'ai décidé de créer le mien après avoir essayé les solutions fournies. Pour moi, ils fonctionnaient mal, avaient une barrière d'entrée élevée et le code de la plaque de la chaudière ou n'avaient pas la vue d'extension sous la barre de navigation. Pour utiliser ce composant, il suffit de:

self.shyNavBarManager.scrollView = self.scrollView;

Oh, et c'est testé au combat dans notre propre application.

43
Mazyod

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

Remarque: Si vous avez une barre de navigation OPAQUE, la vue de défilement doit être élargie car la barre de navigation devient masquée. C’est exactement ce que fait GTScrollNavigationBar. (Comme par exemple Safari sur iOS.)

33
Thuy

iOS8 inclut des propriétés permettant de masquer gratuitement la barre de navigation. Il existe une vidéo WWDC qui le démontre, recherchez "Voir les améliorations du contrôleur dans 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 triggers 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/

25
Michael Peterson

Ceci fonctionne pour iOS 8 et supérieur et garantit que la barre d'état conserve toujours 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 souhaitez 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;
}
12
Zhong Huiwen

J'ai une sorte de solution rapide et sale pour cela. Nous n'avons pas encore effectué de tests approfondis, mais voici l'idée:

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

@property (strong, nonatomic) NSArray *navBarItems;

Dans la même classe 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];
}

Ce n'est que pour ios> = 7, c'est moche, je sais, mais un moyen rapide d'y parvenir Tous les commentaires/suggestions sont les bienvenus :)

12
peerless

Voici mon implémentation: 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).

10
Valentin Shergin

S'il vous plaît essayez cette solution et laissez-moi savoir pourquoi cela 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];
}
7
Nishant

L’une des façons dont j’ai accompli ceci est la suivante.

Enregistrez votre contrôleur de vue pour qu'il soit la UIScrollViewDelegate de votre UITableView par exemple.

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

Depuis les méthodes de UIScrollViewDelegate, vous pouvez obtenir 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 valeurs de seuil et de facteurs que vous pouvez définir et calculer.

J'espère que ça aide!

6
Diana Sule

En plus de la réponse d'Iwburk, j'ai ajouté ce qui suit pour résoudre le problème alpha relatif aux 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) {
        NSString *className = NSStringFromClass([view class]);

        if ( ![className isEqualToString:@"_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];
}
4
blueice

Je cherchais une solution qui permette n'importe quel style et n'importe quel comportement. Vous remarquerez que le comportement de condensation est différent dans de nombreuses applications différentes. Et bien sûr, l'apparence de la barre est totalement différente entre les applications. 

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

Vous pouvez définir vos propres règles de comportement pour contrôler le moment et le moment où la barre diminue et s'agrandit, et vous pouvez définir exactement comment vous voulez que les sous-vues de la barre réagissent à la condensation ou à la croissance de la barre.

Jetez un coup d'œil à mon projet si vous voulez beaucoup de flexibilité pour créer le type de barre d'en-tête que vous pouvez imaginer. 

4
blkhp19

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

HidingNavigationBar prend en charge le masquage/l'affichage de la vue suivante éléments:

Barre de navigation

UINavigationBar et une extension UIView

UINavigationBar et une UIToolbar

UINavigationBar et un UITabBar

https://github.com/tristanhimmelman/HidingNavigationBar

3
Michael Peterson

J'essayais d'imiter ce comportement dans une situation où j'avais besoin d'un en-tête personnalisé reposant sur un UITableView. J'ai fait rouler ma propre barre de "navigation", car celle-ci se trouve sous un tas d'autres éléments de la page et je voulais que les en-têtes de section suivent le comportement par défaut "d'ancrage". Je pense avoir trouvé une façon assez intelligente et succincte d’ajuster un UITableView/UIScrollView avec un autre objet dans un style similaire à celui de Facebook/Instagram/Chrome/etc. applications. 

Dans mon fichier .xib, mes composants sont chargés dans une vue de forme libre: http://imgur.com/0z9yebJ (désolé, je n'ai pas le représentant dans les images en ligne).

Notez que, dans la barre latérale gauche, la table est ordonnée derrière la vue d’en-tête principale. Vous ne pouvez pas le savoir à partir de la capture d'écran, mais elle a également la même position que la vue d'en-tête principale. Dans la mesure où elle s'étend à l'abri des regards, la propriété contentInset de UITableView est définie sur 76 (hauteur de la vue d'en-tête principal).

Pour que la vue en-tête principale glisse à l'unisson avec UIScrollView, j'utilise les méthodes scrollView de UIScrollViewDelegate pour effectuer certains calculs et modifier le contentInset de UIScrollView ainsi que le cadre de la vue d'en-tête principal.

- (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 je devais inclure les deux autres pour gérer les situations où l'utilisateur fait glisser avec force et où les valeurs contentOffset initiales envoyées à scrollViewDidScroll sont en dehors de la plage de la première si déclaration. 

En fin de compte, cela fonctionne très bien pour moi. Je déteste charger mes projets avec un tas de sous-classes gonflées. Je ne peux pas vous dire s'il s'agit de la meilleure solution en termes de performances (j'ai toujours hésité à insérer un code dans scrollViewDidScroll car il est appelé en permanence), mais l'empreinte du code est la plus petite que j'ai jamais vue. Une solution à ce problème et cela n'implique pas l'imbrication d'un UITableView dans un UIScrollView (Apple déconseille cela dans la documentation et les événements tactiles sont un peu géniaux sur le UITableView). J'espère que cela aide quelqu'un!

3
Brian

J'ai essayé d'implémenter GTScrollNavigationBar mais mon application m'a obligé à modifier les contraintes de mise en page automatique. J'ai décidé de mettre un exemple de mon implémentation sur GitHub au cas où quelqu'un d'autre devrait le faire avec la mise en page automatique. L'autre problème que j'ai avec la plupart des autres implémentations est que les utilisateurs ne définissent pas les limites de la vue de défilement pour éviter l'effet de défilement de parallaxe que vous créez lorsque vous faites défiler et ajustez simultanément la taille de la vue.

Consultez JSCollapsingNavBarViewController si vous devez le faire avec la mise en page automatique. J'ai inclus deux versions, l'une avec la barre de navigation uniquement et l'autre avec une sous-barre située au-dessous de la barre de navigation, qui s'effondre avant de réduire la barre de navigation.

2
jwswart

je l’ai essayé de cette façon, j’espère que cela aidera… .. il suffit d’implémenter le code dans la méthode déléguée et de le régler 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
                }
        }
1
user2968901

Une extension de la réponse de @Iwburk ... Au lieu de modifier 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;
}

Cela ne fonctionne pas encore avec la méthode stoppedScrolling, je poste mal une mise à jour quand je l'ai

1
jsetting32

Toutes ces approches semblent trop compliquées ... Alors naturellement, j'ai construit la mienne:

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
    }

}
0
Oxcug

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 qui peut être utilisé directement. Cela fonctionne à la fois 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.titleTextAttributes = [NSForegroundColorAttributeName: 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 définition de l'alpha pour les éléments de navigation est copiée à partir de @ WayneBurkett answer et réécrite dans Swift 3.

0
Dev