Swift TableViewCell xib 没有约束
Swift TableViewCell xib doesn't have constraints
我有两个 TableView
,当我设计原件时,我使用情节提要中的原型单元设计它,为了使其可重复使用,我尝试将其拉出到 .xib
中并加载那反而。但是,当它从 cellID.xib
加载时,它会在运行时失去所有约束,并且所有内容都层叠在一起。
TableViewController
let cellIdentifier = "cellID"
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: cellIdentifier, bundle: nil), forCellReuseIdentifier: cellIdentifier)
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 300
}
情节提要中的原型单元格(曾经有效)
复制粘贴到 XIB 时的单元格
约束
XIB 视图层次结构
好像是单元格高度的问题吧?因此,约束无法正常工作。
当你使用 XIB files
:
时你需要这个覆盖函数
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 300
}
但是你的约束一定是错误的,或者说它们并不完美。我知道他们正在使用故事板中的原型单元,但这并不能使它们 100% 完美地工作,对吗?
你的 UIImageView
没有 height
和 width
。你应该尝试用一些约束来设置它。 (或者您设置 width
并使用 Aspect Ratio
约束)
问题
在您的问题标题中,您询问了如何解决 "Swift TableViewCell xib doesn't have constraints." 此外,在赏金中,您指定的答案应该是:
An adequate answer showing why constraints are lost when moved to a
xib, when they exist in the xib, and how to address this problem.
据我了解,您的问题分为两部分:
- 自定义 TableViewCell xib 没有约束的问题
- 为什么当 copy/pasted 从 Storyboard 原型到 xib 时约束丢失
对于问题 #1,我使用了与您在自定义单元格 xib 的屏幕截图中显示的几乎所有相同的约束,并且它有效。我会在下面详细解释。
对于问题 #2,我发现约束没有丢失。您可能需要共享您的项目,以便其他人可以复制您遇到的问题。我 copy/pasted 一个 Storyboard 原型单元与 xib 具有类似的约束,我没有任何约束问题。所以我可以确认 copy/paste 功能确实有效。
解决方案演示
我创建了以下演示来测试您的问题。请参阅下面的屏幕截图。 tableView 包含六个单元格。第 1 个和第 2 个相同,并且具有重用标识符 MyCell
。第 3 个和第 4 个相同,并且具有重用标识符 MyCopyPasteCell
。第 5 个和第 6 个相同,并且具有重用标识符 MyPrototypeCell
。
MyCell
存在于 xib 中并使用几乎所有与您在屏幕截图中显示的相同的约束。如您所见,不存在约束问题。 MyCopyPasteCell
使用类似的约束并且 copy/pasted 从 Storyboard 原型到 xib。 MyPrototypeCell
是复制过来的原型。这最后四个单元格看起来完全一样,即使原型被复制到 xib。约束从原型转移到 xib 没有问题。
约束
在下面的代码中,我列出了您在 ContentView 屏幕截图中显示的所有约束。 (请注意,我还实现了 height=20
、aspect=1:1
和 height=75
约束,尽管它们没有在下面列出。)两个约束被注释掉了,因为我没有使用它们。我还添加了一个约束来替换另一个未使用的约束。
// bottomMargin = Profile Image View.bottom + 188.5
bottomMargin = Profile Image.bottom + 17
Profile Image View.top = Username Label.top
Profile Image View.leading = leadingMargin + 2
Profile Image View.top = topMargin + 20
Username Label.leading = Content Text View.leading
// Username Label.top = topMargin + 20
Username Label.trailing = Content Text View.trailing
Username Label.leading = Profile Image View.trailing + 15
trailingMargin = Username Label.trailing + 10
Content Text View.top = Username Label.bottom + 5
Content Text View.leading = leadingMargin + 92
bottomMargin = Content Text View.bottom + 20
第一个注释约束 // bottomMargin = Profile Image View.bottom + 188.5
没有意义,因为它将图像底部与单元格底部分开 188.5。这也不符合您在 Storyboard 中的 Prototype cell (Used to work) 屏幕截图。我将其替换为 bottomMargin = Profile Image.bottom + 17
,这只是将 188.5 替换为 17.
第二个注释约束 // Username Label.top = topMargin + 20
(用 20 分隔用户名和 topMargin)在技术上可以工作。但是,它的功能对于 Profile Image.top = topMargin + 20
(将个人资料图片和 topMargin 相隔 20)和 Profile Image View.top = Username Label.top
(将个人资料图片和用户名设置为相同的顶部分隔,即相隔 20)是多余的。
MyTableViewController
以下是我的视图控制器。基本上我创建了三个部分,每个部分有两个 cells/rows。 MyCell
和 MyCopyPasteTableViewCell
来自 xib,并在 viewDidLoad()
中注册。 MyPrototypeCell
来自 Storyboard,未在 viewDidLoad()
中注册。
// MyTableViewController.swift
import UIKit
class MyTableViewController: UITableViewController {
let myCell = "MyCell"
let myCopyPasteCell = "MyCopyPasteTableViewCell"
let myPrototypeCell = "MyPrototypeCell"
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: myCell, bundle: nil), forCellReuseIdentifier: myCell)
tableView.register(UINib(nibName: myCopyPasteCell, bundle: nil), forCellReuseIdentifier: myCopyPasteCell)
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 300
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: myCell, for: indexPath)
return cell
} else if indexPath.section == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: myCopyPasteCell, for: indexPath)
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: myPrototypeCell, for: indexPath)
return cell
}
}
}
Github link
https://github.com/starkindustries/TableViewCellConstraintsTest
问题与解决方案
您可能已禁用 XIB 的 Autolayout
以打开它 ON
从右上角菜单转到 File Inspector
您可以按照简单的步骤从 Xib 创建单元格
1. Create UITableCell
with .xib
( ✅ 同时创建 Xib 文件)
2.给出小区标识符
3.布局约束
ImageView
给出来自 Top
、 Left
和 Bottom
的对比
高度常量和纵横比[我设置了边框以显示框架]
UIlable
for title lable
从三个方面给约束
来自 Top
, Left
和 Right
NumberOflines=0
(不要给出高度常量 -
⚠️ 如果给出警告,请继续并更新约束)
UIlable
对于 Descriptions lable
也从所有四个方面给出约束 NumberOflines=0
(⚠️这会给你一个错误去建议和改变UIlable
约束的优先级)
见下方截图
更改优先级
比再次更新约束
4.代码在UIViewController
@IBOutlet var tableView: UITableView!
var cellIdentifier="cell"
var cellXibName = "MyTableCell"
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: cellXibName, bundle: nil), forCellReuseIdentifier: cellIdentifier)
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 320
}
func numberOfSections(in tableView1: UITableView) -> Int {
return 3
}
func tableView(_ tableView1: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell :MyTableCell = tableView1.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! MyTableCell
return cell
}
最后输出如你所愿
输出
我希望这对你有用......如果这对你有用,请告诉我。
解决方案:
我已找到您问题的解决方案。希望对您有所帮助
我用uiview裁剪所有单元格元素。
和细胞设计
代码:
class ViewController: UIViewController {
@IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.estimatedRowHeight = 100
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.register(UINib(nibName: "TestCell", bundle: Bundle.main), forCellReuseIdentifier: "TestCell")
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension ViewController : UITableViewDataSource, UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return 1;
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TestCell", for: indexPath) as! TestCell
cell.selectionStyle = .none
return cell
}
}
结果:
我有两个 TableView
,当我设计原件时,我使用情节提要中的原型单元设计它,为了使其可重复使用,我尝试将其拉出到 .xib
中并加载那反而。但是,当它从 cellID.xib
加载时,它会在运行时失去所有约束,并且所有内容都层叠在一起。
TableViewController
let cellIdentifier = "cellID"
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: cellIdentifier, bundle: nil), forCellReuseIdentifier: cellIdentifier)
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 300
}
情节提要中的原型单元格(曾经有效)
复制粘贴到 XIB 时的单元格
约束
XIB 视图层次结构
好像是单元格高度的问题吧?因此,约束无法正常工作。
当你使用 XIB files
:
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 300
}
但是你的约束一定是错误的,或者说它们并不完美。我知道他们正在使用故事板中的原型单元,但这并不能使它们 100% 完美地工作,对吗?
你的 UIImageView
没有 height
和 width
。你应该尝试用一些约束来设置它。 (或者您设置 width
并使用 Aspect Ratio
约束)
问题
在您的问题标题中,您询问了如何解决 "Swift TableViewCell xib doesn't have constraints." 此外,在赏金中,您指定的答案应该是:
An adequate answer showing why constraints are lost when moved to a xib, when they exist in the xib, and how to address this problem.
据我了解,您的问题分为两部分:
- 自定义 TableViewCell xib 没有约束的问题
- 为什么当 copy/pasted 从 Storyboard 原型到 xib 时约束丢失
对于问题 #1,我使用了与您在自定义单元格 xib 的屏幕截图中显示的几乎所有相同的约束,并且它有效。我会在下面详细解释。
对于问题 #2,我发现约束没有丢失。您可能需要共享您的项目,以便其他人可以复制您遇到的问题。我 copy/pasted 一个 Storyboard 原型单元与 xib 具有类似的约束,我没有任何约束问题。所以我可以确认 copy/paste 功能确实有效。
解决方案演示
我创建了以下演示来测试您的问题。请参阅下面的屏幕截图。 tableView 包含六个单元格。第 1 个和第 2 个相同,并且具有重用标识符 MyCell
。第 3 个和第 4 个相同,并且具有重用标识符 MyCopyPasteCell
。第 5 个和第 6 个相同,并且具有重用标识符 MyPrototypeCell
。
MyCell
存在于 xib 中并使用几乎所有与您在屏幕截图中显示的相同的约束。如您所见,不存在约束问题。 MyCopyPasteCell
使用类似的约束并且 copy/pasted 从 Storyboard 原型到 xib。 MyPrototypeCell
是复制过来的原型。这最后四个单元格看起来完全一样,即使原型被复制到 xib。约束从原型转移到 xib 没有问题。
约束
在下面的代码中,我列出了您在 ContentView 屏幕截图中显示的所有约束。 (请注意,我还实现了 height=20
、aspect=1:1
和 height=75
约束,尽管它们没有在下面列出。)两个约束被注释掉了,因为我没有使用它们。我还添加了一个约束来替换另一个未使用的约束。
// bottomMargin = Profile Image View.bottom + 188.5
bottomMargin = Profile Image.bottom + 17
Profile Image View.top = Username Label.top
Profile Image View.leading = leadingMargin + 2
Profile Image View.top = topMargin + 20
Username Label.leading = Content Text View.leading
// Username Label.top = topMargin + 20
Username Label.trailing = Content Text View.trailing
Username Label.leading = Profile Image View.trailing + 15
trailingMargin = Username Label.trailing + 10
Content Text View.top = Username Label.bottom + 5
Content Text View.leading = leadingMargin + 92
bottomMargin = Content Text View.bottom + 20
第一个注释约束 // bottomMargin = Profile Image View.bottom + 188.5
没有意义,因为它将图像底部与单元格底部分开 188.5。这也不符合您在 Storyboard 中的 Prototype cell (Used to work) 屏幕截图。我将其替换为 bottomMargin = Profile Image.bottom + 17
,这只是将 188.5 替换为 17.
第二个注释约束 // Username Label.top = topMargin + 20
(用 20 分隔用户名和 topMargin)在技术上可以工作。但是,它的功能对于 Profile Image.top = topMargin + 20
(将个人资料图片和 topMargin 相隔 20)和 Profile Image View.top = Username Label.top
(将个人资料图片和用户名设置为相同的顶部分隔,即相隔 20)是多余的。
MyTableViewController
以下是我的视图控制器。基本上我创建了三个部分,每个部分有两个 cells/rows。 MyCell
和 MyCopyPasteTableViewCell
来自 xib,并在 viewDidLoad()
中注册。 MyPrototypeCell
来自 Storyboard,未在 viewDidLoad()
中注册。
// MyTableViewController.swift
import UIKit
class MyTableViewController: UITableViewController {
let myCell = "MyCell"
let myCopyPasteCell = "MyCopyPasteTableViewCell"
let myPrototypeCell = "MyPrototypeCell"
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: myCell, bundle: nil), forCellReuseIdentifier: myCell)
tableView.register(UINib(nibName: myCopyPasteCell, bundle: nil), forCellReuseIdentifier: myCopyPasteCell)
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 300
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 2
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: myCell, for: indexPath)
return cell
} else if indexPath.section == 1 {
let cell = tableView.dequeueReusableCell(withIdentifier: myCopyPasteCell, for: indexPath)
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: myPrototypeCell, for: indexPath)
return cell
}
}
}
Github link
https://github.com/starkindustries/TableViewCellConstraintsTest
问题与解决方案
您可能已禁用 XIB 的 Autolayout
以打开它 ON
从右上角菜单转到 File Inspector
您可以按照简单的步骤从 Xib 创建单元格
1. Create UITableCell
with .xib
( ✅ 同时创建 Xib 文件)
2.给出小区标识符
3.布局约束
ImageView
给出来自 Top
、 Left
和 Bottom
的对比
高度常量和纵横比[我设置了边框以显示框架]
UIlable
for title lable
从三个方面给约束
来自 Top
, Left
和 Right
NumberOflines=0
(不要给出高度常量 -
⚠️ 如果给出警告,请继续并更新约束)
UIlable
对于 Descriptions lable
也从所有四个方面给出约束 NumberOflines=0
(⚠️这会给你一个错误去建议和改变UIlable
约束的优先级)
见下方截图
更改优先级
4.代码在UIViewController
@IBOutlet var tableView: UITableView!
var cellIdentifier="cell"
var cellXibName = "MyTableCell"
override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: cellXibName, bundle: nil), forCellReuseIdentifier: cellIdentifier)
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 320
}
func numberOfSections(in tableView1: UITableView) -> Int {
return 3
}
func tableView(_ tableView1: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell :MyTableCell = tableView1.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! MyTableCell
return cell
}
最后输出如你所愿
输出
我希望这对你有用......如果这对你有用,请告诉我。
解决方案:
我已找到您问题的解决方案。希望对您有所帮助
我用uiview裁剪所有单元格元素。
和细胞设计
代码:
class ViewController: UIViewController {
@IBOutlet var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.estimatedRowHeight = 100
self.tableView.rowHeight = UITableViewAutomaticDimension
self.tableView.register(UINib(nibName: "TestCell", bundle: Bundle.main), forCellReuseIdentifier: "TestCell")
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
extension ViewController : UITableViewDataSource, UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return 1;
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TestCell", for: indexPath) as! TestCell
cell.selectionStyle = .none
return cell
}
}
结果: