我错误地告诉我,我的viewcontroller恰好是一个tableViewController,它不知道它的属性是NSArray还是NSDictionary,它保存了要加载到表中以供显示的数据。
似乎我应该明确说出类似的内容:
[self.tableView useData:self.MyArray];
我希望在tableViewController中有多个数组,并以编程方式在一个和另一个之间切换。
我注意到当tableViewController使用searchViewController时,你可以这样做:
if (tableView == self.searchDisplayController.searchResultsTableView) {
我甚至能够做到这一点:
self.tableView = self.searchDisplayController.searchResultsTableView;
[self.tableView reloadData];
但是我无处可以找到如何将self.tableView设置回主数据源!
答案 0 :(得分:41)
好的,我理解您的挫折感,因为绝大多数iPhone教学材料都没有足够重视整体应用程序设计。它们直接用于眼睛糖果界面并且只为应用程序应该处理数据的方式付费,即使处理数据是应用程序的全部目的!
教学材料没有花足够的时间来解释整个iPhone / Cocoa API所基于的模型 - 视图 - 控制器设计模式。你很难理解任何事情,因为你错误地认为UI视图是程序的核心,因为你一直在努力将功能塞入错误的对象,因为教学材料让你相信。在这种误解下,没有任何意义,甚至不是Apple文档。
您需要退后一步并重新思考。决定要显示哪些数据以及何时显示数据不是视图的功能。表视图控制器的功能不是保存,管理或存储应用程序的数据。这些函数正确属于数据模型对象(您可能从未听说过。)您遇到了麻烦,因为您试图在视图中拆分数据模型任务,而视图控制器则不属于它们。
显然,您的应用甚至没有数据模型,因为您将表的数据作为tableview控制器的属性。虽然你经常在简单的教程示例中看到这一点,但它是一个糟糕的设计,在任何但最简单的应用程序的复杂性下都会崩溃。
相反,您的数据应存储在自己的自定义对象中并在其中进行管理。这是数据模型。在您的情况下,听起来您的数据分布在两个数组中,因此您将创建一个这样的数据模型对象:
@interface MyDataModel : NSObject {
@protected
NSArray *arrayOne;
NSArray *arrayTwo;
@public
NSArray *currentlyUsedArray;
}
@property(nonatomic, retain) NSArray *currentlyUsedArray;
-(void) switchToArrayOne;
-(void) switchToArrayTwo;
-(void) toggleUsedArray;
@end
#import "MyDataModel.h"
@interface MyDataModel ()
@property(nonatomic, retain) NSArray *arrayOne;
@property(nonatomic, retain) NSArray *arrayTwo;
@end
@implementation MyDataModel
- (id) init{
if (self=[super init]) {
self.arrayOne=//... initialize array from some source
self.arrayTwo=//... initialize array from some source
self.currentlyUsedArray=self.arrayOne; //whatever default you want
}
return self;
}
-(void) switchToArrayOne{
self.currentlyUsedArray=self.arrayOne;
}
-(void) switchToArrayTwo{
self.currentlyUsedArray=self.arrayTwo;
}
- (void) toggleUsedArray{
if (self.currentlyUsedArray==self.arrayOne) {
self.currentlyUsedArray=self.arrayTwo;
}else {
self.currentlyUsedArray=self.arrayOne;
}
}
(请注意,实际数据已封装,其他对象只能访问currentlyUsedArray
。数据模型根据数据的内部状态决定提供哪些数据。)
此数据模型对象应位于通用位置。最好的方法是使它成为单例,但快速而肮脏的方法是将其作为app委托的属性。
所以在你的tableview控制器中你会有一个属性:
MyDataModel *theDataModel;
@property (nonatomic, retain) MyDataModel *theDataModel;
然后在实施中
@synthesize theDataModel;
-(MyDataModel *) theDataModel; {
if (theDataModel; !=nil) {
return theDataModel; ;
}
id appDelegate=[[UIApplication sharedApplication] delegate];
self.theDataModel=appDelegate.theDataModelProperty;
return theDataModel;
}
然后在tableview数据源方法中:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
...
cell.textLabel.text=[self.theDataModel.currentlyUsedArray objectAtIndex:indexPath.row];
return cell;
}
如果应用 中的某个事件 要求您切换数组,则只需从应用委托调用数据模型对象并向其发送相应的交换机阵列消息
id appDelegate=[[UIApplication sharedApplication] delegate];
[appDelegate.theDataModelProperty toggleUsedArray];
现在所有后续数据操作,无论是在特定的表视图还是其他完全不相关的视图中,都将使用数据形成正确的数组。
为什么要经历所有这些麻烦?它使应用程序模块化。您可以轻松添加不同的视图,每个视图以不同的方式显示数据,而无需每次都重写数据管理。您可以使用数据模型来管理将在表格,Web视图或命令行中显示的数据。您甚至可以轻松地将数据模型移动到完全不同的应用程序。
这种模块化使大型复杂应用程序的管理变得更加容易。您只有一个操作和控制数据的对象。您不必担心某些很少使用的代码段中的一些小错误会丢弃整个应用程序。您可以轻松地插入视图或轻松删除它们而不会破坏应用程序。
这当然是一个微不足道的例子,但它显示了良好的做法。
但是,您可能会问,这如何解决tableview的问题,知道要加载哪些数据以及何时加载它?很简单,它没有。知道要加载什么数据或加载什么数据不是tableview的工作。数据模型处理what-data和tableview控制器处理何时。 (您甚至可以在更新数据模型时发出通知,例如,对于网址。然后视图控制器可以注册通知并在数据模型发生更改时调用reloadData
。)
通过在MVC中无情地划分和封装功能,您可以从易于维护和调试的简单,可重用组件创建复杂的应用程序。
对于大多数教学材料而言,实际上只是为这个完全关键的概念付出代价。
答案 1 :(得分:2)
表格视图的控制器不会“知道没有被告知”任何东西 - 它本身并没有像你提到的那样的数据来自数据。您通常在视图控制器子类中提供该数据,一次一个单元格。
通常,表视图控制器对象既是表视图的委托,也是表视图的数据源委托。来自the Apple docs:
UITableView对象必须有 委托和数据源。以下 模型 - 视图 - 控制器设计 模式,数据源调解 应用程序的数据模型之间 (即它的模型对象)和 表格视图;代表,另一方面 手,管理外观和 表视图的行为。 数据 来源和代表经常(但是 不一定)相同的对象,和 该对象通常是一种习惯 UITableViewController的子类。
表视图不接受数组或字典并从中提取数据;它会在您的数据源中询问您每个单元格应该是什么样子。您只需实现此方法:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
并返回您要询问的行所需的单元格中的任何内容。您可以将逻辑放在那里以混合/匹配/从任何地方提取数据。
你的混淆可能来自一些示例代码,可能还不清楚发生了什么?我建议从头开始构建一个表视图,看看它是如何工作的 - 通过在项目中添加一个新类很容易实现,你可以在“new”向导中从XCode中选择一个UITableViewController子类。它将使用所有相关的空方法(包括上述方法)预填充.m文件。
编辑:执行搜索时,请勿更改视图控制器拥有的表视图。您将视图控制器拥有的名为“tableView”的实例引用与委托方法tableView:cellForRowAtIndexPath:
的参数混淆,后者只是传入来告诉您哪个表视图正在要求一个细胞。当您以正常方式设置搜索时,同一个viewcontroller是默认/内容表和搜索结果的委托,您可以使用其中任何一个调用。 See here for the docs on this