在 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的主要内容,如果未能解决你的问题,请参考以下文章

iOS - 从外部类引用活动故事板视图控制器

带有条件 ViewController 的情节提要

(目标:对象)不适用于从 viewController 外部设置 UIButton

在循环中删除所有带有其他实例视图的 viewController

如何在 viewController 中使用 UITabbar?

从SuperView的ViewController外部的类调用时,willRemoveSubview不会删除子视图吗?