0

Hi fellow programmers.

I've spent many hours trying to design this layout in Swift, with little success.

Drawing of the layout

The idea is having two TableViews:

  • The top one, with zero or more (indefined) cells, which can't be scrollable and its height grows with the number of cells.
  • The bottom one, with zero or more (indefined) cells, which is scrollable, and thus its height adapts to the space left from the latter tableview.

I've already tried to play with the constraints programmatically, by setting both tables top constraint to the top of the screen and then modifying that constraint depending on the size of the top TableView. Doesn't seem to work.

I also tried to modify the height of the tables programmatically, based on the number of cells that will be draw. Didn't work either.

Honestly, I'm at a deadpoint right now, and I'm looking out for help or some new ideas to make it work. Any help will be highly appreciated. Thanks in advance!

AtulParmar
  • 4,358
  • 1
  • 24
  • 45
IloneSP
  • 429
  • 2
  • 13
  • Use constraints in your interface builder. Set the second table as top 0 under first table. – ares777 Mar 08 '19 at 13:31
  • it's pretty hard to provide the programmatically constrain. Its compulsory to provide the constrain programmatically ? – Krunal Nagvadia Mar 08 '19 at 13:31
  • @user3344236 The issue is that by using the interface builder to set up the constraints, I need to set up a fixed height for the top table. I need it to grow dynamically. – IloneSP Mar 08 '19 at 13:34
  • No, you can do it other way. Add an @IBOutlet weak var dynamicHEIGHT: NSLayoutConstraint! in your class. Then set a fixed height in interface builder for your table. Then take that height constraint and link that with dynamicHEIGHT. Then in your code you can manipulate this height by your wish as dynamicHEIGHT.constant = yourarray.count * desired height for row ... where yourarray is what datasource you have for first tableview. In this way second table still go under first, even if you increase number of elements in first table view (rows) – ares777 Mar 08 '19 at 13:43
  • Instead of the two table view's, you can create two table view cell's which holds a tableview each. Than it would be pretty easy :) – J. Doe Mar 08 '19 at 13:54
  • @Roberto - based on your layout sketch... What should happen if `TableView1` has so many rows it won't fit vertically on the screen? – DonMag Mar 08 '19 at 14:13
  • @user3344236 I'll try that, thanks! – IloneSP Mar 08 '19 at 14:25
  • @DonMag The 2nd TableView won't be shown in that case. It's a weird design, I know, but... *shrug* They asked me to do this in that way – IloneSP Mar 08 '19 at 14:26

1 Answers1

1

You'll need to decide on some additional logic - as in, what to do when there are too many rows in the top table view, but this is one approach:

Use this custom table view class (from https://stackoverflow.com/a/48623673/6257435):

final class ContentSizedTableView: UITableView {
    override var contentSize:CGSize {
        didSet {
            invalidateIntrinsicContentSize()
        }
    }

    override var intrinsicContentSize: CGSize {
        layoutIfNeeded()
        return CGSize(width: UIView.noIntrinsicMetric, height: contentSize.height)
    }
}

This will "auto-size" your table view whenever the content size changes. To use it, add a table view and set its class to ContentSizedTableView.

  • Constrain the top, leading and trailing as desired.
  • Constrain the bottom to the top of the bottom table view.
  • Give it a height constraint - doesn't really matter what, but using 200 let's you work with the layout. But... edit that height constraint and check the Placeholder - Remove at build time checkbox.
  • Constrain the bottom table view to leading, trailing and bottom as desired.

Now, when you add rows to the top table view, it will auto-expand vertically, and the bottom table view will auto-contract vertically (until it disappears). Because the bottom of the top table view is still constrained to the top of the bottom table view, the top table view will stop growing and be scrollable (instead of extending off the bottom of the view).

Here's a very simple example:

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 SimpleTopCell: UITableViewCell {

    @IBOutlet var theLabel: UILabel!

}

class SimpleBottomCell: UITableViewCell {

    @IBOutlet var theLabel: UILabel!

}


class AutoSizeTableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    @IBOutlet var topTableView: ContentSizedTableView!

    @IBOutlet var bottomTableView: UITableView!

    var numRowsTop = 8
    var numRowsBottom = 12

    override func viewDidLoad() {
        super.viewDidLoad()

        topTableView.dataSource = self
        bottomTableView.dataSource = self

        topTableView.delegate = self
        bottomTableView.delegate = self

    }

    @IBAction func addRowTapped(_ sender: Any) {
        numRowsTop += 1
        topTableView.reloadData()
    }

    @IBAction func delRowTapped(_ sender: Any) {
        numRowsTop -= 1
        // make sure it's at least 1
        numRowsTop = max(numRowsTop, 1)
        topTableView.reloadData()
    }

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

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if tableView == topTableView {
            return numRowsTop
        }
        return numRowsBottom
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if tableView == topTableView {
            let cell = tableView.dequeueReusableCell(withIdentifier: "SimpleTopCell", for: indexPath) as! SimpleTopCell
            cell.theLabel.text = "Top: \(indexPath)"
            return cell
        }
        let cell = tableView.dequeueReusableCell(withIdentifier: "SimpleBottomCell", for: indexPath) as! SimpleBottomCell
        cell.theLabel.text = "Bottom: \(indexPath)"
        return cell
    }

}

With 8 rows in the top, and 12 rows in the bottom, we get:

enter image description here

and with 11 rows in the top:

enter image description here

and finally with 17 rows in the top (scrolled so you can see):

enter image description here

Here is the storyboard source for that example:

<?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="Qun-XE-Shi">
    <device id="retina4_7" orientation="portrait">
        <adaptation id="fullscreen"/>
    </device>
    <dependencies>
        <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>
        <!--Auto Size Table View Controller-->
        <scene sceneID="fo7-Cf-tO5">
            <objects>
                <viewController id="Qun-XE-Shi" customClass="AutoSizeTableViewController" customModule="XC10SWScratch" customModuleProvider="target" sceneMemberID="viewController">
                    <view key="view" contentMode="scaleToFill" id="Vvb-uu-phq">
                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <subviews>
                            <stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" spacing="30" translatesAutoresizingMaskIntoConstraints="NO" id="uEB-QI-6Jy">
                                <rect key="frame" x="87.5" y="44" width="200" height="30"/>
                                <subviews>
                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="qaz-hh-38L">
                                        <rect key="frame" x="0.0" y="0.0" width="85" height="30"/>
                                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                        <state key="normal" title="Add Row"/>
                                        <connections>
                                            <action selector="addRowTapped:" destination="Qun-XE-Shi" eventType="touchUpInside" id="01O-ew-Ezw"/>
                                        </connections>
                                    </button>
                                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="aEk-df-qAr">
                                        <rect key="frame" x="115" y="0.0" width="85" height="30"/>
                                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                        <state key="normal" title="Del Row"/>
                                        <connections>
                                            <action selector="delRowTapped:" destination="Qun-XE-Shi" eventType="touchUpInside" id="qXr-S1-utk"/>
                                        </connections>
                                    </button>
                                </subviews>
                                <constraints>
                                    <constraint firstAttribute="width" constant="200" id="zau-ia-umI"/>
                                </constraints>
                            </stackView>
                            <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="2Kp-ZU-yGW" customClass="ContentSizedTableView" customModule="XC10SWScratch" customModuleProvider="target">
                                <rect key="frame" x="40" y="100" width="295" height="240"/>
                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                <constraints>
                                    <constraint firstAttribute="height" constant="240" placeholder="YES" id="BHc-OJ-3pa"/>
                                </constraints>
                                <prototypes>
                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="SimpleTopCell" id="TcM-H4-6wf" customClass="SimpleTopCell" customModule="XC10SWScratch" customModuleProvider="target">
                                        <rect key="frame" x="0.0" y="28" width="295" height="44"/>
                                        <autoresizingMask key="autoresizingMask"/>
                                        <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="TcM-H4-6wf" id="Zvz-a3-fip">
                                            <rect key="frame" x="0.0" y="0.0" width="295" height="43.5"/>
                                            <autoresizingMask key="autoresizingMask"/>
                                            <subviews>
                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="VZP-Nh-tor">
                                                    <rect key="frame" x="15" y="11" width="265" height="22"/>
                                                    <fontDescription key="fontDescription" type="system" pointSize="17"/>
                                                    <nil key="textColor"/>
                                                    <nil key="highlightedColor"/>
                                                </label>
                                            </subviews>
                                            <constraints>
                                                <constraint firstItem="VZP-Nh-tor" firstAttribute="top" secondItem="Zvz-a3-fip" secondAttribute="topMargin" id="0CD-yt-vZ5"/>
                                                <constraint firstItem="VZP-Nh-tor" firstAttribute="leading" secondItem="Zvz-a3-fip" secondAttribute="leadingMargin" id="7LB-3U-q99"/>
                                                <constraint firstAttribute="trailingMargin" secondItem="VZP-Nh-tor" secondAttribute="trailing" id="Pp4-PA-tvk"/>
                                                <constraint firstItem="VZP-Nh-tor" firstAttribute="bottom" secondItem="Zvz-a3-fip" secondAttribute="bottomMargin" id="xP2-ad-aPi"/>
                                            </constraints>
                                        </tableViewCellContentView>
                                        <connections>
                                            <outlet property="theLabel" destination="VZP-Nh-tor" id="aiq-L8-DYT"/>
                                        </connections>
                                    </tableViewCell>
                                </prototypes>
                            </tableView>
                            <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="UCl-gz-0HT">
                                <rect key="frame" x="40" y="360" width="295" height="287"/>
                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                <prototypes>
                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="SimpleBottomCell" id="PtI-n4-c5u" customClass="SimpleBottomCell" customModule="XC10SWScratch" customModuleProvider="target">
                                        <rect key="frame" x="0.0" y="28" width="295" height="44"/>
                                        <autoresizingMask key="autoresizingMask"/>
                                        <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="PtI-n4-c5u" id="bCo-w1-VQ7">
                                            <rect key="frame" x="0.0" y="0.0" width="295" height="43.5"/>
                                            <autoresizingMask key="autoresizingMask"/>
                                            <subviews>
                                                <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Inu-NF-CgL">
                                                    <rect key="frame" x="15" y="11" width="265" height="22"/>
                                                    <fontDescription key="fontDescription" type="system" pointSize="17"/>
                                                    <nil key="textColor"/>
                                                    <nil key="highlightedColor"/>
                                                </label>
                                            </subviews>
                                            <constraints>
                                                <constraint firstItem="Inu-NF-CgL" firstAttribute="bottom" secondItem="bCo-w1-VQ7" secondAttribute="bottomMargin" id="2Nz-Y2-ceX"/>
                                                <constraint firstItem="Inu-NF-CgL" firstAttribute="leading" secondItem="bCo-w1-VQ7" secondAttribute="leadingMargin" id="HIx-hC-b4c"/>
                                                <constraint firstItem="Inu-NF-CgL" firstAttribute="top" secondItem="bCo-w1-VQ7" secondAttribute="topMargin" id="Xfn-2l-Dyw"/>
                                                <constraint firstAttribute="trailingMargin" secondItem="Inu-NF-CgL" secondAttribute="trailing" id="f8F-YZ-jJh"/>
                                            </constraints>
                                        </tableViewCellContentView>
                                        <connections>
                                            <outlet property="theLabel" destination="Inu-NF-CgL" id="fsi-1C-DCU"/>
                                        </connections>
                                    </tableViewCell>
                                </prototypes>
                            </tableView>
                        </subviews>
                        <color key="backgroundColor" red="1" green="0.83234566450000003" blue="0.47320586440000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                        <constraints>
                            <constraint firstItem="UCl-gz-0HT" firstAttribute="top" secondItem="2Kp-ZU-yGW" secondAttribute="bottom" constant="20" id="2Kx-3i-Hd9"/>
                            <constraint firstItem="PTh-mY-2F7" firstAttribute="bottom" secondItem="UCl-gz-0HT" secondAttribute="bottom" constant="20" id="D50-Uk-fN0"/>
                            <constraint firstItem="PTh-mY-2F7" firstAttribute="trailing" secondItem="2Kp-ZU-yGW" secondAttribute="trailing" constant="40" id="F1H-8j-bVy"/>
                            <constraint firstItem="PTh-mY-2F7" firstAttribute="trailing" secondItem="UCl-gz-0HT" secondAttribute="trailing" constant="40" id="KXW-yh-GAA"/>
                            <constraint firstItem="uEB-QI-6Jy" firstAttribute="top" secondItem="PTh-mY-2F7" secondAttribute="top" constant="24" id="M5V-JO-Jfw"/>
                            <constraint firstItem="uEB-QI-6Jy" firstAttribute="centerX" secondItem="Vvb-uu-phq" secondAttribute="centerX" id="TlF-gf-jKf"/>
                            <constraint firstItem="2Kp-ZU-yGW" firstAttribute="top" secondItem="PTh-mY-2F7" secondAttribute="top" constant="80" id="XFo-ry-u9Y"/>
                            <constraint firstItem="2Kp-ZU-yGW" firstAttribute="leading" secondItem="PTh-mY-2F7" secondAttribute="leading" constant="40" id="iu7-sU-Gki"/>
                            <constraint firstItem="UCl-gz-0HT" firstAttribute="leading" secondItem="PTh-mY-2F7" secondAttribute="leading" constant="40" id="ni9-df-1kD"/>
                        </constraints>
                        <viewLayoutGuide key="safeArea" id="PTh-mY-2F7"/>
                    </view>
                    <navigationItem key="navigationItem" id="6mN-ug-bQV"/>
                    <connections>
                        <outlet property="bottomTableView" destination="UCl-gz-0HT" id="Yny-Gc-zhG"/>
                        <outlet property="topTableView" destination="2Kp-ZU-yGW" id="puB-6Z-xMa"/>
                    </connections>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="kps-Ns-Nci" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="1088.8" y="2196.2518740629689"/>
        </scene>
    </scenes>
</document>
DonMag
  • 69,424
  • 5
  • 50
  • 86