Подражать Facebook hide/show expand/contractising Панель навигации - программирование

Подражать Facebook hide/show expand/contractising Панель навигации

В новом приложении iOS7 Facebook iPhone, когда пользователь прокручивает страницу navigationBar, постепенно скрывается до точки, где он полностью исчезает. Затем, когда пользователь прокручивает вниз, navigationBar постепенно показывает себя.

Как бы вы реализовали это поведение самостоятельно? Я знаю следующее решение, но оно сразу исчезает, и оно не привязано к скорости жестов прокрутки пользователя вообще.

[navigationController setNavigationBarHidden: YES animated:YES];

Надеюсь, это не дубликат, так как я не уверен, как лучше описать поведение "расширяющегося/сжимающегося".

4b9b3361

Ответ 1

Решение, данное @peerless, - отличный старт, но он запускает только анимацию, когда начинается перетаскивание, без учета скорости прокрутки. Это приводит к более сложному опыту, чем в приложении Facebook. Чтобы соответствовать поведению Facebook, нам необходимо:

  • скрыть/показать навигационную панель со скоростью, пропорциональной скорости перетаскивания.
  • запускает анимацию, чтобы полностью скрыть панель, если прокрутка останавливается, когда панель частично скрыта.
  • исчезают элементы навигационной панели, когда панель сжимается.

Сначала вам понадобится следующее свойство:

@property (nonatomic) CGFloat previousScrollViewYOffset;

И вот методы 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];
    }
}

Вам также понадобятся следующие вспомогательные методы:

- (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];
    }];
}

Для немного другого поведения замените строку, которая переклассифицирует панель при прокрутке (блок else в scrollViewDidScroll) с этим:

frame.origin.y = MIN(20, 
                     MAX(-size, frame.origin.y - 
                               (frame.size.height * (scrollDiff / scrollHeight))));

Это позиционирует панель на основе последнего процента прокрутки, а не абсолютной величины, что приводит к более медленному исчезновению. Оригинальное поведение больше похоже на Facebook, но мне тоже нравится этот.

Примечание. Это решение - только iOS 7+. Обязательно добавьте необходимые проверки, если вы поддерживаете более старые версии iOS.

Ответ 2

EDIT: Только для iOS 8 и выше.

Вы можете попробовать использовать

self.navigationController.hidesBarsOnSwipe = YES;

Работает для меня.

Если ваша кодировка в swift вам нужно использовать таким образом (от fooobar.com/questions/47751/...)

navigationController?.hidesBarsOnSwipe = true

Ответ 3

Вот еще одна реализация: TLYShyNavBar v1.0.0 выпущен!

Я решил сделать свое, попробовав предоставленные решения, и для меня они либо плохо работали, либо имели высокий барьер входа и код плиты котла, либо не имели расширения, расположенного под навигационной панелью. Чтобы использовать этот компонент, все, что вам нужно сделать, это:

self.shyNavBarManager.scrollView = self.scrollView;

О, и это битва протестирована в нашем собственном приложении.

Ответ 4

Вы можете посмотреть мой GTScrollNavigationBar. Я подклассифицировал UINavigationBar, чтобы он прокручивался на основе прокрутки UIScrollView.

Примечание. Если у вас есть панель навигации OPAQUE, scrollview должен ОБРАТИТЬСЯ, когда панель навигации получает HIDDEN. Это именно то, что делает GTScrollNavigationBar. (Так же, как, например, в Safari на iOS.)

Ответ 5

iOS8 содержит свойства, позволяющие бесплатно скрывать навигационную панель. Существует видео WWDC, которое демонстрирует его, ищите "Просмотр контроллеров в iOS 8".

Пример:

class QuotesTableViewController: UITableViewController {

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    navigationController?.hidesBarsOnSwipe = true
}

}

Другие свойства:

class UINavigationController : UIViewController {

    //... truncated

    /// When the keyboard appears, the navigation controller 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 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 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 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 }
}

Найдено через http://natashatherobot.com/navigation-bar-interactions-ios8/

Ответ 6

У меня есть какое-то быстрое и грязное решение для этого. Не проводили углубленного тестирования, но вот идея:

Это свойство сохранит все элементы на панели навигации для моего класса UITableViewController

@property (strong, nonatomic) NSArray *navBarItems;

В том же классе UITableViewController у меня есть:

-(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];
}

Это только для ios >= 7, это уродливо, я знаю, но быстрый способ добиться этого. Любые комментарии/предложения приветствуются:)

Ответ 7

Вот моя реализация: SherginScrollableNavigationBar.

В моем подходе я использую KVO для наблюдения состояния UIScrollView, поэтому нет необходимости использовать делегат (и вы можете использовать этот делегат для всего, что вам нужно).

Ответ 8

Это работает для iOS 8 и выше и гарантирует, что строка состояния все еще сохраняет свой фон

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

И если вы хотите отобразить панель навигации, когда вы нажимаете на строку состояния, вы можете сделать это:

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

Ответ 9

Пожалуйста, попробуйте это мое решение и сообщите мне, почему это не так хорошо, как предыдущие ответы.

- (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];
}

Ответ 10

Один из способов выполнения Ive заключается в следующем.

Зарегистрируйте ваш контроллер просмотра как UIScrollViewDelegate вашего UITableView, например.

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

С помощью методов UIScrollViewDelegate вы можете получить новое contentOffset и перевести ваш UINavigationBar вверх или вниз соответственно.

Настройка альфа-подзонов также может быть выполнена на основе некоторых пороговых значений и факторов, которые вы можете установить и вычислить.

Надеюсь, что это поможет!

Ответ 11

В дополнение к ответу Iwburk я добавил следующее, чтобы исправить альфа-проблему на нестандартных навигационных панелях и reset панель навигации в методе 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];
}

Ответ 12

Я искал решение, которое позволяло использовать любой стиль и любое поведение. Вы заметите, что поведение конденсатора в барах отличается во многих разных приложениях. И, конечно, то, как выглядит панель, совершенно несовместимо между приложениями.

Я создал решение для этой проблемы с https://github.com/bryankeller/BLKFlexibleHeightBar/

Вы можете определить свои собственные правила поведения, чтобы контролировать, как и когда стержень сжимается и растет, и вы можете точно определить, как вы хотите, чтобы субблоки бара реагировали на бар, конденсирующиеся или растущие.

Взгляните на мой проект, если вы хотите получить большую гибкость, чтобы сделать любую панель заголовка, которую вы можете придумать.

Ответ 13

Я попробовал реализовать GTScrollNavigationBar, но мое приложение потребовало от меня изменить ограничения макета. Я решил привести пример моей реализации на GitHub, если кто-то еще должен сделать это с автоматической компоновкой. Другая проблема, с которой я столкнулся с большинством других реализаций, заключается в том, что люди не устанавливают границы прокрутки, чтобы избежать эффекта прокрутки параллакса, создаваемого при прокрутке и одновременном изменении размера прокрутки.

Обратите внимание на JSCollapsingNavBarViewController, если вам нужно сделать это с помощью автоматического макета. Я включил две версии: одну с навигационной панелью, а другую - под панелью под панелью навигации, которая сворачивается, прежде чем сбрасывать навигационную панель.

Ответ 14

Я пытался подражать этому поведению в ситуации, когда мне нужен настраиваемый заголовок, сидящий вокруг UITableView. Я перевернул свою собственную панель навигации, потому что это сидит ниже кучи других вещей на странице, и я хотел, чтобы заголовки разделов соответствовали поведению "стыковки" по умолчанию. Я думаю, что нашел довольно умный и лаконичный способ настроить UITableView/UIScrollView вместе с другим объектом в стиле, подобном тому, что наблюдается в Facebook/Instagram/Chrome/etc. Программы.

В моем .xib файле у меня есть мои компоненты, загруженные в представление свободной формы: http://imgur.com/0z9yebJ (извините, у меня нет репутации к встроенным изображениям)

Обратите внимание, что в левой боковой панели таблица упорядочена за главным заголовком. Вы не можете сказать со скриншота, но он также имеет ту же позицию y, что и заголовок основного заголовка. Поскольку он выходит из поля зрения, свойство contentInset в UITableView устанавливается на 76 (высота основного заголовка).

Чтобы сделать представление основного заголовка слайдом в унисон с UIScrollView, я использую методы scrollViewDidScroll для UIScrollViewDelegate для выполнения некоторых вычислений и изменения UIScrollView contentInset, а также основного кадра представления заголовка.

- (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);
    }
}

Первый оператор if делает большую часть тяжелого подъема, но мне пришлось включить другие два, чтобы обрабатывать ситуации, когда пользователь перетаскивает принудительно, а начальные значения contentOffset, отправленные в scrollViewDidScroll, находятся за пределами диапазона первого оператора if.

В конечном счете, это работает очень хорошо для меня. Я ненавижу загружать мои проекты с кучей раздутых подклассов. Я не могу говорить о том, является ли это лучшим решением по производительности (я всегда сомневался в том, что любой скрипт в scrollViewDidScroll был вызван, так как он все время называется), но размер кода - это самый маленький, который я видел в любом решение для этой проблемы, и это не связано с вложением UITableView в UIScrollView (Apple советует против этого в документации и событиях касания в конечном итоге немного напуганы на UITableView). Надеюсь, это поможет кому-то!

Ответ 15

HidingNavigationBar отличный проект, который скрывает панель навигации и панель вкладок, если вы хотите.

HidingNavigationBar поддерживает скрытие/отображение следующего вида элементы:

UINavigationBar

UINavigationBar и расширение UIView

UINavigationBar и UIToolbar

UINavigationBar и UITabBar

https://github.com/tristanhimmelman/HidingNavigationBar

Ответ 16

Я попробовал это таким образом, надеюсь, это поможет. просто реализуйте код в методе делегирования и установите на требуемое представление /subview

-(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
                }
        }

Ответ 17

Расширение ответа @Iwburk... Вместо изменения начала навигационной панели мне нужно было расширить/уменьшить размер панели навигации.

- (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;
}

Он еще не работает с методом stoppedScrolling, плохо сообщите об обновлении, когда у меня есть

Ответ 18

Все эти подходы кажутся слишком сложными... Поэтому, естественно, я построил свой собственный:

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
    }

}

Ответ 19

Я нашел все ответы, приведенные в Objective-C. Это мой ответ в Swift 3. Это очень общий код и может использоваться напрямую. Он работает как с UIScrollView, так и с 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
    }
}

Логика настройки альфа-навигационных элементов копируется из @WayneBurkett и перезаписывается в Swift 3.