【问题标题】:UITableView in SwiftSwift 中的 UITableView
【发布时间】:2014-07-24 05:41:39
【问题描述】:

我正在努力找出这段代码 sn-p 有什么问题。这目前在 Objective-C 中有效,但在 Swift 中,这只是在方法的第一行崩溃。它在控制台日志中显示一条错误消息:Bad_Instruction

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

        if (cell == nil) {
            cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: "Cell")
        }

        cell.textLabel.text = "TEXT"
        cell.detailTextLabel.text = "DETAIL TEXT"

        return cell
    }

【问题讨论】:

标签: uitableview swift ios8


【解决方案1】:

另见matt's answer,其中包含解决方案的后半部分

让我们在不创建自定义子类或 nib 的情况下找到解决方案

真正的问题在于 Swift 区分可以为空的对象 (nil) 和不能为空的对象。如果你没有为你的标识符注册一个 nib,那么dequeueReusableCellWithIdentifier 可以返回nil

这意味着我们必须将变量声明为可选:

var cell : UITableViewCell?

我们必须使用as? 而不是as 进行投射

//variable type is inferred
var cell = tableView.dequeueReusableCellWithIdentifier("CELL") as? UITableViewCell

if cell == nil {
    cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: "CELL")
}

// we know that cell is not empty now so we use ! to force unwrapping but you could also define cell as
// let cell = (tableView.dequeue... as? UITableViewCell) ?? UITableViewCell(style: ...)

cell!.textLabel.text = "Baking Soda"
cell!.detailTextLabel.text = "1/2 cup"

cell!.textLabel.text = "Hello World"

return cell

【讨论】:

  • 感谢您的回答!自从你的权利以来,我一直在努力理解 Swift 中的“可选”是什么,创建一个没有自定义的表格视图单元格的子类是没有意义的。我一定会试试这个!
  • @Sulthan 我使用的代码与您的相同,但我在 dequeueReusableCellWithIdentifier 行上收到了 EXC_BAD_INSTRUCTION。知道为什么吗?
  • @kmcgrady 一定有一个nil 不在预期的地方。我刚刚用tableView 替换了self.tableView(参数tableView 不能是nilself.tableView 可以)。确保您拥有as? 而不是as
  • @Sulthan 感谢您的回复。你是对的,这是一个“零”问题。我为我的标签设置的文本从字典中得到零。谢谢!
  • 此方法需要返回一个 UITableViewCell。不是可选的。那么你是不是打算在返回时强制打开它?
【解决方案2】:

Sulthan 的回答很聪明,但真正的解决方案是:不要打电话给dequeueReusableCellWithIdentifier。那是你一开始的错误。

这种方法已经完全过时了,我很惊讶它还没有被正式弃用;任何可以容纳 Swift(iOS 7 或 iOS 8)的系统都不需要它用于任何目的。

改为调用现代方法dequeueReusableCellWithIdentifier:forIndexPath:。这样做的好处是不涉及任何选项;您保证将返回一个单元格。所有的问号和感叹号都消失了,您可以使用let 而不是var,因为细胞的存在是有保证的,并且您生活在一个方便的现代世界中。

如果您不使用情节提要,则必须事先为该标识符注册表格,注册一个类或一个 nib。执行此操作的常规位置是viewDidLoad,它早在表格视图存在的时候。

这是一个使用自定义单元类的示例:

override func viewDidLoad() {
    super.viewDidLoad()
    self.tableView.registerClass(MyCell.self, forCellReuseIdentifier: "Cell")
}

// ...

override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
    let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath:indexPath) as MyCell
    // no "if" - the cell is guaranteed to exist
    // ... do stuff to the cell here ...
    cell.textLabel.text = // ... whatever
    // ...
    return cell
}

但如果您使用的是故事板(大多数人都这样做),您甚至不需要在viewDidLoad 中注册表格视图!只需在情节提要中输入单元格标识符即可使用dequeueReusableCellWithIdentifier:forIndexPath:

【讨论】:

  • 如果 reusableCell 被称为“Cell”,那么在您的示例中 MyCell 来自哪里?
  • @JoshL 你的问题毫无意义。单元重用标识符和单元类彼此无关。
  • 这应该被标记为正确答案。 @Sulthan 的答案是我以前在哪里,努力将一个返回可选的出队方法硬塞到返回非可选的数据源方法中。这个解决方案让所有这些都消失了。
【解决方案3】:

@Sulthan 的回答很到位。一种可能的方便修改是将单元格转换为 UITableViewCell!,而不是 UITableViewCell。

func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
    var cell = tableView.dequeueReusableCellWithIdentifier("CELL") as UITableViewCell!
    if !cell {
        cell = UITableViewCell(style:.Default, reuseIdentifier: "CELL")
    }
    // setup cell without force unwrapping it
    cell.textLabel.text = "Swift"
    return cell
}

现在,您可以修改单元格变量,而无需每次都强制解包。使用隐式展开的选项时要小心。您必须确定您正在访问的值是有价值的。

有关详细信息,请参阅Swift 编程语言的“隐式解包选项”部分。

【讨论】:

    【解决方案4】:

    试试这个:

    func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell
        cell.textLabel.text = "\(indexPath.row)"
    
        return cell
    }
    

    请注意,在创建 UITableView 的实例时,您应该注册您的 UITableViewCell 和 ID:

    tableView.delegate = self
    tableView.dataSource = self
    tableView.registerClass(UITableViewCell.classForCoder(), forCellReuseIdentifier: "Cell")
    

    【讨论】:

    • 这行得通,所以它肯定让我更接近,但这需要你注册一个单元格,因为如果你使用'let'你不能分配给单元格并且注册一个单元格会阻止使用 CellStyleValue1 的能力
    • 如果您想更改单元格样式。您只需要创建一个自定义单元子类并在 init 方法中使用您想要的样式。
    • 对我来说,不同的单元格样式假定每个单元格的行为不同,因此创建自定义 UITableViewCell 子类对我来说更有意义。
    • 好吧,这对我来说很有意义。你是对的它确实工作得很好,幸运的是 swift 可以更容易地将另一个子类偷偷放到同一个文件中
    • 是的!您不必再导入任何自定义类!很漂亮:-)
    【解决方案5】:

    这是我为让它工作而写的...

    首先将表格视图单元格注册到表格视图中

    self.tableView.registerClass(MyTableViewCell.self, forCellReuseIdentifier: "Cell")
    

    然后配置cellForRowAtIndexPath

    func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!  {
        var cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as MyTableViewCell
    
        cell.textLabel.text = "Cell Text"
        cell.detailTextLabel.text = "Cell Detail Text in Value 1 Style"
    
        return cell
    }
    

    然后我在文件底部定义了一个自定义单元子类写入(因为它现在容易多了)

    class MyTableViewCell : UITableViewCell {
    
        init(style: UITableViewCellStyle, reuseIdentifier: String!) {
            super.init(style: UITableViewCellStyle.Value1, reuseIdentifier: reuseIdentifier)
        }
    
    }
    

    【讨论】:

      【解决方案6】:

      这是在 swift 2 中定义表格单元格的简单方法:

      func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
          let identifier = "cell"
          let cell = tableView.dequeueReusableCellWithIdentifier(identifier) ??
              UITableViewCell.init(style: UITableViewCellStyle.Default, reuseIdentifier: identifier)
          cell.textLabel!.text = "my text"
          return cell
      }
      

      斯威夫特 3:

      func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
          let identifier = "cell"
          let cell = tableView.dequeueReusableCell(withIdentifier: identifier) ??
              UITableViewCell(style: .default, reuseIdentifier: identifier)
          cell.textLabel!.text = "my text"
          return cell
      }
      

      【讨论】:

        【解决方案7】:

        这里有一些答案,但我认为它们中的任何一个都不是理想的,因为在声明之后你会得到一个可选的 UITableViewCell,然后在任何声明中都需要一个cell!...。我认为这是一种更好的方法(我可以确认它在 Xcode 6.1 上编译):

        var cell:UITableViewCell
        
        if let c = tableView.dequeueReusableCellWithIdentifier("cell") as? UITableViewCell {
            cell = c
        }
        else {
            cell = UITableViewCell()
        }
        

        【讨论】:

          【解决方案8】:

          嗯,我是这样做的:

          UITableView 使用 Swift 的步骤:

          • ViewController中取UITableView
          • ViewController.swift 类中提供 Reference Outlets
          • 将 Outlets dataSource委托 分配给 ViewController

          ViewController.swift 类中的 Swift 代码:

          class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
          
              @IBOutlet weak var mTableView: UITableView!
          
              var items: [String] = ["Item 1","Item 2","Item 3", "Item 4", "Item 5"]
          
              override func viewDidLoad() {
                  super.viewDidLoad()
                  // Do any additional setup after loading the view, typically from a nib.
          
                  self.mTableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: "cell")
              }
          
              override func didReceiveMemoryWarning() {
                  super.didReceiveMemoryWarning()
                  // Dispose of any resources that can be recreated.
              }
          
              func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
                  return self.items.count;
              }
          
              func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
                  var cell:UITableViewCell = self.mTableView.dequeueReusableCellWithIdentifier("cell") as! UITableViewCell
          
                  cell.textLabel?.text = self.items[indexPath.row]
                   println(self.items[indexPath.row])
          
                  return cell
              }
          
              func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
                  println("You have selected cell #\(indexPath.row)!")
              }
          }
          

          现在是运行您的程序的时候了。

          完成

          【讨论】:

          • 我收到一个错误,即在每个“func”之前没有“覆盖”。我猜是因为我使用的是 UITableViewController,而不是 UITableViewDelegate。
          • @the0ther,是的。你能按照我的步骤做同样的吗?
          【解决方案9】:

          其实在Apple的TableView Guide文档和Sample Code中你会发现下面这句话:

          如果 dequeueReusableCellWithIdentifier: 方法要求一个在故事板中定义的单元格,该方法总是返回一个有效的单元格。如果没有等待重用的回收单元,则该方法使用情节提要本身中的信息创建一个新单元。这样就无需检查 nil 的返回值并手动创建单元格。

          所以,我们可以这样编码:

          var identifer: String = "myCell"
          var cell = tableView.dequeueReusableCellWithIdentifier(identifer) as UITableViewCell
          cell.textLabel.text = a[indexPath.row].name
          cell.detailTextLabel.text = "detail"
          

          我认为这是使用tableView的合适方式

          【讨论】:

          • 这是正确的,但除非您指定单元格样式为subtitle,否则您将永远无法显示detailLabel。那么,如何告诉 Xcode 使用 tableView.dequeueReusableCellWithIdentifiersubtitle 样式呢?
          【解决方案10】:

          使用“as”关键字将执行以下两个步骤:
          1.创建一个包装UITableViewCell变量的可选值;
          2.解开可选值。

          所以,通过这样做

          var cell : UITableViewCell = tableView.dequeueReusableCellWithIdentifier("Component") as UITableViewCell
          

          你会得到一个“普通”的 UITableViewCell 类型变量:cell。理论上来说,这样做是可以的。但是下一行

          if (cell == nil) {}
          

          很麻烦,因为在swift中,只有可选值可以用nil赋值。

          所以,要解决这个问题,你必须让 cell 成为 Optional 类型的变量。就像这样:

          var cell = tableView.dequeueReusableCellWithIdentifier("Component") as? UITableViewCell
          

          使用关键字“as?”会创建一个 Optional 变量,毫无疑问,它可以用 nil 赋值。

          【讨论】:

            【解决方案11】:

            对于单元格模板:

            func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
                    让 myCell : youCell = youCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "cell")
                    返回我的细胞
                }
            

            【讨论】:

              【解决方案12】:

              【讨论】:

                【解决方案13】:

                为什么不这样?

                (如果我不在目标中,请删除...)

                func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {
                
                    if let cell: UITableViewCell = theTableView.dequeueReusableCellWithIdentifier("myCell", forIndexPath: indexPath) as? UITableViewCell {
                        // cell ok
                    }else{
                       // not ok
                    }
                }
                

                【讨论】:

                  【解决方案14】:

                  我通过以下方式完成:显示detailTextLabel。文本值

                   func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
                  
                      let CellIdentifier: String = "cell"
                  
                      var cell = tableView.dequeueReusableCellWithIdentifier(CellIdentifier) as? UITableViewCell
                  
                      if cell == nil {
                          cell = UITableViewCell(style: UITableViewCellStyle.Value1, reuseIdentifier: CellIdentifier)
                      }
                  
                      //cell.accessoryType = UITableViewCellAccessoryType.DisclosureIndicator
                  
                      // parse the value of records
                      let dataRecord = self.paymentData[indexPath.row] as! NSDictionary
                  
                      let receiverName = dataRecord["receiver_name"] as! String
                      let profession = dataRecord["profession"] as! String
                      let dateCreated = dataRecord["date_created"] as! String
                      let payAmount = dataRecord["pay_amount"] as! String
                  
                      println("payment \(payAmount)")
                      cell!.textLabel?.text = "\(receiverName)\n\(profession)\n\(dateCreated)"
                      cell!.detailTextLabel?.text = "$\(payAmount)"
                      cell!.textLabel?.numberOfLines = 4
                  
                      return cell!
                  
                  }// end tableview
                  

                  【讨论】:

                    【解决方案15】:

                    使用 Playground 的 UITableView 演示

                    //: Playground - noun: a place where people can play
                    
                    import UIKit
                    import PlaygroundSupport
                    
                    class TableviewDemoDelegate:NSObject,UITableViewDataSource,UITableViewDelegate {
                    
                    
                        func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
                            return 100
                        }
                    
                        func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
                    
                            var cell:UITableViewCell? = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath as IndexPath)
                    
                            if cell == nil {
                                cell = UITableViewCell(style: .default, reuseIdentifier: "cell")
                            }
                    
                    
                            cell?.textLabel?.text = "Item \(indexPath.row+1)"
                    
                            return cell!
                        }
                    
                    
                        func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
                            print("You have selected cell #\(indexPath.row)!")
                    
                        }
                    }
                    
                    var tableView = UITableView(frame:CGRect(x: 0, y: 0, width: 320, height: 568), style: .plain)
                    tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
                    
                    let delegate = TableviewDemoDelegate()
                    tableView.delegate = delegate
                    tableView.dataSource = delegate
                    
                    
                    PlaygroundPage.current.liveView = tableView
                    

                    【讨论】:

                      【解决方案16】:

                      我浏览了你的代码,很可能崩溃的原因是你试图对一个未分配的可选值进行类型转换

                      现在考虑下面的代码行

                      var cell : UITableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell") as UITableViewCell
                      

                      当 tableview 中没有单元格时,您仍在尝试将类型转换为 UITableView。当编译器尝试对 nil 值进行类型转换时,您将面临此问题

                      正确的说法应该是

                      var cell : UITableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell") 
                      

                      您可以使用 if else 语句来对持有的值进行类型转换

                      【讨论】:

                        【解决方案17】:

                        试试这个代码

                        var cell:CustomTableViewCell = tableView.dequeueReusableCellWithIdentifier("CustomTableViewCell") as CustomTableViewCell
                        cell.cellTitle.text="vijay"
                        

                        https://github.com/iappvk/TableView-Swift

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 2022-01-24
                          • 2016-06-24
                          • 2015-09-13
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          • 1970-01-01
                          相关资源
                          最近更新 更多