【问题标题】:Refreshing XML data and updating a UITableView刷新 XML 数据并更新 UITableView
【发布时间】:2023-03-21 08:49:01
【问题描述】:

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

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

XML 的东西是从我的 AppDelegate 调用的,所有的解析逻辑都在 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

【问题讨论】:

    标签: iphone iphone-sdk-3.0 uitableview


    【解决方案1】:

    您应该只设置一次数据源,并且它不应该是 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,然后在表格视图上调用 reloadData。假设您有这样的重新加载:

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

    这将导致表格视图向其视图控制器查询要显示的行数以及通过访问 NSArray 的计数和内容提供的每行的单元格。

    HTH。

    【讨论】:

    • 感谢您的回复。两种必需的方法都在 RootViewController 中实现——我已经用代码更新了最初的问题。 dataSource 的数据是一个 NSArray 并通过重写的 initWithNibName 方法提供。所以我不确定这是否与未实现的方法有关。
    • 表格视图的数据源不能是 NSArray,因为 NSArray 没有实现 UITableViewDataSource 协议。如果您已经在 RootViewController 中实现了所需的方法,则将 RootViewController 实例(如果它在 RootViewController 内部,则设置为 self)作为表视图的数据源。您的问题与 initWithNibName 无关。问题是你不了解 MVC。数据源是一个委托,而不是数据容器。委托应访问数据容器(在您的情况下为 NSArray),以返回正确的行/节计数和表格单元格。
    • 抱歉混淆了术语,NSArray 确实是数据容器而不是数据源(不过,它是数据源,因此混淆了)。 RootViewController 实现了 UITableViewDataSource 协议的方法,是 TableView 的正确数据源。我想在这种情况下我的问题是如何提供更新的数据容器 NSArray 并相应地更新 TableView。
    • 我用一些代码更新了我的答案。如果您还有其他问题,请告诉我。
    • 谢谢,这当然很有帮助!我已经设法解决了我的问题。
    【解决方案2】:

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

    只要确保您从不从其他线程修改 tablecontrollersdata 的内容。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-11
      • 1970-01-01
      • 1970-01-01
      • 2012-01-19
      • 1970-01-01
      相关资源
      最近更新 更多