Почему popViewController работает только через раз - PullRequest
6 голосов
/ 04 мая 2009

Я полностью в тупике, вот ситуация:

Мое приложение использует платформу Core Location для получения текущего местоположения пользователя, а затем отправляет запрос на мой сервер по адресу TrailBehind для поиска интересных мест поблизости и отображает их в виде списка. Нет проблем.

Для экономии батарей я отключаю службу GPS после получения данных с сервера. Если пользователь перемещается во время использования приложения и хочет создать новый список, он нажимает «Обновить» на контроллере навигации, и служба CLLocation снова активируется, с сервера извлекается новый пакет данных и таблица перерисовывается.

Пока приложение получает данные с моего сервера, я загружаю экран загрузки с вращающимся глобусом с надписью «Идет загрузка, пожалуйста, подождите», и я скрываю панель навигации, чтобы они не нажимали «назад».

Итак, начальный захват данных с сервера проходит без нареканий.

В первый раз, когда я нажимаю, обновляется весь код, выполняемый для получения нового местоположения, снова проверяет сервер на новый список данных и обновляет ячейки. Однако вместо загрузки табличного представления, как и должно быть, восстанавливается панель контроллера навигации для табличного представления, но все равно отображается мое загрузочное представление в главном окне. Это верно только для устройства, в симуляторе все прекрасно работает.

ВТОРОЙ раз, когда я нажимаю обновить, функция работает нормально.

В третий раз, когда я нажимаю обновить, происходит сбой, как указано выше.

В четвертый раз, когда я нажимаю «Обновить», он работает нормально.

ПЯТЫЙ раз, когда я нажимаю обновить, происходит сбой, как указано выше.

и т. Д., Четные обновления выполняются успешно, а нечетные обновления не выполняются. Я перешагнул весь мой код построчно, и, кажется, все работает нормально. Я на самом деле продолжал перешагивать основные инструкции, и после огромного количества нажатий «переступить через» я обнаружил, что табличное представление действительно отображается на экране в какой-то момент в 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];
    }
}

Ответы [ 5 ]

1 голос
/ 10 мая 2009

Первой мыслью было то, что вы можете не захотеть отправлять startUpdatingLocation в CLLocationManager до тех пор, пока вы не загрузите представление загрузки. Часто первое -locationManager: didUpdateToLocation: fromLocation: сообщение появляется мгновенно с кэшированными данными GPS. Это имеет значение только в том случае, если вы действуете для каждого сообщения и не фильтруете данные GPS, как показано в примере кода здесь. Однако это не приведет к описанной вами ситуации - это приведет к зависанию экрана загрузки.

Подобное странное поведение я испытывал в другой ситуации, когда пытался получить доступ к контроллеру корневого представления при переключении на другую вкладку, и вызов не был выполнен в правильном месте. Я считаю, что popToRootViewController вызывался для меня дважды. Я подозреваю, что ваш вид загрузки либо выдвигается дважды, либо выдается дважды.

Я рекомендую реализовать -viewWillAppear :, -viewDidAppear :, -viewWillDisappear: и -viewDidDisappear: с минимальным входом в систему в вашем DownloadViewController.

- (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 прекратить отправку новых данных.

0 голосов
/ 06 декабря 2011

Я сталкивался с этим во время поиска точно такой же проблемы, поэтому, хотя я уверен, что вы уже решили свою проблему к настоящему времени, я решил опубликовать свое решение на случай, если кто-то еще столкнется с ним ...

Эта ошибка, по-видимому, возникает, когда вы назначаете два IBActions одной и той же кнопке UIB в конструкторе интерфейса. Оказалось, что кнопка, которую я использовал для помещения контроллера представления в стек, была назначена двум IBActions, и каждый из них помещал свой контроллер в стек навигационного контроллера (хотя в конечном итоге вы увидите только один из них - возможно, последний один будет называться). Так или иначе, нажатие кнопки «Назад» в самом верхнем виде на самом деле не отклоняет ее (или, возможно, это отменяет 2-й, невидимый контроллер), и вы должны нажать дважды, чтобы вернуться.

В любом случае, проверьте свои кнопки и убедитесь, что они назначены только одному IBAction. Это исправило это для меня.

0 голосов
/ 24 мая 2009

Глядя на ваш оригинальный код, я очень подозреваю этот блок:

- (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). Не назначайте им напрямую; вы почти всегда будете делать это неправильно (как вы, вероятно, здесь делаете).

0 голосов
/ 24 мая 2009

Итак, я никогда не заставлял это работать. Я наблюдаю такое поведение на устройстве только каждый раз, когда я вызываю popViewController программно, а не позволяю кнопке «Назад» по умолчанию на навигационном контроллере выполнять всплывающее окно.

Мой обходной путь заключался в создании пользовательского представления загрузки и переключении экрана на это представление каждый раз, когда будет задержка из-за доступа к Интернету. Мой метод принимает булеву переменную yes или no - 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];
}

}

0 голосов
/ 04 мая 2009

Можете ли вы попробовать и отладить ваше приложение, чтобы увидеть, где находится элемент управления при вызове updateCells? Похоже, что-то не так с приложением.

Убедитесь, что в памяти нет класса предупреждений, пока вы находитесь в классе LoadingViewController. Если в памяти появляется предупреждение и ваше представление RootViewController освобождается, то viewDidLoad будет вызываться снова, когда вы выполняете всплывающее окно в RootViewController.

Сохраняйте точки останова в viewDidLoad и updateCells. Вы уверены, что не вызываете LoadingViewController где-либо еще?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...