在 ViewController 中使用带有外部 DataSource 和 UITableView 的自定义 UITableViewCell
Posted
技术标签:
【中文标题】在 ViewController 中使用带有外部 DataSource 和 UITableView 的自定义 UITableViewCell【英文标题】:Using a custom UITableViewCell w/ external DataSource and UITableView in a ViewController 【发布时间】:2017-04-17 20:25:45 【问题描述】:好的,我想在 UITableView 中有一个自定义 UITableViewCell。
我需要尽可能模块化和可重用的每个组件,因此我决定将它们全部放在不同的类中:
我的设置现在如下所示: 我的 DataSoure 有一个 swift 文件,我的 CustomTableViewCell 有一个文件,在我的故事板中,我在其他 UIViees 旁边有一个 UITableView,我声明使用自定义单元格。
故事板如下所示:
TableView(属性检查器):
TableView(身份检查器):
TableView(大小检查器):
TableViewCell(身份检查器):
TableViewCell(属性检查器):
TableViewCell(大小检查器):
我的 ViewController 类如下所示:
import UIKit
class MyShitViewController: UIViewController, UITableViewDelegate
@IBOutlet weak var importantTableView: UITableView!
var importantItems = [ContentItem]()
override func viewWillAppear(_ animated: Bool)
super.viewWillAppear(animated)
override func viewDidLoad()
super.viewDidLoad()
// Test data
importantItems.append(ContentItem(contentType: 1, image: #imageLiteral(resourceName: "ContentIcon"), title: "SQL - Basics", subject: "informatics", grade: 11, progress: 35, action: ContentItem.ACTION_MORE))
importantItems.append(ContentItem(contentType: 1, image: #imageLiteral(resourceName: "ContentIcon"), title: "SQL - Pros", subject: "informatics", grade: 12, progress: 0, action: ContentItem.ACTION_MORE))
// Data source
let dataSource = ContentItemDataSource(items: importantItems)
importantTableView.rowHeight = 75
importantTableView.dataSource = dataSource
importantTableView.reloadData()
//MARK: Table view delegate
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
// For debugging, never get's called, when some one clicks on any cell
let row = indexPath.row
print(row)
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
print("height")
return 48
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat
print("estimated height")
return 48
我的 CustomTableViewCell 类:
import UIKit
class ContentItemView: UITableViewCell
//MARK: Properties
var contentItem: ContentItem?
private var contentImageView: UIImageView?
private var primaryTextView: UILabel?
private var secondaryTextView: UILabel?
private var progressView: UILabel?
private var actionView: UIButton?
private var verifiedIcon: UIImageView?
private var layoutConstraints: [NSLayoutConstraint] = []
//MARK: Initialisation
func setContent(item: ContentItem)
self.contentItem = item
setContent()
override init(style: UITableViewCellStyle, reuseIdentifier: String?)
super.init(style: style, reuseIdentifier: reuseIdentifier)
print(style)
setUpView()
required init?(coder: NSCoder)
super.init(coder: coder)
print("coder")
setUpView()
//MARK: Set Up
private func setUpView()
self.backgroundView?.backgroundColor = Colors.biology
self.textLabel?.text = "Test"
// Create views
contentImageView = UIImageView()
primaryTextView = UILabel()
secondaryTextView = UILabel()
progressView = UILabel()
actionView = UIButton()
verifiedIcon = UIImageView()
// Add Content to views
primaryTextView?.font = getFont(withSize: 14)
primaryTextView?.textColor = Colors.toolbarColor
secondaryTextView?.font = getFont(withSize: 12)
secondaryTextView?.textColor = Colors.toolbarColor
progressView?.font = getFont(withSize: 12)
progressView?.textColor = Colors.toolbarColor
// Add sub views
self.contentView.addSubview(contentImageView!)
self.contentView.addSubview(primaryTextView!)
self.contentView.addSubview(secondaryTextView!)
self.contentView.addSubview(progressView!)
self.contentView.addSubview(actionView!)
self.contentView.addSubview(verifiedIcon!)
// Apply Constraints
makeViewConstraints()
// MARK: Layout
private func setContent()
contentImageView?.image = contentItem?.image
primaryTextView?.text = contentItem?.title
secondaryTextView?.text = (contentItem?.done)! ? "DONE" : (contentItem?.subject)! + " - " + getLocalizedGrade(_for: (contentItem?.grade)!)
progressView?.text = contentItem?.progress != nil ? "\(String(describing: contentItem?.progress))%" : ""
if (contentItem?.verified)! verifiedIcon?.image = #imageLiteral(resourceName: "verified")
else verifiedIcon?.image = nil
let actionImage = getActionImage()
actionView?.setImage(actionImage, for: .normal)
private func makeViewConstraints()
// Clear constraints
self.contentView.removeConstraints(layoutConstraints)
layoutConstraints.removeAll()
// Force elements to exist
let imageView = self.contentImageView!
let primaryTextView = self.primaryTextView!
let secondaryTextView = self.secondaryTextView!
let progressView = self.progressView!
let actionView = self.actionView!
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.widthAnchor.constraint(equalToConstant: 48)
imageView.heightAnchor.constraint(equalToConstant: 48)
layoutConstraints.append(
NSLayoutConstraint(item: imageView, attribute: .leading, relatedBy: .equal,
toItem: self.contentView, attribute: .leading, multiplier: 1, constant: 0))
layoutConstraints.append(
NSLayoutConstraint(item: imageView, attribute: .centerY, relatedBy: .equal,
toItem: self.contentView, attribute: .centerY, multiplier: 1, constant: 0))
primaryTextView.translatesAutoresizingMaskIntoConstraints = false
layoutConstraints.append(
NSLayoutConstraint(item: primaryTextView, attribute: .leading, relatedBy: .equal,
toItem: imageView, attribute: .trailing, multiplier: 1, constant: 16))
layoutConstraints.append(
NSLayoutConstraint(item: primaryTextView, attribute: .top, relatedBy: .equal,
toItem: self.contentView, attribute: .top, multiplier: 1, constant: 8))
secondaryTextView.translatesAutoresizingMaskIntoConstraints = false
layoutConstraints.append(
NSLayoutConstraint(item: secondaryTextView, attribute: .leading, relatedBy: .equal,
toItem: imageView, attribute: .trailing, multiplier: 1, constant: 16))
layoutConstraints.append(
NSLayoutConstraint(item: secondaryTextView, attribute: .bottom, relatedBy: .equal,
toItem: self.contentView, attribute: .bottom, multiplier: 1, constant: -8))
progressView.translatesAutoresizingMaskIntoConstraints = false
layoutConstraints.append(
NSLayoutConstraint(item: progressView, attribute: .centerX, relatedBy: .equal,
toItem: imageView, attribute: .centerX, multiplier: 1, constant: 0))
layoutConstraints.append(
NSLayoutConstraint(item: progressView, attribute: .centerY, relatedBy: .equal,
toItem: imageView, attribute: .centerY, multiplier: 1, constant: 0))
actionView.translatesAutoresizingMaskIntoConstraints = false
layoutConstraints.append(
NSLayoutConstraint(item: actionView, attribute: .trailing, relatedBy: .equal,
toItem: self.contentView, attribute: .trailing, multiplier: 1, constant: 0))
layoutConstraints.append(
NSLayoutConstraint(item: actionView, attribute: .centerY, relatedBy: .equal,
toItem: self.contentView, attribute: .centerY, multiplier: 1, constant: 0))
self.contentView.addConstraints(layoutConstraints)
// MARK: Additional Helpers
private func getActionImage() -> UIImage?
if contentItem?.action == ContentItem.ACTION_MORE
return #imageLiteral(resourceName: "ic_more_horiz_white")
if contentItem?.action == ContentItem.ACTION_ADD
return #imageLiteral(resourceName: "ic_add_circle_outline_white")
if contentItem?.action == ContentItem.ACTION_REMOVE
return #imageLiteral(resourceName: "ic_remove_circle_outline_white")
return nil
最后是我的数据源:
import UIKit
class ContentItemDataSource: NSObject, UITableViewDataSource
var items = [ContentItem]()
init(items: [ContentItem])
self.items = items
// MARK: - Table view data source
func numberOfSectionsInTableView(tableView: UITableView) -> Int
return 1
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
return items.count
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
let item = items[indexPath.row]
guard let cell = tableView.dequeueReusableCell(withIdentifier: Config.CONTENT_ITEM_CELL, for: indexPath) as? ContentItemView else
fatalError("The dequeued cell is not an instance of ContentItemView.")
cell.setContent(item: item)
return cell
我不知道为什么它不起作用,可能是 DataSource 没有按预期工作或将 dataSource 分配给 TableView...
CustomTableViewCell 应该没问题,因为它以前工作过,我更改了将我的视图添加到 self.contentView
的代码。
实际输出:
预期输出:
【问题讨论】:
您似乎对编写可重用组件感兴趣。也许您对这个链接感兴趣:***.com/a/43426337/6595536 这是一种非常有趣的应用程序编码方式 :D 我会在下一个项目中记住它:) 【参考方案1】:数据源被定义为弱:
weak open var dataSource: UITableViewDataSource?
您的代码:
let dataSource = ContentItemDataSource(items: importantItems)
importantTableView.dataSource = dataSource
您没有持有引用,因此在方法结束后它将再次为零。
解决方案:定义一个类 var 并根据需要保留它。
var dataSource: UITableViewDataSource!
和:
let dataSource = ContentItemDataSource(items: importantItems)
importantTableView.dataSource = dataSource
self.dataSource = dataSource
【讨论】:
顺便说一句:你没有设置importantTableView.delegate = self
。
哦,好点 :D 我在那里有一段时间了 :) 但我使用了一个插座,因为我尝试了很多方法来让它工作,并认为这可以解决问题,正如我在其他项目。你猜怎么着:它没有?
非常感谢,我什至没有考虑这个事实。你让我跳起来扔我的房间,因为它终于对我有用了:D以上是关于在 ViewController 中使用带有外部 DataSource 和 UITableView 的自定义 UITableViewCell的主要内容,如果未能解决你的问题,请参考以下文章
(目标:对象)不适用于从 viewController 外部设置 UIButton
在循环中删除所有带有其他实例视图的 viewController