Ios 在ViewController中使用带有外部数据源和UITableView的自定义UITableViewCell

Ios 在ViewController中使用带有外部数据源和UITableView的自定义UITableViewCell,ios,uitableview,swift3,uistoryboard,Ios,Uitableview,Swift3,Uistoryboard,好的,我想在UITableView中有一个自定义UITableViewCell 我需要每个组件尽可能模块化和可重用,因此我决定将它们都放在不同的类中: 我的设置现在如下所示: 我有一个用于我的数据源的swift文件,一个用于我的CustomTableViewCell的文件,在我的故事板中,我在其他UIvie旁边有一个UITableView,我声明使用自定义单元格 情节提要如下所示: import UIKit class MyShitViewController: UIViewControlle

好的,我想在UITableView中有一个自定义UITableViewCell

我需要每个组件尽可能模块化和可重用,因此我决定将它们都放在不同的类中:

我的设置现在如下所示: 我有一个用于我的数据源的swift文件,一个用于我的CustomTableViewCell的文件,在我的故事板中,我在其他UIvie旁边有一个UITableView,我声明使用自定义单元格

情节提要如下所示:

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
    }
}
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
    }
}
我不知道为什么它不工作,可能数据源没有按预期工作,或者将数据源分配给TableView

CustomTableViewCell应该没有问题,因为它以前工作过,我更改了视图添加到
self.contentView
的代码

实际产量:

预期产出:
数据源被定义为弱:

weak open var dataSource: UITableViewDataSource?
您的代码:

let dataSource = ContentItemDataSource(items: importantItems)
importantTableView.dataSource = dataSource
您不持有引用,所以在方法结束后它将再次为零

解决方案:定义一个类变量并根据需要保留它

var dataSource: UITableViewDataSource!
以及:


顺便说一句:你没有设置importantattableview.delegate=self。哦,好的一点:D我有一段时间在里面了:)但是我使用了一个插座,因为我尝试了很多方法让它工作,并且认为这可能解决问题,正如我在其他项目中看到的那样。你猜怎么着:这并不是非常感谢你,我甚至没有想到这个事实。你把我的房间弄得乱七八糟,因为它终于为我解决了:你似乎对编写可重用组件感兴趣。也许你对这个链接感兴趣:这是编写应用程序的一种非常有趣的方式:D我将在下一个项目中记住它:)