包含堆栈视图(and/or 内容视图)的滚动视图无法正常工作
Scroll view that contains a stack view (and/or a content view) not working expectedly
我想要一个滚动视图,在堆栈视图中垂直显示我的所有内容。
- 首先,我在包含堆栈视图的滚动视图中创建了一个名为
contentView
的视图,这意味着我现在有 view
-> scrollView
-> contentView
-> stackView
.
- 我将内容视图的前导、尾随、顶部和底部锚点设置为与滚动视图内容布局指南的约束相同。
- 我使内容视图的宽度与滚动视图框架布局指南的宽度相同。
- 我使堆栈视图的前导、尾随、顶部和底部锚点等于内容视图的相应锚点。
这不会滚动。
我试着按照这个 SO answer:
- 我摆脱了
Content Layout Guides
并将 contentView
的约束应用到所有 4 个边为 0,0,0,0 并将其水平和垂直居中到滚动视图。
- 在尺寸检查器中,将底部和对齐中心 Y 优先级更改为 250。
- 将堆栈视图的底部锚点设置为
view
(不是滚动视图)。
这只会滚动一点点,但不会完全滚动底部。大部分视图都隐藏在屏幕之外。
我也尝试一起摆脱 contentView
并将我的堆栈视图固定到滚动视图或直接固定到 view
,但是 none 有效。
最后,我尝试了这个超级 hacky-looking 的解决方案:
override func viewWillLayoutSubviews(){
super.viewWillLayoutSubviews()
scrollView.contentSize = CGSize(width: view.bounds.width, height: view.bounds.height+300)
}
但是,它会垂直挤压堆栈视图并且不会完全显示内容。
P.S。我正在以编程方式为堆栈视图添加约束:
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
stackView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
看来你只需要一个UIScrollView
和一个UIStackView
这样的:
UIScrollView
和 UIStackView
都为各自的父视图设置了顶部、底部、前导和尾随约束。唯一的区别是 UIStackView
宽度和 Safe Area
宽度之间的最后一个约束。 (这取决于您希望 UIStackView
的宽度)
您可能还需要将 UIStackView
的 Distribution
设置为 Equal Spacing
之类的内容,以确保始终根据其内容进行扩展。
此外,在 UIStackView
中添加一些内容,以便自动布局停止抱怨。 (避免界面生成器上的红色约束)
试一试。
这是一个可行的示例 - 看看您是否能弄清楚您可能做了哪些不同的事情。
我们不使用“内容视图”,而是直接通过代码将堆栈视图添加到滚动视图。
这是故事板布局:
这是 Storyboard 的来源,因此您可以直接检查它:
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="dVO-AO-rAX">
<device id="retina3_5" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Kevvv View Controller-->
<scene sceneID="e7x-2X-Pdg">
<objects>
<viewController id="dVO-AO-rAX" customClass="KevvvViewController" customModule="MiniScratch" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="ZMq-2S-yNo">
<rect key="frame" x="0.0" y="0.0" width="320" height="480"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bEj-BB-5lU">
<rect key="frame" x="0.0" y="44" width="320" height="402"/>
<viewLayoutGuide key="contentLayoutGuide" id="VmC-Gj-CCr"/>
<viewLayoutGuide key="frameLayoutGuide" id="HBJ-Ua-m26"/>
</scrollView>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints>
<constraint firstItem="bEj-BB-5lU" firstAttribute="leading" secondItem="goZ-oS-cQl" secondAttribute="leading" id="Jwq-Tg-wRK"/>
<constraint firstItem="goZ-oS-cQl" firstAttribute="bottom" secondItem="bEj-BB-5lU" secondAttribute="bottom" constant="34" id="bHJ-DL-1xi"/>
<constraint firstItem="bEj-BB-5lU" firstAttribute="trailing" secondItem="goZ-oS-cQl" secondAttribute="trailing" id="gIL-OY-ENf"/>
<constraint firstItem="bEj-BB-5lU" firstAttribute="top" secondItem="goZ-oS-cQl" secondAttribute="top" constant="44" id="zAh-qk-82E"/>
</constraints>
<viewLayoutGuide key="safeArea" id="goZ-oS-cQl"/>
</view>
<connections>
<outlet property="scrollView" destination="bEj-BB-5lU" id="jYI-Wh-d6w"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="ieG-NN-t0K" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="136.875" y="105"/>
</scene>
</scenes>
</document>
下面是将堆栈视图添加到滚动视图的示例代码,将 40 个标签添加到堆栈视图,然后将堆栈视图适当地约束到滚动视图:
class KevvvViewController: UIViewController {
@IBOutlet var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
let stack = UIStackView()
stack.axis = .vertical
stack.spacing = 12
stack.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(stack)
for i in 1...40 {
let v = UILabel()
v.backgroundColor = .yellow
v.text = "Label \(i)"
stack.addArrangedSubview(v)
}
NSLayoutConstraint.activate([
stack.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor),
stack.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor),
stack.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor),
stack.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor),
stack.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor),
])
// to make it easy to see the scroll view frame
scrollView.backgroundColor = .cyan
}
}
结果,向下滚动到第 17 个标签 (iPhone 8) 后:
我想要一个滚动视图,在堆栈视图中垂直显示我的所有内容。
- 首先,我在包含堆栈视图的滚动视图中创建了一个名为
contentView
的视图,这意味着我现在有view
->scrollView
->contentView
->stackView
. - 我将内容视图的前导、尾随、顶部和底部锚点设置为与滚动视图内容布局指南的约束相同。
- 我使内容视图的宽度与滚动视图框架布局指南的宽度相同。
- 我使堆栈视图的前导、尾随、顶部和底部锚点等于内容视图的相应锚点。
这不会滚动。
我试着按照这个 SO answer:
- 我摆脱了
Content Layout Guides
并将contentView
的约束应用到所有 4 个边为 0,0,0,0 并将其水平和垂直居中到滚动视图。 - 在尺寸检查器中,将底部和对齐中心 Y 优先级更改为 250。
- 将堆栈视图的底部锚点设置为
view
(不是滚动视图)。
这只会滚动一点点,但不会完全滚动底部。大部分视图都隐藏在屏幕之外。
我也尝试一起摆脱 contentView
并将我的堆栈视图固定到滚动视图或直接固定到 view
,但是 none 有效。
最后,我尝试了这个超级 hacky-looking 的解决方案:
override func viewWillLayoutSubviews(){
super.viewWillLayoutSubviews()
scrollView.contentSize = CGSize(width: view.bounds.width, height: view.bounds.height+300)
}
但是,它会垂直挤压堆栈视图并且不会完全显示内容。
P.S。我正在以编程方式为堆栈视图添加约束:
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
stackView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true
看来你只需要一个UIScrollView
和一个UIStackView
这样的:
UIScrollView
和 UIStackView
都为各自的父视图设置了顶部、底部、前导和尾随约束。唯一的区别是 UIStackView
宽度和 Safe Area
宽度之间的最后一个约束。 (这取决于您希望 UIStackView
的宽度)
您可能还需要将 UIStackView
的 Distribution
设置为 Equal Spacing
之类的内容,以确保始终根据其内容进行扩展。
此外,在 UIStackView
中添加一些内容,以便自动布局停止抱怨。 (避免界面生成器上的红色约束)
试一试。
这是一个可行的示例 - 看看您是否能弄清楚您可能做了哪些不同的事情。
我们不使用“内容视图”,而是直接通过代码将堆栈视图添加到滚动视图。
这是故事板布局:
这是 Storyboard 的来源,因此您可以直接检查它:
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="dVO-AO-rAX">
<device id="retina3_5" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Kevvv View Controller-->
<scene sceneID="e7x-2X-Pdg">
<objects>
<viewController id="dVO-AO-rAX" customClass="KevvvViewController" customModule="MiniScratch" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="ZMq-2S-yNo">
<rect key="frame" x="0.0" y="0.0" width="320" height="480"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bEj-BB-5lU">
<rect key="frame" x="0.0" y="44" width="320" height="402"/>
<viewLayoutGuide key="contentLayoutGuide" id="VmC-Gj-CCr"/>
<viewLayoutGuide key="frameLayoutGuide" id="HBJ-Ua-m26"/>
</scrollView>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints>
<constraint firstItem="bEj-BB-5lU" firstAttribute="leading" secondItem="goZ-oS-cQl" secondAttribute="leading" id="Jwq-Tg-wRK"/>
<constraint firstItem="goZ-oS-cQl" firstAttribute="bottom" secondItem="bEj-BB-5lU" secondAttribute="bottom" constant="34" id="bHJ-DL-1xi"/>
<constraint firstItem="bEj-BB-5lU" firstAttribute="trailing" secondItem="goZ-oS-cQl" secondAttribute="trailing" id="gIL-OY-ENf"/>
<constraint firstItem="bEj-BB-5lU" firstAttribute="top" secondItem="goZ-oS-cQl" secondAttribute="top" constant="44" id="zAh-qk-82E"/>
</constraints>
<viewLayoutGuide key="safeArea" id="goZ-oS-cQl"/>
</view>
<connections>
<outlet property="scrollView" destination="bEj-BB-5lU" id="jYI-Wh-d6w"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="ieG-NN-t0K" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="136.875" y="105"/>
</scene>
</scenes>
</document>
下面是将堆栈视图添加到滚动视图的示例代码,将 40 个标签添加到堆栈视图,然后将堆栈视图适当地约束到滚动视图:
class KevvvViewController: UIViewController {
@IBOutlet var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
let stack = UIStackView()
stack.axis = .vertical
stack.spacing = 12
stack.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(stack)
for i in 1...40 {
let v = UILabel()
v.backgroundColor = .yellow
v.text = "Label \(i)"
stack.addArrangedSubview(v)
}
NSLayoutConstraint.activate([
stack.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor),
stack.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor),
stack.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor),
stack.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor),
stack.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor),
])
// to make it easy to see the scroll view frame
scrollView.backgroundColor = .cyan
}
}
结果,向下滚动到第 17 个标签 (iPhone 8) 后: