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 viewController
s 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
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
- Attach
UITableView
's top toSafeArea's
top, but this approach lose effect of transparentUINavigationBar
andLargeTitle
collapses very fast on scrolling - Use
UITableViewController
instead ofUIViewController+UITableView
, but this approach loses known flexibility. It's not trivial to hide tableView or add subviews. 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.
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
Why Does the Following Code Crash on an iPhone 5 But Not an iPhone 5S
Wkwebview Auto Fill Login Form Swift 2
Cannot Add Alamofire to Swift Project
Enumerate Is Unavailable Call the Enumerate Method on the Sequence
How to Create a Multi Line Text Field in Swiftui for MACos
An Issue with Cashapelayer and Uibezierpath Drawing Circle Counterclockwise
Differencebetween ":" and "=" in Swift
Expand and Contract Tableview Cell When Tapped, in Swift
How to Build a Swift Package for iOS Over Command Line
Play Local Audio File with Avaudioplayer
What Is Trailing Closure Syntax in Swift
Realitykit as a Framework to Build 3D Nonar Apps
Subtle Cast Warning When Using SQLite.Swift ... Binding? to Any
Get Lat and Long from Tapped Overlay in Google Maps