【问题标题】:Using a custom UITableViewCell w/ external DataSource and UITableView in a ViewController在 ViewController 中使用带有外部 DataSource 和 UITableView 的自定义 UITableViewCell
【发布时间】: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 的代码。

实际输出:

预期输出:

【问题讨论】:

  • 您似乎对编写可重用组件感兴趣。也许您对这个链接感兴趣:stackoverflow.com/a/43426337/6595536
  • 这是一种非常有趣的应用程序编码方式 :D 我会在下一个项目中记住它:)

标签: ios uitableview swift3 uistoryboard


【解决方案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
猜你喜欢
  • 1970-01-01
  • 2012-02-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-03
相关资源
最近更新 更多