How to Mimic 'Uitableviewcontroller' Showing of the Large Titles in 'Navigationbar' on iOS 11

How to mimic `UITableViewController` showing of the large titles in `navigationBar` on iOS 11

I noticed another aspect of prefersLargeTitle behavior that in some cases might provide even a simpler and more elegant solution. In my case, the viewController contained not only the tableView (otherwise I would simply use UITableViewController and I would get standard prefersLargeTitle behavior out-of-the-box), but also some other views. Now I noticed, that if you add the tableView as a first subview of the viewController.view, the table will control the large title feature:

// this will work
fileprivate func setupInitialHierarchy() {
self.view.addSubview(tableView)
self.view.addSubview(logoffButton)
}

Before I was creating the view hierarchy as follows:

// for some reason now the tableView will not control the large title
fileprivate func setupInitialHierarchy() {
self.view.addSubview(logoffButton)
self.view.addSubview(tableView)
}

So it seems that if the tableView is the first subview of the viewControllers view, we get the standard large titles behavior.

Alternative solution

However, if this is not possible, I have been able to simulate the standard behavior programmatically this way:

Implementing the delegate method for the tableView that reacts to scrolling, and then running the code that uses current contentOffset to either show, or hide the large title (UITableView inherits from UIScrollView, so the scrollView parameter refers in this case to the tableView):

func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset.y <= 0 {
self.navigationItem.largeTitleDisplayMode = .always
} else {
self.navigationItem.largeTitleDisplayMode = .never
}
self.navigationController?.navigationBar.setNeedsLayout()
self.view.setNeedsLayout()
UIView.animate(withDuration: 0.25, animations: {
self.navigationController?.navigationBar.layoutIfNeeded()
self.view.layoutIfNeeded()
})
}

Just remember that scrollViewDidScroll gets called repeatedly, so some guard there might be desirable.

How to turn off adjusting large titles by UITableView in iOS 11?

I've achieved it unintentionally when embedded UITableViewController inside UIViewController.

I'm not sure whether it is an Apple's bug or intended behavior.

So stack is as simple as UINavigationController -> UIViewController(used as container) -> UITableViewController

Here is sample of view controller with embedded UITableViewController fullscreen

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

var vc = UITableViewController(style: .plain)
var array: [String] = []

override func viewDidLoad() {
super.viewDidLoad()

vc.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(vc.view)
view.addConstraint(view.leadingAnchor.constraint(equalTo: vc.view.leadingAnchor))
view.addConstraint(view.rightAnchor.constraint(equalTo: vc.view.rightAnchor))
view.addConstraint(view.safeAreaLayoutGuide.topAnchor.constraint(equalTo: vc.view.topAnchor))
view.addConstraint(view.bottomAnchor.constraint(equalTo: vc.view.bottomAnchor))

vc.tableView.delegate = self
vc.tableView.dataSource = self

array = "0123456789".characters.map(String.init)
vc.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "identifier")

title = "Title"
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return array.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "identifier", for: indexPath)
cell.textLabel?.text = array[indexPath.row]
return cell
}
}

Here is the result

Sample Image

Hope it helps.

P.S. Surprisingly, my current problem is that I don't know how to get collapsing behavior with such architecture :)

iOS 11 large title navigation bar snaps instead of smooth transition

I faced same issue - I had UIViewController embedded in UINavigationController, the UIViewController had tableview with leading, trailing, top, bottom constraints to safe area. The whole tableview behaved jumpy / snappy. The trick was to change top constraint of tableview to superview.

Here I recorded changing the constraint
tableview iOS 11 bug constraint change

iOS 11 - UINavigationItem titleView when using Large Titles mode

I was able to replace the navigation bar big title with a custom view by using a subclass of UINavigationBar and manually changing the view hierarchy :

@interface MYNavigationBar : UINavigationBar

@end
@implementation MYNavigationBar

// ...

- (void)layoutIfNeeded
{
[self setupTitle];
[super layoutIfNeeded];
}

- (void)setupTitle
{
// UINavigationBar
// -> ...
// -> _UINavigationBarLargeTitleView
// -> UILabel << Big Title Label
// -> UIView
// -> UILabel << Big Title Label animating from back button during transitions

for (UIView *view in self.subviews) {
NSString *className = NSStringFromClass(view.classForCoder);
if ([className containsString:@"LargeTitleView"]) {
for (UIView *view2 in view.subviews) {
if ([view2 isKindOfClass:[UILabel class]]) {
[self convertLabel:(UILabel *)view2];
}
for (UIView *view3 in view2.subviews) {
if ([view3 isKindOfClass:[UILabel class]]) {
[self convertLabel:(UILabel *)view3];
}
}
}
}
}
}

- (void)convertLabel:(UILabel*)label
{
// I kept the original label in the hierarchy (with the background color as text color)
// and added my custom view as a subview.
// This allow the transformations applied to the original label to be also applied to mine.
}

Please note that Apple might reject your app because of this trick.

iOS 11 large-title navigation bar not collapsing

Good news! I've just figured out that if I set "Large Titles" to "Never" on the storyboard, and then set it via code, then it works:

- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.largeTitleDisplayMode = UINavigationItemLargeTitleDisplayModeAutomatic;
}

Seems like Apple forgot to handle the case when the navigation item has its largeTitleDisplayMode set via the Interface Builder.

So until they fix this issue, leave "Large Titles" as "Never" on storyboards, and set them via code in viewDidLoad.

You just need to do that to the first view controller. Subsequent view controllers honor the value in storyboard.

UINavigationBar jumps from Large Title to small one while pushing to UIViewController with embedded UITableView iOS 11

Workarounds I found so far

  1. Attach UITableView's top to SafeArea's top, but this approach lose effect of transparent UINavigationBar and LargeTitle collapses very fast on scrolling
  2. Use UITableViewController instead of UIViewController+UITableView, but this approach loses known flexibility. It's not trivial to hide tableView or add subviews.
  3. pushViewController without animation

iOS 11 large navigation bar title unexpected velocity

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.

iOS 11 prefersLargeTitles not displaying until scroll

You need to enable large titles on the navigation controller. It is tricky to find, so please see the screenshot below.
Sample Image

If your table view controller seques on to other detail view controllers then you should set large title to Never in the storyboard for those other view controllers.

DO NOT MESS with prefersLargeTitles in code like this:

      self.navigationController?.navigationBar.prefersLargeTitles = false


Related Topics



Leave a reply



Submit