【问题标题】:How do you load custom UITableViewCells from Xib files?如何从 Xib 文件中加载自定义 UITableViewCells?
【发布时间】:2010-10-07 02:56:07
【问题描述】:

问题很简单:如何从 Xib 文件中加载自定义 UITableViewCell?这样做允许您使用 Interface Builder 来设计您的单元格。由于内存管理问题,答案显然并不简单。 This thread 提到了这个问题并提出了解决方案,但它是在 NDA 发布之前并且缺少代码。这是一个long thread,它讨论了这个问题,但没有提供明确的答案。

这是我用过的一些代码:

static NSString *CellIdentifier = @"MyCellIdentifier";

MyCell *cell = (MyCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:self options:nil];
    cell = (MyCell *)[nib objectAtIndex:0];
}

要使用此代码,请创建 UITableViewCell 的新子类 MyCell.m/.h,并为所需的组件添加 IBOutlets。然后创建一个新的“空 XIB”文件。在 IB 中打开 Xib 文件,添加一个 UITableViewCell 对象,将其标识符设置为“MyCellIdentifier”,并将其类设置为 MyCell 并添加您的组件。最后,将IBOutlets 连接到组件。请注意,我们没有在 IB 中设置文件的所有者。

如果 Xib 没有通过额外的工厂类加载,其他方法提倡设置文件的所有者并警告内存泄漏。我在 Instruments/Leaks 下测试了上述内容,没有发现内存泄漏。

那么从 Xibs 加载单元的规范方法是什么?我们设置文件的所有者吗?我们需要工厂吗?如果是这样,工厂的代码是什么样的?如果有多种解决方案,让我们阐明每种解决方案的优缺点...

【问题讨论】:

  • 有人可以编辑主题以实际提出问题,即“如何从 Xib 文件加载自定义 UITableViewCells?” (如果这在 stackoverflow 上是不可能的,请忽略。)
  • 对于 iOS 5 及更高版本,这是解决方案:stackoverflow.com/questions/15591364/…,与 giuseppe 的解决方案相同。
  • 快速注释,更简单(2013 年环境)在这里回答stackoverflow.com/questions/15378788/… jamihash

标签: ios uitableview object cocoa-touch xib


【解决方案1】:

正确的解决方案是这样的:

- (void)viewDidLoad
{
    [super viewDidLoad];
    UINib *nib = [UINib nibWithNibName:@"ItemCell" bundle:nil];
    [[self tableView] registerNib:nib forCellReuseIdentifier:@"ItemCell"];
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    // Create an instance of ItemCell
    PointsItemCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ItemCell"];

    return cell;
}

【讨论】:

  • 这会破坏 iOS5 应用吗?我真的从未见过 UINib
  • @AdamWaite 注册 NIB 文件适用于 iOS 5 及更高版本,因此不会破坏 iOS 5 应用程序。 UINib 甚至从 iOS 4 开始就存在了。
  • 举个很好的例子,请查看此处最佳答案中引用的 git repo:stackoverflow.com/questions/18746929/…
【解决方案2】:

这里有两种方法original author states was recommended by an IB engineer

查看实际帖子了解更多详情。我更喜欢方法#2,因为它看起来更简单。

方法#1:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BDCustomCell"];
    if (cell == nil) {
        // Create a temporary UIViewController to instantiate the custom cell.
        UIViewController *temporaryController = [[UIViewController alloc] initWithNibName:@"BDCustomCell" bundle:nil];
        // Grab a pointer to the custom cell.
        cell = (BDCustomCell *)temporaryController.view;
        [[cell retain] autorelease];
        // Release the temporary UIViewController.
        [temporaryController release];
    }

    return cell;
}

方法#2:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BDCustomCell"];
    if (cell == nil) {
        // Load the top-level objects from the custom cell XIB.
        NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"BDCustomCell" owner:self options:nil];
        // Grab a pointer to the first object (presumably the custom cell, as that's all the XIB should contain).
        cell = [topLevelObjects objectAtIndex:0];
    }

    return cell;
}

更新(2014 年): 方法 #2 仍然有效,但不再有相关文档。它曾经在 official docs 中,但现在被删除以支持故事板。

我在 Github 上发布了一个工作示例:
https://github.com/bentford/NibTableCellExample

为 Swift 4.2 编辑

override func viewDidLoad() {
    super.viewDidLoad()

    // Do any additional setup after loading the view.
    self.tblContacts.register(UINib(nibName: CellNames.ContactsCell, bundle: nil), forCellReuseIdentifier: MyIdentifier)
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCell(withIdentifier: MyIdentifier, for: indexPath) as! ContactsCell

    return cell
}

【讨论】:

  • 对于方法1,你不应该做类似“cell = (BDCustomCell *)[[temporaryController.view retain] autorelease];”的事情所以释放临时控制器时不会释放单元格?
  • 嗯。讨论 #2 的文档仍然告诉您在 XIB 文件中将单元的所有者设置为已知的控制器类。也许在加载过程中设置所有者并不重要。
  • @OscarGoldman XIB 文件中单元格的所有者是一个类(即所有者的类型)。loadNibNamed:owner:options: 中的单元格所有者是 XIB 中指定类型的对象。
  • @CoolDocMan 选项 #2 仍然有效。问题很可能出在笔尖上。这是一个示例:github.com/bentford/NibTableCellExample
  • 为什么这个超级老代码排名这么高。 Stackoverflow 做点什么:/
【解决方案3】:

Swift 4.2 和 Xcode 10

我有三个 XIB 单元文件

在 ViewDidLoad 中像这样注册您的 XIB 文件...

这是第一种方法

tableView.register(UINib.init(nibName: "XIBCell", bundle: nil), forCellReuseIdentifier: "cell1")
tableView.register(UINib.init(nibName: "XIBCell2", bundle: nil), forCellReuseIdentifier: "cell2")
//tableView.register(UINib.init(nibName: "XIBCell3", bundle: nil), forCellReuseIdentifier: "cell3")

第二种方法直接在cellForRowAt indexPath:

中注册XIB文件

这是我的 tableview 委托函数

//MARK: - Tableview delegates
override func numberOfSections(in tableView: UITableView) -> Int {

    return 1
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

    return 6
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    //This is first approach
    if indexPath.row == 0 {//Load first XIB cell
        let placeCell = tableView.dequeueReusableCell(withIdentifier: "cell1") as! XIBCell
        return placeCell
    //Second approach
    } else if indexPath.row == 5 {//Load XIB cell3
        var cell = tableView.dequeueReusableCell(withIdentifier:"cell3") as? XIBCell3
        if cell == nil{
            let arrNib:Array = Bundle.main.loadNibNamed("XIBCell3",owner: self, options: nil)!
            cell = arrNib.first as? XIBCell3
        }

        //ADD action to XIB cell button
        cell?.btn.tag = indexPath.row//Add tag to button
        cell?.btn.addTarget(self, action: #selector(self.bookbtn1(_:)), for: .touchUpInside);//selector

        return cell!
    //This is first approach
    } else {//Load XIB cell2
        let placeCell = tableView.dequeueReusableCell(withIdentifier: "cell2") as! XIBCell2

        return placeCell
    }

}

【讨论】:

    【解决方案4】:
     func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
                let cellReuseIdentifier = "collabCell"
                var cell:collabCell! = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as? collabCell
                if cell == nil {
                    tableView.register(UINib(nibName: "collabCell", bundle: nil), forCellReuseIdentifier: cellReuseIdentifier)
                    cell = tableView.dequeueReusableCell(withIdentifier: cellReuseIdentifier) as! collabCell!
                }
    
    
                return cell
    
    }
    

    【讨论】:

      【解决方案5】:

      这是在UITableView 中注册单元格的通用方法:

      protocol Reusable {
          static var reuseID: String { get }
      }
      
      extension Reusable {
          static var reuseID: String {
              return String(describing: self)
          }
      }
      
      extension UITableViewCell: Reusable { }
      
      extension UITableView {
      
      func register<T: UITableViewCell>(cellClass: T.Type = T.self) {
          let bundle = Bundle(for: cellClass.self)
          if bundle.path(forResource: cellClass.reuseID, ofType: "nib") != nil {
              let nib = UINib(nibName: cellClass.reuseID, bundle: bundle)
              register(nib, forCellReuseIdentifier: cellClass.reuseID)
          } else {
              register(cellClass.self, forCellReuseIdentifier: cellClass.reuseID)
          }
      }
      

      解释:

      1. Reusable 协议根据其类名生成单元 ID。请务必遵守约定:cell ID == class name == nib name
      2. UITableViewCell 符合 Reusable 协议。
      3. UITableView 扩展抽象出通过 nib 或类注册单元格的区别。

      使用示例:

      override func viewDidLoad() {
          super.viewDidLoad()
          let tableView = UITableView()
          let cellClasses: [UITableViewCell.Type] = [PostCell.self, ProfileCell.self, CommentCell.self]
          cellClasses.forEach(tableView.register)
      }
      
      override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
          let cell = tableView.dequeueReusableCell(withIdentifier: PostCell.self.reuseID) as? PostCell
          ...
          return cell
      }
      

      【讨论】:

        【解决方案6】:
        1. UITableViewCell创建您自己的自定义类AbcViewCell子类(确保您的类文件名和nib文件名相同)

        2. 创建这个扩展类方法。

          extension UITableViewCell {
              class func fromNib<T : UITableViewCell>() -> T {
                  return Bundle.main.loadNibNamed(String(describing: T.self), owner: nil, options: nil)?[0] as! T
              }
          }
          
        3. 使用它。

          let cell: AbcViewCell = UITableViewCell.fromNib()

        【讨论】:

          【解决方案7】:

          注册

          在 iOS 7 之后,这个过程被简化为(swift 3.0):

          // For registering nib files
          tableView.register(UINib(nibName: "MyCell", bundle: Bundle.main), forCellReuseIdentifier: "cell")
          
          // For registering classes
          tableView.register(MyCellClass.self, forCellReuseIdentifier: "cell")
          

          注意)这也可以通过在.xib.stroyboard 文件中创建单元格作为原型单元格来实现。 如果你需要给它们附加一个类,你可以选择单元格原型并添加对应的类(当然必须是UITableViewCell的后代)。

          出队

          然后,使用 (swift 3.0) 出列:

          override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
          {
              let cell : UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
          
              cell.textLabel?.text = "Hello"
          
              return cell
          }
          

          不同之处在于,这种新方法不仅使单元格出列,它还会创建不存在的单元格(这意味着您不必执行if (cell == nil) 恶作剧),并且单元格已准备好使用,就像在上面的例子。

          (Warning) tableView.dequeueReusableCell(withIdentifier:for:) 具有新行为,如果您调用另一个(没有indexPath:),您会得到旧行为,您需要在其中检查nil 和自己实例化,注意 UITableViewCell? 返回值。

          if let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? MyCellClass
          {
              // Cell be casted properly
              cell.myCustomProperty = true
          }
          else
          {
              // Wrong type? Wrong identifier?
          }
          

          当然,单元格关联类的类型是您在 .xib 文件中为 UITableViewCell 子类定义的类型,或者使用其他注册方法。

          配置

          理想情况下,您的单元格在您注册它们时已经在外观和内容定位(如标签和图像视图)方面进行了配置,并且您只需在 cellForRowAtIndexPath 方法中填写它们。

          大家一起

          class MyCell : UITableViewCell
          {
              // Can be either created manually, or loaded from a nib with prototypes
              @IBOutlet weak var labelSomething : UILabel? = nil
          }
          
          class MasterViewController: UITableViewController 
          {
              var data = ["Hello", "World", "Kinda", "Cliche", "Though"]
          
              // Register
              override func viewDidLoad()
              {
                  super.viewDidLoad()
          
                  tableView.register(MyCell.self, forCellReuseIdentifier: "mycell")
                  // or the nib alternative
              }
          
              override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
              {
                  return data.count
              }
          
              // Dequeue
              override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
              {
                  let cell = tableView.dequeueReusableCell(withIdentifier: "mycell", for: indexPath) as! MyCell
          
                  cell.labelSomething?.text = data[indexPath.row]
          
                  return cell
              }
          }
          

          当然,这在 ObjC 中都可以使用相同的名称。

          【讨论】:

          • 这里是objC版本:[self.tableView registerNib:[UINib nibWithNibName:@"BlaBlaTableViewCell" bundle:nil] forCellReuseIdentifier:kCellIdentifier];
          【解决方案8】:

          正确的解决方案是这个

          - (void)viewDidLoad
          {
              [super viewDidLoad];
              [self.tableView registerNib:[UINib nibWithNibName:@"CustomCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"CustomCell"];
          }
          
          - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
              UITableViewCell  *cell = [tableView dequeueReusableCellWithIdentifier:@"CustomCell"];
              return cell; 
              }
          

          【讨论】:

            【解决方案9】:

            此扩展需要 Xcode7 beta6

            extension NSBundle {
                enum LoadViewError: ErrorType {
                    case ExpectedXibToExistButGotNil
                    case ExpectedXibToContainJustOneButGotDifferentNumberOfObjects
                    case XibReturnedWrongType
                }
            
                func loadView<T>(name: String) throws -> T {
                    let topLevelObjects: [AnyObject]! = loadNibNamed(name, owner: self, options: nil)
                    if topLevelObjects == nil {
                        throw LoadViewError.ExpectedXibToExistButGotNil
                    }
                    if topLevelObjects.count != 1 {
                        throw LoadViewError.ExpectedXibToContainJustOneButGotDifferentNumberOfObjects
                    }
                    let firstObject: AnyObject! = topLevelObjects.first
                    guard let result = firstObject as? T else {
                        throw LoadViewError.XibReturnedWrongType
                    }
                    return result
                }
            }
            

            创建一个仅包含 1 个自定义 UITableViewCell 的 Xib 文件。

            加载它。

            let cell: BacteriaCell = try NSBundle.mainBundle().loadView("BacteriaCell")
            

            【讨论】:

              【解决方案10】:

              从 XIB 加载 UITableViewCells 可以节省大量代码,但通常会导致滚动速度非常糟糕(实际上,不是 XIB 而是过度使用 UIViews 导致了这种情况)。

              我建议你看看这个:Link reference

              【讨论】:

                【解决方案11】:
                 NSString *CellIdentifier = [NSString stringWithFormat:@"cell %ld %ld",(long)indexPath.row,(long)indexPath.section];
                
                
                    NewsFeedCell *cell = (NewsFeedCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
                    cell=nil;
                
                    if (cell == nil)
                    {
                        NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"NewsFeedCell" owner:nil options:nil];
                
                        for(id currentObject in topLevelObjects)
                        {
                            if([currentObject isKindOfClass:[NewsFeedCell class]])
                            {
                                cell = (NewsFeedCell *)currentObject;
                                break;
                            }
                        }
                }
                return cell;
                

                【讨论】:

                  【解决方案12】:

                  首先导入您的自定义单元格文件#import "CustomCell.h",然后更改委托方法,如下所述:

                  - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
                  
                  static NSString *simpleTableIdentifier = @"CustomCell";
                  
                  CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
                  if (cell == nil)
                  {
                      NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:self options:nil];
                      cell = [nib objectAtIndex:0];
                  
                      [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
                  }         
                  
                       return cell;
                  }
                  

                  【讨论】:

                    【解决方案13】:

                    我决定发帖是因为我不喜欢这些答案——事情总是更简单,这是迄今为止我发现的最简洁的方式。

                    1.随心所欲地在 Interface Builder 中构建您的 Xib

                    • 将文件的所有者设置为 NSObject 类
                    • 添加一个 UITableViewCell 并将其类设置为 MyTableViewCellSubclass - 如果您的 IB 崩溃(在撰写本文时发生在 Xcode > 4 中),只需使用 UIView 做 Xcode 4 中的界面(如果您仍然放置它)李>
                    • 在此单元格内布局您的子视图,并将您的 IBOutlet 连接附加到 .h 或 .m 中的 @interface(.m 是我的首选)

                    2。在你的 UIViewController 或 UITableViewController 子类中

                    @implementation ViewController
                    
                    static NSString *cellIdentifier = @"MyCellIdentier";
                    
                    - (void) viewDidLoad {
                    
                        ...
                        [self.tableView registerNib:[UINib nibWithNibName:@"MyTableViewCellSubclass" bundle:nil] forCellReuseIdentifier:cellIdentifier];
                    }
                    
                    - (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
                    {
                        MyTableViewCellSubclass *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
                    
                        ...
                    
                        return cell;
                    }
                    

                    3.在你的 MyTableViewCellSubclass

                    - (id) initWithCoder:(NSCoder *)aDecoder {
                        if (self = [super initWithCoder:aDecoder]) {
                            ...
                        }
                    
                        return self;
                    }
                    

                    【讨论】:

                      【解决方案14】:

                      为此我要做的是在你的控制器类中声明一个IBOutlet UITableViewCell *cell。 然后调用NSBundle loadNibNamed 类方法,它将UITableViewCell 提供给上面声明的单元格。

                      对于 xib,我将创建一个空的 xib 并在 IB 中添加 UITableViewCell 对象,可以根据需要进行设置。这个视图然后连接到控制器类中的单元格IBOutlet

                      - (UITableViewCell *)tableView:(UITableView *)table
                               cellForRowAtIndexPath:(NSIndexPath *)indexPath
                      {
                          NSLog(@"%@ loading RTEditableCell.xib", [self description] );
                      
                          static NSString *MyIdentifier = @"editableCellIdentifier";
                          cell = [table dequeueReusableCellWithIdentifier:MyIdentifier];
                      
                          if(cell == nil) {
                              [[NSBundle mainBundle] loadNibNamed:@"RTEditableCell"
                                                            owner:self
                                                          options:nil];
                          }
                      
                          return cell;
                      }
                      

                      NSBundle additions loadNibNamed (ADC login)

                      cocoawithlove.com article I sourced the concept from (get the phone numbers sample app)

                      【讨论】:

                        【解决方案15】:

                        这是我一直用来从 XIB 中创建自定义单元格的类方法:

                        + (CustomCell*) createNewCustomCellFromNib {
                        
                            NSArray* nibContents = [[NSBundle mainBundle]
                                                    loadNibNamed:@"CustomCell" owner:self options:NULL];
                        
                            NSEnumerator *nibEnumerator = [nibContents objectEnumerator];
                            CustomCell *customCell= nil;
                            NSObject* nibItem = nil;
                        
                            while ( (nibItem = [nibEnumerator nextObject]) != nil) {
                        
                                if ( [nibItem isKindOfClass: [CustomCell class]]) {
                                    customCell = (CustomCell*) nibItem;
                        
                                    if ([customCell.reuseIdentifier isEqualToString: @"CustomCell"]) {
                                        break; // we have a winner
                                    }
                                    else
                                        fuelEntryCell = nil;
                                }
                            }
                            return customCell;
                        }
                        

                        然后,在 XIB 中,我设置了类名,并重用了标识符。之后,我可以在视图控制器中调用该方法而不是

                        [[UITableViewCell] alloc] initWithFrame:]
                        

                        它足够快,并且在我的两个运输应用程序中使用。它比调用[nib objectAtIndex:0] 更可靠,至少在我看来,它比 Stephan Burlot 的示例更可靠,因为您保证只能从正确类型的 XIB 中获取视图。

                        【讨论】:

                          【解决方案16】:

                          检查这个 - http://eppz.eu/blog/custom-uitableview-cell/ - 非常方便的方法,使用一个小类,在控制器实现中结束一行:

                          -(UITableViewCell*)tableView:(UITableView*) tableView cellForRowAtIndexPath:(NSIndexPath*) indexPath
                          {
                              return [TCItemCell cellForTableView:tableView
                                                    atIndexPath:indexPath
                                                withModelSource:self];
                          }
                          

                          【讨论】:

                            【解决方案17】:

                            正确的做法是创建一个 UITableViewCell 子类实现、标头和 XIB。在 XIB 中删除所有视图并添加一个表格单元格。将类设置为 UITableViewCell 子类的名称。对于文件所有者,将其设为 UITableViewController 子类类名。使用 tableViewCell 插座将文件所有者连接到单元格。

                            在头文件中:

                            UITableViewCell *_tableViewCell;
                            @property (assign) IBOutlet UITableViewCell *tableViewCell;
                            

                            在实现文件中:

                            @synthesize tableViewCell = _tableViewCell;
                            
                            - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
                                static NSString *kCellIdentifier = @"reusableCell";
                            
                                UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier];
                                if (cell == nil) {
                                    [[NSBundle mainBundle] loadNibNamed:kCellIdentifier owner:self options:nil];
                                    cell = _tableViewCell;
                                    self.tableViewCell = nil;
                                }
                            
                                return cell;
                            }
                            

                            【讨论】:

                              【解决方案18】:

                              我使用了bentford的方法#2

                              - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
                                  UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BDCustomCell"];
                                  if (cell == nil) {
                                      // Load the top-level objects from the custom cell XIB.
                                      NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"BDCustomCell" owner:self options:nil];
                                      // Grab a pointer to the first object (presumably the custom cell, as that's all the XIB should contain).
                                      cell = [topLevelObjects objectAtIndex:0];
                                  }
                              
                                  return cell;
                              }
                              

                              它可以工作,但请注意在您的自定义 UITableViewCell .xib 文件中与 File's Owner 的连接。

                              通过在loadNibNamed 语句中传递owner:self,您将UITableViewController 设置为UITableViewCell 的文件所有者。

                              如果你拖放到IB中的头文件来设置动作和出口,它会默认将它们设置为文件的所有者。

                              loadNibNamed:owner:options 中,Apple 的代码将尝试在您的UITableViewController 上设置属性,因为那是所有者。但是你没有在那里定义这些属性,所以你会得到一个关于键值编码兼容的错误

                              *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason:     '[<MyUITableViewController 0x6a383b0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key myLabel.'
                              

                              如果一个事件被触发,你会得到一个 NSInvalidArgumentException:

                              -[MyUITableViewController switchValueDidChange:]: unrecognized selector sent to instance 0x8e9acd0
                              *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MyUITableViewController switchValueDidChange:]: unrecognized selector sent to instance 0x8e9acd0'
                              *** First throw call stack:
                              (0x1903052 0x15eed0a 0x1904ced 0x1869f00 0x1869ce2 0x1904ec9 0x5885c2 0x58855a 0x62db76 0x62e03f 0x77fa6c 0x24e86d 0x18d7966 0x18d7407 0x183a7c0 0x1839db4 0x1839ccb 0x1f8b879 0x1f8b93e 0x585a9b 0xb904d 0x2c75)
                              terminate called throwing an exceptionCurrent language:  auto; currently objective-c
                              

                              一个简单的解决方法是将您的 Interface Builder 连接指向 UITableViewCell 而不是 File's Owner:

                              1. 右键单击 File's Owner 以调出连接列表
                              2. 使用 Command-Shift-4 截屏(拖动以选择要截屏的区域)
                              3. 删除来自文件所有者的连接
                              4. 右键单击对象层次结构中的 UITableCell 并重新添加连接。

                              【讨论】:

                              • 我遇到了你提到的问题,但是如何将连接指向 UITableViewCell 而不是 File 的所有者?我不明白你的步骤,例如为什么需要截图?当我点击插座旁边的添加按钮时,什么也没有发生
                              • @xuhuanze 我建议截屏,这样您就可以记录文件所有者已经连接到的内容。然后您可以重新创建这些相同的连接。您需要拖放来添加连接——不仅仅是单击。
                              • 非常感谢,我有“这个类不符合键的键值编码”问题,并在您的帮助下解决了。我想告诉其他人,您还应该将 UITableViewCell 的一个类更改为您的类,用作自定义单元类。
                              【解决方案19】:

                              重新加载 NIB 的成本很高。最好加载一次,然后在需要单元格时实例化对象。请注意,您可以使用此方法将 UIImageViews 等添加到笔尖,甚至多个单元格(Apple 的“registerNIB”iOS5 只允许一个顶级对象 - 错误 10580062 “iOS5 tableView registerNib:过于严格”

                              所以我的代码在下面 - 你在 NIB 中读过一次(像我一样在初始化或在 viewDidload 中 - 无论如何。从那时起,你将 nib 实例化为对象,然后选择你需要的对象。这比一遍又一遍地加载笔尖。

                              static UINib *cellNib;
                              
                              + (void)initialize
                              {
                                  if(self == [ImageManager class]) {
                                      cellNib = [UINib nibWithNibName:@"ImageManagerCell" bundle:nil];
                                      assert(cellNib);
                                  }
                              }
                              
                              - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
                              {
                                  static NSString *cellID = @"TheCell";
                              
                                  UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
                                  if(cell == nil) {
                                      NSArray *topLevelItems = [cellNib instantiateWithOwner:nil options:nil];
                                      NSUInteger idx = [topLevelItems indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop)
                                                          {
                                                              UITableViewCell *cell = (UITableViewCell *)obj;
                                                              return [cell isKindOfClass:[UITableViewCell class]] && [cell.reuseIdentifier isEqualToString:cellID];
                                                          } ];
                                      assert(idx != NSNotFound);
                                      cell = [topLevelItems objectAtIndex:idx];
                                  }
                                  cell.textLabel.text = [NSString stringWithFormat:@"Howdie %d", indexPath.row];
                              
                                  return cell;
                              }
                              

                              【讨论】:

                                【解决方案20】:

                                这是我的方法:Loading Custom UITableViewCells from XIB Files… Yet Another Method

                                这个想法是创建一个具有IBOutlet UIView *content 属性的UITableViewCell 的SampleCell 子类,并为您需要从代码中配置的每个自定义子视图创建一个属性。然后创建一个 SampleCell.xib 文件。在此 nib 文件中,将文件所有者更改为 SampleCell。添加内容UIView 大小以满足您的需求。添加和配置您想要的所有子视图(标签、图像视图、按钮等)。最后,将内容视图和子视图链接到文件所有者。

                                【讨论】:

                                  【解决方案21】:

                                  接受了 Shawn Craver 的回答并稍微整理了一下。

                                  BBCell.h:

                                  #import <UIKit/UIKit.h>
                                  
                                  @interface BBCell : UITableViewCell {
                                  }
                                  
                                  + (BBCell *)cellFromNibNamed:(NSString *)nibName;
                                  
                                  @end
                                  

                                  BBCell.m:

                                  #import "BBCell.h"
                                  
                                  @implementation BBCell
                                  
                                  + (BBCell *)cellFromNibNamed:(NSString *)nibName {
                                      NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:NULL];
                                      NSEnumerator *nibEnumerator = [nibContents objectEnumerator];
                                      BBCell *customCell = nil;
                                      NSObject* nibItem = nil;
                                      while ((nibItem = [nibEnumerator nextObject]) != nil) {
                                          if ([nibItem isKindOfClass:[BBCell class]]) {
                                              customCell = (BBCell *)nibItem;
                                              break; // we have a winner
                                          }
                                      }
                                      return customCell;
                                  }
                                  
                                  @end
                                  

                                  我把我所有的UITableViewCell的子类都做成了BBCell,然后替换掉标准

                                  cell = [[[BBDetailCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"BBDetailCell"] autorelease];
                                  

                                  与:

                                  cell = (BBDetailCell *)[BBDetailCell cellFromNibNamed:@"BBDetailCell"];
                                  

                                  【讨论】:

                                    【解决方案22】:

                                    如果您使用 Interface Builder 制作单元格,请检查您是否已在 Inspector 中设置了 Identifier。然后在调用 dequeueReusableCellWithIdentifier 时检查是否相同。

                                    在一个繁重的项目中不小心忘记设置一些标识符,性能变化就像白天和黑夜。

                                    【讨论】:

                                      【解决方案23】:

                                      我不知道是否有规范的方法,但这是我的方法:

                                      • 为 ViewController 创建一个 xib
                                      • 将 File Owner 类设置为 UIViewController
                                      • 删除视图并添加 UITableViewCell
                                      • 将 UITableViewCell 的 Class 设置为自定义类
                                      • 设置你的 UITableViewCell 的标识符
                                      • 将视图控制器视图的出口设置为 UITableViewCell

                                      并使用此代码:

                                      MyCustomViewCell *cell = (MyCustomViewCell *)[_tableView dequeueReusableCellWithIdentifier:CellIdentifier];
                                      if (cell == nil) {
                                        UIViewController* c = [[UIViewController alloc] initWithNibName:CellIdentifier bundle:nil];
                                        cell = (MyCustomViewCell *)c.view;
                                        [c release];
                                      }
                                      

                                      在您的示例中,使用

                                      [nib objectAtIndex:0]
                                      

                                      如果 Apple 更改 xib 中的项目顺序,可能会中断。

                                      【讨论】:

                                      • 对我来说,这会导致总是创建一个新实例。 dequeue 似乎每次都返回 nil。
                                      猜你喜欢
                                      • 2012-02-12
                                      • 1970-01-01
                                      • 2017-10-01
                                      • 2016-10-31
                                      • 1970-01-01
                                      • 2015-02-03
                                      • 2017-07-22
                                      • 1970-01-01
                                      • 1970-01-01
                                      相关资源
                                      最近更新 更多