应用程序唤醒后 UINavigationController 堆栈上的 UIViewController 重复
Posted
技术标签:
【中文标题】应用程序唤醒后 UINavigationController 堆栈上的 UIViewController 重复【英文标题】:Duplicate UIViewControllers on UINavigationController stack after application wakes 【发布时间】:2013-05-23 10:26:11 【问题描述】:我刚刚学习了 ios 编程,正在编写我的第一个 iPhone 应用程序。
我的应用程序提供有关 MKMapView 的信息,其视图控制器是 UINavigationController 的根视图控制器。如果移动信号不好,我使用 mapViewDidFailLoadingMap:withError: 使应用程序根据用户的操作将两个不同的视图控制器之一推送到导航控制器堆栈上。代码如下:
- (void)mapViewDidFailLoadingMap:(MKMapView *)mapView withError:(NSError *)error
NSLog(@"mapViewDidFailLoadingMap: %@", [error localizedDescription]);
[aiView stopAnimating];
if (mapTypeInt == 0)
NSString *message =
[NSString stringWithFormat:@"Your signal is not currently
strong enough to download a map. Switching to table view."];
UIAlertView *av = [[UIAlertView alloc] initWithTitle:@"Maps Unavailable"
message:message
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[av show];
if (currentMode == @"retracingSteps")
RetraceViewController *rvc =
[[RetraceViewController alloc] initWithNibName:@"RetraceViewController" bundle:nil];
[[self navigationController] pushViewController:rvc animated:YES];
else
TripTableViewController *ttvc = [[TripTableViewController alloc] init];
[[self navigationController] pushViewController:ttvc animated:YES];
else
[self setMapType:0];
NSString *message = [NSString stringWithFormat:
@"Your signal is not currently strong enough to download a satellite map.
Switching to Standard Map view."];
UIAlertView *av = [[UIAlertView alloc] initWithTitle:@"Can't Use Satellite Maps"
message:message
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[av show];
昨天我在一个乡村山谷测试了较差的移动信号,并且正确的第二个视图控制器被推送到堆栈上。然后,当我锁定手机并在几分钟后重新检查应用程序时,我注意到的是,在醒来时,根视图控制器很快就会显示出来,然后是我预期的视图控制器。实际上,这样做是将第二个视图控制器的相同副本推送到堆栈上。当我不得不点击后退按钮六次才能返回根视图控制器时,我发现了这一点。
我希望应用在唤醒时立即显示在手机锁定时处于活动状态的视图控制器,而不是根视图控制器。我不知道该怎么做。
【问题讨论】:
你在使用故事板吗? 我没有使用故事板。 【参考方案1】:这可能是因为即使您锁定屏幕,您的应用程序仍然接收位置更新(位置服务可以在真实后台模式下运行代码),而且当您推送视图控制器时,堆栈中的前一个仍然存在并且仍在接收位置更新和做所有事情都是设计好的,所以即使你推送另一个控制器,如果发生某些事情,为了实现的逻辑必须推送另一个控制器,这个视图控制器可以访问导航控制器,所以只需将另一个添加到堆栈中控制器并推送它(对于堆栈中的所有 VC,导航控制器仍然相同)
所以你要做的就是在你按下另一个控制器时停止处理这种情况,并在你弹回控制器时重新开始处理
【讨论】:
我确实需要在手机锁定时保持定位控制器运行。你是说我需要在应用程序委托中像 applicationDidEnterBackground: 这样的地方弹出导航堆栈吗? 如果是基于位置的应用程序,您可以在后台保持位置服务处于活动状态,我的意思是,当您推送视图控制器时,您应该更改逻辑以拨打与管理控制器的事实地图视图不是当前视图控制器,因此如果已推送 RetraceViewController 并且地图视图再次失败将推送另一个 RetraceViewController,避免这种情况,如果地图视图控制器不是当前的 vc 或之前,您可以停止推送push 弹出所有推送的视图控制器,并推送正确的那个状态 好的,我明白了。我会试试看。 由于我的位置控制器委托是一个单独的对象,它在位置更改时发送通知,我修改了地图视图控制器以在推送 tripTableViewController 之前停止接收通知。这没什么区别。然后我尝试在创建它之前将其设置为 nil,这会导致错误:嵌套推送动画会导致导航栏损坏;这个错误是由处理程序 mapViewDidFailLoadingMap:withError: 在控制传递给推送的视图控制器之前被多次调用引起的。我通过对处理程序方法进行三处更改来修复该错误: 1)我删除了 UIAlertView 显示并将其移至推送的视图控制器; 2)如果处理程序被第二次调用,我使用了一个标志(在 viewDidAppear 中初始化:)从处理程序返回而不执行推送; 3) 我在分配之前检查了推送的视图控制器是否存在。
当所有这三个更改都完成后,视图控制器之间的转换就会正确发生。代码如下:
- (void)mapViewDidFailLoadingMap:(MKMapView *)mapView withError:(NSError *)error
[aiView stopAnimating];
if (mapViewFailed > 0)
mapViewFailed ++;
return;
if (mapTypeInt == 0)
[map setUserTrackingMode:MKUserTrackingModeNone];
[self removeAsNoticationsObserver];
if ([currentMode isEqualToString:@"retracingSteps"])
if (!rvc)
rvc = [[RetraceViewController alloc] initWithNibName:@"RetraceViewController" bundle:nil];
[[self navigationController] pushViewController:rvc animated:YES];
else
if (!ttvc)
ttvc = [[TripTableViewController alloc] init];
[[self navigationController] pushViewController:ttvc animated:YES];
mapViewFailed ++;
else
[self setMapType:0];
NSString *message = [NSString stringWithFormat:@"Your signal is not currently strong enough to download a satellite map. Switching to Standard Map view."];
UIAlertView *av = [[UIAlertView alloc] initWithTitle:@"Can't Use Satellite Maps"
message:message
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[av show];
毫无疑问,作为 iOS 新手,有一个更优雅的解决方案可用,我还不知道。
【讨论】:
代码看起来不错,但是,==
不应该用于字符串比较。使用-isEqualToString:
。
谢谢。我已经更正了我的代码并编辑了我的帖子来解决这个问题。以上是关于应用程序唤醒后 UINavigationController 堆栈上的 UIViewController 重复的主要内容,如果未能解决你的问题,请参考以下文章