Currently, I have this:
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:
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.