6

我完全被难住了,情况如下:

我的应用程序使用核心位置框架来获取用户的当前位置,然后在TrailBehind上 ping 我的服务器以查找附近有趣的地方并将它们显示为列表。没问题。

为了节省电池,我在从服务器获取数据后关闭了 GPS 服务。如果用户在使用应用程序时四处走动并想要一个新列表,他单击导航控制器上的“刷新”并再次激活 CLLocation 服务,从服务器检索一批新数据并重绘表格。

当应用程序从我的服务器获取数据时,我加载了一个带有旋转地球的加载屏幕,上面写着“正在加载,请稍候”,我隐藏了导航栏,这样他们就不会点击“返回”。

因此,从服务器获取的初始数据完美无缺。

我第一次点击刷新所有代码执行以获取新位置,再次 ping 服务器以获取新的数据列表并更新单元格。但是,它不会按应有的方式加载表格视图,而是恢复表格视图的导航控制器栏,但仍会在主窗口中显示我的加载视图。这仅在设备上是正确的,在模拟器中一切正常。

我第二次点击刷新功能正常工作。

我第三次刷新它失败如上。

我第四次刷新它正常工作。

我第五次刷新它失败如上。

等等,偶数刷新成功,奇数刷新失败。我逐行遍历了所有代码,一切似乎都在正常执行。我实际上继续跳过核心指令,在大量单击“跳过”之后,我发现表格视图确实在 CFRunLoopRunSpecific 中的某个时间点显示在屏幕上,但然后我单击“继续”并且我的加载视图接管了屏幕。

我完全感到困惑。请帮忙!!非常感谢您的洞察力。

奇怪行为的视频:

相关代码:

RootViewControllerMethods(这是这个 TableView 项目的基础视图)

- (void)viewDidLoad {
    //Start the Current Location controller as soon as the program starts.  The Controller calls delegate methods
    //that will update the list and refresh
    [MyCLController sharedInstance].delegate = self;
    [[MyCLController sharedInstance].locationManager startUpdatingLocation];
    lv = [[LoadingViewController alloc] initWithNibName:@"Loading" bundle:nil];
    [self.navigationController pushViewController:lv animated:YES];
    [super viewDidLoad];
}



- (void)updateClicked {
    //When the location is successfully updated the UpdateCells method will stop the CL manager from updating, so when we want to update the location
    //all we have to do is start it up again.  I hope.
    [[MyCLController sharedInstance].locationManager startUpdatingLocation];
    [self.navigationController pushViewController:lv animated:YES];
    //LV is a class object which is of type UIViewController and contains my spinning globe/loading view.
}



-(void)updateCells {
    //When the Core Location controller has updated its location it calls this metod.  The method sends a request for a JSON dictionary
    //to trailbehind and stores the response in the class variable jsonArray.  reloadData is then called which causes the table to
    //re-initialize the table with the new data in jsonArray and display it on the screen.  

    [[MyCLController sharedInstance].locationManager stopUpdatingLocation];

    if(self.navigationController.visibleViewController != self) {
        self.urlString = [NSString stringWithFormat:@"http://www.trailbehind.com/iphone/nodes/%@/%@/2/10",self.lat,self.lon];
        NSURL *jsonURL = [NSURL URLWithString:self.urlString];
        NSString *jsonData = [[NSString alloc] initWithContentsOfURL:jsonURL];
        NSLog(@"JsonData = %@ \n", jsonURL);
        self.jsonArray = [jsonData JSONValue];
        [self.tableView reloadData];
        [self.navigationController popToRootViewControllerAnimated:YES];
        [jsonData release];
    }
}

CLController 方法:基本上只是将所有数据直接发送回 RootViewController

// Called when the location is updated
- (void)locationManager:(CLLocationManager *)manager
    didUpdateToLocation:(CLLocation *)newLocation
           fromLocation:(CLLocation *)oldLocation
{
    NSLog(@"New Location: %@ \n", newLocation);
    NSLog(@"Old Location: %@ \n", oldLocation);
    @synchronized(self) {
        NSNumber *lat = [[[NSNumber alloc] init] autorelease];
        NSNumber *lon = [[[NSNumber alloc] init] autorelease];
        lat = [NSNumber numberWithFloat:newLocation.coordinate.latitude];
        lon = [NSNumber numberWithFloat:newLocation.coordinate.longitude];
        [self.delegate noteLat:lat];
        [self.delegate noteLon:lon];
        [self.delegate noteNewLocation:newLocation];
        [self.delegate updateCells];
    }
}
4

5 回答 5

1

第一个想法是,在您推送加载视图之前,您可能不想将 startUpdatingLocation 发送到 CLLocationManager。通常第一个 -locationManager:didUpdateToLocation:fromLocation: 消息会立即与缓存的 GPS 数据一起出现。仅当您对每条消息都采取行动并且不过滤 GPS 数据(如此处的示例代码中所示)时,这才重要。但是,这不会导致您描述的情况 - 它会导致加载屏幕卡住。

我在不同的情况下经历过类似的奇怪行为,当我在切换到不同的选项卡时尝试弹出到根视图控制器并且没有在正确的位置进行调用。我相信 popToRootViewController 被我调用了两次。我怀疑您的加载视图要么被推送两次,要么被弹出两次。

我建议在 LoadingViewController 中实现 -viewWillAppear:、-viewDidAppear:、-viewWillDisappear: 和 -viewDidDisappear: 的日志记录最少。

- (void)viewWillAppear:(BOOL)animated {
    NSLog(@"[%@ viewWillAppear:%d]", [self class], animated);
    [super viewWillAppear:animated];
}

- (void)viewDidAppear:(BOOL)animated {
    NSLog(@"[%@ viewDidAppear:%d]", [self class], animated);
    [super viewDidAppear:animated];
}

- (void)viewWillDisappear:(BOOL)animated {
    NSLog(@"[%@ viewWillDisappear:%d]", [self class], animated);
    [super viewWillDisappear:animated];
}

- (void)viewDidDisappear:(BOOL)animated {
    NSLog(@"[%@ viewDidDisappear:%d]", [self class], animated);
    [super viewDidDisappear:animated];
}

然后,在您的设备上运行测试以查看它们是否总是被发送到您的视图控制器以及多久发送一次。您可以向 -updateClicked 添加一些日志记录以显示双击。

另一个想法,虽然你的 @synchronized 块是一个好主意,但它只会阻止其他线程执行这些语句,直到第一个线程退出块。我建议将 -stopUpdatingLocation 消息移动到该 @synchronized 块内的第一条语句。这样,一旦你决定对一些新的 GPS 数据采取行动,你会立即告诉 CLLocationManager 停止发送新数据。

于 2009-05-09T21:59:34.213 回答
0

所以,我从来没有让这个工作。我仅在每次以编程方式调用 popViewController 时才在设备上观察到这种行为,而不是允许导航控制器上的默认后退按钮进行弹出。

我的解决方法是构建一个自定义加载视图,并在每次由于访问互联网而出现延迟时将屏幕翻转到该视图。我的方法采用布尔变量是或否 - yes 切换到加载屏幕,no 切换回正常视图。这是代码:

- (void)switchViewsToLoading:(BOOL)loading {
// Start the Animation Block  
CGContextRef context = UIGraphicsGetCurrentContext();  
[UIView beginAnimations:nil context:context];  
[UIView setAnimationTransition: UIViewAnimationTransitionFlipFromLeft forView:self.tableView cache:YES];  
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];  
[UIView setAnimationDuration:.75];  

// Animations  
if(loading) { 
    if (lv == nil) { lv = [[LoadingViewController alloc] initWithNibName:@"Loading" bundle:nil]; }
    [self.view addSubview:lv.view];
    [self.view sendSubviewToBack:self.tableView];
    self.title = @"TrailBehind";
}
else {
    [lv.view removeFromSuperview];

}
// Commit Animation Block  
[UIView commitAnimations];  
//It looks kind of dumb to animate the nav bar buttons, so set those here
if(loading) {
    self.navigationItem.rightBarButtonItem = nil;
    self.navigationItem.leftBarButtonItem = nil;
    self.title = @"TrailBehind";
}
else {
    UIBarButtonItem *feedback = [[UIBarButtonItem alloc] initWithTitle:@"Feedback" style:UIBarButtonItemStylePlain target:self action:@selector(feedbackClicked)];
    self.navigationItem.rightBarButtonItem = feedback;
    UIBarButtonItem *update = [[UIBarButtonItem alloc] initWithTitle:@"Move Me" style:UIBarButtonItemStylePlain target:self action:@selector(updateClicked)];
    self.navigationItem.leftBarButtonItem = update;
    [feedback release];
    [update release];
}

}

于 2009-05-24T00:59:35.230 回答
0

查看您的原始代码,我非常怀疑这个块:

- (void)viewDidLoad {
    ...
    lv = [[LoadingViewController alloc] initWithNibName:@"Loading" bundle:nil];
    [self.navigationController pushViewController:lv animated:YES];
    [super viewDidLoad];
 }

viewDidLoad每次加载 NIB 时都会调用它,这可能会发生多次,尤其是在内存不足的情况下(考虑到它仅在设备上发生的说法,这似乎很可能)。我建议你实现-didReciveMemoryWarning,并且在调用 super 之后,至少打印一个日志,这样你就可以看到它是否发生在你身上。

上面的代码让我困扰的是你几乎肯定在泄漏lv,这意味着可能会有越来越多的LoadingViewControllers跑来跑去。你说它是一个类变量。你真的是说它是一个实例变量吗?ivars 应始终使用访问器(self.lv[self lv]而不是lv)。不要直接分配给他们;您几乎总是会做错(因为您可能在这里做错了)。

于 2009-05-24T01:36:49.257 回答
0

我在搜索完全相同的问题时遇到了这个问题,所以虽然我确定你现在已经解决了你的问题,但我想我会发布我的解决方案以防其他人遇到它......

当您在界面生成器中将两个 IBAction 分配给同一个 UIButton 时,似乎会导致此错误。事实证明,我用来将视图控制器推送到堆栈上的按钮被分配给了两个 IBAction,每个 IBAction 都将不同的控制器推送到 navigationController 的堆栈上(尽管您最终只会看到其中一个 - 可能是最后一个一个被称为)。所以无论如何,按下最顶层视图上的后退按钮并不会真正关闭它(或者它可能正在关闭第二个看不见的控制器),您必须按两次才能返回。

无论如何,请检查您的按钮并确保它们仅分配给单个 IBAction。那为我修好了。

于 2011-12-06T13:26:16.693 回答
0

您可以尝试调试您的应用程序以查看调用 updateCells 时控件的位置吗?该应用程序似乎没有任何明显错误。

确保在 LoadingViewController 类中没有内存警告。如果有内存警告并且您的 RootViewController 的视图正在被释放,那么当您对 RootViewController 执行弹出操作时,将再次调用 viewDidLoad。

在 viewDidLoad 和 updateCells 中保留断点。你确定你没有在其他任何地方调用 LoadingViewController 吗?

于 2009-05-04T06:27:07.053 回答