UIStackView的preservesSuperviewLayoutMargins属性失效问题排查
问题分析与解决方案
你遇到的问题主要出在约束设置目标错误和对preservesSuperviewLayoutMargins作用逻辑的误解上,具体有这几个核心问题:
preservesSuperviewLayoutMargins的作用是让当前视图遵守父视图的布局边距,但你的stackView是scrollView的子视图,它的父视图是scrollView而非控制器的view,所以给view设置的layoutMargins对stackView完全无效。- 你把
stackView的左右约束直接绑定到了view的leftAnchor/rightAnchor,这直接跳过了布局边距的逻辑,自然不会产生边距效果。 scrollView的contentLayoutGuide约束设置有误,没有正确限制stackView的宽度,可能导致滚动视图的内容宽度异常。
修正后的代码
import UIKit class ViewController: UIViewController { let stackView: UIStackView = { let sv = UIStackView() sv.axis = .vertical sv.preservesSuperviewLayoutMargins = true // 现在会遵守scrollView的布局边距 sv.translatesAutoresizingMaskIntoConstraints = false return sv }() let scrollView: UIScrollView = { let sv = UIScrollView() sv.translatesAutoresizingMaskIntoConstraints = false sv.alwaysBounceVertical = true return sv }() override func viewDidLoad() { // 给scrollView设置布局边距,因为stackView的父视图是scrollView scrollView.layoutMargins = UIEdgeInsets(top: 0, left: 64, bottom: 0, right: 64) view.addSubview(scrollView) scrollView.addSubview(stackView) NSLayoutConstraint.activate([ // 把stackView的左右约束绑定到scrollView的layoutMarginsGuide,而非直接的anchor stackView.leftAnchor.constraint(equalTo: scrollView.layoutMarginsGuide.leftAnchor), stackView.rightAnchor.constraint(equalTo: scrollView.layoutMarginsGuide.rightAnchor), // 限制stackView的宽度等于scrollView的可视宽度,避免横向滚动 stackView.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor, constant: -128) // 64*2的边距总和 ]) NSLayoutConstraint.activate([ // 正确关联scrollView的内容布局指南与stackView scrollView.contentLayoutGuide.topAnchor.constraint(equalTo: stackView.topAnchor), scrollView.contentLayoutGuide.bottomAnchor.constraint(equalTo: stackView.bottomAnchor), scrollView.contentLayoutGuide.leftAnchor.constraint(equalTo: stackView.leftAnchor), scrollView.contentLayoutGuide.rightAnchor.constraint(equalTo: stackView.rightAnchor), ]) NSLayoutConstraint.activate([ // scrollView的约束绑定到view的边缘 scrollView.topAnchor.constraint(equalTo: view.topAnchor), scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor), scrollView.leftAnchor.constraint(equalTo: view.leftAnchor), scrollView.rightAnchor.constraint(equalTo: view.rightAnchor), ]) let l = UILabel() l.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Itaque his sapiens semper vacabit. An est aliquid, quod te sua sponte delectet? Aliud igitur esse censet gaudere, aliud non dolere. Quasi ego id curem, quid ille aiat aut neget. Cur post Tarentum ad Archytam? Duo Reges: constructio interrete. Atque etiam valítudinem, vires, vacuitatem doloris non propter utilitatem solum, sed etiam ipsas propter se expetemus. Naturales divitias dixit parabiles esse, quod parvo esset natura contenta." l.numberOfLines = 0 l.translatesAutoresizingMaskIntoConstraints = false stackView.addArrangedSubview(l) super.viewDidLoad() } }
关键修正点说明
- 调整布局边距的设置对象:把
layoutMargins设置给scrollView,因为stackView是它的子视图,preservesSuperviewLayoutMargins才会生效。 - 使用
layoutMarginsGuide绑定约束:stackView的左右约束要绑定到scrollView.layoutMarginsGuide的对应锚点,这样才会应用父视图的布局边距。 - 限制
stackView的宽度:通过stackView.widthAnchor和scrollView.frameLayoutGuide.widthAnchor的约束,确保stackView的宽度不会超过滚动视图的可视宽度,避免出现横向滚动,同时匹配边距的宽度。
另外,其实preservesSuperviewLayoutMargins在这里可以不用设置,因为我们已经直接通过layoutMarginsGuide绑定了约束,效果是一样的,但保留它也不会有问题。
内容的提问来源于stack exchange,提问作者Kristof Van Landschoot
相关产品推荐
相关产品推荐

