Home > Software design >  Add leading and trailing constant to a ContentView inside a UIScrollView
Add leading and trailing constant to a ContentView inside a UIScrollView

Time:08-28

Currently, I have this:

enter image description here

I want the width of my red ContentView not 100%, I want a leading and trailing space.

I tried to set leading and trailing constants of my red ContentView which resulted in horizontal scrolling. I tried to disable horizontal scrolling, which resulted in a fixed ContentView with leading space to my UIScrollView.

How can I define values for trailing and leading constants on my ContentView, without the unwanted behaviour (horizontal scrolling)?

CodePudding user response:

You want to take advantage of the scroll view's Content Layout Guide and Frame Layout Guide.

Constrain your "ContentView" to the Content Layout Guide to control the "scrollable size" and constrain it to the Frame Layout Guide to control its size.

So, for example, if you want 20-points of "padding" on left and right, constrain its Leading to the Content Layout Guide Leading with a Constant of 20, and constraint its Width to the Frame Layout Guide with a constant of minus 40 (20 points on each side).

Here's how it can look if you're doing this in Storyboard:

enter image description here

change the Height constraint on the ContentView from 120 to 1200 to see the vertical scrolling.

Here's the source for that storyboard so you can inspect it:

<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Y6W-OH-hqX">
    <device id="retina3_5" orientation="portrait" appearance="light"/>
    <dependencies>
        <deployment identifier="iOS"/>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
        <capability name="System colors in document resources" minToolsVersion="11.0"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <scenes>
        <!--View Controller-->
        <scene sceneID="s0d-6b-0kx">
            <objects>
                <viewController id="Y6W-OH-hqX" sceneMemberID="viewController">
                    <view key="view" contentMode="scaleToFill" id="5EZ-qb-Rvc">
                        <rect key="frame" x="0.0" y="0.0" width="320" height="480"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <subviews>
                            <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="gbkg" translatesAutoresizingMaskIntoConstraints="NO" id="kGy-CW-5oZ">
                                <rect key="frame" x="20" y="20" width="280" height="440"/>
                            </imageView>
                            <scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="f6w-Hj-SnQ">
                                <rect key="frame" x="20" y="20" width="280" height="440"/>
                                <subviews>
                                    <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="bKG-7C-UV8" userLabel="ContentView">
                                        <rect key="frame" x="20" y="20" width="240" height="120"/>
                                        <subviews>
                                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Your Content View" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5vI-Ro-IQh">
                                                <rect key="frame" x="8" y="8" width="224" height="24"/>
                                                <fontDescription key="fontDescription" type="system" pointSize="20"/>
                                                <color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                                                <nil key="highlightedColor"/>
                                            </label>
                                        </subviews>
                                        <color key="backgroundColor" systemColor="systemRedColor"/>
                                        <constraints>
                                            <constraint firstAttribute="trailing" secondItem="5vI-Ro-IQh" secondAttribute="trailing" constant="8" id="SOR-6B-qUy"/>
                                            <constraint firstItem="5vI-Ro-IQh" firstAttribute="leading" secondItem="bKG-7C-UV8" secondAttribute="leading" constant="8" id="Tdy-3P-f8D"/>
                                            <constraint firstItem="5vI-Ro-IQh" firstAttribute="top" secondItem="bKG-7C-UV8" secondAttribute="top" constant="8" id="V5A-6U-nMX"/>
                                            <constraint firstAttribute="height" constant="120" id="egx-Ye-Pn6"/>
                                        </constraints>
                                    </view>
                                </subviews>
                                <constraints>
                                    <constraint firstItem="bKG-7C-UV8" firstAttribute="leading" secondItem="ds4-RC-JaM" secondAttribute="leading" constant="20" id="0Gd-qA-Ieg"/>
                                    <constraint firstItem="bKG-7C-UV8" firstAttribute="trailing" secondItem="ds4-RC-JaM" secondAttribute="trailing" id="WGf-cI-cwg"/>
                                    <constraint firstItem="bKG-7C-UV8" firstAttribute="bottom" secondItem="ds4-RC-JaM" secondAttribute="bottom" constant="-20" id="hkb-0t-zAy"/>
                                    <constraint firstItem="bKG-7C-UV8" firstAttribute="width" secondItem="pcX-Se-Do0" secondAttribute="width" constant="-40" id="ndP-Ww-TIg"/>
                                    <constraint firstItem="bKG-7C-UV8" firstAttribute="top" secondItem="ds4-RC-JaM" secondAttribute="top" constant="20" id="xbu-qJ-MLf"/>
                                </constraints>
                                <viewLayoutGuide key="contentLayoutGuide" id="ds4-RC-JaM"/>
                                <viewLayoutGuide key="frameLayoutGuide" id="pcX-Se-Do0"/>
                            </scrollView>
                        </subviews>
                        <viewLayoutGuide key="safeArea" id="vDu-zF-Fre"/>
                        <color key="backgroundColor" white="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
                        <constraints>
                            <constraint firstItem="f6w-Hj-SnQ" firstAttribute="bottom" secondItem="kGy-CW-5oZ" secondAttribute="bottom" id="INT-yJ-xuH"/>
                            <constraint firstItem="kGy-CW-5oZ" firstAttribute="top" secondItem="vDu-zF-Fre" secondAttribute="top" constant="20" id="QF2-gZ-vye"/>
                            <constraint firstItem="f6w-Hj-SnQ" firstAttribute="top" secondItem="kGy-CW-5oZ" secondAttribute="top" id="S8N-mU-Qc2"/>
                            <constraint firstItem="f6w-Hj-SnQ" firstAttribute="trailing" secondItem="kGy-CW-5oZ" secondAttribute="trailing" id="Uv9-tZ-0tY"/>
                            <constraint firstItem="kGy-CW-5oZ" firstAttribute="leading" secondItem="vDu-zF-Fre" secondAttribute="leading" constant="20" id="YQh-qf-dC6"/>
                            <constraint firstItem="vDu-zF-Fre" firstAttribute="bottom" secondItem="kGy-CW-5oZ" secondAttribute="bottom" constant="20" id="ZCH-4J-Gvo"/>
                            <constraint firstItem="vDu-zF-Fre" firstAttribute="trailing" secondItem="kGy-CW-5oZ" secondAttribute="trailing" constant="20" id="eAS-mf-ueb"/>
                            <constraint firstItem="f6w-Hj-SnQ" firstAttribute="leading" secondItem="kGy-CW-5oZ" secondAttribute="leading" id="gvC-t3-aUK"/>
                        </constraints>
                    </view>
                </viewController>
                <placeholder placeholderIdentifier="IBFirstResponder" id="Ief-a0-LHa" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
            </objects>
            <point key="canvasLocation" x="13.125" y="138.75"/>
        </scene>
    </scenes>
    <resources>
        <image name="gbkg" width="667" height="1200"/>
        <systemColor name="systemRedColor">
            <color red="1" green="0.23137254901960785" blue="0.18823529411764706" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
        </systemColor>
    </resources>
</document>

And here's how you could do it via code:

class ViewController: UIViewController {

    let contentView = UIView()
    let contentLabel = UILabel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        guard let bkgImage = UIImage(named: "gbkg") else { return }

        view.backgroundColor = .darkGray
        
        // background image view
        let bkgImageView = UIImageView()
        bkgImageView.image = bkgImage
        
        // create the scroll view
        let scrollView = UIScrollView()
        
        // label preoperties
        contentLabel.textAlignment = .center
        contentLabel.font = .systemFont(ofSize: 24.0)
        contentLabel.textColor = .white
        contentLabel.numberOfLines = 0
        contentLabel.text = "Your Content View"
        
        // add label to contentView
        contentView.addSubview(contentLabel)
        
        // content view properties
        contentView.backgroundColor = .systemRed
        contentView.layer.cornerRadius = 24
        
        // add content view to scroll view
        scrollView.addSubview(contentView)

        // we'll use auto-layout for all views
        [bkgImageView, scrollView, contentView, contentLabel].forEach { v in
            v.translatesAutoresizingMaskIntoConstraints = false
        }
        
        // add views to main view
        view.addSubview(bkgImageView)
        view.addSubview(scrollView)

        // so we can do less typing in constraints
        let g = view.safeAreaLayoutGuide
        let cg = scrollView.contentLayoutGuide
        let fg = scrollView.frameLayoutGuide

        NSLayoutConstraint.activate([
            
            // inset background image view 20-points on all 4 sides
            bkgImageView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            bkgImageView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            bkgImageView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            bkgImageView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
            
            // constrain scroll view to background image view
            scrollView.topAnchor.constraint(equalTo: bkgImageView.topAnchor, constant: 0.0),
            scrollView.leadingAnchor.constraint(equalTo: bkgImageView.leadingAnchor, constant: 0.0),
            scrollView.trailingAnchor.constraint(equalTo: bkgImageView.trailingAnchor, constant: 0.0),
            scrollView.bottomAnchor.constraint(equalTo: bkgImageView.bottomAnchor, constant: 0.0),
            
            // constrain content view to scroll view's Content Layout Guide
            //  top/leading/bottom with 20-points "padding"
            contentView.topAnchor.constraint(equalTo: cg.topAnchor, constant: 20.0),
            contentView.leadingAnchor.constraint(equalTo: cg.leadingAnchor, constant: 20.0),
            contentView.bottomAnchor.constraint(equalTo: cg.bottomAnchor, constant: -20.0),
            // we can leave trailing at 0.0
            contentView.trailingAnchor.constraint(equalTo: cg.trailingAnchor, constant: 0.0),

            // constrain content view width to scroll view's Frame Layout Guide
            //  minus 40-points (20 on each side)
            contentView.widthAnchor.constraint(equalTo: fg.widthAnchor, constant: -40.0),
            
            // constrain label near top of content view
            contentLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8.0),
            contentLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8.0),
            contentLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -8.0),

            // content view height will probably be determined by your UI elements,
            //  but for this example we'll use an explicit height of 120
            contentView.heightAnchor.constraint(equalToConstant: 120.0),
            
            // to see the vertical scrolling, use this height instead
            //contentView.heightAnchor.constraint(equalToConstant: 1200.0),
            
        ])
        
    }
}

CodePudding user response:

Try to set up a width constraint for your scroll view. For example, if you want to have its width the same as the root view's width:

scrollView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true

This way, the layout system knows that the scroll view should not grow horizontally.

  • Related