Как обновить пользовательский интерфейс из таймера на iOS?

#objective-c #ios #cocoa-touch

#objective-c #iOS #cocoa-touch

Вопрос:

У меня есть таймер, который я использую для проверки веб-адреса каждые 11 секунд. Я использую ASIHTTPRequest для установки делегата, который сообщает ответ от веб-сервера. Если я получу искомый ответ, я хочу обновить пользовательский интерфейс. К сожалению, это не работает, я подозреваю, потому что я не обновляю пользовательский интерфейс из потока пользовательского интерфейса.

Код для запуска и обработки таймера:

 - (void)startTimer {
BOOL timerStarted = false;
BOOL running = false;
BOOL readyToProcess = false;

if(!timerStarted){
    running = true;
    timerStarted = true;
    readyToProcess = true;
    autosaveTimer = [[NSTimer scheduledTimerWithTimeInterval:11
                                                      target:self
                                              selector:@selector(processWebCheckTimer:)
                                                    userInfo:nil
                                                     repeats:YES] retain];
}
 

}

 - (void)processWebCheckTimer:(NSTimer*)theTimer {
    if ([VariableStore sharedInstance].success < 1) { //conversion not done, check again
        NSURL *url = [NSURL URLWithString:@"WEBSERVERURLHERE"];
        ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
        [request setPostValue:oc_request forKey:@"queue"];
        [request setRequestMethod:@"POST"];
        [request setDelegate:self];
        [request startAsynchronous];
    }
}
 

Проверьте ответ и обновите пользовательский интерфейс:

 - (void)requestFinished:(ASIFormDataRequest *)request
{
    // Use when fetching text data
    NSString *responseString = [request responseString];
    if ([responseString rangeOfString:@"uccessfully converted"].location != NSNotFound) {
        [self performSelectorOnMainThread:@selector(threadStopAnimatingTwo:) withObject:nil waitUntilDone:YES]; //DOESNT WORK
        [autosaveTimer invalidate];

    }   

}
 

Функция для обновления пользовательского интерфейса:

 - (void) threadStopAnimatingTwo:(id)data {
    NSLog(@"stop animating two");
    [loadingTwo stopAnimating];
    [labelTwo setTextColor:[UIColor lightGrayColor]];
}
 

Комментарии:

1. Почему вы создаете таймер, который повторяется, а затем аннулируете его?

2. Я аннулирую только после того, как получу то, что проверяю, в противном случае он проверяет снова через 11 секунд. В любом случае, это не имеет значения, потому что я знаю threadStopAnimatingTwo , что работает, он просто не запускается в правильном потоке, потому что пользовательский интерфейс не меняется.

3. Проверить, в каком потоке выполняется строка кода, легко. 1) Поставьте точку останова в рассматриваемой строке. 2) Запускайте до точки останова. 3) В меню выберите Вид-> Навигаторы-> Показать навигатор отладки там вы сможете увидеть свой стек вызовов, разделенный потоком.

4. Код для обновления пользовательского интерфейса выполняется в потоке 1 в соответствии с навигатором отладки. Есть идеи, почему пользовательский интерфейс на самом деле не обновляется? Если я вызываю ту же функцию, ViewDidLoad она работает, поэтому я знаю, что в коде обновления пользовательского интерфейса нет ничего плохого.

5. В threadDtopAnimatingTwo попробуйте NSLog (@»loadingTwo = %@»,loadingTwo) Это проверит, на что направлен ваш указатель.

Ответ №1:

Попробуйте обновить пользовательский интерфейс, используя свойства, а не переменные экземпляра.

Например:

 - (void) threadStopAnimatingTwo:(id)data {
    NSLog(@"stop animating two");
    [self.loadingTwo stopAnimating];
    [self.labelTwo setTextColor:[UIColor lightGrayColor]];
}
 

Комментарии:

1. self.loadingTwo также подходит null . Возможно ли self , что это не то, что обычно, поскольку оно находится внутри делегата ответа ASIHTTPRequest?

2. Нет, но ваша формулировка меня беспокоит. Насколько я понимаю, threadStopAnimatingTwo — это метод вашего класса контроллера представления, который просто вызывается ASIHTTPRequest как метод делегирования. Если это правильно, то единственным оставшимся объяснением может быть то, что что-то устанавливает для ваших компонентов пользовательского интерфейса значение nil после метода viewDidLoad . Просто для справки, ни для одного компонента пользовательского интерфейса не должно быть установлено значение nil в любом месте перед viewDidUnload. Попробуйте выполнить поиск в любом месте, где для любого компонента пользовательского интерфейса установлено значение nil.

3. По какой-либо причине любой компонент пользовательского интерфейса, к которому обращаются из методов делегирования ASIHTTP, имеет значение null. Я «решил» проблему, сохранив компоненты пользовательского интерфейса, которые мне нужны для использования в делегировании глобалам в viewDidLoad.

4. Self.properties являются глобальными. Что-то все еще не так, и я знаю, что вы знаете, основываясь на «решаемых» кавычках. Вы пробовали вызывать эту функцию где-нибудь еще, кроме viewDidLoad?

Ответ №2:

// можете ли вы добавить журнал? печать?

 NSLog(@"execute performSelectorOnMainThread");
[self performSelectorOnMainThread:@selector(threadStopAnimatingTwo:) 
                       withObject:nil 
                    waitUntilDone:YES]; //DOESNT WORK
 

Комментарии:

1. Да, если я добавлю этот оператор log, он появится в выходных данных. То же самое для threadStopAnimatingTwo функции. Я знаю, что обе функции запущены, но пользовательский интерфейс никогда не обновляется.

Ответ №3:

Оказывается, я создавал новый экземпляр моего контроллера представления, когда мне это было не нужно. Поскольку я создал новый экземпляр контроллера представления, [self.loadingTwo] сослался на null Компонент пользовательского интерфейса.