【问题标题】:creating custom tableview cells in swift快速创建自定义表格视图单元格
【发布时间】:2014-08-01 23:35:10
【问题描述】:

我有一个带有几个 IBOutlets 的自定义单元格类。我已将课程添加到情节提要中。我已经连接了我所有的插座。我的 cellForRowAtIndexPath 函数如下所示:

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

        cell.mainTextLabel.text = self.venueService.mainCategoriesArray()[indexPath.row]

        return cell
    }

这是我的自定义单元格类:

class SwipeableCell: UITableViewCell {
    @IBOutlet var option1: UIButton
    @IBOutlet var option2: UIButton
    @IBOutlet var topLayerView : UIView
    @IBOutlet var mainTextLabel : UILabel
    @IBOutlet var categoryIcon : UIImageView

    init(style: UITableViewCellStyle, reuseIdentifier: String!) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)


    }
}

当我运行应用程序时,我的所有单元格都是空的。我已注销self.venueService.mainCategoriesArray(),它包含所有正确的字符串。我也尝试过放置一个与标签相等的实际字符串,这会产生相同的结果。

我错过了什么?任何帮助表示赞赏。

【问题讨论】:

  • 我在一遍又一遍地寻找和挠头时遇到了同样的问题,然后从自定义单元格中删除了所有图像视图和标签,并使用新的约束重新设计了它。它工作得很好。因此,约束也可能是您的问题。先手动调整行大小,再添加组件

标签: swift uitableview cocoa-touch ios8 storyboard


【解决方案1】:

自定义表格视图单元格示例

使用 Xcode 9(编辑也在 11 / 12 Beta 2 上测试过)和 Swift 4(编辑:也在 5.2 上测试过)

原始问题的提问者已经解决了他们的问题。我将此答案添加为一个小型自包含示例项目,供其他尝试做同样事情的人使用。

完成的项目应如下所示:

创建一个新项目

它可以只是一个单一视图应用程序。

添加代码

向您的项目添加一个新的 Swift 文件。将其命名为 MyCustomCell.swift。此类将保存您添加到情节提要中的单元格的视图的出口。

import UIKit
class MyCustomCell: UITableViewCell {
    @IBOutlet weak var myView: UIView!
    @IBOutlet weak var myCellLabel: UILabel!
}

我们稍后会连接这些插座。

打开 ViewController.swift 并确保你有以下内容:

import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
    
    // These strings will be the data for the table view cells
    let animals: [String] = ["Horse", "Cow", "Camel", "Sheep", "Goat"]
    
    // These are the colors of the square views in our table view cells.
    // In a real project you might use UIImages.
    let colors = [UIColor.blue, UIColor.yellow, UIColor.magenta, UIColor.red, UIColor.brown]
    
    // Don't forget to enter this in IB also
    let cellReuseIdentifier = "cell"
    
    @IBOutlet var tableView: UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView.delegate = self
        tableView.dataSource = self
    }
    
    // number of rows in table view
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.animals.count
    }
    
    // create a cell for each table view row
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        
        let cell:MyCustomCell = self.tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! MyCustomCell
        
        cell.myView.backgroundColor = self.colors[indexPath.row]
        cell.myCellLabel.text = self.animals[indexPath.row]
        
        return cell
    }
    
    // method to run when table view cell is tapped
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("You tapped cell number \(indexPath.row).")
    }
}

设置故事板

向您的视图控制器添加一个表格视图,并使用自动布局将其固定到视图控制器的四个侧面。然后将 Table View Cell 拖到 Table View 上。然后将 View 和 Label 拖到 Prototype 单元格上。 (您可能需要在尺寸检查器中选择表格视图单元格和manually set the Row Height 到更高的位置,以便您有更多的工作空间。)使用自动布局来修复视图和标签在内容中的排列方式表视图单元格的视图。例如,我将 View 设为 100x100。

其他 IB 设置

自定义类名和标识符

选择 Table View Cell 并将自定义类设置为MyCustomCell(我们添加的 Swift 文件中的类的名称)。还将标识符设置为cell(与我们在上面的代码中用于cellReuseIdentifier 的字符串相同。

连接网点

  • 控制从情节提要中的表格视图拖动到ViewController 代码中的tableView 变量。
  • 对 Prototype 单元格中的 View 和 Label 执行相同的操作,以处理 MyCustomCell 类中的 myViewmyCellLabel 变量。

完成

就是这样。您现在应该可以运行您的项目了。

注意事项

  • 我在这里使用的彩色视图可以替换为任何内容。一个明显的例子是UIImageView
  • 如果您只是想让 TableView 工作,请参阅 this even more basic example
  • 如果您需要具有可变单元格高度的表格视图,请参阅this example

【讨论】:

  • @codebased,我自己还没有真正尝试过,但我会先将gesture recognizers 添加到单独的视图中。
  • @Suragch 谢谢你的详细解释。按照您的步骤,我在控制台“致命错误:在展开可选值时意外发现 nil”和 errmsg “EXC_BAD_INSTRUCTION (code=EXC_1386_INVOP, subcode=0x0) 在 let cell:MyCustomCell = self.tableView.... 行。你知道我错过了什么吗?非常感谢。我所做的唯一更改是使用 2 个 UILabel 而不是图像和标签。
  • @rockhammer,两种可能是你(1)没有连接IBOutlet或(2)在IB中写错了cellReuseIdentifier字符串名称。如果两者都不是,请检查 SO 上描述您的问题的其他答案。其中有很多。 Example.
  • 如何设置tableViewCell的样式?我不能自定义具有 rightDetail 样式的 UITableViewCell 吗?我想以编程方式完成这一切,但我只是不知道如何使用该样式设置单元格
  • @Honey,我也没有这样做。如果this 没有解决您的问题,那么它可能是一个很好的候选新问题。
【解决方案2】:

这适用于使用 .xib 使用自定义单元格的人

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{

    let identifier = "Custom"

    var cell: CustomCell! = tableView.dequeueReusableCellWithIdentifier(identifier) as? CustomCel  

      if cell == nil {
            tableView.registerNib(UINib(nibName: "CustomCell", bundle: nil), forCellReuseIdentifier: identifier)
           cell =tableView.dequeueReusableCellWithIdentifier(identifier) as? CustomCell
        }return cell}

【讨论】:

  • 单元格 xib 应该在其他地方注册,例如在 viewDidLoad 中,这样它就不会被多次调用
【解决方案3】:

我也有同样的问题。

一般来说我做的和你一样。

class dynamicCell: UITableViewCell {

    @IBOutlet var testLabel : UILabel

    init(style: UITableViewCellStyle, reuseIdentifier: String) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
    }

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

    override func setSelected(selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)
    }
}

在uitableviewcell方法中:

func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
    var cell :dynamicCell = tableView.dequeueReusableCellWithIdentifier("cell") as dynamicCell

    cell.testLabel.text = "so sad"

    println(cell.testLabel)

    return cell;
}

是的,tableview 什么也没显示!但你猜怎么着,它实际上显示了一些东西......因为我从 println(cell.testLabel) 得到的日志显示所有标签实际上都显示出来了。

但是!他们的框架很奇怪,有这样的东西:

帧 = (0 -21; 42 21);

所以它有一个 (0,-21) 作为 (x,y),这意味着标签只出现在单元格边界之外的某个地方。

所以我尝试像这样手动添加调整框架:

cell.testLabel.frame = CGRectMake(10, 10, 42, 21)

很遗憾,它不起作用。

---------------10分钟后更新-----

我做到了。 所以,问题似乎来自大小类。

单击您的 .storyboard 文件并转到“文件检查器”选项卡

取消选中尺寸等级复选框

最后,我的“好伤心”标签出来了!

【讨论】:

  • 这到底是什么“大小班”的东西?为什么它可以解决问题?我遇到了完全相同的问题,即使自定义标签的来源相同(0 -21)。但有趣的是,在取消选中“使用尺寸等级”后它起作用了,当我再次检查它时它也起作用了......所以它看起来像一个错误。
  • size 类在我看来可以让 Xcode 将 Views 和 ViewControllers 调整到不同的设备,而无需做太多额外的工作。一旦你删除它,其他设备的数据就会被删除。因此,再次重新检查它不会自动创建它,因此它仍然可以工作。我遇到了同样的问题。
【解决方案4】:

感谢所有不同的建议,但我终于想通了。自定义类已正确设置。我需要做的就是在故事板中选择自定义类:删除它,然后再次选择它。这没有多大意义,但这最终对我有用。

【讨论】:

  • 当你选择一个自定义类时,它也需要一个模块。如果在实际创建类之前输入自定义类名,则不会设置模块。我以前也遇到过这种情况,也许你也遇到过这种情况。
  • 我遇到了类似的问题。帮助重命名@IBOutlet 弱变量图像:UIImageView!因为“图像” - 系统名称。
【解决方案5】:

上次更新的版本是 xCode 6.1

class StampInfoTableViewCell: UITableViewCell{


@IBOutlet weak var stampDate: UILabel!
@IBOutlet weak var numberText: UILabel!


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

required init(coder aDecoder: NSCoder) {
    //fatalError("init(coder:) has not been implemented")
    super.init(coder: aDecoder)
}

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

override func setSelected(selected: Bool, animated: Bool) {
    super.setSelected(selected, animated: animated)
}
}

【讨论】:

    【解决方案6】:

    详情

    • Xcode 版本 10.2.1 (10E1001),Swift 5

    解决方案

    import UIKit
    
    // MARK: - IdentifiableCell protocol will generate cell identifier based on the class name
    
    protocol Identifiable: class {}
    extension Identifiable { static var identifier: String { return "\(self)"} }
    
    // MARK: - Functions which will use a cell class (conforming Identifiable protocol) to `dequeueReusableCell`
    
    extension UITableView {
        typealias IdentifiableCell = UITableViewCell & Identifiable
        func register<T: IdentifiableCell>(class: T.Type) { register(T.self, forCellReuseIdentifier: T.identifier) }
        func register(classes: [Identifiable.Type]) { classes.forEach { register($0.self, forCellReuseIdentifier: $0.identifier) } }
        func dequeueReusableCell<T: IdentifiableCell>(aClass: T.Type, initital closure: ((T) -> Void)?) -> UITableViewCell {
            guard let cell = dequeueReusableCell(withIdentifier: T.identifier) as? T else { return UITableViewCell() }
            closure?(cell)
            return cell
        }
        func dequeueReusableCell<T: IdentifiableCell>(aClass: T.Type, for indexPath: IndexPath, initital closure: ((T) -> Void)?) -> UITableViewCell {
            guard let cell = dequeueReusableCell(withIdentifier: T.identifier, for: indexPath) as? T else { return UITableViewCell() }
            closure?(cell)
            return cell
        }
    }
    
    extension Array where Element == UITableViewCell.Type  {
        var onlyIdentifiables: [Identifiable.Type] { return compactMap { $0 as? Identifiable.Type } }
    }
    

    用法

    // Define cells classes
    class TableViewCell1: UITableViewCell, Identifiable { /*....*/ }
    class TableViewCell2: TableViewCell1 { /*....*/ }
    
    // .....
    
    // Register cells
    tableView.register(classes: [TableViewCell1.self, TableViewCell2.self]. onlyIdentifiables)
    
    // Create/Reuse cells
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        if (indexPath.row % 2) == 0 {
            return tableView.dequeueReusableCell(aClass: TableViewCell1.self, for: indexPath) { cell in
                // ....
            }
        } else {
            return tableView.dequeueReusableCell(aClass: TableViewCell2.self, for: indexPath) { cell in
                // ...
            }
        }
    }
    

    完整样本

    不要忘记在此处添加解决方案代码

    import UIKit
    
    class ViewController: UIViewController {
        private weak var tableView: UITableView?
        override func viewDidLoad() {
            super.viewDidLoad()
            setupTableView()
        }
    }
    
    // MARK: - Setup(init) subviews
    
    extension ViewController {
        private func setupTableView() {
            let tableView = UITableView()
            view.addSubview(tableView)
            self.tableView = tableView
            tableView.translatesAutoresizingMaskIntoConstraints = false
            tableView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
            tableView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
            tableView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
            tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
            tableView.register(classes: [TableViewCell1.self, TableViewCell2.self, TableViewCell3.self].onlyIdentifiables)
            tableView.dataSource = self
        }
    }
    
    // MARK: - UITableViewDataSource
    
    extension ViewController: UITableViewDataSource {
        func numberOfSections(in tableView: UITableView) -> Int { return 1 }
        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return 20 }
        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
            switch (indexPath.row % 3) {
            case 0:
                return tableView.dequeueReusableCell(aClass: TableViewCell1.self, for: indexPath) { cell in
                    cell.textLabel?.text = "\(cell.classForCoder)"
                }
            case 1:
                return tableView.dequeueReusableCell(aClass: TableViewCell2.self, for: indexPath) { cell in
                    cell.textLabel?.text = "\(cell.classForCoder)"
                }
            default:
                return tableView.dequeueReusableCell(aClass: TableViewCell3.self, for: indexPath) { cell in
                    cell.textLabel?.text = "\(cell.classForCoder)"
                }
            }
        }
    }
    

    结果

    【讨论】:

      【解决方案7】:

      取消选中“Size Classes”复选框也适用于我,但您也可以在界面构建器中添加缺少的约束。如果您不想自己添加约束,只需使用内置函数即可。在我看来,使用约束是更好的方法,因为布局独立于设备(iPhone 或 iPad)。

      【讨论】:

        【解决方案8】:

        这对我来说是一种纯粹的快速表示法

        func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
            {
                var cellIdentifier:String = "CustomFields"
                var cell:CustomCell? = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? CustomCell
                if (cell == nil)
                {
                    var nib:Array = NSBundle.mainBundle().loadNibNamed("CustomCell", owner: self, options: nil)
                    cell = nib[0] as? CustomCell
                }
                return cell!
            }
        

        【讨论】:

          【解决方案9】:

          [1] 首先在 StoryBoard 中设计您的 tableview 单元格。

          [2] 将表格视图委托方法放在下面

          //MARK: - Tableview 委托方法

          func numberOfSectionsInTableView(tableView: UITableView) -> Int
          {
              return 1
          }
          
          func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
          {
             
              return <“Your Array”>
          }
          
          
          func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat
          {
                      
              var totalHeight : CGFloat = <cell name>.<label name>.frame.origin.y    
          
              totalHeight +=   UpdateRowHeight(<cell name>.<label name>, textToAdd: <your array>[indexPath.row])    
                     
              return totalHeight
          }
          
          
          func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
          {
              
              
              var cell : <cell name>! = tableView.dequeueReusableCellWithIdentifier(“<cell identifier>”, forIndexPath: indexPath) as! CCell_VideoCall
              
              if(cell == nil)
              {
                  cell = NSBundle.mainBundle().loadNibNamed("<cell identifier>", owner: self, options: nil)[0] as! <cell name>;
              }
              
          
              <cell name>.<label name>.text = <your array>[indexPath.row] as? String
                    
              
              
              return cell as <cell name>
          }
          

          //MARK: - 自定义方法

          func UpdateRowHeight ( ViewToAdd : UILabel , textToAdd : AnyObject  ) -> CGFloat{
              
                     
              var actualHeight : CGFloat = ViewToAdd.frame.size.height
              
              if let strName : String? = (textToAdd as? String)
                  where !strName!.isEmpty
              {
                  
                  actualHeight = heightForView1(strName!, font: ViewToAdd.font, width: ViewToAdd.frame.size.width, DesignTimeHeight: actualHeight )
                  
              }
              return actualHeight
          }
          

          【讨论】:

            【解决方案10】:

            在单元格中为图像视图和标签设置标签

            func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
            {
                return self.tableData.count
            }
            
            func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
            {
            
                let cell = tableView.dequeueReusableCellWithIdentifier("imagedataCell", forIndexPath: indexPath) as! UITableViewCell
            
                let rowData = self.tableData[indexPath.row] as! NSDictionary
            
                let urlString = rowData["artworkUrl60"] as? String
                // Create an NSURL instance from the String URL we get from the API
                let imgURL = NSURL(string: urlString!)
                // Get the formatted price string for display in the subtitle
                let formattedPrice = rowData["formattedPrice"] as? String
                // Download an NSData representation of the image at the URL
                let imgData = NSData(contentsOfURL: imgURL!)
            
            
                (cell.contentView.viewWithTag(1) as! UIImageView).image = UIImage(data: imgData!)
            
                (cell.contentView.viewWithTag(2) as! UILabel).text = rowData["trackName"] as? String
            
                return cell
            }
            

            func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
            {
                let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "imagedataCell")
            
                if let rowData: NSDictionary = self.tableData[indexPath.row] as? NSDictionary,
                    urlString = rowData["artworkUrl60"] as? String,
                    imgURL = NSURL(string: urlString),
                    formattedPrice = rowData["formattedPrice"] as? String,
                    imgData = NSData(contentsOfURL: imgURL),
                    trackName = rowData["trackName"] as? String {
                        cell.detailTextLabel?.text = formattedPrice
                        cell.imageView?.image = UIImage(data: imgData)
                        cell.textLabel?.text = trackName
                }
            
                return cell
            }
            

            另请参阅github 的 TableImage 加载器

            【讨论】:

              【解决方案11】:
              猜你喜欢
              • 1970-01-01
              • 2020-09-11
              • 1970-01-01
              • 1970-01-01
              • 2014-11-09
              • 1970-01-01
              • 2019-04-18
              • 1970-01-01
              • 2019-07-18
              相关资源
              最近更新 更多