刷新XML数据并更新UITableView

时间:2009-12-14 16:30:06

标签: iphone iphone-sdk-3.0 uitableview

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

一切都很好,花花公子,但我想实现一个刷新按钮,这样用户就可以刷新内容(全局,所以所有的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

2 个答案:

答案 0 :(得分: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。

答案 1 :(得分:0)

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

请确保永远不要从其他线程修改tablecontrollersdata的内容。

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