prefersLargeTitles - does not collapse when scrolling up

The main page of my app is a UIViewController that primarily contains a UICollectionView, but has a UISegmentedControl above it to swap out the data in the UICollectionView.


I'm using prefersLargeTitles=true, and according to the "What's New in Cocoa Touch" WWDC Session 201, the framework should be able to identify the primary (?) UIScrollView in the UI and collapse the large title accordingly when the user scrolls up:


"Now, UINavigationController will automatically find the main primary content scroll view inside of your currently visible view controller. And track its content offset to make sure that it can do this on your behalf. So, in most cases, there's actually not a lot of work that you need to do, in order to adopt that."

I cannot get this to work, both in Betas 3 and 4.


Should this work, when the scrolling view is a level down the hierarchy? Or does it only work if you use, say, a UITableViewController, UICollectionViewController or the like? Or is there some other way to indicate that this UICollectionView is the "main primary content scroll view"?

This does not work in Beta 5 either.


Should it?

I'm also seeing this behaviour.

It's possible to do it. Here is the source for the storyboard & view controller


ViewController:


import UIKit
class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource {
    @IBOutlet weak var mySegmentedControl: UISegmentedControl!
    @IBOutlet weak var collectionView: UICollectionView!
   
    override func viewDidLoad() {
        super.viewDidLoad()
        /
       
        navigationController?.navigationBar.largeTitleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white]
       
        navigationItem.largeTitleDisplayMode = .always
        navigationItem.searchController = UISearchController(searchResultsController: nil)
        navigationItem.hidesSearchBarWhenScrolling = true
       
        additionalSafeAreaInsets = UIEdgeInsetsMake(30 + mySegmentedControl.bounds.height, 0, 0, 0)
       
        view.safeAreaLayoutGuide.topAnchor.constraint(equalTo: mySegmentedControl.bottomAnchor, constant: 15).isActive = true
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        /
    }
    override func viewSafeAreaInsetsDidChange() {
        super.viewSafeAreaInsetsDidChange()
    }
   
    func numberOfSections(in collectionView: UICollectionView) -> Int {
        return 1
    }
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 20
    }
   
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        return collectionView.dequeueReusableCell(withReuseIdentifier: "myCell", for: indexPath)
    }   
}


Storyboard:


<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13178.6" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="pcH-wx-K7b">
    <device id="retina4_7" orientation="portrait">
        <adaptation id="fullscreen"/>
    </device>
    <dependencies>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13156.2"/>
        <capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--Hello 1234-->
        <scene sceneID="tne-QT-ifu">
            <objects>
                <viewController id="BYZ-38-t0r" customClass="ViewController" customModule="TestPrefersLargeTitles001" customModuleProvider="target" sceneMemberID="viewController">
                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <subviews>
                            <collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="69t-gn-xot">
                                <rect key="frame" x="0.0" y="64" width="375" height="603"/>
                                <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
                                <collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="Tnn-bs-qsy">
                                    <size key="itemSize" width="100" height="100"/>
                                    <size key="headerReferenceSize" width="0.0" height="0.0"/>
                                    <size key="footerReferenceSize" width="0.0" height="0.0"/>
                                    <inset key="sectionInset" minX="10" minY="10" maxX="10" maxY="10"/>
                                </collectionViewFlowLayout>
                                <cells>
                                    <collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" reuseIdentifier="myCell" id="GFK-Bg-khs">
                                        <rect key="frame" x="10" y="10" width="100" height="100"/>
                                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                                        <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO">
                                            <rect key="frame" x="0.0" y="0.0" width="100" height="100"/>
                                            <autoresizingMask key="autoresizingMask"/>
                                        </view>
                                        <color key="backgroundColor" red="0.73076175200000004" green="1" blue="0.70549388930000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                    </collectionViewCell>
                                </cells>
                                <connections>
                                    <outlet property="dataSource" destination="BYZ-38-t0r" id="zCS-vQ-Nvu"/>
                                    <outlet property="delegate" destination="BYZ-38-t0r" id="sZn-sA-Q9D"/>
                                </connections>
                            </collectionView>
                            <segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="HCF-pe-nm2">
                                <rect key="frame" x="37" y="21" width="300" height="29"/>
                                <segments>
                                    <segment title="First"/>
                                    <segment title="Second"/>
                                </segments>
                            </segmentedControl>
                        </subviews>
                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                        <constraints>
                            <constraint firstItem="69t-gn-xot" firstAttribute="top" secondItem="HCF-pe-nm2" secondAttribute="bottom" constant="15" placeholder="YES" id="1Zi-6k-Jfa"/>
                            <constraint firstItem="69t-gn-xot" firstAttribute="centerX" secondItem="HCF-pe-nm2" secondAttribute="centerX" id="2kF-z1-mc4"/>
                            <constraint firstItem="69t-gn-xot" firstAttribute="width" secondItem="6Tk-OE-BBY" secondAttribute="width" id="8T3-l6-ArO"/>
                            <constraint firstItem="69t-gn-xot" firstAttribute="centerX" secondItem="6Tk-OE-BBY" secondAttribute="centerX" id="H3w-bc-1bv"/>
                            <constraint firstItem="6Tk-OE-BBY" firstAttribute="bottom" secondItem="69t-gn-xot" secondAttribute="bottom" id="Jb1-eX-0KV"/>
                            <constraint firstItem="HCF-pe-nm2" firstAttribute="width" secondItem="6Tk-OE-BBY" secondAttribute="width" multiplier="0.8" id="LOx-4d-vcr"/>
                            <constraint firstItem="HCF-pe-nm2" firstAttribute="centerX" secondItem="6Tk-OE-BBY" secondAttribute="centerX" id="QKe-Xq-a3x"/>
                            <constraint firstItem="69t-gn-xot" firstAttribute="top" secondItem="6Tk-OE-BBY" secondAttribute="top" id="UkK-dX-PXj"/>
                        </constraints>
                        <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
                    </view>
                    <navigationItem key="navigationItem" title="Hello 1234" largeTitleDisplayMode="never" id="HS2-kE-x90"/>
                    <connections>
                        <outlet property="collectionView" destination="69t-gn-xot" id="IKJ-0b-Tyt"/>
                        <outlet property="mySegmentedControl" destination="HCF-pe-nm2" id="wwM-iV-p7S"/>
                    </connections>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="476" y="169.56521739130437"/>
        </scene>
        <!--Navigation Controller-->
        <scene sceneID="8Za-3j-TCb">
            <objects>
                <navigationController automaticallyAdjustsScrollViewInsets="NO" id="pcH-wx-K7b" sceneMemberID="viewController">
                    <toolbarItems/>
                    <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="nnn-xL-vWL">
                        <rect key="frame" x="0.0" y="20" width="375" height="96"/>
                        <autoresizingMask key="autoresizingMask"/>
                        <color key="barTintColor" red="0.9860952221" green="0.81682872529999995" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                        <textAttributes key="titleTextAttributes">
                            <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
                        </textAttributes>
                        <textAttributes key="largeTitleTextAttributes">
                            <fontDescription key="fontDescription" type="system" weight="medium" pointSize="25"/>
                            <color key="textColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
                        </textAttributes>
                    </navigationBar>
                    <nil name="viewControllers"/>
                    <connections>
                        <segue destination="BYZ-38-t0r" kind="relationship" relationship="rootViewController" id="GSk-Hy-FOQ"/>
                    </connections>
                </navigationController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="3Oa-Sp-SVl" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="-276" y="170"/>
        </scene>
    </scenes>
</document>

So - more info on this. I have a sample app that I'll send to Apple:


v1: UINavigationController: prefersLargeTitles=true

v2: UITableViewController: Large Title = automatic in Storyboard

v3: UIViewController containing a UICollectionView as a child view

v4: UIViewController (empty)


Selecting a row in v2 pushes v3.

Selecting a cell in v3 pushes v4.


Scrolling up in either v2 or v3 does not collapse the large title.


Scrolling up in v2, selecting the bottom row to push v3, and then hitting Back to v2: collapsed title FINALLY DOES show up in v2, only on the way back in.


Scrolling up in v3, selecting the last cell to push v4, then hitting Back to v3: collapsed title MOMENTARILY shows up in v3, but it shifts immediately to the large title.


Any other insights out there? Hopefully there will be progress in the next beta...

navigationItem.largeTitleDisplayMode = .always - Needs to be set in code. Storyboard setting does not seem to work.


Check my response above.

Thanks so much for replying (...your post appeared while I was typing my previous post). But I'm playing with your code now. One thing I noticed, is you have


largeTitleDisplayMode="never"


in your storyboard for the "Hello 1234" navigation bar. That seems to make the difference - in terms of it collapsing properly. But I would have thought "Never" should have meant never ever EVER show the large title, right?? I have been setting it to "automatic".

Accepted Answer

So yes, it appears the magical - yet broken - combination is:


  1. In the storyboard, set "Large Title: Never" for the first view controller in the UINavigationController chain
  2. In viewDidAppear, set "navigationItem.largeTitleDisplayMode = .always" for that same view controller.


That first step seems odd, right? But I get the desired behavior throughout now...

This didn't work for me :-( did you find a different spell for later betas?

No - I filed a bug with Apple, that is a duplicate of another, so they're evidently aware of it. I ultimately had to bail on the concept - my hangup was that I have a UITabViewController that has a UINavigationController for each tab, and they all are similar. It looked too weird to have one tab transition from Large to Small, and then switch to the next tab and get Large again. Not important enough of a feature to fight any further on, unfortunately.

Fair enough, I will do the same, hopefully it will stabilise in future releases. Please post back here if you get an update on the radar.

It seems like people are experiencing this problem (Large Titles not collapsing when the view is scrolled) and it's being caused my many different things.


I can tell you that I deconstructed my app to find the cause, which was the viewhierarchy in the storyboard. It apprears that the UITableView view HAS to the the first view in your view controller. I had a UITableView with two UIImageViews behind it and that's what was causing the issue.


Once I removed those UIImageViews everything worked correctly. I ended up creating a UIview in code, adding my two image views to that, THEN adding that UIView to the UITableview.backgroundView.


Hope this helps someone.

It's an odd bug. The fix is to toggle OFF prefersLargeTitles in the storyboard and to set this in viewDidLoad of your nav controller's root vc:


navigationController?.navigationBar.prefersLargeTitles = true

Your suggestion did not fix the issue for me, which is why I posted my solution.

I meet the same View ,I have a view in the storyboard and a UIImageView on the View as a background imageView.Also has a tableView on the UIImageView. When I remove the UIImageView, it works well.

Have you gotten child controllers to work with large titles and hiding them on scroll?

prefersLargeTitles - does not collapse when scrolling up
 
 
Q