简体   繁体   中英

Layout constraints inside a stackview (swift programmatically)

I'm trying to create an about page for my app but I'm struggling with constraints which I've added programmatically. I'm still learning the whole concept.

Here is my code, I copied over to playground

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


This is how the page looks like I marked 3 things

图片1 图片2

  1. I would like to position the large house icon in the center but I'm not sure how because my stackview's alignment is leading.

  2. I make the spacing between the links large so my longer text can be visible, so my question here is how to set the size of the inner stackview to depend on the height of the containing text.

  3. How can I vertically center the lightning icon and the text inside the stackview?

  4. I would like to add tap gesture to my links inside the stackview (the ones with the lightning icon) and I would like to do it with closures if it possible. Could you please help me with that or suggest other solution if closures are not for this. I tried it like this but that didn't work for me somehow link

Thank you in advance.

FYI, you should try to stick to one question at a time - or at least only related questions (in other words, #4 should be a different question).

I'll address 1 through 3.

First, you have a comment in you code:

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

Which is wrong. Your stack view width should be constrained to the scroll view width. Otherwise, if your scroll view does not stretch all the way across the screen, it will scroll horizontally.

In your case, you (appear to) inset your scroll view by 5-pts leading and trailing:

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

However, your trailingAnchor constant should be -5 .

Once that is corrected...

1) Change your "main" stackView's .alignment too .fill instead of .leading , and make sure your image view has .contentMode = .scaleAspectFit . That will center the "house" horizontally.

2) You can constrain the height of the "inner stack views" relative to the height of the label. Just make sure you add that constraint after adding the label as an arrangedSubview

3) To vertically center the icon -> label, set .alignment = .center for the horizontal stack views.

Here is slightly modified version of the code you posted:

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
    }

}

Result:

在此处输入图片说明

在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM