45

I am trying to implement the iOS 11 native large navigation bar title on my new application. By calling below functions in viewDidLoad():

navigationController?.navigationBar.prefersLargeTitles = true navigationController?.navigationItem.largeTitleDisplayMode = .always

I do get what I desired. enter image description here

But, when I start scrolling up (the only view inside the main view is a scroll view), the scrolling makes the large title disappear at a faster velocity than actual scroll with the finger. (that is, if I move 2cm on screen, the scroll view actually scrolls more than 2cm, up until the large title shrinks to the 'usual' size.)

The below is the gif of my app being scrolled. I actually move very little, and it automatically scrolls up that much. This differs from the Apple-made applications (the app store for instance, shown below my app).

Does anyone have solution to solving this abnormal behaviour?

enter image description here

enter image description here

EDIT: Per request, I am adding the current View hierarchy. There isn't anything special in my code, I just set the title and flag for prefersLargeTitles.

enter image description here

Seop Yoon
  • 2,429
  • 3
  • 14
  • 18
  • I tried it on emulator. The behavior was slower than your own example. It's same with App Store's scrollView animation. If I scroll to top till the half of the largeTitle is unseen, it's automatically scrolled to the topmost point. Isn't it the behavior that you expect to see? – Dorukhan Arslan Dec 05 '17 at 10:30
  • @DorukhanArslan, the App Store's scrollView while finger is still on screen, you can stop at any point and stay until you let go. But with my application, even while my finger is on screen, it automatically scrolls to the top point. – Seop Yoon Dec 06 '17 at 02:55
  • @DorukhanArslan To provide a little more info, scrolling contents down (i.e. moving finger up) makes the abrupt, quick change of the title, while scrolling contents up (i.e. moving finger down) behaves as expected. – Seop Yoon Dec 06 '17 at 03:41
  • I see. To achieve the view in App Store case, you should do nothing. Hence, it's already the expected behavior. Such abnormalities happen when someone didn't set the scrollView as the root of the controller in general. If it's possible, can you add your some more code? As I said, I simply created a similar view controller and everything was fine. – Dorukhan Arslan Dec 06 '17 at 06:47
  • 1
    @DorukhanArslan I added the view hierarchy! – Seop Yoon Dec 06 '17 at 08:14
  • Interesting, it seems completely okay. – Dorukhan Arslan Dec 06 '17 at 10:02
  • 1
    Possible duplicate of [Smooth scrolling with prefersLargeTitles and UITableView](https://stackoverflow.com/questions/46570313/smooth-scrolling-with-preferslargetitles-and-uitableview) – Jakub Truhlář Sep 30 '18 at 11:23

6 Answers6

49

I solved the problem with these constraints:

    NSLayoutConstraint.activate([
        scrollView.topAnchor.constraint(equalTo: view.topAnchor),
        scrollView.leftAnchor.constraint(equalTo: view.leftAnchor),
        scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
        scrollView.rightAnchor.constraint(equalTo: view.rightAnchor)
        ])

You also have to set this property on your UIViewController subclass:

extendedLayoutIncludesOpaqueBars = YES
Roman
  • 949
  • 7
  • 16
35

I found this problem as well.

In Interface Builder

  1. Select your "viewController"
  2. Attribute Inspector tick "Under Opaque Bars" and "Under Top Bars"
  3. Make your top constraints of your scrollView second Item to "SuperView" not "SafeArea"
user8637708
  • 3,303
  • 2
  • 13
  • 15
9

I have been debugging this for a couple days, and I have found a workaround.

First, What Was Going On?

It is the UIScrollView that is not performing well with the largeTitle. Since there is scrolling up on the scroll view at the same time as the navigation bar becoming smaller, there exists twice the scrolling compared to the actual scroll.

I confirmed this by intentionally setting:

scrollView.contentOffset.y = scrollView.contentOffset.y * 0.5

This indeed made it move as desired. But, this couldn't solve the problem entire problem because it did not yield a smooth transition while going from large navigation bar to small navigation bar. You can try the below code out.

if scrollView.contentOffset.y > 0 {
  if self.navigationController!.navigationBar.frame.size.height > 44.0 {
    scrollView.contentOffset.y = scrollView.contentOffset.y * 0.5
  }
}

This worked 'okay' when scrolled slowly, but when you fling downward, it acts slow at first (while navigation height is large), and then speeds up afterwards.

WORKAROUND

Simply put, you CANNOT use UIScrollView with the iOS 11 large navigation bar. You ought to use UITableViewController instead.
Since my view is composed of multiple horizontal UICollectionViews spread along vertically, I used UITableView with different sections to form the UI using storyboard. If you use UITableViewController, it performs as desired.
AppStore and all other Apple-made native apps must do it this way.

josip04
  • 224
  • 3
  • 13
Seop Yoon
  • 2,429
  • 3
  • 14
  • 18
  • i am also getting same issue in UITableView with large title , any solution? – Bucket Jan 06 '18 at 07:06
  • @barbarity I made a mistake writing the above answer. It works when you use a UITableViewController, not a UITableView inside a UIViewController. (will fix answer above) – Seop Yoon Jan 07 '18 at 11:08
  • 1
    Nice find! This whole large-title navbar is such a shameful, buggy API... – Tamás Zahola Jan 07 '18 at 11:14
  • This bug also occurs when using a UITableViewController allong with a UISerchController. So we can all agree that large titles are verry buggy. I don;t know how Whats App has a searchController and smooth scrolling at the same time. – Lucas P. Jul 23 '18 at 07:50
  • I disagree that this should be the approved answer. There are times that a scroll view is warranted. This has a similar solution to the answer provided here: https://stackoverflow.com/a/55523549/1909108 – Tyten Apr 04 '19 at 19:51
7

I found this happens when the navigation bar has large titles and is not translucent. If you are using a CollectionView or TableView, and it is constrained to the safe area or its superview, then it should handle the insets for you.

For scroll views, check out the Apple docs, there are some new variables (like contentInsetAdjustmentBehavior) for the safe area changes that were introduced in iOS 11: https://developer.apple.com/documentation/uikit/uiscrollview

To make the Navigation Bar Translucent

In viewDidLoad(), try adding:

navigationController?.navigationBar.isTranslucent = true

Or set this checkmark in IB:

translucent checkmark

Community
  • 1
  • 1
JWhitey
  • 216
  • 3
  • 5
5

The problem in case of double velocity can be that your view size smaller then navigation controller view size and you have view hierarhy like(you can check it in view debugger)

V:|-[navbar]-[view]-|

so when scrolling your view's frame is changing during changing of content offset and it doubles velocity.

extendedLayoutIncludesOpaqueBars = true should help.

Artjom Bastun
  • 61
  • 1
  • 3
5

First of all, make sure that the second item of scrollView's top constraints is equal to Superview.Top, not Safe Area.

Second, add this line to your view controller: extendedLayoutIncludesOpaqueBars = true. For example in viewDidLoad.

Arthur Stepanov
  • 511
  • 7
  • 4