2009-12-14 116 views
0

我正在开发一个iPhone应用程序,它使用TableView来显示XML数据。 XML数据来自外部资源并被解析并放入对象中以用作表数据源。该应用程序使用UITabBar和多个ViewControllers,这些都是以编程方式创建的,并使用相同的数据源。刷新XML数据和更新UITableView

一切都很好,但我想实现一个刷新按钮,以便用户可以刷新内容(全局,所以所有的ViewControllers应该更新)。解析器将再次查询XML并创建一个新对象。我遇到的问题是我似乎无法用我的新数据重新填充tableview。我得到更新的数据对象。实际上更新tableview是个问题。我尝试设置setDataSource并致电reloadData,但由于无法识别的选择器导致崩溃。

从我的AppDelegate调用XML的东西,所有的解析逻辑都在Parser.m中。刷新函数被调用RootViewController.m,它实现了UITableViewDataSource协议:

- (void)refreshXMLFeed:(id)sender { 
    NSArray *tableControllersData = [appDelegate getData]; 
    [self.tableView setDataSource: tableControllersData]; 
    [self.tableView reloadData]; 
} 

我将如何处理这一问题?应该获取新数据并重新加载RootViewController中的表视图,正如我一直试图完成的那样。或者应该在AppDelegate中触发数据解析,并且只需要重新加载RootViewController中的TableView。

如果有必要,我可以用必要的代码更新我的问题,我不确定哪一部分在这一点上是相关的。

RootViewController.h:

#import <UIKit/UIKit.h> 

@interface RootViewController : UITableViewController { 
    MyAppDelegate *appDelegate; 
    NSArray *tableDataArray; 
} 

@property (nonatomic, retain) NSArray *tableDataArray; 

- (IBAction)refreshXMLFeed:(id)sender; 
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil tableDataSource:(NSArray*)tableData; 

@end 

RootViewController.m:

#import "CustomCell.h" 
#import "MyAppDelegate.h" 
#import "RootViewController.h" 
#import "DetailViewController.h" 

@implementation RootViewController 
@synthesize tableDataArray; 

- (void)viewDidLoad { 
    [super viewDidLoad]; 
} 

//Override the default initWithNibName method 
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil tableDataSource:(NSArray*)tableData { 
    if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { 
    // Custom initialization 
    tableDataArray = [tableData retain]; 
    } 
    return self; 
} 

-(void)viewWillAppear:(BOOL)animated { 
    appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];  
    [super viewWillAppear:animated]; 
    //Set the colour of the navigationController and add buttons 
    self.navigationController.navigationBar.tintColor = [UIColor blackColor]; 

    //Add the refresh button 
    UIBarButtonItem* refreshButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(refreshXMLFeed:)]; 
    [self.navigationItem setLeftBarButtonItem:refreshButton animated:YES]; 
    [refreshButton release]; 
} 

#pragma mark Table 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 
    return [self.tableDataArray count]; 
} 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 
    static NSString *CustomCellIdentifier = @"CustomCellIdentifier"; 
    CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier: CustomCellIdentifier]; 
    if (cell == nil) { 
    NSArray *cellNib = [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:self options:nil]; 
    for (id oneObject in cellNib) { 
     if ([oneObject isKindOfClass:[CustomCell class]]) { 
     cell = (CustomCell *)oneObject; 
     } 
    } 
    } 

    NSUInteger row = [indexPath row]; 
    NSDictionary *rowData = [self.tableDataArray objectAtIndex:row]; 
    cell.colorLabel.text = [rowData objectForKey:@"Type"]; 
    cell.nameLabel.text = [rowData objectForKey:@"Name"]; 
    UIImage *image = [UIImage imageNamed:[rowData objectForKey:@"Icon"]]; 
    cell.image = image; 
    return cell; 
} 

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 
    NSString *selectedRow = [tableDataArray objectAtIndex:indexPath.row]; 

    DetailViewController *detailViewController = [[DetailViewController alloc] initWithNibName:@"DetailView" bundle:[NSBundle mainBundle]]; 
    detailViewController. selectedRow = selectedRow; 
    [self.navigationController pushViewController:detailViewController animated:YES]; 

    UIBarButtonItem *backButton = [[UIBarButtonItem alloc] init]; 
    backButton.title = @"Back"; 
    self.navigationItem.backBarButtonItem = backButton; 
    [backButton release]; 
    [detailViewController release]; 
    detailViewController = nil; 
} 

#pragma mark Refresh 

- (void)refreshXMLFeed:(id)sender { 
    NSArray *tableControllersData = [appDelegate getData]; 
    [self.tableView setDataSource: tableControllersData]; 
    [self.tableView reloadData]; 
} 

- (void)didReceiveMemoryWarning { 
    // Releases the view if it doesn't have a superview. 
    [super didReceiveMemoryWarning]; 

    // Release any cached data, images, etc that aren't in use. 
} 

- (void)viewDidUnload { 
    // Release any retained subviews of the main view. 
    // e.g. self.myOutlet = nil; 
} 


- (void)dealloc { 
    [super dealloc]; 
} 

@end 

回答

3

你应该只设置你的数据源一次,它不应该是一个NSArray。从中拉取记录的数据容器可能会更改,但数据源应始终保持不变。在大多数情况下,数据源应该只是您的视图控制器。然后你的视图控制器应该实现在UITableViewDataSource protocol的方法,包括:

– tableView:cellForRowAtIndexPath: required method 
– numberOfSectionsInTableView: 
– tableView:numberOfRowsInSection: required method 
– sectionIndexTitlesForTableView: 
– tableView:sectionForSectionIndexTitle:atIndex: 
– tableView:titleForHeaderInSection: 
– tableView:titleForFooterInSection: 

一个NSArray没有任何这些方法这就是为什么你所得到的是无法识别的选择消息作出响应。你必须自己实现它们。

您应该熟悉Table View Programming Guide以了解如何有效使用表视图。

此致敬礼。

更新:这里有一些代码可以帮助你。在你的RootViewController中用-viewDidLoad中的alloc/init实例化NSArray。说它项目:

- (void)viewDidLoad; 
{ 
    [super viewDidLoad]; 
    items = [[NSArray alloc] init]; 
} 

然后实现你的表视图的数据源代表这样的:

- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section 
{ 
    return [items count]; 
} 

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; 
{ 
    return 1; 
} 

然后,你需要实现你的cellForRowAtIndexPath:

- (UITableViewCell *)tableView:(UITableView *)tv cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    UITableViewCell *cell; 

    cell = [tv dequeueReusableCellWithIdentifier:@"Cell"]; 
    if (cell == nil) 
    { 
     cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"Cell"] autorelease]; 

    } 

    // Get your item from the items array here 
    NSDictionary *item = [items objectAtIndex:[indexPath row]]; 
    NSString *text = [item valueForKey:@"text"]; 

    [[cell textLabel] setText:text]; 

    return cell; 

} 

此代码假定对象在你的NSArray是NSDictionaries。如果您正在使用某个自定义对象,请将该索引路径中的对象转换为您的自定义类型,然后使用它的字段。

当你有新的数据可用并需要重新加载你的表视图时,你只需重新分配你的项目NSArray,然后在tableview上调用reloadData。说你有发生像这样的重新加载:

- (void)didReciveNewData:(NSArray*)newItems; 
{ 
    if (items) [items release], items = nil; 
    items = [newItems copy]; 
    [tableView reloadData]; 
} 

这将导致表视图来查询它的视图控制器,用于要显示的行数和被访问计数和内容提供每行的细胞再次的NSArray。

HTH。

+0

感谢您的回复。所需的方法都在RootViewController中实现 - 我用代码更新了最初的问题。 dataSource的数据是一个NSArray,并通过重写的initWithNibName方法提供。所以我不确定这是否与未实现的方法有关。 – mensch 2009-12-15 09:17:39

+0

表视图的数据源*不能是NSArray,因为NSArray没有实现UITableViewDataSource协议。如果您在RootViewController中实现了所需的方法,那么将RootViewController实例(或者如果它位于RootViewController内部的self)设置为表视图的数据源。你的问题与initWithNibName无关。问题是你不了解MVC。数据源是一个委托 - 而不是数据容器。委托人应该访问数据容器(在你的情况下是一个NSArray),以便返回正确的行/段数和表格单元格。 – 2009-12-15 19:27:45

+0

对不起,混淆了术语,NSArray的确是数据容器而不是dataSource(它是数据源,但是,因此混合)。 RootViewController实现了UITableViewDataSource协议的方法,并且是TableView的正确数据源。我猜我在这种情况下的问题是如何提供更新的数据容器NSArray并相应地更新TableView。 – mensch 2009-12-15 23:01:59

0

我猜你的数据异步加载在你的GetData方法(如果你不是你应该)当ui至少期望它时更新/无效对数据源的引用,当ui尝试使用指向其数据的陈旧指针进行呈现时导致崩溃。

只要确保不要从不同的线程修改tablecontrollersdata的内容。

或者你正在尝试使用coredata,在这种情况下,我不知道。