2009-12-14 98 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,在這種情況下,我不知道。