堆栈视图内的布局约束(以编程方式快速)

Posted

技术标签:

【中文标题】堆栈视图内的布局约束(以编程方式快速)【英文标题】:Layout constraints inside a stackview (swift programmatically) 【发布时间】:2020-03-02 20:05:21 【问题描述】:

我正在尝试为我的应用创建一个关于页面,但我正在努力解决我以编程方式添加的限制。我还在学习整个概念。

这是我的代码,我复制到游乐场

import UIKit
import PlaygroundSupport

class TestViewController: UIViewController 

    var aboutText:[String] = []
    var fbLinks:[String] = []

    let scrollView = UIScrollView()
    let stackView = UIStackView()

    override func viewDidLoad() 
        super.viewDidLoad()

        //Add and setup scroll view
        self.view.addSubview(self.scrollView)
        self.scrollView.translatesAutoresizingMaskIntoConstraints = false;


        //Constrain scroll view
        self.scrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 5).isActive = true;
        self.scrollView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 30).isActive = true;
        self.scrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 5).isActive = true;
        self.scrollView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0).isActive = true;

        self.scrollView.addSubview(self.stackView)
        self.stackView.translatesAutoresizingMaskIntoConstraints = false
        self.stackView.axis = .vertical
        self.stackView.alignment = UIStackView.Alignment.leading
        self.stackView.spacing = 10;

        //constrain stack view to scroll view
        self.stackView.leadingAnchor.constraint(equalTo: self.scrollView.leadingAnchor).isActive = true;
        self.stackView.topAnchor.constraint(equalTo: self.scrollView.topAnchor).isActive = true;
        self.stackView.trailingAnchor.constraint(equalTo: self.scrollView.trailingAnchor).isActive = true;
        self.stackView.bottomAnchor.constraint(equalTo: self.scrollView.bottomAnchor).isActive = true;

        //constrain width of stack view to width of self.view, NOT scroll view
        self.stackView.widthAnchor.constraint(equalTo: self.view.widthAnchor).isActive = true;

        //Text Label
        aboutText.append("Lorem ipsum dolor sit amet, consectetur adipiscing elit.")
        aboutText.append("Maecenas sed pulvinar est. Integer mattis mollis eleifend. Integer suscipit arcu sit amet erat rhoncus malesuada. Nam feugiat augue id leo maximus dignissim id sed libero. Proin dapibus metus vel nisl ultrices, quis laoreet metus malesuada. Lorem ipsum dolor sit amet, consectetur adipiscing elit. ")
        aboutText.append(" penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed sit amet dui consectetur, vulputate felis sed, volutpat dui. Quisque eu ex eu nulla facilisis aliquet. Vestibulum vitae lacus non sapien posuere commodo et eget arcu. Sed quis eros condimentum, pharetra ligula non, gravida ex. Cras luctus com")
        aboutText.append(" Praesent luctus nulla eget condimentum volutpat. Nunc metus odio, commodo sit amet placerat non, cursus posuere sem. Mauris lorem felis, elementum vel purus")


        fbLinks.append("Some text")
        fbLinks.append("Some other text but longer")
        fbLinks.append("Some other text but way longer then the previous was")
        fbLinks.append("text again what a surprise")
        fbLinks.append("guess what this is a text too")



        let image = UIImage(systemName: "house")
        let imageView = UIImageView(image: image!)
        imageView.heightAnchor.constraint(equalToConstant: 60).isActive = true
        imageView.widthAnchor.constraint(equalToConstant: 60).isActive = true
        stackView.addArrangedSubview(imageView)

        for text in aboutText
        
            stackView.addArrangedSubview(generateText(text:text))
        



        stackView.addArrangedSubview(generateStackedItem(imageName:"bell",text: "contact_us"))
        stackView.addArrangedSubview(generateStackedItem(imageName:"bolt",text: "rate_us_ios"))

        for link in fbLinks
        
            let sw:UIStackView = generateStackedItem(imageName:"bolt",text: link)
            stackView.addArrangedSubview(sw)
            sw.leadingAnchor.constraint(equalTo: self.stackView.leadingAnchor).isActive = true;
            sw.trailingAnchor.constraint(equalTo: self.stackView.trailingAnchor).isActive = true;
        



    

    func generateText(text:String)->UILabel
    
        let textLabel = UILabel()
        textLabel.widthAnchor.constraint(equalToConstant: self.view.frame.width).isActive = true
        textLabel.text  = NSLocalizedString(text, comment: "")
        textLabel.textAlignment = .left
        textLabel.numberOfLines = 0
        textLabel.lineBreakMode = .byWordWrapping


        return textLabel
    

    func generateStackedItem(imageName:String,text:String)->UIStackView
    
        let stackView   = UIStackView()
        stackView.axis  = NSLayoutConstraint.Axis.horizontal
        stackView.distribution  = .fill
        stackView.alignment = UIStackView.Alignment.leading
        stackView.spacing   = 5.0

        let image = UIImage(systemName: imageName)
        let imageView = UIImageView(image: image!)
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.heightAnchor.constraint(equalToConstant: 20).isActive = true
        imageView.widthAnchor.constraint(equalToConstant: 20).isActive = true

        let label = generateText(text: text)

        stackView.addArrangedSubview(imageView)
        stackView.addArrangedSubview(label)

        stackView.translatesAutoresizingMaskIntoConstraints = false

        //stackView.heightAnchor.constraint(equalToConstant: label.frame.height).isActive = true

        stackView.heightAnchor.constraint(equalToConstant: 50).isActive = true



        return stackView
    





let vc = TestViewController()
vc.view.backgroundColor = .white
PlaygroundPage.current.liveView = vc


这就是页面的样子,我标记了 3 件事

    我想将大房子图标放在中间,但我 不知道怎么做,因为我的 stackview 的对齐是领先的。

    我使链接之间的间距变大,以便可以看到较长的文本,所以我的问题是如何将内部堆栈视图的大小设置为取决于包含文本的高度。

    如何让闪电图标和堆栈视图中的文本垂直居中?

    我想在 stackview 中的链接(带有闪电图标的链接)中添加点击手势,如果可能的话,我想用闭包来实现。如果关闭不是为此,您能否帮助我或建议其他解决方案。我这样尝试过,但不知何故对我不起作用link

提前谢谢你。

【问题讨论】:

【参考方案1】:

仅供参考,您应该尝试一次只回答一个问题 - 或者至少只回答相关个问题(换句话说,#4 应该是一个不同的问题) .

我将解决 1 到 3。

首先,您的代码中有一条注释:

//constrain width of stack view to width of self.view, NOT scroll view

这是错误的。您的堆栈视图宽度限制为滚动视图宽度。否则,如果您的滚动视图没有在屏幕上一直伸展,它将水平滚动。

在您的情况下,您(似乎)将滚动视图插入前导和尾随 5 分:

self.scrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 5).isActive = true;
self.scrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 5).isActive = true;

但是,您的 trailingAnchor 常量应该是 -5

一旦改正...

1) 将您的“主”stackView 的.alignment 也更改为.fill 而不是.leading,并确保您的图像视图具有.contentMode = .scaleAspectFit。这将使“房子”水平居中。

2) 您可以限制“内部堆栈视图”相对于标签高度的高度。只需确保添加该约束将标签添加为排列的子视图

3)要使图标垂直居中 -> 标签,请将.alignment = .center 设置为水平堆栈视图。

这是您发布的代码的略微修改版本:

class TestViewController: UIViewController 

    var aboutText:[String] = []
    var fbLinks:[String] = []

    let scrollView = UIScrollView()
    let stackView = UIStackView()

    override func viewDidLoad() 
        super.viewDidLoad()

        //Add and setup scroll view
        self.view.addSubview(self.scrollView)
        self.scrollView.translatesAutoresizingMaskIntoConstraints = false;

        //Constrain scroll view
        self.scrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 5).isActive = true;
        self.scrollView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 30).isActive = true;
        // trailing constant should be negative
        self.scrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -5).isActive = true;
        self.scrollView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0).isActive = true;

        self.scrollView.addSubview(self.stackView)
        self.stackView.translatesAutoresizingMaskIntoConstraints = false
        self.stackView.axis = .vertical
        // change to .fill
        self.stackView.alignment = UIStackView.Alignment.leading
        self.stackView.alignment = .fill
        self.stackView.spacing = 10;

        //constrain stack view to scroll view
        self.stackView.leadingAnchor.constraint(equalTo: self.scrollView.leadingAnchor).isActive = true;
        self.stackView.topAnchor.constraint(equalTo: self.scrollView.topAnchor).isActive = true;
        self.stackView.trailingAnchor.constraint(equalTo: self.scrollView.trailingAnchor).isActive = true;
        self.stackView.bottomAnchor.constraint(equalTo: self.scrollView.bottomAnchor).isActive = true;

        //constrain width of stack view to width of self.view, NOT scroll view
        //self.stackView.widthAnchor.constraint(equalTo: self.view.widthAnchor).isActive = true;
        // you SHOULD constrain the stackView width to the width of the scrollView (assuming you do not want horizontal scrolling)
        self.stackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true;

        //Text Label
        aboutText.append("Lorem ipsum dolor sit amet, consectetur adipiscing elit.")
        aboutText.append("Maecenas sed pulvinar est. Integer mattis mollis eleifend. Integer suscipit arcu sit amet erat rhoncus malesuada. Nam feugiat augue id leo maximus dignissim id sed libero. Proin dapibus metus vel nisl ultrices, quis laoreet metus malesuada. Lorem ipsum dolor sit amet, consectetur adipiscing elit. ")
        aboutText.append(" penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed sit amet dui consectetur, vulputate felis sed, volutpat dui. Quisque eu ex eu nulla facilisis aliquet. Vestibulum vitae lacus non sapien posuere commodo et eget arcu. Sed quis eros condimentum, pharetra ligula non, gravida ex. Cras luctus com")
        aboutText.append(" Praesent luctus nulla eget condimentum volutpat. Nunc metus odio, commodo sit amet placerat non, cursus posuere sem. Mauris lorem felis, elementum vel purus")


        fbLinks.append("Some text")
        fbLinks.append("Some other text but longer")
        fbLinks.append("Some other text but way longer then the previous was")
        fbLinks.append("text again what a surprise")
        fbLinks.append("guess what this is a text too")



        let image = UIImage(systemName: "house")
        let imageView = UIImageView(image: image!)
        imageView.contentMode = .scaleAspectFit

        imageView.heightAnchor.constraint(equalToConstant: 60).isActive = true

        stackView.addArrangedSubview(imageView)

        for text in aboutText
        
            stackView.addArrangedSubview(generateText(text:text))
        

        stackView.addArrangedSubview(generateStackedItem(imageName:"bell",text: "contact_us"))
        stackView.addArrangedSubview(generateStackedItem(imageName:"bolt",text: "rate_us_ios"))

        for link in fbLinks
        
            let sw:UIStackView = generateStackedItem(imageName:"bolt",text: link)
            stackView.addArrangedSubview(sw)
            // next two lines are not needed
            //sw.leadingAnchor.constraint(equalTo: self.stackView.leadingAnchor).isActive = true;
            //sw.trailingAnchor.constraint(equalTo: self.stackView.trailingAnchor).isActive = true;
        

    

    func generateText(text:String)->UILabel
    
        let textLabel = UILabel()
        // no need to set widthAnchor
        //textLabel.widthAnchor.constraint(equalToConstant: self.view.frame.width).isActive = true
        textLabel.text  = NSLocalizedString(text, comment: "")
        textLabel.textAlignment = .left
        textLabel.numberOfLines = 0
        textLabel.lineBreakMode = .byWordWrapping

        return textLabel
    

    func generateStackedItem(imageName:String,text:String)->UIStackView
    
        let stackView   = UIStackView()
        stackView.axis  = NSLayoutConstraint.Axis.horizontal
        stackView.distribution  = .fill
        // horizontal stack view alignment defines the Vertical alignment
        //stackView.alignment = UIStackView.Alignment.leading
        // so set it to .center to get vertical centering
        stackView.alignment = .center
        stackView.spacing   = 5.0

        let image = UIImage(systemName: imageName)
        let imageView = UIImageView(image: image!)
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.heightAnchor.constraint(equalToConstant: 20).isActive = true
        imageView.widthAnchor.constraint(equalToConstant: 20).isActive = true

        let label = generateText(text: text)

        stackView.addArrangedSubview(imageView)
        stackView.addArrangedSubview(label)

        stackView.translatesAutoresizingMaskIntoConstraints = false

        // constrain stackView height to label height + constant
        //stackView.heightAnchor.constraint(equalToConstant: 50).isActive = true
        stackView.heightAnchor.constraint(equalTo: label.heightAnchor, constant: 16).isActive = true

        return stackView
    


结果:

【讨论】:

谢谢@DonMag!您能否推荐一个网站或书籍或其他我可以正确学习此约束内容的东西? @Mr.J - 我总是建议从 Apple 开始:Auto Layout Guide: Understanding Auto Layout - 然后只需搜索(谷歌或任何地方)ios auto layout tutorial。很多链接......通过几十个教程。到那时你应该有一个坚实的基础,应该能够解决任何出现的问题。

以上是关于堆栈视图内的布局约束(以编程方式快速)的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式将视图添加到垂直堆栈视图打破了垂直堆栈视图的约束

使用堆栈视图和自动布局创建自定义 UITableViewCell

容器视图内的 UIPageViewController 内的堆栈视图的自动布局?

Swift - StackView 和添加约束,以编程方式添加视图到堆栈 - 加载后调整单元格高度?

使用不使用硬编码的 CGRect 和最小数量的约束以编程方式将堆栈视图与屏幕顶部对齐

以编程方式使用堆栈视图