Swift纯代码实现TableView嵌套:内部TableView无法显示求助
问题:纯代码实现TableView嵌套时内部TableView无法显示
我正尝试用纯代码实现TableView嵌套(目的是搞懂Storyboard实现的底层原理),但目前遇到内部TableView完全无法显示的问题。之前试过把内部TableView放进单独View里,虽然能打印内部日志,但整个UI布局彻底混乱了。
相关代码
外部ViewController代码
class ContactsViewController: UIViewController, UITabBarDelegate, UITableViewDataSource, UITableViewDelegate { private let contacts = ContactAPI.getData() let contactsTableView = UITableView() override func viewDidLoad() { super.viewDidLoad() view.addSubview(contactsTableView) contactsTableView.translatesAutoresizingMaskIntoConstraints = false // 启用自动布局 contactsTableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true contactsTableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true contactsTableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true contactsTableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true contactsTableView.dataSource = self contactsTableView.delegate = self contactsTableView.register(ContactsCell.self, forCellReuseIdentifier: "contactCell") contactsTableView.rowHeight = 1000 contactsTableView.estimatedRowHeight = 100 } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.contacts.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "contactCell", for: indexPath) as! ContactsCell cell.jobLabel.text = contacts[indexPath.row].jobTitle cell.nameLabel.text = contacts[indexPath.row].name return cell } }
外部TableViewCell代码
class ContactsCell: UITableViewCell { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) contentView.addSubview(image) contentView.addSubview(nameLabel) contentView.addSubview(jobLabel) contentView.addSubview(tablello) } // 已实现的image、nameLabel、jobLabel代码 let tablello: UITableView = { let tab = UITableView() let data = innerTable() tab.dataSource = data tab.delegate = data tab.frame = CGRect (x: 0, y: 0, width: 300, height: 300) tab.register(gInnerCell.self, forCellReuseIdentifier: "gennaroCell") tab.translatesAutoresizingMaskIntoConstraints = false return tab }() }
内部相关类代码
class innerTable: UIViewController, UITableViewDelegate, UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 10 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "gennaroCell", for: indexPath) as! gInnerCell cell.labelloNapoli.text = "\(indexPath.row)" print("AO GENNà") return cell } override func viewDidLoad() { super.viewDidLoad() // 加载视图后的额外设置 print("loaded inner") } } class gInnerCell: UITableViewCell { override func awakeFromNib() { super.awakeFromNib() // 初始化代码 } override func setSelected(_ selected: Bool, animated: Bool) { super.setSelected(selected, animated: animated) // 配置选中状态的视图 } override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) contentView.addSubview(labelloNapoli) } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } let labelloNapoli:UILabel = { let lable = UILabel() lable.frame = CGRect(x: 0, y: 0, width: 300, height: 300) return lable }() }
问题原因与解决办法
核心问题点
- 数据源/代理设计错误:用
innerTable这个UIViewController实例当内部TableView的数据源,但该VC未被加入视图层级,生命周期无法正常触发,且用VC做数据源完全没必要。 - 布局约束缺失:内部TableView开启了自动布局,却只设置了frame——AutoLayout环境下frame会被忽略,导致TableView没有尺寸,自然无法显示。
- 内部Cell布局错误:内部Cell的Label用固定frame,未添加AutoLayout约束,且未设置Cell行高,就算显示也会布局混乱。
具体修改步骤
1. 重构内部数据源类
把innerTable改成普通NSObject子类,去掉UIViewController继承:
class InnerTableDataSource: NSObject, UITableViewDelegate, UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 10 } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "gennaroCell", for: indexPath) as! gInnerCell cell.labelloNapoli.text = "\(indexPath.row)" print("AO GENNà") return cell } // 给内部Cell设置合理行高 func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return 44 } }
2. 修正外部Cell的布局与数据源绑定
给内部TableView添加AutoLayout约束,并正确绑定数据源:
class ContactsCell: UITableViewCell { // 初始化内部数据源实例 private let innerDataSource = InnerTableDataSource() override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) contentView.addSubview(image) contentView.addSubview(nameLabel) contentView.addSubview(jobLabel) contentView.addSubview(tablello) // 给内部TableView添加约束(可根据UI需求调整参数) NSLayoutConstraint.activate([ tablello.topAnchor.constraint(equalTo: jobLabel.bottomAnchor, constant: 16), tablello.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16), tablello.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16), tablello.heightAnchor.constraint(equalToConstant: 200), tablello.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -16) ]) // 务必给image、nameLabel、jobLabel也补全AutoLayout约束 } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } // 已实现的image、nameLabel、jobLabel代码 let tablello: UITableView = { let tab = UITableView() tab.register(gInnerCell.self, forCellReuseIdentifier: "gennaroCell") tab.translatesAutoresizingMaskIntoConstraints = false return tab }() // 在布局完成后绑定数据源和代理 override func layoutSubviews() { super.layoutSubviews() tablello.dataSource = innerDataSource tablello.delegate = innerDataSource } }
3. 修正内部Cell的布局
给内部Cell的Label添加AutoLayout约束,抛弃固定frame:
class gInnerCell: UITableViewCell { override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) contentView.addSubview(labelloNapoli) // 添加约束让Label撑满Cell内容区 NSLayoutConstraint.activate([ labelloNapoli.topAnchor.constraint(equalTo: contentView.topAnchor), labelloNapoli.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16), labelloNapoli.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), labelloNapoli.bottomAnchor.constraint(equalTo: contentView.bottomAnchor) ]) } required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } let labelloNapoli: UILabel = { let label = UILabel() label.translatesAutoresizingMaskIntoConstraints = false // 启用自动布局 label.font = UIFont.systemFont(ofSize: 16) return label }() }
4. 调整外部TableView的行高
把固定行高改成自动行高,适配内部内容:
// 在ContactsViewController的viewDidLoad里替换原行高设置 contactsTableView.rowHeight = UITableView.automaticDimension contactsTableView.estimatedRowHeight = 300 // 设置合理的预估高度
内容的提问来源于stack exchange,提问作者Noob lvl 1
相关产品推荐
相关产品推荐

