4

我的视图控制器(恰好是一个 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 设置回主数据源!

4

2 回答 2

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。数据模型根据数据的内部状态决定提供哪些数据。)

此数据模型对象应位于普遍可访问的位置。最好的方法是让它成为一个单例,但快速而肮脏的方法是将它作为应用程序委托的属性停放。

因此,在您的 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控制器处理when。(您甚至可以在更新数据模型时发出通知,例如针对 url。然后视图控制器可以注册通知并reloadData在数据模型更改时调用。)

通过在 MVC 中无情地划分和封装功能,您可以从易于维护和调试的简单、可重用的组件创建复杂的应用程序。

真的很糟糕,大多数教学材料只对这个完全关键的概念作了口头上的服务。

于 2010-03-14T17:23:27.103 回答
2

表视图的控制器不会“不被告知就知道”任何事情——它本身并不具有像您提到的数据来自的属性。您通常在您的视图控制器子类中提供该数据,一次一个单元格。

通常,您的表视图控制器对象既是表视图的委托,又是表视图的数据源委托。来自苹果文档

UITableView 对象必须有一个委托和一个数据源。遵循模型-视图-控制器设计模式,数据源在应用程序的数据模型(即它的模型对象)和表视图之间进行中介;另一方面,委托管理表视图的外观和行为。数据源和委托通常(但不一定)是同一个对象,并且该对象通常是 UITableViewController 的自定义子类。

表格视图不接受数组或字典并从中提取数据;它会在您的数据源中询问您每个单元格的外观。您只需实现此方法:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

并返回单元格中您被询问的行所需的任何内容。您可以在其中放置逻辑以从您想要的任何地方混合/匹配/提取数据。

您的困惑是否来自大量示例代码,也许不清楚发生了什么?我建议从头开始构建一个表格视图,看看它是如何工作的——通过向您的项目添加一个新类很容易做到这一点,您可以在“新建”向导中从 XCode 中选择一个 UITableViewController 子类。它将使用所有相关的空方法(包括上述方法)预先填充 .m 文件。


编辑:在进行搜索时不要更改视图控制器拥有的表视图。您将视图控制器拥有的名为“tableView”的实例引用与委托方法的参数混淆了,委托方法的参数tableView:cellForRowAtIndexPath:只是被传入以告诉您哪个表视图正在请求一个单元格。当您以正常方式设置搜索时,使用相同的视图控制器作为默认/内容表和搜索结果的代表,您可以使用其中任何一个进行调用。有关 this 的文档,请参见此处

于 2010-03-14T14:55:27.703 回答