How to set the scrollview height when UI elements are dynamically created
Asked Answered
M

2

5

On the storyboard, inside a view controller I have a scrollview. A navigation bar and a stack view are embedded inside the scrollview. Programmatically I am adding some UI elements one below the other inside the stack view.

Via storyboard, constraints of the scrollview are set to the same as safe area - top, bottom, leading, trailing and width.

Navigation bar constraints - trailing, leading, top, width are same as super view.

Stackview constraints - trailing, leading, bottom, width are same as superview.

Navigation bar's bottom is same as stackview's top so that they are one below the other.

After setting these constraints, there is an error about scrollview - need constraints for: Y position or Height

Since the UI elements are being added dynamically to the stackview, How do I set the height in storyboard? Is there a better way to do this?

I tried "add missing constraint" option and it adds a constant height to the stack view which does not work well for all devices. Any help is appreciated since I am new to iOS app development. Thanks.

Mady answered 18/1, 2019 at 6:13 Comment(2)
Can you post a screenshot of the storyboard ?Bluebell
The idea is to have some sort of height that the scrollview can use. Now, it doesn't matter if your stack view is going to be dynamic, as, when the height of your stack view increases dynamically, so would the scrollview's content size. So make sure you have something for the scrollview to calculate the content size. Like there should be at least something inside the stackview that has some kind of height. I hope you get my point. Also more info about your problem would be helpful to solve it. CheersObscurant
V
8

An empty UIStackView has no intrinsic height, so IB / Storyboard doesn't know how to satisfy the contentSize of the scroll view.

You need to give it a height constraint at design-time - it can be any value, because it's going to change at runtime. So, give your stack view a height constraint of, say, 300.

Of course, that won't allow it to "grow" as you dynamically add arranged subviews, so you have a couple options.

1 - Give the height constraint a low priority:

enter image description here

This will satisfy IB, but will allow the stack view to grow.

2 - Set the height constraint to be a "placeholder":

enter image description here

At build time, this constraint will be removed, again allowing the stack view to grow.


Edit: Using the first option (low priority), this is how your Document Outline should look:

enter image description here

Simple example... This class will add 30 labels as arranged subviews of the initially empty stack view:

import UIKit

class StackInScrollViewController: UIViewController {

    @IBOutlet var theStackView: UIStackView!

    override func viewDidLoad() {
        super.viewDidLoad()

        for i in 1...30 {
            let v = UILabel()
            v.translatesAutoresizingMaskIntoConstraints = false
            v.backgroundColor = .yellow
            v.text = "This is Label \(i)"
            theStackView.addArrangedSubview(v)
        }

    }

}

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="v4n-Ck-gNo">
    <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>
        <!--Stack In Scroll View Controller-->
        <scene sceneID="85i-FA-G1W">
            <objects>
                <viewController storyboardIdentifier="pageOne" id="v4n-Ck-gNo" customClass="StackInScrollViewController" customModule="XC10SWScratch" customModuleProvider="target" sceneMemberID="viewController">
                    <view key="view" contentMode="scaleToFill" id="meJ-hR-GiR">
                        <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="h2O-la-OOI">
                                <rect key="frame" x="0.0" y="20" width="375" height="647"/>
                                <subviews>
                                    <navigationBar contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Ozf-iG-Ogw">
                                        <rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
                                        <items>
                                            <navigationItem title="Title" id="gMo-gQ-HLZ"/>
                                        </items>
                                    </navigationBar>
                                    <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="10" translatesAutoresizingMaskIntoConstraints="NO" id="FOm-go-etU">
                                        <rect key="frame" x="0.0" y="44" width="375" height="300"/>
                                        <constraints>
                                            <constraint firstAttribute="height" priority="250" constant="300" id="4A2-93-8hK"/>
                                        </constraints>
                                    </stackView>
                                </subviews>
                                <color key="backgroundColor" red="0.46202266219999999" green="0.83828371759999998" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                                <constraints>
                                    <constraint firstAttribute="trailing" secondItem="FOm-go-etU" secondAttribute="trailing" id="2wm-HM-UgW"/>
                                    <constraint firstItem="Ozf-iG-Ogw" firstAttribute="width" secondItem="h2O-la-OOI" secondAttribute="width" id="5m2-MQ-vcg"/>
                                    <constraint firstItem="Ozf-iG-Ogw" firstAttribute="top" secondItem="h2O-la-OOI" secondAttribute="top" id="76R-nE-Vve"/>
                                    <constraint firstAttribute="bottom" secondItem="FOm-go-etU" secondAttribute="bottom" id="ASq-4m-5zD"/>
                                    <constraint firstAttribute="trailing" secondItem="Ozf-iG-Ogw" secondAttribute="trailing" id="GHy-BT-HmJ"/>
                                    <constraint firstItem="FOm-go-etU" firstAttribute="width" secondItem="h2O-la-OOI" secondAttribute="width" id="LcJ-rd-yDs"/>
                                    <constraint firstItem="Ozf-iG-Ogw" firstAttribute="leading" secondItem="h2O-la-OOI" secondAttribute="leading" id="VCw-iY-MfZ"/>
                                    <constraint firstItem="FOm-go-etU" firstAttribute="top" secondItem="Ozf-iG-Ogw" secondAttribute="bottom" id="l3k-Nm-hTu"/>
                                    <constraint firstItem="FOm-go-etU" firstAttribute="leading" secondItem="h2O-la-OOI" secondAttribute="leading" id="r3A-iy-6bu"/>
                                </constraints>
                            </scrollView>
                        </subviews>
                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                        <constraints>
                            <constraint firstItem="h2O-la-OOI" firstAttribute="leading" secondItem="k8i-v0-f9m" secondAttribute="leading" id="0YL-7I-Lst"/>
                            <constraint firstItem="h2O-la-OOI" firstAttribute="top" secondItem="k8i-v0-f9m" secondAttribute="top" id="4Zo-wX-Rir"/>
                            <constraint firstItem="k8i-v0-f9m" firstAttribute="bottom" secondItem="h2O-la-OOI" secondAttribute="bottom" id="FUr-4Z-b6w"/>
                            <constraint firstItem="k8i-v0-f9m" firstAttribute="trailing" secondItem="h2O-la-OOI" secondAttribute="trailing" id="Ic8-zA-xpZ"/>
                        </constraints>
                        <viewLayoutGuide key="safeArea" id="k8i-v0-f9m"/>
                    </view>
                    <connections>
                        <outlet property="theStackView" destination="FOm-go-etU" id="72X-V9-Lzg"/>
                    </connections>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="Ora-A0-Q75" userLabel="First Responder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="-327" y="165"/>
        </scene>
    </scenes>
</document>
Valiancy answered 18/1, 2019 at 13:28 Comment(7)
Is there anything else to be done programmatically after this? Because the scroll view does not scroll after doing these suggested settings.Mady
Are you adding enough arranged subviews to exceed the height of the screen?Valiancy
@Mady - compare your Document Outline to the image I added to my answer.Valiancy
Yes. I have the exact same outline and the scroll view does not scroll. Not sure what could be going wrong hereMady
Also I tried both of your suggestions. It had the same problemMady
@Mady - I added the source for an example that works... simple view controller class that adds 30 labels to an initially empty stack view, and the source for the storyboard.Valiancy
Thank you @DonMag. theStackView.addArrangedSubview(v) was the key here. Appreciate your help.Mady
M
1

As per your question you need scroll view with dynamic content in it. You can solve your problem in 2 ways. First is using Tableview instead of the scroll view. But I will try to answer your questions with your implementation.

  • Add ScrollView to the ViewController with constraint top, bottom, leading, trailing with its super view.

  • Add StackView with the constraint of leading, trailing, top and bottom.

  • Whenever you add StackView content programmatically, call self.view.layoutIfNeeded() and self.view.layoutSubviews().

  • Finally set the content size of scroll view.scrollView.contentSize = CGSize(width:CGFloat, height: CGFloat)

Mercy answered 18/1, 2019 at 6:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.