UITextView не обновляется

Я пытаюсь интегрировать функцию "Открыть с помощью...".

В моем AppDelegate.m у меня есть

-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
...
_fileContent = [NSString stringWithContentsOfURL:url
                                                encoding:NSUTF8StringEncoding
                                                   error:&error];
...
ViewController *vc = (ViewController*)self.window.rootViewController;
        [vc refreshUI:nil];
}

Я использую ARC, поэтому я вызываю только следующее в своем ViewController.h и позже опускаю @synthesize в .m

@property (nonatomic, retain) IBOutlet UITextView *textView;

В моем ViewController.m у меня есть следующее

-(void)refreshUI:(id)sender
{
    AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];

    if ([appDelegate openedFromURL])
    {
        NSLog(@"[refreshUI] appDelegate openFromURL (Length: %d)", [appDelegate.fileContent length]);
        NSLog(@"Contents before: %@", [_textView text]);
        [_textView setText:appDelegate.fileContent];
        NSLog(@"Contents after: %@", [_textView text]);
    }
...
}

Когда я открываю свой файл из другого приложения (Dropbox), первый NSLog дает мне правильную длину моего файла. Второй NSLog дает мне правильное «старое» содержимое UITextView, а третий NSLog дает мне правильное «новое» содержимое (начало содержимого файла, который я «открыл с помощью ...» через приложение Dropbox). Таким образом, данные попадают в мое приложение.

Проблема в том, что UITextView не обновляется. Если я нажму любую другую кнопку в UIViewController (что заставит меня переключиться на другой UIViewController), а затем вернусь к «ViewController», UITextView обновится с правильными данными. Даже если я добавлю UIButton и установлю его действие только для вызова [self refreshUI] обновлений UITextView.

Он просто не обновляется сам по себе из метода, вызываемого из AppDelegate.

Что я делаю неправильно? Я даже пытался вручную перерисовать UITextView с помощью setNeedsDisplay. Но это не имело никакого эффекта.

Спасибо


person Joseph    schedule 13.12.2012    source источник
comment
Мое первое предположение будет заключаться в том, что вы вызываете stringWithContentsOfURL:encoding:error: в фоновом потоке, как и должны, но не вызываете refreshUI: в основном потоке. UIKit любит сбрасывать звонки, сделанные в фоновом потоке.   -  person NJones    schedule 13.12.2012
comment
Я сознательно не вызывал stringWithContensOfURL в фоновом потоке, чтобы избежать такой проблемы... это происходит само по себе? Оператор refreshUI находится всего через пару строк после stringWithContensOfURL, и оба находятся в одном и том же методе.   -  person Joseph    schedule 13.12.2012
comment
И, судя по трем утверждениям NSLog, он успевает вовремя — верно? Было бы решением сохранить значение _fileContent в NSUserDefaults, а затем использовать UIApplicationWillEnterForegroundNotification для запуска refreshUI, который затем просто читает то, что находится в NSUserDefaults   -  person Joseph    schedule 13.12.2012
comment
Попробуйте обернуть вызов refreshUI: в блоке отправки в основной поток следующим образом: dispatch_async(dispatch_get_main_queue(), ^{[vc refreshUI:nil];}); Если это работает, то, вероятно, это проблема с потоками.   -  person NJones    schedule 13.12.2012
comment
поведение dispatch_async(dispatch_get_main_queue(), ^{[vc refreshUI:nil];}); не изменилось.   -  person Joseph    schedule 13.12.2012
comment
Даже если я помещу в блок как stringWithContentsOfURL, так и refreshUI, UITextView все равно не обновится.   -  person Joseph    schedule 13.12.2012
comment
Я добавил [_textView setBackgroundColor:[UIColor redColor]]; прямо перед [_textView setText:appDelegate.fileContent];. UITextView остается белым. Я проверил/перепроверил соединение в IB, и все в порядке. Может ли быть так, что методы вызываются для _textView еще до того, как он загружен? Тот же оператор в т.е. viewDidAppear изменяет цвет UITextView, поэтому он правильно подключен.   -  person Joseph    schedule 13.12.2012
comment
Да, это определенно возможно. Вот что я бы проверил дальше. Попробуйте поместить это в свой метод refreshUI:: NSLog(@"address of textfield is %p",self.textView);. Откройте URL-адрес через свое приложение, а затем нажмите кнопку обновления вручную. Если адрес изменился, вы пишете текст в текстовое поле, которое заменяется. Кроме того, я знаю, что textView подключен правильно, так как кнопка ручного обновления работает.   -  person NJones    schedule 13.12.2012
comment
Хорошо, на один шаг ближе... если приложение загружается через open с... действием, оно работает. Как только пользователь использует приложение и переключает ViewControllers (с раскадровкой и переходами), адрес меняется, и обновление с помощью open with... перестает работать.   -  person Joseph    schedule 13.12.2012


Ответы (2)


Как теперь можно сделать вывод из ваших комментариев. Кажется, что ваши переходы раскадровки создают новый экземпляр вашего класса «ViewController», а не возвращают вас к исходному. Однако оригинал по-прежнему является делегатом приложения rootViewController. Это приводит к тому, что вы отправляете сообщение экземпляру вашего контроллера представления, который не представлен в данный момент. По сути, вы меняете метку, которую не видите. Есть несколько способов это исправить.

  • Измените свои переходы, чтобы они возвращали вас к rootViewController:

Это, вероятно, самый простой способ, который, вероятно, повысит вашу эффективность. В ваших методах, вызывающих возврат, используйте методы со словами типа pop... или dismiss.

  • Используйте делегирование:

Добавьте urlLaunchDelegate к вашему делегату приложения, и в соответствующее время (viewDidLoad или viewWillAppear:) ваш контроллер представления будет установлен как urlLaunchDelegate.

  • Используйте NSNotificationCenter

Имейте уведомление типа MyApplication_LaunchURLNotification, которое наблюдает ваш контроллер представления. Вы можете добавить NSURL в качестве объекта.

person NJones    schedule 13.12.2012
comment
Спасибо за вашу помощь! Я выбрал первый вариант (открыть/закрыть). - person Joseph; 14.12.2012

@property (nonatomic, retain) IBOutlet UITextView

Необходимо объявить имя

@property (nonatomic, retain) IBOutlet UITextView *textView

Вам также необходимо связать textView с вашим IBOutlet.

person mac10688    schedule 13.12.2012
comment
извините... была опечатка... отредактирую пост... Я так и сделал, конечно - person Joseph; 13.12.2012
comment
А вы уверены, что подключили его через IBOutlet? - person mac10688; 13.12.2012
comment
да - уверен, что в противном случае другие сценарии (например, кнопка обновления) не сработают. - person Joseph; 13.12.2012
comment
Почему вы используете _textView, а не self.textView? - person mac10688; 13.12.2012
comment
Почему я не должен? Я думал, что это то же самое? Я только что проверил это, но self.textView ведет себя так же. - person Joseph; 13.12.2012