UITableViewCell 内的多个堆栈视图内的多行标签
Posted
技术标签:
【中文标题】UITableViewCell 内的多个堆栈视图内的多行标签【英文标题】:Multilinelabel inside multiple stackviews inside UITableViewCell 【发布时间】:2019-10-21 07:54:18 【问题描述】:我有如下视图层次结构;
UITableViewCell ->
-> UIView -> UIStackView (axis: vertical, distribution: fill)
-> UIStackView (axis: horizontal, alignment: top, distribution: fillEqually)
-> UIView -> UIStackView(axis:vertical, distribution: fill)
-> TwoLabelView
我的问题是标签不能超过一行。我阅读了 SO 中的每一个问题,也尝试了每一种可能性,但都没有奏效。在下面的屏幕截图中,在左上角的框中,应该有两对标签,但其中一个没有显示。
我的问题是如何在第一个框中实现多行(左右两边)?
如果我将顶部堆栈视图分布更改为 fillProportionally
,标签会变为多行,但第一个框的最后一个元素与框本身之间会有间隙
我的第一个顶部堆栈视图
//This is the Stackview used just below UITableViewCell
private let stackView: UIStackView =
let s = UIStackView()
s.distribution = .fill
s.axis = .vertical
s.spacing = 10
s.translatesAutoresizingMaskIntoConstraints = false
return s
()
//This is used to create two horizontal box next to each other
private let myStackView: UIStackView =
let s = UIStackView()
s.distribution = .fillEqually
s.spacing = 10
s.axis = .horizontal
//s.alignment = .center
s.translatesAutoresizingMaskIntoConstraints = false
return s
()
UILabel 类:
fileprivate class FixAutoLabel: UILabel
override func layoutSubviews()
super.layoutSubviews()
if(self.preferredMaxLayoutWidth != self.bounds.size.width)
self.preferredMaxLayoutWidth = self.bounds.size.width
@IBDesignable class TwoLabelView: UIView
var topMargin: CGFloat = 0.0
var verticalSpacing: CGFloat = 3.0
var bottomMargin: CGFloat = 0.0
@IBInspectable var firstLabelText: String = "" didSet updateView()
@IBInspectable var secondLabelText: String = "" didSet updateView()
fileprivate var firstLabel: FixAutoLabel!
fileprivate var secondLabel: FixAutoLabel!
override init(frame: CGRect)
super.init(frame: frame)
setUpView()
required public init?(coder: NSCoder)
super.init(coder:coder)
setUpView()
override func prepareForInterfaceBuilder()
super.prepareForInterfaceBuilder()
setUpView()
func setUpView()
firstLabel = FixAutoLabel()
firstLabel.font = UIFont.systemFont(ofSize: 18.0, weight: UIFont.Weight.bold)
firstLabel.numberOfLines = 0
firstLabel.lineBreakMode = NSLineBreakMode.byTruncatingTail
secondLabel = FixAutoLabel()
secondLabel.font = UIFont.systemFont(ofSize: 13.0, weight: UIFont.Weight.regular)
secondLabel.numberOfLines = 1
secondLabel.lineBreakMode = NSLineBreakMode.byTruncatingTail
addSubview(firstLabel)
addSubview(secondLabel)
// we're going to set the constraints
firstLabel .translatesAutoresizingMaskIntoConstraints = false
secondLabel.translatesAutoresizingMaskIntoConstraints = false
// pin both labels' left-edges to left-edge of self
firstLabel.leftAnchor.constraint(equalTo: leftAnchor, constant: 0.0).isActive = true
secondLabel.leftAnchor.constraint(equalTo: leftAnchor, constant: 0.0).isActive = true
// pin both labels' right-edges to right-edge of self
firstLabel.rightAnchor.constraint(equalTo: rightAnchor, constant: 0.0).isActive = true
secondLabel.rightAnchor.constraint(equalTo: rightAnchor, constant: 0.0).isActive = true
// pin firstLabel to the top of self + topMargin (padding)
firstLabel.topAnchor.constraint(equalTo: topAnchor, constant: topMargin).isActive = true
// pin top of secondLabel to bottom of firstLabel + verticalSpacing
secondLabel.topAnchor.constraint(equalTo: firstLabel.bottomAnchor, constant: verticalSpacing).isActive = true
// pin bottom of self to bottom of secondLabel + bottomMargin (padding)
bottomAnchor.constraint(equalTo: secondLabel.bottomAnchor, constant: bottomMargin).isActive = true
// call common "refresh" func
updateView()
func updateView()
firstLabel.preferredMaxLayoutWidth = self.bounds.width
secondLabel.preferredMaxLayoutWidth = self.bounds.width
firstLabel.text = firstLabelText
secondLabel.text = secondLabelText
firstLabel.sizeToFit()
secondLabel.sizeToFit()
setNeedsUpdateConstraints()
override open var intrinsicContentSize : CGSize
// just has to have SOME intrinsic content size defined
// this will be overridden by the constraints
return CGSize(width: 1, height: 1)
UIView -> UIStackView 类
class ViewWithStack: UIView
let verticalStackView: UIStackView =
let s = UIStackView()
s.distribution = .fillEqually
s.spacing = 10
s.axis = .vertical
s.translatesAutoresizingMaskIntoConstraints = false
return s
()
override init(frame: CGRect)
super.init(frame: frame)
self.translatesAutoresizingMaskIntoConstraints = false
self.backgroundColor = UIColor.white
self.layer.cornerRadius = 6.0
self.layer.applySketchShadow(color: UIColor(red:0.56, green:0.56, blue:0.56, alpha:1), alpha: 0.2, x: 0, y: 0, blur: 10, spread: 0)
addSubview(verticalStackView)
let lessThan = verticalStackView.bottomAnchor.constraint(lessThanOrEqualTo: self.bottomAnchor, constant: 0)
lessThan.priority = UILayoutPriority(1000)
lessThan.isActive = true
verticalStackView.leftAnchor.constraint(equalTo: self.leftAnchor,constant: 0).isActive = true
verticalStackView.rightAnchor.constraint(equalTo: self.rightAnchor,constant: 0).isActive = true
verticalStackView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
verticalStackView.layoutMargins = UIEdgeInsets(top: 10, left: 20, bottom: 10, right: 20)
verticalStackView.isLayoutMarginsRelativeArrangement = true
convenience init(orientation: NSLayoutConstraint.Axis,labelsArray: [UIView])
self.init()
verticalStackView.axis = orientation
for label in labelsArray
verticalStackView.addArrangedSubview(label)
required init?(coder: NSCoder)
fatalError("init(coder:) has not been implemented")
示例控制器类(这是整个项目的最小化版本):
class ViewController: UIViewController, UITableViewDelegate,UITableViewDataSource
@IBOutlet weak var tableView: UITableView!
let viewWithStack = BoxView()
override func viewDidLoad()
super.viewDidLoad()
// Do any additional setup after loading the view.
tableView.delegate = self
tableView.dataSource = self
tableView.register(TableViewCell.self, forCellReuseIdentifier: "myCell")
tableView.rowHeight = UITableView.automaticDimension
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
return 2
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let cell: TableViewCell = tableView.dequeueReusableCell(withIdentifier: "myCell") as! TableViewCell
if (indexPath.row == 0)
cell.setup(viewWithStack: self.viewWithStack)
else
cell.backgroundColor = UIColor.black
return cell
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
//return 500
if ( indexPath.row == 0)
return UITableView.automaticDimension
else
return 40
编辑 我创建了一个最小的项目,然后我发现我的问题是我的项目实现了 heightForRow
函数,该函数覆盖了 UITableViewAutomaticDimension
,因此它为我的组件提供了错误的高度。我想我应该看看如何获得组件的高度大小?因为我无法删除解决我的问题的heightForRow
函数。
示例项目链接 https://github.com/emreond/tableviewWithStackView/tree/master/tableViewWithStackViewEx
当您打开视图调试器时,示例项目具有雄心勃勃的布局。我想当我修复它们时,一切都会好起来的。
【问题讨论】:
正如我在您的类似问题 (***.com/questions/58413397/…) 中提到的,仅查看代码片段时很难提供帮助。将minimal reproducible example 放在一起,以便有人可以确切地看到发生了什么。 我正在做一个非常大的项目,我什至无法重现该示例,但我会尝试。 看起来你有很多事情是不必要的。很少需要设置.preferredMaxLayoutWidth
或调用sizeToFit()
或setNeedsUpdateConstraints()
。尝试简化您正在做的事情。
我编辑了我的问题。你能检查一下吗?
为什么要实施heightForRowAt
?您可以在下面的示例中看到它是不需要的。
【参考方案1】:
这是一个完整的示例,应该可以满足您的需求(这就是我所说的 minimal reproducible example):
检查此问题的最佳方法是:
创建一个新项目 创建一个新文件,命名为TestTableViewController.swift
将以下代码复制并粘贴到该文件中(替换默认模板代码)
将UITableViewController
添加到情节提要
将其自定义类分配给TestTableViewController
将其嵌入UINavigationController
将UINavigationController
设置为Is Initial View Controller
运行应用程序
这是你应该看到的结果:
我根据您发布的内容创建课程(删除了不必要的代码,我假设您的其他单元格按需要工作)。
//
// TestTableViewController.swift
//
// Created by Don Mag on 10/21/19.
//
import UIKit
class SideBySideCell: UITableViewCell
let horizStackView: UIStackView =
let v = UIStackView()
v.axis = .horizontal
v.alignment = .fill
v.distribution = .fillEqually
v.spacing = 10
v.translatesAutoresizingMaskIntoConstraints = false
return v
()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?)
super.init(style: style, reuseIdentifier: reuseIdentifier)
commonInit()
required init?(coder: NSCoder)
super.init(coder: coder)
commonInit()
override func prepareForReuse()
horizStackView.arrangedSubviews.forEach
$0.removeFromSuperview()
func commonInit() -> Void
contentView.backgroundColor = UIColor(white: 0.8, alpha: 1.0)
contentView.addSubview(horizStackView)
let g = contentView.layoutMarginsGuide
NSLayoutConstraint.activate([
horizStackView.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
horizStackView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
horizStackView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
horizStackView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
])
func addViewWithStack(_ v: ViewWithStack) -> Void
horizStackView.addArrangedSubview(v)
class TestTableViewController: UITableViewController
let sideBySideReuseID = "sbsID"
override func viewDidLoad()
super.viewDidLoad()
// register custom SideBySide cell for reuse
tableView.register(SideBySideCell.self, forCellReuseIdentifier: sideBySideReuseID)
tableView.separatorStyle = .none
override func numberOfSections(in tableView: UITableView) -> Int
return 1
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
return 10
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
if indexPath.row == 0
let cell = tableView.dequeueReusableCell(withIdentifier: sideBySideReuseID, for: indexPath) as! SideBySideCell
let twoLabelView1 = TwoLabelView()
twoLabelView1.firstLabelText = "Text for first label on left-side."
twoLabelView1.secondLabelText = "10.765,00TL"
let twoLabelView2 = TwoLabelView()
twoLabelView2.firstLabelText = "Text for second-first label on left-side."
twoLabelView2.secondLabelText = "10.765,00TL"
let twoLabelView3 = TwoLabelView()
twoLabelView3.firstLabelText = "Text for the first label on right-side."
twoLabelView3.secondLabelText = "10.765,00TL"
let leftStackV = ViewWithStack(orientation: .vertical, labelsArray: [twoLabelView1, twoLabelView2])
let rightStackV = ViewWithStack(orientation: .vertical, labelsArray: [twoLabelView3])
cell.addViewWithStack(leftStackV)
cell.addViewWithStack(rightStackV)
return cell
// create ViewWithStack using just a simple label
let cell = tableView.dequeueReusableCell(withIdentifier: sideBySideReuseID, for: indexPath) as! SideBySideCell
let v = UILabel()
v.text = "This is row \(indexPath.row)"
let aStackV = ViewWithStack(orientation: .vertical, labelsArray: [v])
cell.addViewWithStack(aStackV)
return cell
@IBDesignable class TwoLabelView: UIView
var topMargin: CGFloat = 0.0
var verticalSpacing: CGFloat = 3.0
var bottomMargin: CGFloat = 0.0
@IBInspectable var firstLabelText: String = "" didSet updateView()
@IBInspectable var secondLabelText: String = "" didSet updateView()
fileprivate var firstLabel: UILabel =
let v = UILabel()
return v
()
fileprivate var secondLabel: UILabel =
let v = UILabel()
return v
()
override init(frame: CGRect)
super.init(frame: frame)
setUpView()
required public init?(coder: NSCoder)
super.init(coder:coder)
setUpView()
override func prepareForInterfaceBuilder()
super.prepareForInterfaceBuilder()
setUpView()
func setUpView()
firstLabel.font = UIFont.systemFont(ofSize: 18.0, weight: UIFont.Weight.bold)
firstLabel.numberOfLines = 0
secondLabel.font = UIFont.systemFont(ofSize: 13.0, weight: UIFont.Weight.regular)
secondLabel.numberOfLines = 1
addSubview(firstLabel)
addSubview(secondLabel)
// we're going to set the constraints
firstLabel .translatesAutoresizingMaskIntoConstraints = false
secondLabel.translatesAutoresizingMaskIntoConstraints = false
// Note: recommended to use Leading / Trailing rather than Left / Right
NSLayoutConstraint.activate([
// pin both labels' left-edges to left-edge of self
firstLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0.0),
secondLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0.0),
// pin both labels' right-edges to right-edge of self
firstLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0.0),
secondLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0.0),
// pin firstLabel to the top of self + topMargin (padding)
firstLabel.topAnchor.constraint(equalTo: topAnchor, constant: topMargin),
// pin top of secondLabel to bottom of firstLabel + verticalSpacing
secondLabel.topAnchor.constraint(equalTo: firstLabel.bottomAnchor, constant: verticalSpacing),
// pin bottom of self to >= (bottom of secondLabel + bottomMargin (padding))
bottomAnchor.constraint(greaterThanOrEqualTo: secondLabel.bottomAnchor, constant: bottomMargin),
])
func updateView() -> Void
firstLabel.text = firstLabelText
secondLabel.text = secondLabelText
class ViewWithStack: UIView
let verticalStackView: UIStackView =
let s = UIStackView()
s.distribution = .fill
s.spacing = 10
s.axis = .vertical
s.translatesAutoresizingMaskIntoConstraints = false
return s
()
override init(frame: CGRect)
super.init(frame: frame)
self.translatesAutoresizingMaskIntoConstraints = false
self.backgroundColor = UIColor.white
self.layer.cornerRadius = 6.0
// self.layer.applySketchShadow(color: UIColor(red:0.56, green:0.56, blue:0.56, alpha:1), alpha: 0.2, x: 0, y: 0, blur: 10, spread: 0)
addSubview(verticalStackView)
NSLayoutConstraint.activate([
// constrain to all 4 sides
verticalStackView.topAnchor.constraint(equalTo: topAnchor, constant: 0.0),
verticalStackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: 0.0),
verticalStackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 0.0),
verticalStackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 0.0),
])
verticalStackView.layoutMargins = UIEdgeInsets(top: 10, left: 20, bottom: 10, right: 20)
verticalStackView.isLayoutMarginsRelativeArrangement = true
convenience init(orientation: NSLayoutConstraint.Axis, labelsArray: [UIView])
self.init()
verticalStackView.axis = orientation
for label in labelsArray
verticalStackView.addArrangedSubview(label)
required init?(coder: NSCoder)
fatalError("init(coder:) has not been implemented")
【讨论】:
以上是关于UITableViewCell 内的多个堆栈视图内的多行标签的主要内容,如果未能解决你的问题,请参考以下文章
容器视图内的 UIPageViewController 内的堆栈视图的自动布局?