从单元格中的超级视图中删除视图

Posted

技术标签:

【中文标题】从单元格中的超级视图中删除视图【英文标题】:Removing views from superview in cell 【发布时间】:2018-06-18 18:08:57 【问题描述】:

我有一个单元类“NewsCell”(UITableViewCell 的子类),用于两种不同类型的新闻:OrganizationNews 和 ProjectNews。这些新闻有共同的东西,但有些元素是不同的。也就是说,当我的单元格用于 ProjectNews 时,我想隐藏组织的徽标,当它用于 OrganizationNews 时,我想隐藏项目的名称按钮。

我有 'configureCell(_, forNews, ofProject)' 方法。我在“NewsViewController”中调用它。我使用了“removeFromSuperview”方法,因为我需要重新排列“NewsCell”中的元素。改变 'isHidden' 的值不会给我那种效果。

所以,这就是问题所在。我在 projectNameButton.removeFromSuperview() 或 logoImageView.removeFromSuperview() 行中有“线程 1:致命错误:在展开可选值时意外发现 nil”异常。 我该怎么办?

// NewsViewController.swift
func configureCell(_ cell: NewsCell, forNews news: News, ofProject project: Project? = nil) 
    //...
    if news is OrganizationNews 
        cell.projectNameButton.removeFromSuperview()
     else if news is ProjectNews 
        cell.logoImageView.removeFromSuperview()
    
    // ...



override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
    let news = newsCollection[indexPath.row]
    let cell = tableView.dequeueReusableCell(withIdentifier: TableViewCellIdentifiers.newsCell, for: indexPath) as! NewsCell
    configureCell(cell, forNews: news)
    cell.delegate = self
    return cell

【问题讨论】:

这只是故事板的出口。 IBOutlet 弱 var logoImageView: UIImageView!和 IBOutlet 弱 var projectNameButton: UIButton! 要么使用两个不同的单元格类,要么将逻辑显示在单元格类中,而不是在视图控制器中。 @rmaddy 当这个“配置”方法在单元类中时,我遇到了同样的问题。 您的网点未连接。 【参考方案1】:

UITableView 或 UICollectionView 建立在 reuse 概念之上,当您处理它时,单元格会被重用和重新填充。

当您尝试调用dequeReusableCell(withIdentifier:) 时,它有时会返回之前创建的内容。因此,假设您在具有所有控件的东西之前先出队,然后删除了一个(removeFromSuperview),然后再次尝试出队,新出队的人可能没有子视图。

我认为对你来说最好的解决方案是制造两个不同的细胞。

示例:

class BaseNewsCell: UITableViewCell 
    // Put the common views here


class OrganizationNewsCell: BaseNewsCell 
    // Put here things that are ONLY for OrganizationNewsCell


class ProjectNewsCell: BaseNewsCell 
    // Put here things that are ONLY for ProjectNewsCell

然后通过两个不同的故事板单元 xibs 将它们从 2 个不同的标识符中提取出来。

或者

class BaseNewsCell: UITableViewCell 
    // Put the common views here


class OrganizationNewsCell: BaseNewsCell 
    // This happens when this kind of cell is created for the first time
    override func awakeFromNib() 
        super.awakeFromNib()
        someNonCommon.removeFromSuperview()
    


class ProjectNewsCell: BaseNewsCell 
    override func awakeFromNib() 
        super.awakeFromNib()
        someOtherNonCommon.removeFromSuperview()
    

注意:这违反了 Liskov 原则(SOLID 原则之一),因为您从子类中的超类中删除了功能。

【讨论】:

【参考方案2】:

如下改变删除线,

if news is OrganizationNews 
    cell.projectNameButton?.removeFromSuperview()
 else if news is ProjectNews 
    cell.logoImageView?.removeFromSuperview()

这将解决问题。但一个好的方法是为每个单元创建单独的类。您可以创建一个基类来保留公共逻辑。

【讨论】:

这样可以避免崩溃。它不能解决问题的原因。【参考方案3】:

您不应该从单元格的外部移除子视图。让我们重构您的代码。

NewsCell.swift

final class NewsCell: UITableViewCell 
    enum Kind 
        case organization
        case project
    

    var logoImageView: UIImageView?
    let nameLabel = UILabel()

    var kind: NewsCell.Kind 
        didSet 
            if kind != oldValue 
                setupLogoImageView()
                self.setNeedsLayout()
            
        
    

    init(kind: NewsCell.Kind, reuseIdentifier: String?) 
        self.kind = kind
        super.init(style: .default, reuseIdentifier: reuseIdentifier)
    

    required init?(coder aDecoder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    


// MARK: - Positioning
extension NewsCell 
    override func layoutSubviews() 
        super.layoutSubviews()

        // Your layouting
        switch kind 
            case .organization:
                // Setup frame for organization typed NewsCell
            case .project:
                // Setup frame for project typed NewsCell
        
    


// MARK: - Setup
extension NewsCell 
    private func setupLogoImageView() 
        logoImageView = kind == .organization ? UIImageView() : nil
    

使用方法:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
    let news = newsCollection[indexPath.row]

    var cell = tableView.dequeueReusableCell(withIdentifier: TableViewCellIdentifiers.newsCell) as? NewsCell
    if cell == nil 
        cell = NewsCell(kind: .organization, reuseIdentifier: TableViewCellIdentifiers.newsCell)
    

    cell!.kind = news is Organization ? .organization: .project

    return cell!

【讨论】:

以上是关于从单元格中的超级视图中删除视图的主要内容,如果未能解决你的问题,请参考以下文章

如何从表格视图单元格中的图像手势获取 indexPath

使用协议从集合视图单元格中删除核心数据

如何更改 Swift 中表格视图单元格中的默认删除按钮?

从表格视图单元格中删除 UIlabel 文本

如何让单元格中的内容视图占据整个单元格?

Obj-C - 点击自定义表格视图单元格中的删除表格视图行按钮