contentView with UITableView not scrolling within UIScrollView
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):
With a couple more rows (still, nothing scrolls):
And now with enough rows the we need scrolling:
This is how it's setup in IB / Storyboard:
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",
"name@email.com",
"name@email.com",
"(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>
Why my UIScrollView not scrolling within this cell?
The problem is that you're using a storyboard with auto layout turned on as the source of your prototype cell with the scroll view inside it. Hence your attempt to set the scroll view's content size manually is failing. Either use internal constraints to set the contents size, or else create and configure the scroll view and its contents entirely in code so that auto layout doesn't apply to them, or else use a separate .xib as the source of your cell (so that you can turn off auto layout there).
I was able to get your scroll view working using constraints in your storyboard. I had to give both the scroll view and the label appropriate constraints.
UITableView inside UIScrollView - Cell not clickable
Do the following thing:
yourView.clipToBounds = true
Now, if UITableView
does not appears means your UIView
is not same bigger to hold down UITableView
.
Make sure that your UIView
height is bigger to hold the contents in it and then try to tap on it.
Updated:
If you are using AutoLayout
, then do the following thing.
- Give the fix height to
UIView
- Take the outlet of height constraint of
UIView
Now, in
viewDidLayoutSubviews
change the constraint ofUIView
toUITableView
contentSize
height.self.heightConstraint = self.tableView.contentSize.height
Let me know, if this helps!
Can't scroll both UIScrollView in UITableViewCell and the UITableView itself
@user2277872's answer put me on the right track to look at the output of the UIScrollView
delegate methods of the UIScrollView
in my UITableViewCell
subclass. Putting an NSLog()
in scrollViewWillBeginDragging:
made me notice that the UIScrollView
was receiving scrolling events while I was trying to scroll the UITableView
. My UIScrollView
had a contentSize
larger than its frame in both directions, but I've forced that view to only scroll horizontal, so ignored the height and reset it. That force was my undoing and I should have known it at the time -- the correct solution is to fix the frame height. If the UIScrollView
doesn't think there is more vertical content, it will correctly forward the swipe up/down gesture to the UITableView
.
While I attempt to figure out why my contentSize
is too large when it wasn't before (thinking I'm missing a clipToBounds
somewhere), what I'm doing to force horizontal scrolling temporarily is (in the UITableViewCell
's subclass):
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
CGSize contentSize = self.scrollView.contentSize;
contentSize.height = self.frame.size.height;
self.scrollView.contentSize = contentSize;
}
EDIT: Actually, this is seemingly better than overriding drawRect
. This would be in the UIScrollView
subclass:
/*
* Lock to horizontal scrolling only.
*/
- (void)setContentSize:(CGSize)contentSize
{
[super setContentSize:CGSizeMake(contentSize.width, 1)];
}
The height
struct
member isn't too important, as long as it's guaranteed to be smaller than the frame.size.height
of the UITableViewCell
. Still hacky, still need to find why I could clip before and not now.
UIScrollView not scrolling in Swift
You need to set the frame
of your UIScrollView
so that it is less than the contentSize
. Otherwise, it won't scroll.
Also, I would recommend that you add the following to your viewDidLoad
method.
Related Topics
Can Push Notifications Be Used to Run Code Without Notifying User
How to Solve Mach-O-Linker Error in iOS7 & Xcode 5.0.1
Cannot Call Value of Non-Function Type 'Module<Firebase>'
Using Auto Layout to Have UIlabel and UItextfield Next to Each Other
Check Whether Time Falls Between Two Time iOS
Detect Permission of Media Library iOS
Using '!' Here Is Deprecated and Will Be Removed in a Future Release - Swift 4.2
Multiple Lines in UItabbaritem Label
Converting Nsdictionary to Xml
Programmatically Change Splash Screen in iOS
Implementing Codable for UIcolor
Add a UIbutton as a Subview to a UItabbar
Added a Custom Framework, Now Swift Can't Unarchive Data
Can't Use @Observedobject on Real iPhone
Applying Different Attributes for Different Portions of an Nsattributedstring
How Come My Drawing Code Keeps Resulting in Fuzzy Shapes
Uipageviewcontroller Setviewcontrollers, UIpagecontrol Not Showing Right Current Number