在多行中将项目包装在水平UIStackView中 [英] Wrap items in a horizontal UIStackView on multiple lines
问题描述
我正在使用显示UITableView
项的iOS应用.
每行将显示具有不同宽度的标签列表(由标签的文本长度决定).
我可以将所有标签放在水平的UIStackView
中,但我希望它们绕多行而不是一个可滚动的行.基本上,我对类似于FlexBox的flex-wrap
属性的功能感兴趣.
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.
有什么想法可以实现这一目标吗?
Any ideas how to achieve this?
推荐答案
有多种方法可以解决此问题.
There are a number of different ways to approach this.
一种方法-不使用堆栈视图:
One approach - not using stack views:
- 将标签添加到容器"视图
- 以
x = 0
和y = 0
开头
- 循环遍历标签,计算出新的
x
值(标签宽度+标签之间所需的间距) - 如果新的
x
将超出容器的边缘,请重置x = 0
并在y
上添加所需的高度以移至下一行" - 放置标签后,设置容器视图的高度
- add your labels to a "container" view
- start with
x = 0
andy = 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, resetx = 0
and add desired height toy
to "move to the next row" - after labels have been laid out, set the height of the container 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
}
}
结果:
这非常简单,并且代码中的注释使您能够适应您的需求.
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
出现很多匹配项.这个(我与此无关)看起来很有趣: https://github.com/ElaWorkshop/TagListView
comes up with lots of matches. This one (I have nothing to do with it) looks interesting: https://github.com/ElaWorkshop/TagListView
这篇关于在多行中将项目包装在水平UIStackView中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!