【问题标题】:Custom UITableViewCell does not display correctly自定义 UITableViewCell 无法正确显示
【发布时间】:2020-12-13 16:30:48
【问题描述】:

我创建了一个自定义 UITableViewCell 以在 TableView 中使用。该单元格在情节提要中正确显示,但是当我在 TableView 中显示它时,它看起来完全混乱。我看过类似的问题,但是他们的问题似乎是对出口和 Xcode 错误的命名。我错过了什么?

UITableViewCell

class ArticleLargeTableViewCell: UITableViewCell {

public static let Identifier = "ArticleLargeTableViewCell"

@IBOutlet weak var shareButton: UIButton!
@IBOutlet weak var favoriteButton: UIButton!
@IBOutlet weak var articleImage: UIImageView!
@IBOutlet weak var articleType: UIImageView!
@IBOutlet weak var articleTitle: UILabel!
@IBOutlet weak var articleDate: UILabel!
@IBOutlet weak var articleSource: UILabel!
@IBOutlet weak var articleFavicon: UIImageView!
private var shareClickListener: (Article) -> Void = { _ in }
private var favoriteClickListener: (Article) -> Void = { _ in }
private var article: Article!

override class func awakeFromNib() {
    super.awakeFromNib()
}

func setData(article: Article, shareClickListener: @escaping (Article) -> Void, favoriteClickListener: @escaping (Article) -> Void, shouldShowMoreButton: Bool = true) {
    self.article = article
    articleImage.sd_setImage(with: article.getImageUrl())
    articleImage.contentMode = UIView.ContentMode.scaleAspectFill
    articleImage.clipsToBounds = true
    articleTitle.text = article.title
    let textColor = UIColor(named: "text")
    articleTitle.textColor = textColor
    articleSource.text = article.source.title
    articleSource.textColor = textColor
    articleDate.textColor = UIColor(named: "caption")
    articleDate.text = article.date.timeAgo()
    self.shareClickListener = shareClickListener
    self.favoriteClickListener = favoriteClickListener
    articleType.image = article.articleType().getTypeImage()
    articleFavicon.sd_setImage(with: URL(string: article.faviconUrl)!)
    favoriteButton.addTarget(self, action: #selector(self.onFavoriteButtonClick), for: .touchUpInside)
    shareButton.addTarget(self, action: #selector(self.onShareButtonClick), for: .touchUpInside)
}

@objc func onShareButtonClick() {
    shareClickListener(article)
}

@objc func onFavoriteButtonClick() {
    favoriteClickListener(article)
}
}

视图控制器

import Foundation
import UIKit
import Combine
import MaterialComponents.MaterialActionSheet

class ArticlesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    
    @IBOutlet weak var articlesList: UITableView!
    private var articles: [Article] = []
    var viewModel: ArticlesViewModel!
    private var useCompactView: Bool = true
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        articlesList.dataSource = self
        articlesList.delegate = self
        viewModel.$viewState.sink(receiveCompletion: { _ in}, receiveValue: { viewState in
            switch viewState {
            case .loading:
                print()
            case .error:
                print()
            case let .data(data):
                self.setData(data: data)
            }
        })
    }
    
    private func setData(data: ArticlesViewModel.ViewState.Data) {
        self.articles = data.articles
        self.title = data.title
        self.useCompactView = data.useCompactView
        if self.useCompactView {
            articlesList.register(UINib(nibName: ArticleUiTableViewCell.Identifier, bundle: Bundle.main), forCellReuseIdentifier: ArticleUiTableViewCell.Identifier)
        } else {
            articlesList.register(UINib(nibName: ArticleLargeTableViewCell.Identifier, bundle: Bundle.main), forCellReuseIdentifier: ArticleLargeTableViewCell.Identifier)
        }
        articlesList.reloadData()
        articlesList.refreshControl?.endRefreshing()
    }
    
    func numberOfSections(in tableView: UITableView) -> Int {
        1
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return articles.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if useCompactView {
            guard let cell = articlesList.dequeueReusableCell(withIdentifier: ArticleUiTableViewCell.Identifier, for: indexPath) as? ArticleUiTableViewCell else {
                fatalError("could not cast to articles cell")
            }
            // display other cell
        } else {
            guard let cell = articlesList.dequeueReusableCell(withIdentifier: ArticleLargeTableViewCell.Identifier, for: indexPath) as? ArticleLargeTableViewCell else {
                fatalError("could not cast to articles cell")
            }
            let article = articles[indexPath.item]
            cell.setData(article: article, shareClickListener: { article in
                self.shareLink(description: article.title, url: article.originalUrl)
            }, favoriteClickListener: { article in
                self.viewModel.favoriteArticle(article: article, delegate: UIApplication.shared.delegate)
            })
            return cell
        }
    }
 
}

自动布局

输出

我已尝试返回一个明确的高度,如下所示,但我收到了类似的错误。

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    return 467
}

【问题讨论】:

  • 检查单元格高度
  • 您没有显示大图像的高度约束...它是一个明确的值,还是一个比率?我假设您有一些图像中没有显示的东西。但是,您也没有在内容视图的底部显示任何约束,这可能是关键问题。从“收藏夹”按钮的底部添加一个约束到内容视图的底部,看看是否可以修复它。
  • 大图只有 18:9 的纵横比。我添加了一个约束 bottom = Favorite.bottom +4 但仍然有这个问题@DonMag
  • 您是否将“ArticleLargeTableViewCell”的“内容视图”类设置为 UIView 的某个自定义子类?当我这样做时遇到了类似的问题,当我尝试通过添加自定义子类为我的所有 UIView 添加外观时,所有子视图和标签都向左收缩。
  • @paky 哇。就是这样。我将其设置为 MDCard。发表您的评论作为答案,我会接受。如果您将解决方法包括在内,我也很高兴(我如何仍将所有内容包装在 MDCard 中)

标签: ios swift uitableview


【解决方案1】:

您应该返回正确的单元格高度,以便 UITableViewCell 正确显示。 有两种方式:

  1. 固定高度:在UITableViewDelegate中实现该函数并返回值
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
//...
}
  1. 如果您希望单元格根据其内容扩展其高度。试试下面的两个功能
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
//...
}

【讨论】:

  • 即使我在 heightForRowAt 返回 500 我也会遇到这个问题
【解决方案2】:

编辑

在看到来自 OP 的其他 cmets 之后 - 他正在 *** 更改单元格的 contentView 的类 - 这个答案没有实际意义(尽管它确实产生了一个有效的单元格)。

这突出了在此处发布问题时包含完整详细信息的原因之一。


原答案

这有点难以弄清楚,因为您没有显示所有的约束。

这是一个应该得到你想要的东西的例子(或者,至少,把你带到你需要的地方)......

布局如下:

注意课堂作业:

为了测试这个,这里是示例代码:

class ArticleLargeTableViewCell: UITableViewCell {
    
    public static let Identifier = "ArticleLargeTableViewCell"
    
    @IBOutlet var title: UILabel!
    @IBOutlet var source: UILabel!

}

class ArticlesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    
    @IBOutlet weak var articlesList: UITableView!

    let myData: [String] = [
        "Source text for first row.",
        "Longer source text which should be enough to word-wrap when running on an iPhone.",
        "This source text\nhas embedded\nnewline characters\nto get four rows in the label."
    ]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
//      articlesList.register(UINib(nibName: ArticleUiTableViewCell.Identifier, bundle: Bundle.main), forCellReuseIdentifier: ArticleUiTableViewCell.Identifier)
        articlesList.register(UINib(nibName: ArticleLargeTableViewCell.Identifier, bundle: Bundle.main), forCellReuseIdentifier: ArticleLargeTableViewCell.Identifier)

        articlesList.dataSource = self
        articlesList.delegate = self
    }
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return myData.count
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: ArticleLargeTableViewCell.Identifier, for: indexPath) as! ArticleLargeTableViewCell
        cell.title.text = "Row \(indexPath.row)"
        cell.source.text = myData[indexPath.row]
        return cell
    }
}

这是ArticleLargeTableViewCell.xib文件的来源:

<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="16096" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
    <device id="retina6_1" orientation="portrait" appearance="light"/>
    <dependencies>
        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <objects>
        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
        <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" rowHeight="402" id="9CN-96-cbN" customClass="ArticleLargeTableViewCell" customModule="TableAdd" customModuleProvider="target">
            <rect key="frame" x="0.0" y="0.0" width="414" height="402"/>
            <autoresizingMask key="autoresizingMask"/>
            <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="9CN-96-cbN" id="hnF-dN-5dt">
                <rect key="frame" x="0.0" y="0.0" width="414" height="402"/>
                <autoresizingMask key="autoresizingMask"/>
                <subviews>
                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Random Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4cS-dj-yG9">
                        <rect key="frame" x="60" y="8" width="108" height="21"/>
                        <fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
                        <nil key="textColor"/>
                        <nil key="highlightedColor"/>
                    </label>
                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="6 minutes ago" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="FaY-ve-Qel">
                        <rect key="frame" x="60" y="29" width="109" height="21"/>
                        <fontDescription key="fontDescription" type="system" pointSize="17"/>
                        <nil key="textColor"/>
                        <nil key="highlightedColor"/>
                    </label>
                    <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="JO1-nS-Fg0">
                        <rect key="frame" x="8" y="8" width="44" height="44"/>
                        <color key="backgroundColor" red="1" green="0.14913141730000001" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                        <constraints>
                            <constraint firstAttribute="width" secondItem="JO1-nS-Fg0" secondAttribute="height" multiplier="1:1" id="ozT-ci-zDr"/>
                            <constraint firstAttribute="width" constant="44" id="z0X-tH-CKE"/>
                        </constraints>
                    </imageView>
                    <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="tub-UY-Qc6">
                        <rect key="frame" x="0.0" y="129" width="414" height="207"/>
                        <color key="backgroundColor" red="0.45009386540000001" green="0.98132258650000004" blue="0.4743030667" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                        <constraints>
                            <constraint firstAttribute="width" secondItem="tub-UY-Qc6" secondAttribute="height" multiplier="18:9" id="Kyi-bC-PWn"/>
                        </constraints>
                    </imageView>
                    <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="bBl-zC-cYt">
                        <rect key="frame" x="386" y="8" width="20" height="20"/>
                        <color key="backgroundColor" red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                        <constraints>
                            <constraint firstAttribute="width" secondItem="bBl-zC-cYt" secondAttribute="height" multiplier="1:1" id="68L-BJ-oE4"/>
                            <constraint firstAttribute="width" constant="20" id="qde-Gw-mBE"/>
                        </constraints>
                    </imageView>
                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="aLg-Yr-ndG">
                        <rect key="frame" x="8" y="344" width="55" height="30"/>
                        <state key="normal" title="Favorite"/>
                    </button>
                    <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Vlh-94-vuc">
                        <rect key="frame" x="79" y="344" width="40" height="30"/>
                        <state key="normal" title="Share"/>
                    </button>
                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="m0R-Do-Mr3">
                        <rect key="frame" x="8" y="60" width="398" height="61"/>
                        <string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</string>
                        <fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
                        <nil key="textColor"/>
                        <nil key="highlightedColor"/>
                    </label>
                </subviews>
                <constraints>
                    <constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="aLg-Yr-ndG" secondAttribute="bottom" constant="8" id="8dT-Db-15Z"/>
                    <constraint firstAttribute="trailing" secondItem="tub-UY-Qc6" secondAttribute="trailing" id="9MJ-uw-1Ca"/>
                    <constraint firstItem="tub-UY-Qc6" firstAttribute="leading" secondItem="hnF-dN-5dt" secondAttribute="leading" id="AQ3-kJ-tVu"/>
                    <constraint firstAttribute="trailing" secondItem="bBl-zC-cYt" secondAttribute="trailing" constant="8" id="Ble-Sl-XKb"/>
                    <constraint firstItem="Vlh-94-vuc" firstAttribute="leading" secondItem="aLg-Yr-ndG" secondAttribute="trailing" constant="16" id="DR1-ea-Juj"/>
                    <constraint firstItem="FaY-ve-Qel" firstAttribute="leading" secondItem="JO1-nS-Fg0" secondAttribute="trailing" constant="8" id="Drr-Jg-bR2"/>
                    <constraint firstItem="JO1-nS-Fg0" firstAttribute="leading" secondItem="hnF-dN-5dt" secondAttribute="leading" constant="8" id="OFG-JM-p5H"/>
                    <constraint firstItem="FaY-ve-Qel" firstAttribute="top" secondItem="4cS-dj-yG9" secondAttribute="bottom" id="RBR-GS-VdH"/>
                    <constraint firstItem="m0R-Do-Mr3" firstAttribute="top" secondItem="JO1-nS-Fg0" secondAttribute="bottom" constant="8" id="Ty0-OF-WPY"/>
                    <constraint firstItem="Vlh-94-vuc" firstAttribute="top" secondItem="tub-UY-Qc6" secondAttribute="bottom" constant="8" id="UEv-aP-CfW"/>
                    <constraint firstItem="bBl-zC-cYt" firstAttribute="top" secondItem="hnF-dN-5dt" secondAttribute="top" constant="8" id="WtV-1Y-RiY"/>
                    <constraint firstItem="JO1-nS-Fg0" firstAttribute="top" secondItem="hnF-dN-5dt" secondAttribute="top" constant="8" id="fBu-gb-Qqo"/>
                    <constraint firstItem="aLg-Yr-ndG" firstAttribute="top" secondItem="tub-UY-Qc6" secondAttribute="bottom" constant="8" id="kn4-84-3pp"/>
                    <constraint firstAttribute="trailing" secondItem="m0R-Do-Mr3" secondAttribute="trailing" constant="8" id="qQ9-nk-akg"/>
                    <constraint firstItem="4cS-dj-yG9" firstAttribute="leading" secondItem="JO1-nS-Fg0" secondAttribute="trailing" constant="8" id="tlM-VO-hmV"/>
                    <constraint firstItem="m0R-Do-Mr3" firstAttribute="leading" secondItem="hnF-dN-5dt" secondAttribute="leading" constant="8" id="ujA-dg-oXB"/>
                    <constraint firstItem="aLg-Yr-ndG" firstAttribute="leading" secondItem="hnF-dN-5dt" secondAttribute="leading" constant="8" id="yGd-EI-YmC"/>
                    <constraint firstItem="4cS-dj-yG9" firstAttribute="top" secondItem="hnF-dN-5dt" secondAttribute="top" constant="8" id="yyJ-Yv-nge"/>
                    <constraint firstItem="tub-UY-Qc6" firstAttribute="top" secondItem="m0R-Do-Mr3" secondAttribute="bottom" constant="8" id="zrG-g2-2ab"/>
                </constraints>
            </tableViewCellContentView>
            <connections>
                <outlet property="source" destination="m0R-Do-Mr3" id="SnG-VN-51c"/>
                <outlet property="title" destination="4cS-dj-yG9" id="uHh-s1-WgP"/>
            </connections>
            <point key="canvasLocation" x="124.6376811594203" y="96.428571428571431"/>
        </tableViewCell>
    </objects>
</document>

iPhone 11 上的结果:

【讨论】:

    【解决方案3】:

    更改 UITableViewCell 的内容视图的类可能会导致意外行为,因为内容视图是内容的默认超级视图,并且 UIKit 可能会对其进行一些配置。 (我在文档中找不到明确的说明)

    Reference from Apple's Documentation

    UITableViewCell 对象的内容视图是单元格显示内容的默认超级视图。如果您想通过简单地添加其他视图来自定义单元格,您应该将它们添加到内容视图中,以便它们在单元格进入和退出编辑模式时适当地定位。

    因此,添加自定义子视图的一种解决方法是将其添加为内容视图的子视图,并保持内容视图的属性不变。然后,您可以将所有内容包装在自定义视图中。

    【讨论】:

    • 您误解了该描述。单元格本身是一个视图。该单元格有一个“内置”视图 - contentView - 这是 Apple 所说的添加子视图的地方......您刚刚通过添加 YourCustomView 来完成。没有区别。
    • 是的,单元格有一个“内置”的 contentView,但是当你覆盖它的类时,正如 Adrain 所说,事情变得一团糟。我的解决方案是基于 Adrain 想要在自动布局中使用他的自定义类 MDCard 的解决方法。
    • 啊 - 我没有看到 Adrian 正在更改单元格 contentView 的类别的附加评论。所以,是的,你是对的……他绝对不应该那样做。
    猜你喜欢
    • 1970-01-01
    • 2016-10-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多