1
votes

I'm creating a simple "Create your Profile" with many fields. My view is not scrolling despite the content being larger than the size of the scroll view. I think part of the issue is the relationship between the contents inside the contentView. I wanted the cells inside of the table view to be dynamic so that I can add more information later and the scrolling would still preform. I set the scrollView delegate and tableView delegate to self in viewDidLoad()

I've looked at other answers and tried their implementations to no avail. I referenced Height of contentView of UIScrollView based on inside content using Storyboard, for instance.

To adjust the table view's content dynamically, I wrote the following:

 override func viewDidLayoutSubviews() {
        DispatchQueue.main.async {
            var frame = self.tableView.frame
            frame.size.height = self.tableView.contentSize.height
            self.tableView.frame = frame
            self.updateViewConstraints()

        }
    }

For constraints, my hierarchy is Super View > Scroll View > Content View > Elements.

Essentially, each element depends on it's upper neighbor and each have set heights. The exception is the TableView, whose height is not fixed, and will be stretched according to it's neighbor below it, the error label. The error label is then constrained to the bottom of the view. Here is a more visual representation. layout constraints

Here is the actual result: actual result

I think the issue is the Error Label's constraint to the Table View is not being updated, but is there something I'm not seeing here? Everything below the top bar should scroll.

1
You can try using a "self-sizing" table view such as this: stackoverflow.com/a/56703886/6257435 - DonMag
@DonMag In that solution, you talked about setting constraints properly. Do you think based on the above information that this is proper constraints? I tried your solution and it didn't work. - Dave Jha
Do you want the "Please put in a profile photo" label/view to sit at the bottom? Or do you want it to scroll with the content? And... do you want the "Import Using LinkedIn" label/view and the "Choose..." buttons to also scroll? Or, do you want only the table rows to scroll? - DonMag
@DonMag I want that to sit at the bottom, which you would have to scroll to reach. And I want both buttons to also scroll as well. I think I got that part figured out, it's scrolling now. However, the textfields that are accessed by scrolling (meaning you have to scroll to get to them) are not editable. Clicking on them does not do anything. - Dave Jha

1 Answers

1
votes

Here is a basic example, using a "self-sizing" table view. No need for heightForRowAt or explicit setting of .contentSize -- it's all handled by auto-layout and constraints.

With a couple rows (nothing scrolls):

enter image description here

With a couple more rows (still, nothing scrolls):

enter image description here

And now with enough rows the we need scrolling:

enter image description here

This is how it's setup in IB / Storyboard:

enter image description here

Here is the source code:

//
//  DaveViewController.swift
//
//  Created by Don Mag on 7/1/19.
//

import UIKit

final class ContentSizedTableView: UITableView {
    override var contentSize:CGSize {
        didSet {
            invalidateIntrinsicContentSize()
        }
    }
    override var intrinsicContentSize: CGSize {
        layoutIfNeeded()
        return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
    }
}

class DaveCell: UITableViewCell {
    @IBOutlet var theLabel: UILabel!
    @IBOutlet var theTextField: UITextField!
}

class DaveViewController: UIViewController {

    @IBOutlet var theTableView: ContentSizedTableView!
    @IBOutlet var theScrollView: UIScrollView!

    let theLabels: [String] = [
        "NAME",
        "COMPANY",
        "POSITION",
        "CITY, STATE",
        "EMAIL ADDRESS",
        "ADDTL EMAIL\nADDRESS",
        "WORK NUMBER",
        "CELL NUMBER",
    ]

    let thePlaceholders: [String] = [
        "your name",
        "e.g. Bizee Inc.",
        "e.g. Regional Manager",
        "e.g. Denver, Colorado",
        "[email protected]",
        "[email protected]",
        "(XXX) XXX-XXXX",
        "(XXX) XXX-XXXX",
    ]

    override func viewDidLoad() {
        super.viewDidLoad()

        theTableView.dataSource = self
        theTableView.delegate = self

        theTableView.isScrollEnabled = false

        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)

        // add a "Done" button to dismiss the keyboard
        let doneBtn = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneTapped))
        self.navigationItem.rightBarButtonItem = doneBtn
    }

    @objc private func doneTapped() {
        view.endEditing(true)
    }

    @objc private func keyboardWillShow(notification: NSNotification){
        guard let keyboardFrame = notification.userInfo![UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { return }
        theScrollView.contentInset.bottom = view.convert(keyboardFrame.cgRectValue, from: nil).size.height
    }

    @objc private func keyboardWillHide(notification: NSNotification){
        theScrollView.contentInset.bottom = 0
    }

}

extension DaveViewController: UITableViewDataSource, UITableViewDelegate {

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return theLabels.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "DaveCell", for: indexPath) as! DaveCell

        cell.theLabel.text = theLabels[indexPath.row]
        cell.theTextField.placeholder = thePlaceholders[indexPath.row]

        cell.selectionStyle = .none

        return cell
    }

}

and here is the Storyboard source:

<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="OCG-fk-O07">
    <device id="retina4_7" orientation="portrait">
        <adaptation id="fullscreen"/>
    </device>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--Dave View Controller-->
        <scene sceneID="gYy-6C-lIo">
            <objects>
                <viewController id="ByF-jo-q34" customClass="DaveViewController" customModule="LaunchTest2" customModuleProvider="target" sceneMemberID="viewController">
                    <view key="view" contentMode="scaleToFill" id="KzY-ir-ZNp">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <subviews>
                            <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="sfU-bN-guh">
                                <rect key="frame" x="0.0" y="64" width="375" height="603"/>
                                <subviews>
                                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="SCY-zE-Vif">
                                        <rect key="frame" x="0.0" y="0.0" width="375" height="603"/>
                                        <subviews>
                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Import Using LinkedIn" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="kg2-26-RDd">
                                                <rect key="frame" x="104" y="12" width="167.5" height="20.5"/>
                                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
                                                <nil key="textColor"/>
                                                <nil key="highlightedColor"/>
                                            </label>
                                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="d56-0u-ihe">
                                                <rect key="frame" x="0.0" y="44.5" width="375" height="102.5"/>
                                                <subviews>
                                                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Choose Profile Photo" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="8zb-Kb-NJY">
                                                        <rect key="frame" x="108" y="20" width="159.5" height="20.5"/>
                                                        <fontDescription key="fontDescription" type="system" weight="thin" pointSize="17"/>
                                                        <nil key="textColor"/>
                                                        <nil key="highlightedColor"/>
                                                    </label>
                                                    <stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" spacing="6" translatesAutoresizingMaskIntoConstraints="NO" id="KiU-vK-XXr">
                                                        <rect key="frame" x="12" y="50.5" width="351" height="32"/>
                                                        <subviews>
                                                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="7Ri-fC-xIB">
                                                                <rect key="frame" x="0.0" y="0.0" width="172.5" height="32"/>
                                                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                                                <fontDescription key="fontDescription" type="system" weight="thin" pointSize="16"/>
                                                                <state key="normal" title="Use Camera">
                                                                    <color key="titleColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                                                </state>
                                                            </button>
                                                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="RYr-md-Xbc">
                                                                <rect key="frame" x="178.5" y="0.0" width="172.5" height="32"/>
                                                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                                                <fontDescription key="fontDescription" type="system" weight="thin" pointSize="16"/>
                                                                <state key="normal" title="Use Photo Library">
                                                                    <color key="titleColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                                                </state>
                                                            </button>
                                                        </subviews>
                                                    </stackView>
                                                </subviews>
                                                <color key="backgroundColor" red="0.91764705882352937" green="0.91764705882352937" blue="0.91764705882352937" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                                <constraints>
                                                    <constraint firstItem="8zb-Kb-NJY" firstAttribute="centerX" secondItem="d56-0u-ihe" secondAttribute="centerX" id="Ipm-mu-u52"/>
                                                    <constraint firstAttribute="trailing" secondItem="KiU-vK-XXr" secondAttribute="trailing" constant="12" id="M6p-kS-2Wj"/>
                                                    <constraint firstItem="8zb-Kb-NJY" firstAttribute="top" secondItem="d56-0u-ihe" secondAttribute="top" constant="20" id="R3e-cc-Fer"/>
                                                    <constraint firstAttribute="bottom" secondItem="KiU-vK-XXr" secondAttribute="bottom" constant="20" id="gX4-Zs-QVl"/>
                                                    <constraint firstItem="KiU-vK-XXr" firstAttribute="top" secondItem="8zb-Kb-NJY" secondAttribute="bottom" constant="10" id="inN-ct-lOI"/>
                                                    <constraint firstItem="KiU-vK-XXr" firstAttribute="leading" secondItem="d56-0u-ihe" secondAttribute="leading" constant="12" id="sFN-WJ-e2x"/>
                                                </constraints>
                                            </view>
                                            <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="lnj-TZ-tdQ" customClass="ContentSizedTableView" customModule="LaunchTest2" customModuleProvider="target">
                                                <rect key="frame" x="20" y="159" width="335" height="160"/>
                                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                                <constraints>
                                                    <constraint firstAttribute="height" priority="250" constant="160" id="OWa-SK-1bb"/>
                                                </constraints>
                                                <prototypes>
                                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="DaveCell" rowHeight="67" id="UZ0-mW-z3R" customClass="DaveCell" customModule="LaunchTest2" customModuleProvider="target">
                                                        <rect key="frame" x="0.0" y="28" width="335" height="67"/>
                                                        <autoresizingMask key="autoresizingMask"/>
                                                        <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="UZ0-mW-z3R" id="Bpu-ZK-jmH">
                                                            <rect key="frame" x="0.0" y="0.0" width="335" height="66.5"/>
                                                            <autoresizingMask key="autoresizingMask"/>
                                                            <subviews>
                                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cta-dh-NKD">
                                                                    <rect key="frame" x="15" y="23" width="42" height="21"/>
                                                                    <fontDescription key="fontDescription" type="system" pointSize="17"/>
                                                                    <nil key="textColor"/>
                                                                    <nil key="highlightedColor"/>
                                                                </label>
                                                                <textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="right" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="CgS-ib-w4y">
                                                                    <rect key="frame" x="69" y="18.5" width="251" height="30"/>
                                                                    <nil key="textColor"/>
                                                                    <fontDescription key="fontDescription" type="system" pointSize="14"/>
                                                                    <textInputTraits key="textInputTraits"/>
                                                                </textField>
                                                            </subviews>
                                                            <constraints>
                                                                <constraint firstItem="cta-dh-NKD" firstAttribute="top" secondItem="Bpu-ZK-jmH" secondAttribute="topMargin" constant="12" id="We1-kz-q2C"/>
                                                                <constraint firstAttribute="bottomMargin" secondItem="cta-dh-NKD" secondAttribute="bottom" constant="12" id="Xrk-31-tpM"/>
                                                                <constraint firstItem="cta-dh-NKD" firstAttribute="leading" secondItem="Bpu-ZK-jmH" secondAttribute="leadingMargin" id="gWG-0p-rAe"/>
                                                                <constraint firstItem="CgS-ib-w4y" firstAttribute="centerY" secondItem="Bpu-ZK-jmH" secondAttribute="centerY" id="lQV-0f-plQ"/>
                                                                <constraint firstItem="CgS-ib-w4y" firstAttribute="leading" secondItem="cta-dh-NKD" secondAttribute="trailing" constant="12" id="t9H-NP-Nun"/>
                                                                <constraint firstAttribute="trailingMargin" secondItem="CgS-ib-w4y" secondAttribute="trailing" id="zfb-h1-T31"/>
                                                            </constraints>
                                                        </tableViewCellContentView>
                                                        <connections>
                                                            <outlet property="theLabel" destination="cta-dh-NKD" id="E47-0o-OD8"/>
                                                            <outlet property="theTextField" destination="CgS-ib-w4y" id="pNN-td-M2T"/>
                                                        </connections>
                                                    </tableViewCell>
                                                </prototypes>
                                            </tableView>
                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Please put in a profile photo." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rAu-zB-NyR">
                                                <rect key="frame" x="74.5" y="573.5" width="226.5" height="21.5"/>
                                                <color key="backgroundColor" red="1" green="0.14913141730000001" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                                <fontDescription key="fontDescription" type="system" weight="thin" pointSize="18"/>
                                                <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                                <nil key="highlightedColor"/>
                                            </label>
                                        </subviews>
                                        <color key="backgroundColor" red="0.45138680930000002" green="0.99309605359999997" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                        <constraints>
                                            <constraint firstItem="kg2-26-RDd" firstAttribute="top" secondItem="SCY-zE-Vif" secondAttribute="top" constant="12" id="80C-f1-Inf"/>
                                            <constraint firstItem="rAu-zB-NyR" firstAttribute="top" relation="greaterThanOrEqual" secondItem="lnj-TZ-tdQ" secondAttribute="bottom" constant="8" id="9Sg-Aa-wir"/>
                                            <constraint firstItem="lnj-TZ-tdQ" firstAttribute="top" secondItem="d56-0u-ihe" secondAttribute="bottom" constant="12" id="EhF-Gu-8vx"/>
                                            <constraint firstAttribute="trailing" secondItem="d56-0u-ihe" secondAttribute="trailing" id="NuV-lh-WRd"/>
                                            <constraint firstItem="d56-0u-ihe" firstAttribute="top" secondItem="kg2-26-RDd" secondAttribute="bottom" constant="12" id="Ppo-X8-gdk"/>
                                            <constraint firstItem="lnj-TZ-tdQ" firstAttribute="leading" secondItem="SCY-zE-Vif" secondAttribute="leading" constant="20" id="QBd-ca-Itt"/>
                                            <constraint firstItem="kg2-26-RDd" firstAttribute="centerX" secondItem="SCY-zE-Vif" secondAttribute="centerX" id="TLP-ZG-cWf"/>
                                            <constraint firstItem="rAu-zB-NyR" firstAttribute="centerX" secondItem="SCY-zE-Vif" secondAttribute="centerX" id="Zat-D4-CeV"/>
                                            <constraint firstAttribute="bottom" secondItem="rAu-zB-NyR" secondAttribute="bottom" constant="8" id="jdI-I6-eub"/>
                                            <constraint firstAttribute="trailing" secondItem="lnj-TZ-tdQ" secondAttribute="trailing" constant="20" id="qls-uL-RxI"/>
                                            <constraint firstItem="d56-0u-ihe" firstAttribute="leading" secondItem="SCY-zE-Vif" secondAttribute="leading" id="yBB-ls-1fU"/>
                                        </constraints>
                                    </view>
                                </subviews>
                                <color key="backgroundColor" red="0.99942404029999998" green="0.98555368190000003" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                <constraints>
                                    <constraint firstItem="SCY-zE-Vif" firstAttribute="width" secondItem="sfU-bN-guh" secondAttribute="width" id="0AS-Bw-vgM"/>
                                    <constraint firstItem="SCY-zE-Vif" firstAttribute="height" secondItem="sfU-bN-guh" secondAttribute="height" priority="250" id="1nY-0L-LLo"/>
                                    <constraint firstItem="SCY-zE-Vif" firstAttribute="top" secondItem="sfU-bN-guh" secondAttribute="top" id="Rqy-JP-jge"/>
                                    <constraint firstAttribute="bottom" secondItem="SCY-zE-Vif" secondAttribute="bottom" id="S3L-Zg-fgS"/>
                                    <constraint firstItem="SCY-zE-Vif" firstAttribute="leading" secondItem="sfU-bN-guh" secondAttribute="leading" id="fON-cd-chM"/>
                                    <constraint firstAttribute="trailing" secondItem="SCY-zE-Vif" secondAttribute="trailing" id="vYb-7j-GxU"/>
                                </constraints>
                            </scrollView>
                        </subviews>
                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                        <constraints>
                            <constraint firstItem="6HN-hq-UwM" firstAttribute="bottom" secondItem="sfU-bN-guh" secondAttribute="bottom" id="1QS-4N-gEW"/>
                            <constraint firstItem="sfU-bN-guh" firstAttribute="leading" secondItem="6HN-hq-UwM" secondAttribute="leading" id="5Ur-Gf-8pP"/>
                            <constraint firstItem="sfU-bN-guh" firstAttribute="top" secondItem="6HN-hq-UwM" secondAttribute="top" id="7DE-le-6vP"/>
                            <constraint firstItem="6HN-hq-UwM" firstAttribute="trailing" secondItem="sfU-bN-guh" secondAttribute="trailing" id="N4k-MG-Uef"/>
                        </constraints>
                        <viewLayoutGuide key="safeArea" id="6HN-hq-UwM"/>
                    </view>
                    <navigationItem key="navigationItem" id="Wwb-5l-bvB"/>
                    <connections>
                        <outlet property="theScrollView" destination="sfU-bN-guh" id="Mtz-hh-9i3"/>
                        <outlet property="theTableView" destination="lnj-TZ-tdQ" id="Rqn-pk-icA"/>
                    </connections>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="mX1-qt-jnP" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="928.79999999999995" y="132.68365817091455"/>
        </scene>
        <!--Navigation Controller-->
        <scene sceneID="1J1-9A-mRF">
            <objects>
                <navigationController automaticallyAdjustsScrollViewInsets="NO" id="OCG-fk-O07" sceneMemberID="viewController">
                    <toolbarItems/>
                    <navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="RAe-Db-aHt">
                        <rect key="frame" x="0.0" y="20" width="375" height="44"/>
                        <autoresizingMask key="autoresizingMask"/>
                    </navigationBar>
                    <nil name="viewControllers"/>
                    <connections>
                        <segue destination="ByF-jo-q34" kind="relationship" relationship="rootViewController" id="9V8-EB-duR"/>
                    </connections>
                </navigationController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="Asf-IX-RlV" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="-10.4" y="132.68365817091455"/>
        </scene>
    </scenes>
</document>