Как определить, что распознавание речи происходит

Проблема:

У меня UITextField бок о бок с UIButton с функцией отправки. Когда пользователь нажимает кнопку отправки, я выполняю простое действие:

- (IBAction)sendMessage: (id)sender {
   [self.chatService sendMessage: self.messageTextField.text];
   self.messageTextField.text = @""; // here I get exception
}

Теперь, когда пользователь начинает использовать диктовку с клавиатуры, затем нажимает на диктовку (клавиатуру) и сразу же нажимает кнопку отправки, у меня возникает исключение «Диапазон или индекс вне границ».

Возможное решение:

Я заметил, что другие приложения отключают эту кнопку «отправить», когда сервер распознавания речи обрабатывает данные. Это ровно между двумя событиями: пользователь нажимает «готово», и результаты отображаются в текстовом поле. Я хочу решить это таким же образом.

Не могу найти в документации, где можно получить это уведомление. Я нашел UITextInput протокол, но это не то, что мне нужно.

Похожие темы:

Что я пробовал:

  1. просто перехватить и игнорировать исключение. Сбой не возник, но виртуальная клавиатура перестала отвечать
  2. Отключение кнопки отправки, когда [UITextInputMode currentInputMode].primaryLanguage равно @"dictation". Уведомление UITextInputCurrentInputModeDidChangeNotification, сообщающее об окончании режима диктовки, приходит до того, как служба диктовки фиксирует новое значение, и я все еще могу нажать кнопку отправки, чтобы вызвать исключение. Я мог бы добавить задержку при primaryLanguage потерях @ значение "диктовка", но мне такой подход не нравится. Скорее всего, эта требуемая задержка зависит от того, насколько быстро реагирует служба распознавания речи.
  3. Я добавил кучу действий по разным событиям (эти события искали в обработке: UIControlEventEditingDidBegin, UIControlEventEditingChanged, UIControlEventEditingDidEnd, UIControlEventEditingDidEndOnExit). Хорошо то, что похоже, что UIControlEventEditingChanged запускается именно в желаемые моменты: когда пользователь нажимает «Готово» в режиме диктовки и когда служба фиксирует или завершает диктовку. Так что это моя лучшая концепция на данный момент. Плохо то, что это срабатывает и в других случаях, и нет информации, чтобы отличить, в каком случае это управляющее событие было запущено, поэтому я не знаю, следует ли мне отключать или включать кнопку или делать ничего такого.

person Marek R    schedule 04.06.2014    source источник
comment
В разделе заметок указано, что UITextInputCurrentInputModeDidChangeNotification не работает, потому что о завершении сообщается слишком рано. Что, если вы воспользуетесь этим подходом, но после возврата к режиму ввода с диктовки выполните команду performSelector с задержкой 0,0, чтобы очистить текст. Уведомление, вероятно, заблокировано в SDK. Может, вам просто нужно отложить до следующего поворота цикла выполнения?   -  person danh    schedule 04.06.2014
comment
да я думал об этом. Мне это не нравится (см. Редактирование), но похоже, что это единственное решение.   -  person Marek R    schedule 04.06.2014
comment
Может быть, вы могли бы отправить вызов в основную очередь через GCD, а не выполнитьSelector с задержкой? Это может поставить его в очередь после того, как диктовка завершит его обновление без спекулятивной задержки.   -  person DavidA    schedule 04.06.2014
comment
Да, если вам нужна задержка, например, N секунд, то я согласен, что это вызывает проблемы, потому что у вас реальное состояние гонки, и задержка может быть либо слишком длинной, либо иногда приводить к сбоям. Но я предлагаю задержку 0,0, просто чтобы донести ваше сообщение до конца линии. Если это вообще сработает, я думаю, это будет работать надежно.   -  person danh    schedule 04.06.2014
comment
как я писал в редактировании, этот тестовый пример уже обеспечивает эту задержку (пользователь должен выполнить два действия, первое действие изменяется primaryLanguage), поэтому нулевая задержка не является решением.   -  person Marek R    schedule 04.06.2014


Ответы (2)


Наконец-то я нашел окончательное решение.

Это просто элегантно пройдет обзор Apple, и это всегда работает. Просто отреагируйте на UIControlEventEditingChanged и обнаружите наличие заменяющего символа следующим образом:

-(void)viewDidLoad {
  [super viewDidLoad];

  [self.textField addTarget: self
                     action: @selector(eventEditingChanged:)
           forControlEvents: UIControlEventEditingChanged];
}

-(IBAction)eventEditingChanged:(UITextField *)sender {
  NSRange range = [sender.text rangeOfString: @"\uFFFC"];
  self.sendButton.enabled = range.location==NSNotFound;
}


Старый подход

Финли: Я нашел решение. Это улучшенная концепция № 3 с сочетанием концепции № 2 (на основе этого ответа).

-(void)viewDidLoad {
  [super viewDidLoad];

  [self.textField addTarget: self
                     action: @selector(eventEditingChanged:)
           forControlEvents: UIControlEventEditingChanged];
}

-(IBAction)eventEditingChanged:(UITextField *)sender {
  NSString *primaryLanguage = [UITextInputMode currentInputMode].primaryLanguage;

  if ([primaryLanguage isEqualToString: @"dictation"]) {
    self.sendButton.enabled = NO;
  } else {
    // restore normal text field state
    self.sendButton.enabled = self.textField.text.length>0;
  }
}

- (IBAction)sendMessage: (id)sender {
   [self.chatService sendMessage: self.messageTextField.text];
   self.messageTextField.text = @"";
}

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
  if (self.textField.text.length==0 || !self.sendButton.enabled) {
     return NO;
   }
   [self sendMessage: textField];
   return YES;
}

// other UITextFieldDelegate methods ...

Теперь проблема не возникает, так как пользователь заблокирован, когда это могло произойти (точно между нажатием кнопки «Готово» в режиме диктовки и получением результатов из службы распознавания речи.
Хорошо то, что используется общедоступный API (только @ "диктовка" может быть проблемой, но я думаю, что Apple должна ее принять).

person Marek R    schedule 05.06.2014
comment
Привет, Марек! Сталкивались ли вы с какими-либо проблемами с вашим окончательным решением? Вы запускаете этот код в производственном приложении? - person josh-fuggle; 27.07.2015
comment
приложение уже есть в магазине приложений, и я не видел для этого никаких новых ошибок, но! Приложение больше не поддерживается (клиент решил запустить новый, более продвинутый продукт), поэтому у меня нет новых отчетов об ошибках. Он должен работать во всех случаях, за исключением случаев, когда вы добавляете некоторую графику в текст (например, смайлики), если вы это делаете, тогда логика обнаружения должна быть более сложной (обнаружение основано на том, что распознавание речи сигнализирует о ходе работы путем временного добавления значка в текст и все значки / изображения помечаются в тексте символом замены). - person Marek R; 27.07.2015
comment
Ваш новый подход больше не работает в iOS 9. Старый подход добавления цели для UIControlEvents.EditingChanged и последующей проверки sender.textInputMode?.primaryLanguage == "dictation" действительно работает. Еще не прошел проверку, но я не понимаю, почему это может быть проблемой. - person Nick Yap; 12.07.2016
comment
по крайней мере, исправленный сбой, который мог произойти при изменении текста во время распознавания речи в iOS 8. Это было причиной, по которой мне понадобилось это решение. - person Marek R; 13.07.2016

В iOS 7 Apple представила TextKit, поэтому для этого вопроса появилась новая информация: NSAttachmentCharacter = 0xfffc Используется для обозначения вложения как говорится.

Итак, если ваша версия больше или равна 7.0, лучше проверить attributedString на наличие вложений.

person riskpp    schedule 22.01.2015
comment
Это не ответ на этот вопрос, а скорее улучшение моего ответа. Спасибо, в любом случае. - person Marek R; 22.01.2015
comment
Эти константы требуют использования сложного API (NSCharacterSet, NSRange), я предпочитаю использовать строковый литерал, это проще. - person Marek R; 22.01.2015