使用 Swift 以编程方式自定义 UITableViewCell

Posted

技术标签:

【中文标题】使用 Swift 以编程方式自定义 UITableViewCell【英文标题】:Custom UITableViewCell programmatically using Swift 【发布时间】:2014-10-14 07:32:07 【问题描述】:

大家好,我正在尝试创建自定义 UITableViewCell,但我在模拟器上什么也看不到。你能帮帮我吗?

只有var labUserName = UILabel(frame: CGRectMake(0.0, 0.0, 130, 30));才能看到标签

但它与单元格重叠。没看懂,Auto Layout 应该知道每个单元格的首选尺寸/最小尺寸吧?

谢谢

import Foundation
import UIKit

class TableCellMessages: UITableViewCell 
    var imgUser     = UIImageView();
    var labUserName = UILabel();
    var labMessage  = UILabel();
    var labTime     = UILabel();


    override init(style: UITableViewCellStyle, reuseIdentifier: String) 
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        imgUser.layer.cornerRadius = imgUser.frame.size.width / 2;
        imgUser.clipsToBounds = true;



        contentView.addSubview(imgUser)
        contentView.addSubview(labUserName)
        contentView.addSubview(labMessage)
        contentView.addSubview(labTime)


        //Set layout
        var viewsDict = Dictionary <String, UIView>()
        viewsDict["image"] = imgUser;
        viewsDict["username"] = labUserName;
        viewsDict["message"] = labMessage;
        viewsDict["time"] = labTime;
        //Image

        //contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[image(100)]-'", options: nil, metrics: nil, views: viewsDict));
        //contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[image(100)]-|", options: nil, metrics: nil, views: viewsDict));
         contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[username]-[message]-|", options: nil, metrics: nil, views: viewsDict));
         contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[username]-|", options: nil, metrics: nil, views: viewsDict));
          contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[message]-|", options: nil, metrics: nil, views: viewsDict));

    

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


【问题讨论】:

如果您在单元格中看到重叠的图像,则可能是单元格高度设置不正确。见:***.com/questions/494562/… 我只在使用 CGRectMake 时看到重叠,但我不想使用 CGRectMake,我想自动使用, 我想要自动布局来控制大小,这是我不明白的一件事。 请帮忙,里面的标签应该有自己的尺寸,不用我告诉他们尺寸是多少? 我知道这个线程很旧,对我来说,当标签、视图等所有 UI 元素上的“translatesAutoresizingMaskIntoConstraints = false”时,约束效果更好 【参考方案1】:

让我们做一些假设:

您有一个带有Storyboardios8 项目,其中包含一个UITableViewController。它的tableView 有一个独特的原型UITableViewCell,具有自定义样式和标识符:“cell”。

UITableViewController 将链接到 TableViewController 类,cell 将链接到 CustomTableViewCell 类。

然后您将能够设置以下代码(针对 Swift 2 更新):

CustomTableViewCell.swift:

import UIKit

class CustomTableViewCell: UITableViewCell 

    let imgUser = UIImageView()
    let labUserName = UILabel()
    let labMessage = UILabel()
    let labTime = UILabel()

    override func awakeFromNib() 
        super.awakeFromNib()

        imgUser.backgroundColor = UIColor.blueColor()

        imgUser.translatesAutoresizingMaskIntoConstraints = false
        labUserName.translatesAutoresizingMaskIntoConstraints = false
        labMessage.translatesAutoresizingMaskIntoConstraints = false
        labTime.translatesAutoresizingMaskIntoConstraints = false

        contentView.addSubview(imgUser)
        contentView.addSubview(labUserName)
        contentView.addSubview(labMessage)
        contentView.addSubview(labTime)

        let viewsDict = [
            "image": imgUser,
            "username": labUserName,
            "message": labMessage,
            "labTime": labTime,
        ]

        contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[image(10)]", options: [], metrics: nil, views: viewsDict))
        contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:[labTime]-|", options: [], metrics: nil, views: viewsDict))
        contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-[username]-[message]-|", options: [], metrics: nil, views: viewsDict))
        contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[username]-[image(10)]-|", options: [], metrics: nil, views: viewsDict))
        contentView.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("H:|-[message]-[labTime]-|", options: [], metrics: nil, views: viewsDict))
    


TableViewController.swift:

import UIKit

class TableViewController: UITableViewController 

    override func viewDidLoad() 
        super.viewDidLoad()

        //Auto-set the UITableViewCells height (requires iOS8+)
        tableView.rowHeight = UITableViewAutomaticDimension
        tableView.estimatedRowHeight = 44
    

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int 
        return 1
    

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        return 100
    

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell 
        let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! CustomTableViewCell

        cell.labUserName.text = "Name"
        cell.labMessage.text = "Message \(indexPath.row)"
        cell.labTime.text = NSDateFormatter.localizedStringFromDate(NSDate(), dateStyle: .ShortStyle, timeStyle: .ShortStyle)

        return cell
    



您会期待这样的显示(iPhone 横向):

【讨论】:

在你给它像 cell.labUerName.text = "Name" 之类的参数之前,这似乎是在执行 awakefromnib 此代码首先调用 awakefromnib,而不是将变量传递给单元类。正因为如此,我遇到了崩溃,应该怎么办? 要移除对 Interface Builder 注册 forCellReuseIdentifier 的依赖,将以下行添加到 loadView() 中的 UITableViewController i> 或 ViewDidLoad()tableView.register(CustomTableViewCell.classForCoder(), forCellReuseIdentifier: "cell").【参考方案2】:

这是答案 Imanou Petit 的 swift 3 的更新。

CustomTableViewCell.swift:

import Foundation
import UIKit

class CustomTableViewCell: UITableViewCell 

    let imgUser = UIImageView()
    let labUerName = UILabel()
    let labMessage = UILabel()
    let labTime = UILabel()

    override init(style: UITableViewCellStyle, reuseIdentifier: String?) 
        super.init(style: style, reuseIdentifier: reuseIdentifier)

        imgUser.backgroundColor = UIColor.blue

        imgUser.translatesAutoresizingMaskIntoConstraints = false
        labUerName.translatesAutoresizingMaskIntoConstraints = false
        labMessage.translatesAutoresizingMaskIntoConstraints = false
        labTime.translatesAutoresizingMaskIntoConstraints = false

        contentView.addSubview(imgUser)
        contentView.addSubview(labUerName)
        contentView.addSubview(labMessage)
        contentView.addSubview(labTime)

        let viewsDict = [
            "image" : imgUser,
            "username" : labUerName,
            "message" : labMessage,
            "labTime" : labTime,
            ] as [String : Any]

        contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-[image(10)]", options: [], metrics: nil, views: viewsDict))
        contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:[labTime]-|", options: [], metrics: nil, views: viewsDict))
        contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-[username]-[message]-|", options: [], metrics: nil, views: viewsDict))
        contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[username]-[image(10)]-|", options: [], metrics: nil, views: viewsDict))
        contentView.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[message]-[labTime]-|", options: [], metrics: nil, views: viewsDict))
    

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


Settigns.swift:

import Foundation
import UIKit


class Settings: UIViewController, UITableViewDelegate, UITableViewDataSource 

    private var myTableView: UITableView!

    private let sections: NSArray = ["fruit", "vegitable"]    //Profile    network    audio Codecs
    private let fruit: NSArray = ["apple", "orange", "banana", "strawberry", "lemon"]
    private let vegitable: NSArray = ["carrots", "avocado", "potato", "onion"]



    override func viewDidLoad() 
        super.viewDidLoad()

        // get width and height of View
        let barHeight: CGFloat = UIApplication.shared.statusBarFrame.size.height
        let navigationBarHeight: CGFloat = self.navigationController!.navigationBar.frame.size.height
        let displayWidth: CGFloat = self.view.frame.width
        let displayHeight: CGFloat = self.view.frame.height

        myTableView = UITableView(frame: CGRect(x: 0, y: barHeight+navigationBarHeight, width: displayWidth, height: displayHeight - (barHeight+navigationBarHeight)))
        myTableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "cell")         // register cell name

        myTableView.dataSource = self
        myTableView.delegate = self

        //Auto-set the UITableViewCells height (requires iOS8+)
        myTableView.rowHeight = UITableViewAutomaticDimension
        myTableView.estimatedRowHeight = 44

        self.view.addSubview(myTableView)
    

    override func didReceiveMemoryWarning() 
        super.didReceiveMemoryWarning()
    


    // return the number of sections
    func numberOfSections(in tableView: UITableView) -> Int
        return sections.count
    



    // return the title of sections
    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? 
        return sections[section] as? String
    


    // called when the cell is selected.
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 

        print("Num: \(indexPath.row)")
        if indexPath.section == 0 
            print("Value: \(fruit[indexPath.row])")
         else if indexPath.section == 1 
            print("Value: \(vegitable[indexPath.row])")
        
    

    // return the number of cells each section.
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        if section == 0 
            return fruit.count
         else if section == 1 
            return vegitable.count
         else 
            return 0
        
    

    // return cells
    public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 

        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CustomTableViewCell

        if indexPath.section == 0 
            cell.labUerName.text = "\(fruit[indexPath.row])"
            cell.labMessage.text = "Message \(indexPath.row)"
            cell.labTime.text = DateFormatter.localizedString(from: NSDate() as Date, dateStyle: .short, timeStyle: .short)
         else if indexPath.section == 1 
            cell.labUerName.text = "\(vegitable[indexPath.row])"
            cell.labMessage.text = "Message \(indexPath.row)"
            cell.labTime.text = DateFormatter.localizedString(from: NSDate() as Date, dateStyle: .short, timeStyle: .short)
        



        return cell
    


【讨论】:

只是想知道...您为什么决定使用 init(style:) 而不是 Imanou Petit 使用的 awakeFromNib()?有区别吗? Imanou Petit 假设用户正在使用 Interface Builder。当我使用他的答案时,我使代码完全独立于任何故事板/xib = nib,因此在我的应用程序中永远不会调用 awakeFromNib()。顺便说一句,我还必须添加这一行: myTableView.register(CustomTableViewCell.self, forCellReuseIdentifier: "cell") 嘿@TiagoMendes 快速问题,我使用情节提要创建了一个自定义单元格,我试图通过一个插座在 cellForRowAtIndexPath 方法中更改其背景颜色,但它似乎不起作用。像 .isHidden 这样的属性可以正常工作。这是这样做的正确方法吗?我错过了什么吗? 这仍然是我在 Swift5 中的修复方法【参考方案3】:

在 Swift 5 中。

自定义 UITableViewCell:

import UIKit

class CourseCell: UITableViewCell 

    let courseName = UILabel()
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) 
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        
        // Set any attributes of your UI components here.
        courseName.translatesAutoresizingMaskIntoConstraints = false
        courseName.font = UIFont.systemFont(ofSize: 20)
        
        // Add the UI components
        contentView.addSubview(courseName)
        
        NSLayoutConstraint.activate([
            courseName.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
            courseName.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -20),
            courseName.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20),
            courseName.heightAnchor.constraint(equalToConstant: 50)
        ])
    
    
    required init?(coder: NSCoder) 
        fatalError("init(coder:) has not been implemented")
    

UITableViewController:

import UIKit

class CourseTableViewController: UITableViewController 

    private var data: [Int] = [1]
    
    override func viewDidLoad() 
        super.viewDidLoad()

        // You must register the cell with a reuse identifier
        tableView.register(CourseCell.self, forCellReuseIdentifier: "courseCell")
        // Change the row height if you want
        tableView.rowHeight = 150
        // This will remove any empty cells that are below your data filled cells
        tableView.tableFooterView = UIView()
    

    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int 

        return 1
    

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int 
        
        return data.count
    


    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 
        let cell = tableView.dequeueReusableCell(withIdentifier: "courseCell", for: indexPath) as! CourseCell
        cell.courseName.text = "Course name"

        return cell
    
 
    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 
        tableView.deselectRow(at: indexPath, animated: true)
    

【讨论】:

以上是关于使用 Swift 以编程方式自定义 UITableViewCell的主要内容,如果未能解决你的问题,请参考以下文章

使用自定义初始化程序 swift 以编程方式实例化和推送视图控制器

如何以编程方式将自定义 uiview 添加到视图控制器 SWIFT

Swift:不能以编程方式配置自定义表格单元格?没有调用初始化?

Swift:以编程方式附加自定义 UIView

Swift:无法以编程方式编辑自定义表格单元格的属性

Swift UItableView 以编程方式自定义单元格(文档)?