UITableViewCell 忽略自动布局
UITableViewCell ignore autolayout
几天以来我尝试了所有方法,甚至尝试使用 automaticDimension 和 estimatedRowHeight = 44,none 运气不错。我是 UITableView 的新手,我试着用它来练习。我到处查看 Whosebug 等,但没有运气。我不确定下面这段代码做错了什么。
在视图控制器中:
reminder_tableview.frame = view.bounds
reminder_tableview.allowsSelection = false
reminder_tableview.estimatedRowHeight = 44
reminder_tableview.rowHeight = UITableView.automaticDimension
reminder_tableview.register(reminder_tableCell.self, forCellReuseIdentifier: "reminderList")
reminder_tableview.delegate = self
reminder_tableview.dataSource = self
tab2_body.addSubview(reminder_tableview)
并且在 UITableViewDelegate 的扩展中,UITableViewDataSource:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {return UITableView.automaticDimension}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {return 50.0}
func numberOfSections(in tableView: UITableView) -> Int {return reminder_category_user.count}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if reminder_tableisready == true {
let category_name = UILabel()
category_name.frame = CGRect(x: 20, y: 0, width: view.frame.width - 80, height: 50)
category_name.font = UIFont(name: "Arial", size: 30)
category_name.text = reminder_category_user[section]
category_name.textColor = UIColor.red
let num_of_reminder = UILabel()
num_of_reminder.frame = CGRect(x: view.frame.width - 75, y: 0, width: 70, height: 50)
num_of_reminder.font = UIFont(name: "Arial", size: 30)
num_of_reminder.text = String(reminder_final_table[section].count)
num_of_reminder.textAlignment = .right
num_of_reminder.textColor = UIColor.red
let headerView = UIView()
headerView.addSubview(category_name)
headerView.addSubview(num_of_reminder)
return headerView
} else {
return UIView()
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if reminder_tableisready == true {
return reminder_final_table[section].count
} else {
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reminderList") as! reminder_tableCell
cell.backgroundColor = UIColor(red: 15/255, green: 15/255, blue: 15/255, alpha: 1)
cell.frame.size.height = 100
cell.textLabel?.text = reminder_final_table[indexPath.section][indexPath.row].pre_title
cell.textLabel?.font = UIFont(name: "Arial", size: 18)
cell.textLabel?.numberOfLines = 0
cell.textLabel?.lineBreakMode = .byWordWrapping
cell.textLabel?.sizeToFit()
let getdatefromdatedue = Date(timeIntervalSince1970: TimeInterval(reminder_final_table[indexPath.section][indexPath.row].pre_datedue))
let duedateformat = DateFormatter()
duedateformat.dateFormat = "MMMM d, yyyy\nh:mm a"
if reminder_final_table[indexPath.section][indexPath.row].pre_datedue != 0 {
cell.layoutMargins.right = 160
cell.reminder_date_due.text = "Date Due\n\(duedateformat.string(from: getdatefromdatedue))"
} else {
cell.reminder_date_due.text = ""
}
cell.reminder_date_due.textColor = UIColor.red
cell.reminder_date_due.textAlignment = .right
cell.reminder_date_due.numberOfLines = 4
cell.reminder_date_due.font = UIFont(name: "Arial", size: 15)
return cell
}
}
class reminder_tableCell: UITableViewCell {
var reminder_date_due = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
reminder_date_due.frame = CGRect(x: reminder_tableview.frame.width - 155, y: 0, width: 150, height: 66)
addSubview(reminder_date_due)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
首先,我强烈建议不要为您的框架(以及一般情况下)使用魔法值,因为如果您想支持所有屏幕尺寸,它很快就会失控.
此外,由于您预先计算了所有框架,因此您实际上并没有使用自动布局。
Apple 引入了 Auto Layout
基于约束的行为,因此您不必为每个屏幕尺寸手动完成所有帧计算工作。 Auto Layout
将根据您设置的 constraints/anchors 动态计算所有视图的大小和位置,无论是使用故事板还是以编程方式。
我还在你的代码中看到你正在使用一个 reminder_tableview
变量,它引用了你的 UITableView
并且让我认为你正在使用你的 table 视图作为全局视图属性:你应该不惜一切代价避免的事情。
对于属性或方法的命名,最佳做法是使用 驼峰式大小写,因为它会使您的代码更易于阅读和理解。驼峰式大小写是当您以小写字母开头的名称,然后大写第二个和所有后续单词的第一个字母,例如:
let reminderTableIsReady = false
var reminderDueDateLabel: UILabel?
func scheduleNewReminder() {}
// ...
命名 类、枚举或结构的普遍接受方式是 大驼峰式:
class ReminderTableViewCell: UITableViewCell {}
现在回到你的代码,我重构并制作了一个最小版本,让你看看它如何与自动布局和约束一起工作,对于 UITableView
和 UITableViewCell
.
我没有从你的代码中添加所有内容,但我认为你可以轻松地自己完成其余部分:
ReminderViewController:
import UIKit
// You don't have to use this Date extension below, but this will improve the performances by keeping only one formatter instance, since you will be reusing it in all your UITableViewCell:
extension Date {
static let formatter = DateFormatter()
func formatted() -> String {
Date.formatter.dateFormat = "MMMM d, yyyy, hh:mm a"
return Date.formatter.string(from: self)
}
}
struct Reminder {
let dueDate: TimeInterval
}
class ReminderViewController: UIViewController {
private let reuseIdentifier = "reuseIdentifier"
private var reminders = [[Reminder]]()
private let tableView = UITableView(frame: .zero, style: .grouped)
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
setupConstraints()
}
func setupViews() {
tableView.delegate = self
tableView.dataSource = self
tableView.register(ReminderTableViewCell.self, forCellReuseIdentifier: reuseIdentifier)
view.addSubview(tableView)
reminders = // ... Set your data here
}
func setupConstraints() {
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
tableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
tableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
}
}
extension ReminderViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return nil
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return CGFloat.leastNonzeroMagnitude
}
}
extension ReminderViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return reminders.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section > reminders.count {
return 0
}
return reminders[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) as! ReminderTableViewCell
let reminder = reminders[indexPath.section][indexPath.row]
let date = Date(timeIntervalSince1970: reminder.dueDate)
if reminder.dueDate > 0 {
cell.dueDateLabel.text = "Due Date: \(date.formatted())"
}
return cell
}
}
ReminderTableViewCell:
class ReminderTableViewCell: UITableViewCell {
let dueDateLabel = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
setupConstraints()
}
func setupViews() {
dueDateLabel.textColor = .red
dueDateLabel.textAlignment = .right
dueDateLabel.numberOfLines = 4
dueDateLabel.font = UIFont(name: "Arial", size: 15)
contentView.addSubview(dueDateLabel)
}
func setupConstraints() {
dueDateLabel.translatesAutoresizingMaskIntoConstraints = false
dueDateLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20).isActive = true
dueDateLabel.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 20).isActive = true
dueDateLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20).isActive = true
dueDateLabel.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -20).isActive = true
}
required init?(coder: NSCoder) {
fatalError()
}
}
您的问题有点不完整,我认为您的问题是 reminder_date_due
标签未按预期获得布局。
首先你没有使用自动布局,你使用的是基于框架的布局系统。
您正在 init 中设置标签的框架,但此时布局引擎尚未正确计算父视图的框架,因此 reminder_tableview.frame.width
无效。
尝试在 func layoutSubviews()
中设置标签的框架。
几天以来我尝试了所有方法,甚至尝试使用 automaticDimension 和 estimatedRowHeight = 44,none 运气不错。我是 UITableView 的新手,我试着用它来练习。我到处查看 Whosebug 等,但没有运气。我不确定下面这段代码做错了什么。
在视图控制器中:
reminder_tableview.frame = view.bounds
reminder_tableview.allowsSelection = false
reminder_tableview.estimatedRowHeight = 44
reminder_tableview.rowHeight = UITableView.automaticDimension
reminder_tableview.register(reminder_tableCell.self, forCellReuseIdentifier: "reminderList")
reminder_tableview.delegate = self
reminder_tableview.dataSource = self
tab2_body.addSubview(reminder_tableview)
并且在 UITableViewDelegate 的扩展中,UITableViewDataSource:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {return UITableView.automaticDimension}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {return 50.0}
func numberOfSections(in tableView: UITableView) -> Int {return reminder_category_user.count}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if reminder_tableisready == true {
let category_name = UILabel()
category_name.frame = CGRect(x: 20, y: 0, width: view.frame.width - 80, height: 50)
category_name.font = UIFont(name: "Arial", size: 30)
category_name.text = reminder_category_user[section]
category_name.textColor = UIColor.red
let num_of_reminder = UILabel()
num_of_reminder.frame = CGRect(x: view.frame.width - 75, y: 0, width: 70, height: 50)
num_of_reminder.font = UIFont(name: "Arial", size: 30)
num_of_reminder.text = String(reminder_final_table[section].count)
num_of_reminder.textAlignment = .right
num_of_reminder.textColor = UIColor.red
let headerView = UIView()
headerView.addSubview(category_name)
headerView.addSubview(num_of_reminder)
return headerView
} else {
return UIView()
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if reminder_tableisready == true {
return reminder_final_table[section].count
} else {
return 0
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "reminderList") as! reminder_tableCell
cell.backgroundColor = UIColor(red: 15/255, green: 15/255, blue: 15/255, alpha: 1)
cell.frame.size.height = 100
cell.textLabel?.text = reminder_final_table[indexPath.section][indexPath.row].pre_title
cell.textLabel?.font = UIFont(name: "Arial", size: 18)
cell.textLabel?.numberOfLines = 0
cell.textLabel?.lineBreakMode = .byWordWrapping
cell.textLabel?.sizeToFit()
let getdatefromdatedue = Date(timeIntervalSince1970: TimeInterval(reminder_final_table[indexPath.section][indexPath.row].pre_datedue))
let duedateformat = DateFormatter()
duedateformat.dateFormat = "MMMM d, yyyy\nh:mm a"
if reminder_final_table[indexPath.section][indexPath.row].pre_datedue != 0 {
cell.layoutMargins.right = 160
cell.reminder_date_due.text = "Date Due\n\(duedateformat.string(from: getdatefromdatedue))"
} else {
cell.reminder_date_due.text = ""
}
cell.reminder_date_due.textColor = UIColor.red
cell.reminder_date_due.textAlignment = .right
cell.reminder_date_due.numberOfLines = 4
cell.reminder_date_due.font = UIFont(name: "Arial", size: 15)
return cell
}
}
class reminder_tableCell: UITableViewCell {
var reminder_date_due = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
reminder_date_due.frame = CGRect(x: reminder_tableview.frame.width - 155, y: 0, width: 150, height: 66)
addSubview(reminder_date_due)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
首先,我强烈建议不要为您的框架(以及一般情况下)使用魔法值,因为如果您想支持所有屏幕尺寸,它很快就会失控.
此外,由于您预先计算了所有框架,因此您实际上并没有使用自动布局。
Apple 引入了 Auto Layout
基于约束的行为,因此您不必为每个屏幕尺寸手动完成所有帧计算工作。 Auto Layout
将根据您设置的 constraints/anchors 动态计算所有视图的大小和位置,无论是使用故事板还是以编程方式。
我还在你的代码中看到你正在使用一个 reminder_tableview
变量,它引用了你的 UITableView
并且让我认为你正在使用你的 table 视图作为全局视图属性:你应该不惜一切代价避免的事情。
对于属性或方法的命名,最佳做法是使用 驼峰式大小写,因为它会使您的代码更易于阅读和理解。驼峰式大小写是当您以小写字母开头的名称,然后大写第二个和所有后续单词的第一个字母,例如:
let reminderTableIsReady = false
var reminderDueDateLabel: UILabel?
func scheduleNewReminder() {}
// ...
命名 类、枚举或结构的普遍接受方式是 大驼峰式:
class ReminderTableViewCell: UITableViewCell {}
现在回到你的代码,我重构并制作了一个最小版本,让你看看它如何与自动布局和约束一起工作,对于 UITableView
和 UITableViewCell
.
我没有从你的代码中添加所有内容,但我认为你可以轻松地自己完成其余部分:
ReminderViewController:
import UIKit
// You don't have to use this Date extension below, but this will improve the performances by keeping only one formatter instance, since you will be reusing it in all your UITableViewCell:
extension Date {
static let formatter = DateFormatter()
func formatted() -> String {
Date.formatter.dateFormat = "MMMM d, yyyy, hh:mm a"
return Date.formatter.string(from: self)
}
}
struct Reminder {
let dueDate: TimeInterval
}
class ReminderViewController: UIViewController {
private let reuseIdentifier = "reuseIdentifier"
private var reminders = [[Reminder]]()
private let tableView = UITableView(frame: .zero, style: .grouped)
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
setupConstraints()
}
func setupViews() {
tableView.delegate = self
tableView.dataSource = self
tableView.register(ReminderTableViewCell.self, forCellReuseIdentifier: reuseIdentifier)
view.addSubview(tableView)
reminders = // ... Set your data here
}
func setupConstraints() {
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
tableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
tableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
}
}
extension ReminderViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
return nil
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
return CGFloat.leastNonzeroMagnitude
}
}
extension ReminderViewController: UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return reminders.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section > reminders.count {
return 0
}
return reminders[section].count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) as! ReminderTableViewCell
let reminder = reminders[indexPath.section][indexPath.row]
let date = Date(timeIntervalSince1970: reminder.dueDate)
if reminder.dueDate > 0 {
cell.dueDateLabel.text = "Due Date: \(date.formatted())"
}
return cell
}
}
ReminderTableViewCell:
class ReminderTableViewCell: UITableViewCell {
let dueDateLabel = UILabel()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
setupConstraints()
}
func setupViews() {
dueDateLabel.textColor = .red
dueDateLabel.textAlignment = .right
dueDateLabel.numberOfLines = 4
dueDateLabel.font = UIFont(name: "Arial", size: 15)
contentView.addSubview(dueDateLabel)
}
func setupConstraints() {
dueDateLabel.translatesAutoresizingMaskIntoConstraints = false
dueDateLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20).isActive = true
dueDateLabel.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 20).isActive = true
dueDateLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20).isActive = true
dueDateLabel.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -20).isActive = true
}
required init?(coder: NSCoder) {
fatalError()
}
}
您的问题有点不完整,我认为您的问题是 reminder_date_due
标签未按预期获得布局。
首先你没有使用自动布局,你使用的是基于框架的布局系统。
您正在 init 中设置标签的框架,但此时布局引擎尚未正确计算父视图的框架,因此 reminder_tableview.frame.width
无效。
尝试在 func layoutSubviews()
中设置标签的框架。