ios - 在多行的水平 UIStackView 中包装项目

标签 ios autolayout uistackview

我正在开发一个显示项目的 UITableView 的 iOS 应用程序。
每行将显示具有不同宽度的标签列表(由​​标签的文本长度给出)。
我可以将所有标签放在一个水平的 UIStackView 中,但我希望它们包装在多行而不是单个可滚动的行上。基本上我对类似于 FlexBox 的 flex-wrap 属性的功能感兴趣。

我附上了一张图片供引用。

enter image description here

任何想法如何实现这一目标?

最佳答案

有许多不同的方法来解决这个问题。

一种方法 - 而不是 使用堆栈 View :

  • 将您的标签添加到“容器” View
  • x = 0y = 0
  • 开头
  • 循环遍历标签,计算新的 x 值(标签宽度 + 标签之间所需的间距)
  • 如果新的 x 将超过容器的边缘,则重置 x = 0 并将所需高度添加到 y 以“移动到下一行”
  • 标签布置好后,设置容器 View 的高度

  • 这是一个简单的例子:
    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
    
        }
    
    }
    

    结果:

    enter image description here

    enter image description here

    它非常简单,通过代码中的注释,您应该能够根据自己的需要进行调整。

    如果您想要一个“预先构建”的解决方案,也许具有更多功能,请搜索
    swift left aligned tags view
    

    想出了很多比赛。这个(我与它无关)看起来很有趣:https://github.com/ElaWorkshop/TagListView

    关于ios - 在多行的水平 UIStackView 中包装项目,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60582129/

    相关文章:

    ios - 以编程方式获取嵌入 UIStackView 中的自定义 UIView

    ios - 在 ScrollView 中使用堆栈 View 并尊重安全区域插图

    ios - 无法验证 iOS 应用程序(已获得有效证书)

    iOS 架构 i386 的 undefined symbol

    ios - UITableView 在旋转时自动布局调整大小

    iOS自动布局,如何让View包裹内容?

    iphone - 使用 NSUserDefaults 保存 NSMutableArray

    ios - 用户使用滑动手势编辑时如何离开 UITableViewCell EditingStyle 删除模式

    ios - 更新 View 的框架保持约束

    ios - 如何以编程方式使 UIStackView 与 UIScrollView 一起滚动?