objective-c 使用单独的委托/数据源时的 UITableView 问题

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/254354/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 20:52:53  来源:igfitidea点击:

UITableView issue when using separate delegate/dataSource

iphoneobjective-ccocoa-touchuitableview

提问by Adam Alexander

General Description:

一般说明:

To start with what works, I have a UITableViewwhich has been placed onto an Xcode-generated view using Interface Builder. The view's File Owner is set to an Xcode-generated subclass of UIViewController. To this subclass I have added working implementations of numberOfSectionsInTableView: tableView:numberOfRowsInSection:and tableView:cellForRowAtIndexPath:and the Table View's dataSourceand delegateare connected to this class via the File Owner in Interface Builder.

首先,我UITableView使用 Interface Builder 将其放置在 Xcode 生成的视图上。视图的文件所有者设置为 Xcode 生成的UIViewController. 为了这个子类我已经加入工作的实施numberOfSectionsInTableView: tableView:numberOfRowsInSection:以及tableView:cellForRowAtIndexPath:与表视图的dataSource,并delegate通过在Interface Builder文件所有者连接到这个类。

The above configuration works with no problems. The issue occurs when I want to move this Table View's dataSourceand delegate-implementations out to a separate class, most likely because there are other controls on the View besides the Table View and I'd like to move the Table View-related code out to its own class. To accomplish this, I try the following:

上述配置工作没有问题。当我想将这个 Table ViewdataSourcedelegate-implementations 移到一个单独的类时会出现问题,很可能是因为除了 Table View 之外,View 上还有其他控件,我想将与 Table View 相关的代码移到它的自己的课。为了实现这一点,我尝试以下操作:

  • Create a new subclass of UITableViewControllerin Xcode
  • Move the known-good implementations of numberOfSectionsInTableView:, tableView:numberOfRowsInSection:and tableView:cellForRowAtIndexPath:to the new subclass
  • Drag a UITableViewControllerto the top level of the existingXIB in InterfaceBuilder, delete the UIView/UITableViewthat are automatically created for this UITableViewController, then set the UITableViewController's class to match the new subclass
  • Remove the previously-working UITableView's existing dataSourceand delegateconnections and connect them to the new UITableViewController
  • UITableViewController在 Xcode 中创建一个新的子类
  • numberOfSectionsInTableView:,tableView:numberOfRowsInSection:和的已知良好实现移动tableView:cellForRowAtIndexPath:到新的子类
  • 将 a拖到 InterfaceBuilder 中现有XIBUITableViewController的顶层 ,删除为此自动创建的/ ,然后设置的类以匹配新的子类UIViewUITableViewUITableViewControllerUITableViewController
  • 删除以前工作UITableView的现有dataSourcedelegate连接并将它们连接到新的UITableViewController

When complete, I do not have a working UITableView. I end up with one of three outcomes which can seemingly happen at random:

完成后,我没有工作UITableView. 我最终得到了看似随机发生的三种结果之一:

  • When the UITableViewloads, I get a runtime error indicating I am sending tableView:cellForRowAtIndexPath:to an object which does not recognize it
  • When the UITableViewloads, the project breaks into the debugger without error
  • There is no error, but the UITableViewdoes not appear
  • UITableView负载,我得到一个运行时错误表明我送tableView:cellForRowAtIndexPath:一个对象,不承认它
  • UITableView负载,该项目破碎成没有错误调试器
  • 没有错误,但是UITableView没有出现

With some debugging and having created a basic project just to reproduce this issue, I am usually seeing the 3rd option above (no error but no visible table view). I added some NSLog calls and found that although numberOfSectionsInTableView:and numberOfRowsInSection:are both getting called, cellForRowAtIndexPath:is not. I am convinced I'm missing something really simple and was hoping the answer may be obvious to someone with more experience than I have. If this doesn't turn out to be an easy answer I would be happy to update with some code or a sample project. Thanks for your time!

通过一些调试并创建了一个基本项目来重现这个问题,我通常会看到上面的第三个选项(没有错误但没有可见的表视图)。我添加了一些 NSLog 调用,发现虽然numberOfSectionsInTableView:numberOfRowsInSection:都被调用,但cellForRowAtIndexPath:不是。我确信我错过了一些非常简单的东西,并希望答案对于比我更有经验的人来说可能是显而易见的。如果这不是一个简单的答案,我很乐意更新一些代码或示例项目。谢谢你的时间!

Complete steps to reproduce:

完整的重现步骤:

  • Create a new iPhone OS, View-Based Application in Xcode and call it TableTest
  • Open TableTestViewController.xibin Interface Builder and drag a UITableViewonto the provided view surface.
  • Connect the UITableView's dataSourceand delegate-outlets to File's Owner, which should already represent the TableTestViewController-class. Save your changes
  • Back in Xcode, add the following code to TableTestViewController.m:
  • 在 Xcode 中创建一个新的 iPhone OS, View-Based Application 并调用它 TableTest
  • TableTestViewController.xib在 Interface Builder 中打开并将 aUITableView拖到提供的视图表面上。
  • UITableView'sdataSourcedelegate-outlets连接到 File's Owner,它应该已经代表TableTestViewController-class。保存您的更改
  • 回到 Xcode,将以下代码添加到 TableTestViewController.m:


- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    NSLog(@"Returning num sections");
    return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    NSLog(@"Returning num rows");
    return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSLog(@"Trying to return cell");
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
    }
    cell.text = @"Hello";
    NSLog(@"Returning cell");
    return cell;
}
  • Build and Go, and you should see the word Helloappear in the UITableView

  • Now to attempt to move this UITableView's logic out to a separate class, first create a new file in Xcode, choosing UITableViewControllersubclass and calling the class TableTestTableViewController

  • Remove the above code snippet from TableTestViewController.mand place it into TableTestTableViewController.m, replacing the default implementation of these three methods with ours.
  • Back in Interface Builder within the same TableTestViewController.xib-file, drag a UITableViewControllerinto the main IB window and delete the new UITableViewobject that automatically came with it
  • Set the class for this new UITableViewControllerto TableTestTableViewController
  • Remove the dataSourceand delegatebindings from the existing, previously-working UITableViewand reconnect the same two bindings to the new TableTestTableViewControllerwe created.
  • Save changes, Build and Go, and if you're getting the results I'm getting, note the UITableViewno longer functions properly
  • Build and Go,你应该看到这个词Hello出现在UITableView

  • 现在尝试将 thisUITableView的逻辑移出一个单独的类,首先在 Xcode 中创建一个新文件,选择UITableViewController子类并调用该类TableTestTableViewController

  • 删除上面的代码片段TableTestViewController.m并将其放入 中TableTestTableViewController.m,用我们的替换这三个方法的默认实现。
  • 回到同一个TableTestViewController.xib文件中的 Interface Builder ,将 aUITableViewController拖入主 IB 窗口并删除UITableView自动附带的新对象
  • 将这个新的类设置UITableViewControllerTableTestTableViewController
  • 从现有的、以前工作的中删除dataSourcedelegate绑定,UITableView并将相同的两个绑定重新连接到TableTestTableViewController我们创建的新绑定。
  • 保存更改,Build and Go,如果你得到我得到的结果,请注意UITableView不再正常运行

Solution:With some more troubleshooting and some assistance from the iPhone Developer Forums, I've documented a solution! The main UIViewControllersubclass of the project needs an outlet pointing to the UITableViewControllerinstance. To accomplish this, simply add the following to the primary view's header (TableTestViewController.h):

解决方案:通过更多的故障排除和iPhone 开发者论坛的一些帮助,我已经记录了一个解决方案!UIViewController项目的主要子类需要一个指向UITableViewController实例的出口。为此,只需将以下内容添加到主视图的标题 ( TableTestViewController.h):

#import "TableTestTableViewController.h"

and

IBOutlet TableTestTableViewController *myTableViewController;

Then, in Interface Builder, connect the new outlet from File's Owner to TableTestTableViewControllerin the main IB window. No changes are necessary in the UI part of the XIB. Simply having this outlet in place, even though no user code directly uses it, resolves the problem completely. Thanks to those who've helped and credit goes to BaldEagle on the iPhone Developer Forums for finding the solution.

然后,在 Interface Builder 中,将 File's Owner 中的新插座连接到TableTestTableViewController主 IB 窗口中。XIB 的 UI 部分不需要更改。即使没有用户代码直接使用它,只要有这个插座就可以完全解决问题。感谢那些提供帮助的人,感谢 iPhone 开发者论坛上的 BaldEagle 找到了解决方案。

采纳答案by keremk

I followed your steps, recreated the project and ran into the same problem. Basically you are almost there. There are 2 things missing (once fixed it works):

我按照您的步骤重新创建了项目并遇到了同样的问题。基本上你就快到了。缺少两件事(一旦修复它就可以工作):

  • You need to connect the tableViewof the TableTestTableViewControllerto the UITableViewyou have on the screen. As I said before because it is not IBOutletyou can override the tableViewproperty and make it and IBOutlet:

    @interface TableTestTableViewController : UITableViewController {
        UITableView *tableView;
    }
    
    @property (nonatomic, retain) IBOutlet UITableView *tableView;
    
  • Next thing is to add a reference to the TableTestTableViewControllerand retain it in the TableTestViewController. Otherwise your TableTestTableViewControllermay be released (after loading the nib with nothing hanging on to it.) and that is why you are seeing the erratic results, crashes or nothing showing. To do that add:

    @interface TableTestViewController : UIViewController {
        TableTestTableViewController *tableViewController;
    }
    
    @property (nonatomic, retain) IBOutlet TableTestTableViewController  *tableViewController;
    

    and connect that in the Interface Builder to the TableTestTableViewControllerinstance.

  • 您需要将连接tableViewTableTestTableViewControllerUITableView你的屏幕上。正如我之前所说,因为它不是IBOutlet您可以覆盖该tableView属性并使其和IBOutlet

    @interface TableTestTableViewController : UITableViewController {
        UITableView *tableView;
    }
    
    @property (nonatomic, retain) IBOutlet UITableView *tableView;
    
  • 下一步是添加对 的引用TableTestTableViewController并将其保留在TableTestViewController. 否则你TableTestTableViewController可能会被释放(在加载笔尖后没有挂任何东西。)这就是为什么你会看到不稳定的结果、崩溃或没有任何显示。要做到这一点,请添加:

    @interface TableTestViewController : UIViewController {
        TableTestTableViewController *tableViewController;
    }
    
    @property (nonatomic, retain) IBOutlet TableTestTableViewController  *tableViewController;
    

    并将其在 Interface Builder 中连接到TableTestTableViewController实例。

With the above this worked fine on my machine.

有了上面这在我的机器上运行良好。

Also I think it would be good to state the motivation behind all this (instead of just using the UITableViewControllerwith its own UITableView). In my case it was to use other views that just the UITableViewon the same screenful of content. So I can add other UILabelsor UIImagesunder UIViewand show the UITableViewunder them or above them.

此外,我认为最好说明所有这一切背后的动机(而不是仅将UITableViewController与它自己的一起使用UITableView)。在我的情况下,它是使用其他视图,只是UITableView在同一屏幕内容上。所以我可以添加其他UILabelsUIImages下方UIView并显示UITableView它们下方或上方。

回答by Pat Niemeyer

I just spent many hours pulling my hair out trying to figure out why a UITableViewwouldn't show up when when I had it embedded in a separate nib instead of in the main nib. I finally found your discussion above and realized that it was because my UITableViewControllerwasn't being retained! Apparently the delegate and datasource properties of UITableVieware not marked "retain" and so my nib was loading but the controller was getting tossed... And due to the wonders of objective-c I got noerror messages at all from this... I still don't understand why it didn't crash. I know that I've seen "message sent to released xxx" before... why wasn't it giving me one of those?!?

我花了好几个小时把头发拔出来,试图弄清楚为什么UITableView当我把它嵌入单独的笔尖而不是主笔尖时,a不会出现。我终于在上面找到了您的讨论,并意识到这是因为我UITableViewController没有被保留!显然, 的委托和数据源属性UITableView没有标记为“保留”,所以我的笔尖正在加载但控制器被扔掉了......由于objective-c的奇迹,我根本没有收到任何错误消息......我仍然不明白为什么它没有崩溃。我知道我之前看过“发送给已发布的 xxx 的消息”……为什么它没有给我其中之一?!?

I think most developers would assume that structure that they build in an interface builder would be held in some larger context (the Nib) and not subject to release. I guess I know why they do this.. so that the iPhone can drop and reload parts of the nib on low memory. But man, that was hard to figure out.

我认为大多数开发人员会假设他们在界面构建器中构建的结构将保存在某个更大的上下文中(Nib)并且不会被发布。我想我知道他们为什么这样做..这样 iPhone 就可以在内存不足的情况下放下和重新加载笔尖的一部分。但是,这很难弄清楚。

Can someone tell me where I should have read about that behavior in the docs?

有人能告诉我应该在哪里阅读文档中的这种行为吗?

Also - about hooking up the view. First, if you drag one in from the UI builder you'll see that they hook up the view property (which is an IBOutlet) to the table view. It's not necessary to expose the tableView, that seems to get set internally. In fact it doesn't even seem to be necessary to set the view unless you want viewDidLoadnotification. I've just broken the view connection between my uitableviewand uitableviewcontroller(only delegate and datasource set) and it's apparently working fine.

另外 - 关于连接视图。首先,如果您从 UI 构建器中拖入一个,您将看到它们将视图属性(即IBOutlet)连接到表视图。没有必要公开tableView,这似乎是在内部设置的。事实上,除非您想要viewDidLoad通知,否则似乎没有必要设置视图。我刚刚打破了我的uitableviewuitableviewcontroller(只有委托和数据源集)之间的视图连接,它显然工作正常。

回答by keremk

Yes for some reason (please chime in if anybody knows why...) tableViewproperty of the UITableViewControlleris not exposed as an IBOutleteven though it is a public property. So when you use Interface Builder, you can't see that property to connect to your other UITableView. So in your subclass, you can create a tableViewproperty marked as an IBOutletand connect that.

是的,出于某种原因(如果有人知道为什么,请插话......) 的tableView财产UITableViewController没有公开,IBOutlet即使它是公共财产。因此,当您使用 Interface Builder 时,您无法看到该属性连接到您的其他UITableView. 因此,在您的子类中,您可以创建一个tableView标记为 an的属性IBOutlet并连接它。

This all seems hacky and a workaround to me, but it seems to be the only way to separate a UITableViewController'sUITableViewand put it somewhere else in UI hierarchy. I ran into the same issue when I tried to design view where there are things other than the UITableViewand that was the way I solved it... Is this the right approach???

这一切对我来说似乎很棘手,也是一种解决方法,但它似乎是将 a 分开UITableViewController'sUITableView并将其放在 UI 层次结构中的其他位置的唯一方法。当我试图设计视图时,我遇到了同样的问题,除了 之外还有其他东西UITableView,这就是我解决它的方式......这是正确的方法吗???

回答by user589642

I was able to make this work. I built a really nice dashboard with 4 TableViews and a webview with video. The key is having the separate tableView controllers and the IBOutlets to the other tableview controllers defined in the view controller. In UIB you just need to connect the other tableview controllers to the file owner of the view controller. Then connect the tables to the corresponding view controllers for the datasource and delegate.

我能够完成这项工作。我用 4 个 TableView 和一个带视频的 webview 构建了一个非常好的仪表板。关键是拥有单独的 tableView 控制器和 IBOutlets 到视图控制器中定义的其他 tableview 控制器。在 UIB 中,您只需要将其他 tableview 控制器连接到视图控制器的文件所有者。然后将表连接到数据源和委托的相应视图控制器。