在 AppDelegate 中重新加载数据后未调用 cellForRowAtIndexPath
cellForRowAtIndexPath not called after reloadData in AppDelegate
我已经搜索了几个小时了,尽管有很多类似的帖子,但我还没有找到解决我的问题的方法。我正在创建一个应用程序,该应用程序(目前正在启动)使用 startMonitoringSignificantLocationChanges
不断更新用户位置。我有一个自定义的 viewcontroller LocationsViewController
,它只是一个表视图,用于显示用户前往的每个位置。在我的 didUpdateLocations
中:
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations
{
_currentLocation = _locationManager.location;
[_locations addObject:_currentLocation];
[_locationsViewController.tableView reloadData];
}
然后在 LocationsViewController 的 numberOfRowsInSection
方法中,我有代码:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
AppDelegate * delegate = [AppDelegate sharedInstance];
NSLog(@"%lu", (unsigned long)[delegate.locations count]);
return [delegate.locations count];
}
现在我在 AppDelegate
中实例化了 LocationsViewController
,如下所示:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
sharedInstance = self;
_datahub = [[Datahub alloc] init];
_locations = [[NSMutableArray alloc] init];
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
UIStoryboard * storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
_locationsViewController = (LocationsViewController*) [storyboard instantiateViewControllerWithIdentifier:@"locationsViewController"];
_timePickViewController = (TimePickViewController*) [storyboard instantiateViewControllerWithIdentifier:@"timePickViewController"];
if ([_locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
[_locationManager requestAlwaysAuthorization];
}
[_locationManager startMonitoringSignificantLocationChanges];
return YES;
}
在LocationsViewController
的numberOfRowsInSection
方法中,当LocationsViewController
首次实例化时delegate.locations
的计数为0。但是在调用 didUpdateLocations
之后,数组 delegate.locations
填充了 1 个位置。然后调用 reloadData
方法,当再次调用 numberOfRowsInSection
时 returns 1. 如果方法 returns 1 那么 cellForRowAtIndexPath
应该在之后立即调用;但是由于某种原因,它没有被调用。我想知道是否有人知道,1. 我的代码可能有什么问题(我不认为它与任何格式或表格视图设置有关,因为我使用了故事板),我有我的 dataSource
和 delegate
都设置为 self.tableView
,我也尝试实现 heightForRowAtIndexPath
方法。无论我做什么,我都没有运气。在调用 reloadData
时,除了 cellForRowAtIndexPath
之外,所有正确的方法都会被调用。任何建议或导致可能出错的地方都会很棒。如果有帮助,这里还有一些代码,谢谢:
LocationsViewController.m
@implementation LocationsViewController
//*******************************************************************************************************************************
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.delegate = self;
self.tableView.dataSource = self;
}
//*******************************************************************************************************************************
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//*******************************************************************************************************************************
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 30.0f;
}
//*******************************************************************************************************************************
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
//*******************************************************************************************************************************
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
AppDelegate * delegate = [AppDelegate sharedInstance];
NSLog(@"%lu", (unsigned long)[delegate.locations count]);
return [delegate.locations count];
}
//*******************************************************************************************************************************
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//Cell setup
AppDelegate * delegate = [AppDelegate sharedInstance];
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CellIdentifier];
}
//Edit cell
CLGeocoder *ceo = [[CLGeocoder alloc]init];
CLLocation * location = delegate.locations[indexPath.row];
[ceo reverseGeocodeLocation:location
completionHandler:^(NSArray * placemarks, NSError *error) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
//String to hold address
cell.textLabel.text = [NSString stringWithFormat:@"%F, %F", location.coordinate.latitude, location.coordinate.longitude];
cell.detailTextLabel.text = [NSString stringWithFormat:@"%@, %@", placemark.locality, placemark.administrativeArea];
}
];
return cell;
}
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
sharedInstance = self;
_datahub = [[Datahub alloc] init];
_locations = [[NSMutableArray alloc] init];
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
UIStoryboard * storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
_locationsViewController = (LocationsViewController*) [storyboard instantiateViewControllerWithIdentifier:@"locationsViewController"];
_timePickViewController = (TimePickViewController*) [storyboard instantiateViewControllerWithIdentifier:@"timePickViewController"];
if ([_locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
[_locationManager requestAlwaysAuthorization];
}
[_locationManager startMonitoringSignificantLocationChanges];
return YES;
}
//*******************************************************************************************************************************
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations
{
_currentLocation = _locationManager.location;
[_locations addObject:_currentLocation];
[_locationsViewController.tableView reloadData];
}
@rdelmar 的评论是问题的核心。在您的应用程序委托中,您正在实例化一个 LocationsViewController,然后从不对其进行任何操作。该视图控制器永远不会出现在屏幕上,也永远不会做任何事情。
一般来说,让应用委托管理除初始设置和处理应用委托方法以外的任何事情都是不好的。您应该让您的视图控制器创建一个位置管理器对象或设置一个单独的位置服务对象来执行此操作。
进入视图控制器并直接操作它的视图对象也是一个非常的坏主意。如果您需要一个视图控制器来更新其中一个视图,那么您应该添加一个外部对象调用的方法,该方法将根据需要更改视图。
让视图控制器管理位置管理器本身是迄今为止最简单的解决方案,尽管它有局限性。只有当视图控制器关心位置服务时,这种方法才真正有效。
如果您的应用中有其他对象(视图控制器等)关心位置信息,您应该创建一个单独的对象来管理位置更新。在这种情况下,您应该使用委托来设置它,或者给它一个位置更新块,以便在更新可用时调用。如果您希望应用中的多个视图控制器同时收到通知,您可能希望使用通知管理器发送自定义位置更新通知。
您可能希望将您的位置服务对象设置为单例。这样,您的视图控制器就可以获取位置服务对象的单个实例,并将它们自己设置为 deleate/set 它们的处理程序块在最前面时作为活动处理程序块。
我已经搜索了几个小时了,尽管有很多类似的帖子,但我还没有找到解决我的问题的方法。我正在创建一个应用程序,该应用程序(目前正在启动)使用 startMonitoringSignificantLocationChanges
不断更新用户位置。我有一个自定义的 viewcontroller LocationsViewController
,它只是一个表视图,用于显示用户前往的每个位置。在我的 didUpdateLocations
中:
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations
{
_currentLocation = _locationManager.location;
[_locations addObject:_currentLocation];
[_locationsViewController.tableView reloadData];
}
然后在 LocationsViewController 的 numberOfRowsInSection
方法中,我有代码:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
AppDelegate * delegate = [AppDelegate sharedInstance];
NSLog(@"%lu", (unsigned long)[delegate.locations count]);
return [delegate.locations count];
}
现在我在 AppDelegate
中实例化了 LocationsViewController
,如下所示:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
sharedInstance = self;
_datahub = [[Datahub alloc] init];
_locations = [[NSMutableArray alloc] init];
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
UIStoryboard * storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
_locationsViewController = (LocationsViewController*) [storyboard instantiateViewControllerWithIdentifier:@"locationsViewController"];
_timePickViewController = (TimePickViewController*) [storyboard instantiateViewControllerWithIdentifier:@"timePickViewController"];
if ([_locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
[_locationManager requestAlwaysAuthorization];
}
[_locationManager startMonitoringSignificantLocationChanges];
return YES;
}
在LocationsViewController
的numberOfRowsInSection
方法中,当LocationsViewController
首次实例化时delegate.locations
的计数为0。但是在调用 didUpdateLocations
之后,数组 delegate.locations
填充了 1 个位置。然后调用 reloadData
方法,当再次调用 numberOfRowsInSection
时 returns 1. 如果方法 returns 1 那么 cellForRowAtIndexPath
应该在之后立即调用;但是由于某种原因,它没有被调用。我想知道是否有人知道,1. 我的代码可能有什么问题(我不认为它与任何格式或表格视图设置有关,因为我使用了故事板),我有我的 dataSource
和 delegate
都设置为 self.tableView
,我也尝试实现 heightForRowAtIndexPath
方法。无论我做什么,我都没有运气。在调用 reloadData
时,除了 cellForRowAtIndexPath
之外,所有正确的方法都会被调用。任何建议或导致可能出错的地方都会很棒。如果有帮助,这里还有一些代码,谢谢:
LocationsViewController.m
@implementation LocationsViewController
//*******************************************************************************************************************************
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.delegate = self;
self.tableView.dataSource = self;
}
//*******************************************************************************************************************************
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
//*******************************************************************************************************************************
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 30.0f;
}
//*******************************************************************************************************************************
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
//*******************************************************************************************************************************
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
AppDelegate * delegate = [AppDelegate sharedInstance];
NSLog(@"%lu", (unsigned long)[delegate.locations count]);
return [delegate.locations count];
}
//*******************************************************************************************************************************
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//Cell setup
AppDelegate * delegate = [AppDelegate sharedInstance];
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:CellIdentifier];
}
//Edit cell
CLGeocoder *ceo = [[CLGeocoder alloc]init];
CLLocation * location = delegate.locations[indexPath.row];
[ceo reverseGeocodeLocation:location
completionHandler:^(NSArray * placemarks, NSError *error) {
CLPlacemark *placemark = [placemarks objectAtIndex:0];
//String to hold address
cell.textLabel.text = [NSString stringWithFormat:@"%F, %F", location.coordinate.latitude, location.coordinate.longitude];
cell.detailTextLabel.text = [NSString stringWithFormat:@"%@, %@", placemark.locality, placemark.administrativeArea];
}
];
return cell;
}
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
sharedInstance = self;
_datahub = [[Datahub alloc] init];
_locations = [[NSMutableArray alloc] init];
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
UIStoryboard * storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
_locationsViewController = (LocationsViewController*) [storyboard instantiateViewControllerWithIdentifier:@"locationsViewController"];
_timePickViewController = (TimePickViewController*) [storyboard instantiateViewControllerWithIdentifier:@"timePickViewController"];
if ([_locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) {
[_locationManager requestAlwaysAuthorization];
}
[_locationManager startMonitoringSignificantLocationChanges];
return YES;
}
//*******************************************************************************************************************************
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations
{
_currentLocation = _locationManager.location;
[_locations addObject:_currentLocation];
[_locationsViewController.tableView reloadData];
}
@rdelmar 的评论是问题的核心。在您的应用程序委托中,您正在实例化一个 LocationsViewController,然后从不对其进行任何操作。该视图控制器永远不会出现在屏幕上,也永远不会做任何事情。
一般来说,让应用委托管理除初始设置和处理应用委托方法以外的任何事情都是不好的。您应该让您的视图控制器创建一个位置管理器对象或设置一个单独的位置服务对象来执行此操作。
进入视图控制器并直接操作它的视图对象也是一个非常的坏主意。如果您需要一个视图控制器来更新其中一个视图,那么您应该添加一个外部对象调用的方法,该方法将根据需要更改视图。
让视图控制器管理位置管理器本身是迄今为止最简单的解决方案,尽管它有局限性。只有当视图控制器关心位置服务时,这种方法才真正有效。
如果您的应用中有其他对象(视图控制器等)关心位置信息,您应该创建一个单独的对象来管理位置更新。在这种情况下,您应该使用委托来设置它,或者给它一个位置更新块,以便在更新可用时调用。如果您希望应用中的多个视图控制器同时收到通知,您可能希望使用通知管理器发送自定义位置更新通知。
您可能希望将您的位置服务对象设置为单例。这样,您的视图控制器就可以获取位置服务对象的单个实例,并将它们自己设置为 deleate/set 它们的处理程序块在最前面时作为活动处理程序块。