5

How to detect if voice dictation was used for UITextField? Or microphone button was tapped on keyboard. Is there any way to do that?

enter image description here

BergP
  • 3,453
  • 4
  • 33
  • 58

2 Answers2

7

UITextField conforms to UITextInput Protocol ( under the section Using Dictation are methods of interest). In this protocol is a method dictationRecordingDidEnd that you can override.

One way is to subclass UITextField and implement the above mentioned method and any others of interest from the UITextInput protocol.

example subclass .h

#import <UIKit/UIKit.h>

@interface BWDictationTextField : UITextField

@end

.m

#import "BWDictationTextField.h"

@implementation BWDictationTextField
     - (void)dictationRecordingDidEnd {
          NSLog(@"%s", __PRETTY_FUNCTION__);
     }// done is pressed by user after dictation
@end

Unfortunately there is no documented way to detect the actual tap of the microphone button ( dictation did start ).

Bamsworld
  • 5,670
  • 2
  • 32
  • 37
  • 1
    Could you add a category on `UITextField` that implements these methods instead of subclassing? – sethfri Mar 10 '16 at 01:33
  • Not sure if this was added recently but you can detect a "dictation did start" by checking: textField.textInputMode?.primaryLanguage == "dictation" when the UITextInputCurrentInputModeDidChange notification is called – David Rees Nov 23 '18 at 01:47
3

A textfield will report changes when the text input changes including when dictation starts and stops. We can listen for this notification and report when dictation starts and stops.

Here is a Swift subclass using this technique.

protocol DictationAwareTextFieldDelegate: class {
    func dictationDidEnd(_ textField: DictationAwareTextField)
    func dictationDidFail(_ textField: DictationAwareTextField)
    func dictationDidStart(_ textField: DictationAwareTextField)
}

class DictationAwareTextField: UITextField {

    public weak var dictationDelegate: DictationAwareTextFieldDelegate?
    private var lastInputMode: String?
    private(set) var isDictationRunning: Bool = false

    override func dictationRecordingDidEnd() {
        isDictationRunning = false
        dictationDelegate?.dictationDidEnd(self)
    }

    override func dictationRecognitionFailed() {
        isDictationRunning = false
        dictationDelegate?.dictationDidEnd(self)
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        commonInit()
    }

    private func commonInit() {
        NotificationCenter.default.addObserver(self, selector: #selector(textInputCurrentInputModeDidChange), name: .UITextInputCurrentInputModeDidChange, object: nil)
    }

    deinit {
        NotificationCenter.default.removeObserver(self)
    }

    @objc func textInputCurrentInputModeDidChange(notification: Notification) {
        guard let inputMode = textInputMode?.primaryLanguage else {
            return
        }

        if inputMode == "dictation" && lastInputMode != inputMode {
            isDictationRunning = true
            dictationDelegate?.dictationDidStart(self)
        }
        lastInputMode = inputMode
    }
}

As this class listens for a notification, the notification will be called many times if there is many DictationAwareTextFields. If this is a problem you must move the notification observing code out of the textField into a higher class like the view controller.

David Rees
  • 6,792
  • 3
  • 31
  • 39