Horizontal Uiscrollview Inside Custom Uitableviewcell - Using Ib Storyboard - Not Scrolling

Horizontal UIScrollView inside custom UITableViewCell - using IB Storyboard - Not scrolling

Autolayout is activated (and that's a good thing) in your storyboard.
The scrollable size of a UIScrollView is computed based on the constraints of its subviews.

So you need to add "top, bottom, leading, trailing space to superview" on the UIImageView inside the UIScrollView. They can be all set to 0.

Now if you don't add any constraint to the UIImageView then its intrinsicContentSize size will be used.

Your constraints should look like this:

autolayout constraints

Note that you were also missing constraints on the content view of your cell.

When you're done with the constraints, remove all the setContentSize: calls from your code.

Horizontal UIScrollView inside a UITableViewCell

I have found a workaround.

Recreating from scratch a very simple UITableView (8 rows), and inserting a UIScrollView in just the second row, works perfectly. Nested scroll views (the TableView and the ScrollView) scroll independently as expected. All that was done in a single-file test project, in the AppDelegate.

So... first I thought "this might be a delegation problem", so I told the scrollView that its delegate was the TableView, not my custom TableViewCell subclass. To no avail.

Then instead of resorting to a custom, clean TableViewCell subclass that just adds the ScrollView and the PageControl, I resorted to creating a plain TableViewCell in the TableView's cellForRowAtIndexPath: method. Then, I create a UIScrollView right after initializing the TableViewCell, then add it to the TableViewCell, and shazam... it works!

Before:

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kVolumePreviewCellIdentifier];
if (cell == nil) {
cell = [[[phVolumePreviewCell alloc] init] autorelease];
}

[(phVolumePreviewCell *)cell setVolume:volume];
// That does the job of creating the cell's scrollView and pageControl

return (UITableViewCell *)cell

After:

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kvcPreviewRowIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kvcPreviewRowIdentifier] autorelease];

previewScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, tableView.frame.size.width, kvcPreviewScrollViewHeight)];
[previewScrollView setContentSize:CGSizeMake(1000, kvcPreviewScrollViewHeight)];
[[cell contentView] addSubview:previewScrollView];

previewPageControl = [[UIPageControl alloc] initWithFrame:CGRectMake(0, kvcPreviewScrollViewHeight, tableView.frame.size.width, kvcPreviewPageControlHeight)];
[previewPageControl setNumberOfPages:4];
[[cell contentView] addSubview:previewPageControl];
}

return (UITableViewCell *)cell;

How the heck comes that when I create my scrollView from the TVCell subclass, it doesn't play nice ; but when I create the scrollView from the TableView right after creating a "classical" TVCell, it works?

I might have found a solution, but I'm still puzzled by the reason.

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):

Sample Image

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

Sample Image

And now with enough rows the we need scrolling:

Sample Image

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

Sample Image

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>

How to use UIScrollView in Storyboard

I'm answering my own question because I just spent 2 hours to find the solution and StackOverflow allows this QA style.

Start to finish here is how to make it work in storyboard.

1: go to you view controller and click on Attribute Inspector.

2: change Size to Freeform instead of Inferred.

3: Go to the main view on that storyboard, not your scrollview but rather the top level view.

4: Click Size Inspector and set this view to your desired size. I changed my height to 1000.

Now you will see that you storyboard has your view setup so you can see the entire height of your scroll for easy design.



Related Topics



Leave a reply



Submit