具有可变单元格高度的 UITableView:在 IB 中工作但不以编程方式工作
UITableView with variable cell height: Working in IB but not programmatically
TL;DR
我以编程方式创建的 table 视图单元格未根据其自定义视图的固有内容高度调整大小,即使我正在使用 UITableViewAutomaticDimension
并设置了顶部和底部约束。
问题可能出在我对UITableViewCell
子类的实现上。请参阅 无法以编程方式工作 > 代码 > MyCustomCell.swift 下的代码。
目标
我正在尝试为自定义蒙古语键盘制作一个建议栏。蒙古文是竖写的。在 Android 中它看起来像这样:
进度
我了解到我应该使用具有可变单元格高度的 UITableView
,从 iOS 8 开始可用。这需要使用自动布局并告诉 table 视图为单元格高度使用自动尺寸。
在我最近的 SO 问题和答案中体现了我一路上必须学习的一些东西:
- How to make a custom table view cell
- Getting variable height to work with in a table view with a standard
UILabel
- Using a programmatically created
UITableViewCell
- Set constraints programmatically
所以我已经到了我拥有支持固有内容大小的垂直标签的地步。这些标签进入我的自定义 table 视图单元格。正如下一节所述,当我在情节提要中执行时它们会起作用,但当我以编程方式创建所有内容时它们不会起作用。
在 IB 工作
为了隔离问题,我创建了两个基本项目:一个用于使用故事板,另一个用于以编程方式执行所有操作。故事板项目有效。如下图所示,每个 table 视图单元格都会调整大小以匹配自定义垂直标签的高度。
在IB中
我设置约束以固定顶部和底部以及将标签居中。
代码
ViewController.swift
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
let myStrings: [String] = ["a", "bbbbbbb", "cccc", "dddddddddd", "ee"]
let cellReuseIdentifier = "cell"
@IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.estimatedRowHeight = 44.0
tableView.rowHeight = UITableViewAutomaticDimension
}
// number of rows in table view
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.myStrings.count
}
// create a cell for each table view row
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:MyCustomCell = self.tableView.dequeueReusableCellWithIdentifier(cellReuseIdentifier) as! MyCustomCell
cell.myCellLabel.text = self.myStrings[indexPath.row]
return cell
}
// method to run when table view cell is tapped
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("You tapped cell number \(indexPath.row).")
}
}
MyCustomCell.swift
import UIKit
class MyCustomCell: UITableViewCell {
@IBOutlet weak var myCellLabel: UIMongolSingleLineLabel!
}
无法以编程方式工作
由于我希望建议栏成为最终键盘的一部分,因此我需要能够以编程方式创建它。但是,当我尝试以编程方式重新创建上述示例项目时,它不起作用。我得到以下结果。
单元格高度未调整大小,自定义垂直标签相互重叠。
我也收到以下错误:
Warning once only: Detected a case where constraints ambiguously
suggest a height of zero for a tableview cell's content view. We're
considering the collapse unintentional and using standard height
instead.
此错误已在 Stack Overflow 上多次出现:
- iOS8 - constraints ambiguously suggest a height of zero
- Detected a case where constraints ambiguously suggest a height of zero
- ios 8 (UITableViewCell) : Constraints ambiguously suggest a height of zero for a tableview cell's content view
但是,对于这些人中的大多数人来说,问题在于他们没有同时设置顶部和底部引脚约束。我是,或者至少我认为我是,如下面的代码所示。
代码
ViewController.swift
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
let myStrings: [String] = ["a", "bbbbbbb", "cccc", "dddddddddd", "ee"]
let cellReuseIdentifier = "cell"
var tableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
// Suggestion bar
tableView.frame = CGRect(x: 0, y: 20, width: view.bounds.width, height: view.bounds.height)
tableView.registerClass(MyCustomCell.self, forCellReuseIdentifier: cellReuseIdentifier)
tableView.delegate = self
tableView.dataSource = self
tableView.estimatedRowHeight = 44.0
tableView.rowHeight = UITableViewAutomaticDimension
view.addSubview(tableView)
}
// number of rows in table view
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.myStrings.count
}
// create a cell for each table view row
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:MyCustomCell = self.tableView.dequeueReusableCellWithIdentifier(cellReuseIdentifier) as! MyCustomCell
cell.myCellLabel.text = self.myStrings[indexPath.row]
return cell
}
// method to run when table view cell is tapped
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("You tapped cell number \(indexPath.row).")
}
}
MyCustomCell.swift
我认为问题可能出在这里因为这是与IB项目的主要区别
import UIKit
class MyCustomCell: UITableViewCell {
var myCellLabel = UIMongolSingleLineLabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.setup()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setup() {
self.myCellLabel.translatesAutoresizingMaskIntoConstraints = false
self.myCellLabel.centerText = false
self.myCellLabel.backgroundColor = UIColor.yellowColor()
self.addSubview(myCellLabel)
// Constraints
// pin top
NSLayoutConstraint(item: myCellLabel, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: self.contentView, attribute: NSLayoutAttribute.TopMargin, multiplier: 1.0, constant: 0).active = true
// pin bottom
NSLayoutConstraint(item: myCellLabel, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: self.contentView, attribute: NSLayoutAttribute.BottomMargin, multiplier: 1.0, constant: 0).active = true
// center horizontal
NSLayoutConstraint(item: myCellLabel, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: self.contentView, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: 0).active = true
}
override internal class func requiresConstraintBasedLayout() -> Bool {
return true
}
}
补充码
我还将包括我在上面两个项目中使用的自定义垂直标签的代码,但由于 IB 项目有效,我认为主要问题不在这里。
import UIKit
@IBDesignable
class UIMongolSingleLineLabel: UIView {
private let textLayer = LabelTextLayer()
var useMirroredFont = false
// MARK: Primary input value
@IBInspectable var text: String = "A" {
didSet {
textLayer.displayString = text
updateTextLayerFrame()
}
}
@IBInspectable var fontSize: CGFloat = 17 {
didSet {
updateTextLayerFrame()
}
}
@IBInspectable var centerText: Bool = true {
didSet {
updateTextLayerFrame()
}
}
// MARK: - Initialization
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
func setup() {
// Text layer
textLayer.backgroundColor = UIColor.yellowColor().CGColor
textLayer.useMirroredFont = useMirroredFont
textLayer.contentsScale = UIScreen.mainScreen().scale
layer.addSublayer(textLayer)
}
override func intrinsicContentSize() -> CGSize {
return textLayer.frame.size
}
func updateTextLayerFrame() {
let myAttribute = [ NSFontAttributeName: UIFont.systemFontOfSize(fontSize) ]
let attrString = NSMutableAttributedString(string: textLayer.displayString, attributes: myAttribute )
let size = dimensionsForAttributedString(attrString)
// This is the frame for the soon-to-be rotated layer
var x: CGFloat = 0
var y: CGFloat = 0
if layer.bounds.width > size.height {
x = (layer.bounds.width - size.height) / 2
}
if centerText {
y = (layer.bounds.height - size.width) / 2
}
textLayer.frame = CGRect(x: x, y: y, width: size.height, height: size.width)
textLayer.string = attrString
invalidateIntrinsicContentSize()
}
func dimensionsForAttributedString(attrString: NSAttributedString) -> CGSize {
var ascent: CGFloat = 0
var descent: CGFloat = 0
var width: CGFloat = 0
let line: CTLineRef = CTLineCreateWithAttributedString(attrString)
width = CGFloat(CTLineGetTypographicBounds(line, &ascent, &descent, nil))
// make width an even integer for better graphics rendering
width = ceil(width)
if Int(width)%2 == 1 {
width += 1.0
}
return CGSize(width: width, height: ceil(ascent+descent))
}
}
// MARK: - Key Text Layer Class
class LabelTextLayer: CATextLayer {
// set this to false if not using a mirrored font
var useMirroredFont = true
var displayString = ""
override func drawInContext(ctx: CGContext) {
// A frame is passed in, in which the frame size is already rotated at the center but the content is not.
CGContextSaveGState(ctx)
if useMirroredFont {
CGContextRotateCTM(ctx, CGFloat(M_PI_2))
CGContextScaleCTM(ctx, 1.0, -1.0)
} else {
CGContextRotateCTM(ctx, CGFloat(M_PI_2))
CGContextTranslateCTM(ctx, 0, -self.bounds.width)
}
super.drawInContext(ctx)
CGContextRestoreGState(ctx)
}
}
更新
项目的全部代码都在这里了,有兴趣的小伙伴可以自己新建一个项目,将上面的代码剪切粘贴到以下三个文件中:
- ViewController.swift
- MyCustomCell.swift
- UIMongolSingleLineLabel.swift
你缺少的是这个函数:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return heightValue
}
我不太确定你应该做什么,但由于你知道你的标签,你应该能够return此方法中每个单元格的精确高度值
我测试了你的代码,发现问题出在设置约束上,请使用下面的代码部分在你的 "MyCustomCell.swift" 文件 setup() 函数中设置常量
let topConstraint = NSLayoutConstraint(item: myCellLabel, attribute: .Top, relatedBy: .Equal, toItem: self, attribute: .Top, multiplier: 1, constant: 0)
let bottomConstraint = NSLayoutConstraint(item: myCellLabel, attribute: .Bottom, relatedBy: .Equal, toItem: self, attribute: .Bottom, multiplier: 1, constant: 0)
let centerConstraint = NSLayoutConstraint(item: myCellLabel, attribute: .CenterX, relatedBy: .Equal, toItem: self, attribute: .CenterX, multiplier: 1, constant: 0)
self.addConstraints([centerConstraint, topConstraint, bottomConstraint])
同时设置剪辑以将 属性 绑定到 "viewcontroller.swift"
中的单元格标签
// create a cell for each table view row
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:MyCustomCell = self.tableView.dequeueReusableCellWithIdentifier(cellReuseIdentifier) as! MyCustomCell
cell.myCellLabel.text = self.myStrings[indexPath.row]
cell.myCellLabel.clipsToBounds=true
return cell
}
为了您的方便,我已将我的示例代码上传到 GitHub Dynamic Height Sample
输出现在看起来像这样
我认为您没有为带有 superview 的 tableView 设置约束。并尝试增加估计的行高。
这个错误很简单:
而不是
self.addSubview(myCellLabel)
使用
self.contentView.addSubview(myCellLabel)
此外,我会替换
// pin top
NSLayoutConstraint(...).active = true
// pin bottom
NSLayoutConstraint(...).active = true
// center horizontal
NSLayoutConstraint(...).active = true
和
let topConstraint = NSLayoutConstraint(...)
let bottomConstraint = NSLayoutConstraint(...)
let centerConstraint = NSLayoutConstraint(...)
self.contentView.addConstraints([topConstraint, bottomConstraint, centerConstraint])
哪个更明确(您必须指定约束所有者)因此更安全。
问题在于,在约束上调用 active = true
时,布局系统必须决定应向哪个视图添加约束。在你的例子中,因为 contentView
和 myCellLabel
的第一个共同祖先是你的 UITableViewCell
,它们被添加到你的 UITableViewCell
,所以它们实际上并没有约束 contentView
(约束在兄弟姐妹之间,而不是在父视图-子视图之间)。
您的代码实际上触发了控制台警告:
Warning once only: Detected a case where constraints ambiguously suggest a height of zero for a tableview cell's content view. We're considering the collapse unintentional and using standard height instead.
这让我立即查看了为您的标签创建约束的方式。
问题似乎出在单元格中的垂直约束
通过将它们相对于 self 而不是 self.contentView 放在 MyCustomCell 中,您可以解决您的问题
NSLayoutConstraint(item: myCellLabel, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.TopMargin, multiplier: 1.0, constant: 0).active = true
// pin bottom
NSLayoutConstraint(item: myCellLabel, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.BottomMargin, multiplier: 1.0, constant: 0).active = true
// center horizontal
NSLayoutConstraint(item: myCellLabel, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: 0).active = true
完整的 class 将是:
import UIKit
class MyCustomCell: UITableViewCell {
var myCellLabel = UIMongolSingleLineLabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.setup()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setup() {
self.myCellLabel.translatesAutoresizingMaskIntoConstraints = false
self.myCellLabel.centerText = false
self.myCellLabel.backgroundColor = UIColor.yellowColor()
self.addSubview(myCellLabel)
// Constraints
// pin top
NSLayoutConstraint(item: myCellLabel, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.TopMargin, multiplier: 1.0, constant: 0).active = true
// pin bottom
NSLayoutConstraint(item: myCellLabel, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.BottomMargin, multiplier: 1.0, constant: 0).active = true
// center horizontal
NSLayoutConstraint(item: myCellLabel, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: 0).active = true
}
override internal class func requiresConstraintBasedLayout() -> Bool {
return true
}
}
TL;DR
我以编程方式创建的 table 视图单元格未根据其自定义视图的固有内容高度调整大小,即使我正在使用 UITableViewAutomaticDimension
并设置了顶部和底部约束。
问题可能出在我对UITableViewCell
子类的实现上。请参阅 无法以编程方式工作 > 代码 > MyCustomCell.swift 下的代码。
目标
我正在尝试为自定义蒙古语键盘制作一个建议栏。蒙古文是竖写的。在 Android 中它看起来像这样:
进度
我了解到我应该使用具有可变单元格高度的 UITableView
,从 iOS 8 开始可用。这需要使用自动布局并告诉 table 视图为单元格高度使用自动尺寸。
在我最近的 SO 问题和答案中体现了我一路上必须学习的一些东西:
- How to make a custom table view cell
- Getting variable height to work with in a table view with a standard
UILabel
- Using a programmatically created
UITableViewCell
- Set constraints programmatically
所以我已经到了我拥有支持固有内容大小的垂直标签的地步。这些标签进入我的自定义 table 视图单元格。正如下一节所述,当我在情节提要中执行时它们会起作用,但当我以编程方式创建所有内容时它们不会起作用。
在 IB 工作
为了隔离问题,我创建了两个基本项目:一个用于使用故事板,另一个用于以编程方式执行所有操作。故事板项目有效。如下图所示,每个 table 视图单元格都会调整大小以匹配自定义垂直标签的高度。
在IB中
我设置约束以固定顶部和底部以及将标签居中。
代码
ViewController.swift
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
let myStrings: [String] = ["a", "bbbbbbb", "cccc", "dddddddddd", "ee"]
let cellReuseIdentifier = "cell"
@IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
tableView.estimatedRowHeight = 44.0
tableView.rowHeight = UITableViewAutomaticDimension
}
// number of rows in table view
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.myStrings.count
}
// create a cell for each table view row
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:MyCustomCell = self.tableView.dequeueReusableCellWithIdentifier(cellReuseIdentifier) as! MyCustomCell
cell.myCellLabel.text = self.myStrings[indexPath.row]
return cell
}
// method to run when table view cell is tapped
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("You tapped cell number \(indexPath.row).")
}
}
MyCustomCell.swift
import UIKit
class MyCustomCell: UITableViewCell {
@IBOutlet weak var myCellLabel: UIMongolSingleLineLabel!
}
无法以编程方式工作
由于我希望建议栏成为最终键盘的一部分,因此我需要能够以编程方式创建它。但是,当我尝试以编程方式重新创建上述示例项目时,它不起作用。我得到以下结果。
单元格高度未调整大小,自定义垂直标签相互重叠。
我也收到以下错误:
Warning once only: Detected a case where constraints ambiguously suggest a height of zero for a tableview cell's content view. We're considering the collapse unintentional and using standard height instead.
此错误已在 Stack Overflow 上多次出现:
- iOS8 - constraints ambiguously suggest a height of zero
- Detected a case where constraints ambiguously suggest a height of zero
- ios 8 (UITableViewCell) : Constraints ambiguously suggest a height of zero for a tableview cell's content view
但是,对于这些人中的大多数人来说,问题在于他们没有同时设置顶部和底部引脚约束。我是,或者至少我认为我是,如下面的代码所示。
代码
ViewController.swift
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
let myStrings: [String] = ["a", "bbbbbbb", "cccc", "dddddddddd", "ee"]
let cellReuseIdentifier = "cell"
var tableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
// Suggestion bar
tableView.frame = CGRect(x: 0, y: 20, width: view.bounds.width, height: view.bounds.height)
tableView.registerClass(MyCustomCell.self, forCellReuseIdentifier: cellReuseIdentifier)
tableView.delegate = self
tableView.dataSource = self
tableView.estimatedRowHeight = 44.0
tableView.rowHeight = UITableViewAutomaticDimension
view.addSubview(tableView)
}
// number of rows in table view
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.myStrings.count
}
// create a cell for each table view row
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:MyCustomCell = self.tableView.dequeueReusableCellWithIdentifier(cellReuseIdentifier) as! MyCustomCell
cell.myCellLabel.text = self.myStrings[indexPath.row]
return cell
}
// method to run when table view cell is tapped
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
print("You tapped cell number \(indexPath.row).")
}
}
MyCustomCell.swift
我认为问题可能出在这里因为这是与IB项目的主要区别
import UIKit
class MyCustomCell: UITableViewCell {
var myCellLabel = UIMongolSingleLineLabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.setup()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setup() {
self.myCellLabel.translatesAutoresizingMaskIntoConstraints = false
self.myCellLabel.centerText = false
self.myCellLabel.backgroundColor = UIColor.yellowColor()
self.addSubview(myCellLabel)
// Constraints
// pin top
NSLayoutConstraint(item: myCellLabel, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: self.contentView, attribute: NSLayoutAttribute.TopMargin, multiplier: 1.0, constant: 0).active = true
// pin bottom
NSLayoutConstraint(item: myCellLabel, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: self.contentView, attribute: NSLayoutAttribute.BottomMargin, multiplier: 1.0, constant: 0).active = true
// center horizontal
NSLayoutConstraint(item: myCellLabel, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: self.contentView, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: 0).active = true
}
override internal class func requiresConstraintBasedLayout() -> Bool {
return true
}
}
补充码
我还将包括我在上面两个项目中使用的自定义垂直标签的代码,但由于 IB 项目有效,我认为主要问题不在这里。
import UIKit
@IBDesignable
class UIMongolSingleLineLabel: UIView {
private let textLayer = LabelTextLayer()
var useMirroredFont = false
// MARK: Primary input value
@IBInspectable var text: String = "A" {
didSet {
textLayer.displayString = text
updateTextLayerFrame()
}
}
@IBInspectable var fontSize: CGFloat = 17 {
didSet {
updateTextLayerFrame()
}
}
@IBInspectable var centerText: Bool = true {
didSet {
updateTextLayerFrame()
}
}
// MARK: - Initialization
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
func setup() {
// Text layer
textLayer.backgroundColor = UIColor.yellowColor().CGColor
textLayer.useMirroredFont = useMirroredFont
textLayer.contentsScale = UIScreen.mainScreen().scale
layer.addSublayer(textLayer)
}
override func intrinsicContentSize() -> CGSize {
return textLayer.frame.size
}
func updateTextLayerFrame() {
let myAttribute = [ NSFontAttributeName: UIFont.systemFontOfSize(fontSize) ]
let attrString = NSMutableAttributedString(string: textLayer.displayString, attributes: myAttribute )
let size = dimensionsForAttributedString(attrString)
// This is the frame for the soon-to-be rotated layer
var x: CGFloat = 0
var y: CGFloat = 0
if layer.bounds.width > size.height {
x = (layer.bounds.width - size.height) / 2
}
if centerText {
y = (layer.bounds.height - size.width) / 2
}
textLayer.frame = CGRect(x: x, y: y, width: size.height, height: size.width)
textLayer.string = attrString
invalidateIntrinsicContentSize()
}
func dimensionsForAttributedString(attrString: NSAttributedString) -> CGSize {
var ascent: CGFloat = 0
var descent: CGFloat = 0
var width: CGFloat = 0
let line: CTLineRef = CTLineCreateWithAttributedString(attrString)
width = CGFloat(CTLineGetTypographicBounds(line, &ascent, &descent, nil))
// make width an even integer for better graphics rendering
width = ceil(width)
if Int(width)%2 == 1 {
width += 1.0
}
return CGSize(width: width, height: ceil(ascent+descent))
}
}
// MARK: - Key Text Layer Class
class LabelTextLayer: CATextLayer {
// set this to false if not using a mirrored font
var useMirroredFont = true
var displayString = ""
override func drawInContext(ctx: CGContext) {
// A frame is passed in, in which the frame size is already rotated at the center but the content is not.
CGContextSaveGState(ctx)
if useMirroredFont {
CGContextRotateCTM(ctx, CGFloat(M_PI_2))
CGContextScaleCTM(ctx, 1.0, -1.0)
} else {
CGContextRotateCTM(ctx, CGFloat(M_PI_2))
CGContextTranslateCTM(ctx, 0, -self.bounds.width)
}
super.drawInContext(ctx)
CGContextRestoreGState(ctx)
}
}
更新
项目的全部代码都在这里了,有兴趣的小伙伴可以自己新建一个项目,将上面的代码剪切粘贴到以下三个文件中:
- ViewController.swift
- MyCustomCell.swift
- UIMongolSingleLineLabel.swift
你缺少的是这个函数:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return heightValue
}
我不太确定你应该做什么,但由于你知道你的标签,你应该能够return此方法中每个单元格的精确高度值
我测试了你的代码,发现问题出在设置约束上,请使用下面的代码部分在你的 "MyCustomCell.swift" 文件 setup() 函数中设置常量
let topConstraint = NSLayoutConstraint(item: myCellLabel, attribute: .Top, relatedBy: .Equal, toItem: self, attribute: .Top, multiplier: 1, constant: 0)
let bottomConstraint = NSLayoutConstraint(item: myCellLabel, attribute: .Bottom, relatedBy: .Equal, toItem: self, attribute: .Bottom, multiplier: 1, constant: 0)
let centerConstraint = NSLayoutConstraint(item: myCellLabel, attribute: .CenterX, relatedBy: .Equal, toItem: self, attribute: .CenterX, multiplier: 1, constant: 0)
self.addConstraints([centerConstraint, topConstraint, bottomConstraint])
同时设置剪辑以将 属性 绑定到 "viewcontroller.swift"
中的单元格标签// create a cell for each table view row
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell:MyCustomCell = self.tableView.dequeueReusableCellWithIdentifier(cellReuseIdentifier) as! MyCustomCell
cell.myCellLabel.text = self.myStrings[indexPath.row]
cell.myCellLabel.clipsToBounds=true
return cell
}
为了您的方便,我已将我的示例代码上传到 GitHub Dynamic Height Sample
输出现在看起来像这样
我认为您没有为带有 superview 的 tableView 设置约束。并尝试增加估计的行高。
这个错误很简单:
而不是
self.addSubview(myCellLabel)
使用
self.contentView.addSubview(myCellLabel)
此外,我会替换
// pin top
NSLayoutConstraint(...).active = true
// pin bottom
NSLayoutConstraint(...).active = true
// center horizontal
NSLayoutConstraint(...).active = true
和
let topConstraint = NSLayoutConstraint(...)
let bottomConstraint = NSLayoutConstraint(...)
let centerConstraint = NSLayoutConstraint(...)
self.contentView.addConstraints([topConstraint, bottomConstraint, centerConstraint])
哪个更明确(您必须指定约束所有者)因此更安全。
问题在于,在约束上调用 active = true
时,布局系统必须决定应向哪个视图添加约束。在你的例子中,因为 contentView
和 myCellLabel
的第一个共同祖先是你的 UITableViewCell
,它们被添加到你的 UITableViewCell
,所以它们实际上并没有约束 contentView
(约束在兄弟姐妹之间,而不是在父视图-子视图之间)。
您的代码实际上触发了控制台警告:
Warning once only: Detected a case where constraints ambiguously suggest a height of zero for a tableview cell's content view. We're considering the collapse unintentional and using standard height instead.
这让我立即查看了为您的标签创建约束的方式。
问题似乎出在单元格中的垂直约束 通过将它们相对于 self 而不是 self.contentView 放在 MyCustomCell 中,您可以解决您的问题
NSLayoutConstraint(item: myCellLabel, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.TopMargin, multiplier: 1.0, constant: 0).active = true
// pin bottom
NSLayoutConstraint(item: myCellLabel, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.BottomMargin, multiplier: 1.0, constant: 0).active = true
// center horizontal
NSLayoutConstraint(item: myCellLabel, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: 0).active = true
完整的 class 将是:
import UIKit
class MyCustomCell: UITableViewCell {
var myCellLabel = UIMongolSingleLineLabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.setup()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setup() {
self.myCellLabel.translatesAutoresizingMaskIntoConstraints = false
self.myCellLabel.centerText = false
self.myCellLabel.backgroundColor = UIColor.yellowColor()
self.addSubview(myCellLabel)
// Constraints
// pin top
NSLayoutConstraint(item: myCellLabel, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.TopMargin, multiplier: 1.0, constant: 0).active = true
// pin bottom
NSLayoutConstraint(item: myCellLabel, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.BottomMargin, multiplier: 1.0, constant: 0).active = true
// center horizontal
NSLayoutConstraint(item: myCellLabel, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.CenterX, multiplier: 1, constant: 0).active = true
}
override internal class func requiresConstraintBasedLayout() -> Bool {
return true
}
}