Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
838 views
in Technique[技术] by (71.8m points)

ios - Wrap items in a horizontal UIStackView on multiple lines

I am working on an iOS app which display a UITableView of items. Each row will show a list of tags with different widths(given by the tag's text length). I can place all the tags in a horizontal UIStackView but I want them to wrap on multiple lines instead of o a single scrollable one. Basically I'm interested in a functionality similar to FlexBox's flex-wrap property.

I've attached an image for reference.

enter image description here

Any ideas how to achieve this?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

There are a number of different ways to approach this.

One approach - not using stack views:

  • add your labels to a "container" view
  • start with x = 0 and y = 0
  • loop through the labels, calculating a new x value (label width + desired spacing between labels)
  • if the new x would be past the edge of the container, reset x = 0 and add desired height to y to "move to the next row"
  • after labels have been laid out, set the height of the container view

Here is a simple example:

class TagLabelsViewController: UIViewController {

    let containerView: UIView = {
        let v = UIView()
        return v
    }()

    let tagNames: [String] = [
        "First Tag",
        "Second",
        "Third Tag",
        "Fourth",
        "The Fifth Tag",
        "Sixth",
        "Seventh",
        "Tag Eight",
        "Here are some Letter Tags",
        "A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
        "Nine",
        "Ten",
        "Eleven",
        "Tag Twelve",
        "Tag 13",
        "Fourteen",
        "Fifteen",
        "Sixteen",
        "Seventeen",
        "Eightteen",
        "Nineteen",
        "Last Tag",
    ]

    var tagLabels = [UILabel]()

    let tagHeight:CGFloat = 30
    let tagPadding: CGFloat = 16
    let tagSpacingX: CGFloat = 8
    let tagSpacingY: CGFloat = 8

    // container view height will be modified when laying out subviews
    var containerHeightConstraint: NSLayoutConstraint = NSLayoutConstraint()

    override func viewDidLoad() {
        super.viewDidLoad()

        // add the container view
        view.addSubview(containerView)

        // give it a background color so we can see it
        containerView.backgroundColor = .yellow

        // use autolayout
        containerView.translatesAutoresizingMaskIntoConstraints = false

        // initialize height constraint - actual height will be set later
        containerHeightConstraint = containerView.heightAnchor.constraint(equalToConstant: 10.0)

        // constrain container safe-area top / leading / trailing to view with 20-pts padding
        let g = view.safeAreaLayoutGuide

        NSLayoutConstraint.activate([
            containerView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            containerView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            containerView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            containerHeightConstraint,
        ])

        // add the buttons to the scroll view
        addTagLabels()

    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        // call this here, after views have been laid-out
        // this will also be called when the size changes, such as device rotation,
        // so the buttons will "re-layout"
        displayTagLabels()

    }

    func addTagLabels() -> Void {

        for j in 0..<self.tagNames.count {

            // create a new label
            let newLabel = UILabel()

            // set its properties (title, colors, corners, etc)
            newLabel.text = tagNames[j]
            newLabel.textAlignment = .center
            newLabel.backgroundColor = UIColor.cyan
            newLabel.layer.masksToBounds = true
            newLabel.layer.cornerRadius = 8
            newLabel.layer.borderColor = UIColor.red.cgColor
            newLabel.layer.borderWidth = 1

            // set its frame width and height
            newLabel.frame.size.width = newLabel.intrinsicContentSize.width + tagPadding
            newLabel.frame.size.height = tagHeight

            // add it to the scroll view
            containerView.addSubview(newLabel)

            // append it to tagLabels array
            tagLabels.append(newLabel)

        }

    }

    func displayTagLabels() {

        let containerWidth = containerView.frame.size.width

        var currentOriginX: CGFloat = 0
        var currentOriginY: CGFloat = 0

        // for each label in the array
        tagLabels.forEach { label in

            // if current X + label width will be greater than container view width
            //  "move to next row"
            if currentOriginX + label.frame.width > containerWidth {
                currentOriginX = 0
                currentOriginY += tagHeight + tagSpacingY
            }

            // set the btn frame origin
            label.frame.origin.x = currentOriginX
            label.frame.origin.y = currentOriginY

            // increment current X by btn width + spacing
            currentOriginX += label.frame.width + tagSpacingX

        }

        // update container view height
        containerHeightConstraint.constant = currentOriginY + tagHeight

    }

}

The results:

enter image description here

enter image description here

It's pretty straight-forward, and with the comments in the code you should be able to adapt it to your needs.

If you want a "pre-built" solution, perhaps with more features, searching for

swift left aligned tags view

comes up with lots of matches. This one (I have nothing to do with it) looks interesting: https://github.com/ElaWorkshop/TagListView


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...