20

Ours is a health care app. We have a HIPAA-compliant speech recognizer in the app through which all the dictation can take place. The hospitals don't want physicians to accidentally start speaking to the Nuance Dragon server which is not HIPAA-compliant. So, I was looking for ways I could supress the dictation key on the keyboard.

I tried putting a fake button on the Dictation button on the key pad, but on the iPad the split dock concept keeps moving the microphone all over the screen. This does not sound like a reasonable solution. Are there any experts out there who could help me?

rid
  • 61,078
  • 31
  • 152
  • 193
Mobilewits
  • 1,743
  • 4
  • 21
  • 34

5 Answers5

14

OKAY, finally got it! The trick is to observe UITextInputMode change notifications, and then to gather the identifier of the changed mode (Code seems to avoid the direct use of Private API, though seems to require a little knowledge of private API in general), and when the mode changes to dictation, resignFirstResponder (which will cancel the voice dictation). YAY! Here is some code:

Somewhere in your app delegate (at least that's where I put it)

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(inputModeDidChange:) name:@"UITextInputCurrentInputModeDidChangeNotification"
                                           object:nil];

And then you can

UIView *resignFirstResponder(UIView *theView)
{
    if([theView isFirstResponder])
    {
        [theView resignFirstResponder];
        return theView;
    }
    for(UIView *subview in theView.subviews)
    {
        UIView *result = resignFirstResponder(subview);
        if(result) return result;
    }
    return nil;
}

- (void)inputModeDidChange:(NSNotification *)notification
{        
    // Allows us to block dictation
    UITextInputMode *inputMode = [UITextInputMode currentInputMode];
    NSString *modeIdentifier = [inputMode respondsToSelector:@selector(identifier)] ? (NSString *)[inputMode performSelector:@selector(identifier)] : nil;

    if([modeIdentifier isEqualToString:@"dictation"])
    {
        [UIView setAnimationsEnabled:NO];
        UIView *resigned = resignFirstResponder(window);
        [resigned becomeFirstResponder];
        [UIView setAnimationsEnabled:YES];

        UIAlertView *denyAlert = [[[UIAlertView alloc] initWithTitle:@"Denied" message:nil delegate:nil cancelButtonTitle:@"Okay" otherButtonTitles:nil] autorelease];
        [denyAlert show];
    }
}
BadPirate
  • 25,802
  • 10
  • 92
  • 123
  • This is the only working solution as far as I can tell. Although the official docs say that UITextView does conform to the UITextInput Protocol and starting in iOS 5.1 the new dictation methods were added, but the dictation methods never get called directly when using a UITextView subclass. It seems to be a known bug according to the Apple Dev Forums. Anyway, did you happen to submit an app using this code snippet without any troubles? I'm just trying to save myself the frustration of being rejected. I'm hoping that since its a known bug, Apple might make an exception here... – Hubert Kunnemeyer Jan 05 '13 at 00:18
  • This doesn't work for iPad actually... (different notification) I was thinking the other day that using the speaker somehow or VOIP service registration / activation may disable the button. I've noticed a Google Voice bug where annotate gets disabled on occassion that might be the clue. – BadPirate Jan 05 '13 at 00:27
  • I don't see how calling the undocumented method "identifier" on UITextInputMode doesn't qualify as private API – Mihai Damian Apr 12 '13 at 14:59
  • @MihaiDamian -- Probably just because apple searches for calls to private methods using the objective-C tokens that are private, and then for strings that represent private methods. Identifier is too common to have a string based filter, and therefore is unlikely to cause any trouble during submission. Worked for me :) Also possible is that Apple intended this to be public, but goofed. As everything indicates that it should be. – BadPirate Apr 17 '13 at 01:33
  • 1
    @BadPirate That may be true, but even if Apple lets this slip through the review process you're still exposed to the risk that this private API can change in a future iOS release with no warning. – Mihai Damian Apr 17 '13 at 07:08
  • Heh... It's a hack. But it works for now. Maybe I'll luck out and they'll offer a way to handle this correctly in the future... I filed a feature request, but we all know where those end up. – BadPirate Apr 17 '13 at 22:11
10

you can create your own keyboard and set the inputView for the text fields that will accept this dictation. then when they press any keys they will get your keyboard, therefore you dont have to override the keys on the standard keyboard, you will be able to customize the entire thing.

self.myButton.inputView = self.customKeyboardView;

here is an example of an extremely custom keyboard

http://blog.carbonfive.com/2012/03/12/customizing-the-ios-keyboard/

Ray also has a teriffic tutorial on custom keyboards.

http://www.raywenderlich.com/1063/ipad-for-iphone-developers-101-custom-input-view-tutorial

I hope that helps.

The Lazy Coder
  • 11,560
  • 4
  • 51
  • 69
  • This is the solution I'm looking at currently... Seems to be one that would work, damn shame is having to reprogram the keyboard myself... (For instance, the little magnifying glass when you put your finger on a character, etc...) Know of any GitHub / Open Source projects that provide nice alternative keyboards? – BadPirate Aug 22 '12 at 19:18
  • I tried to google for some before I made my response. Unfortunately I did not find any. If you do create on. you might consider putting it on github for others to work with, and perhaps help add to :) – The Lazy Coder Aug 22 '12 at 19:29
  • Yeah, I'm in the same boat. Might have to make my class available once we write it so that other people don't have this problem. – BadPirate Aug 22 '12 at 19:33
  • Well if it starts off well, I may fork it myself and develop on it. I have had a want for a custom keyboard, but build entirely from scratch would take me too long and I have not the time nor energy for it at current. Good luck on it tho. – The Lazy Coder Aug 22 '12 at 19:38
  • Yeah, seems to be a bigger undertaking... still looking for other answers.. To get rid of the dictate button you'd have to get replace 4 keyboards (and 4 more in 2012) for the languages that support Siri, and each of those keyboards would have to have capitals, numbers, and special character keyboards (3 separate views), as well as landscape and portrait, and finally iPad and iPhone or 8*3*2*2, or 96 different layouts (yikes). – BadPirate Aug 22 '12 at 22:13
  • Well you could make your keyboard a painted thing rather than a full laid out UI. then it would fit into the space provided. And you could eliminate half the items. but as for the multiple keyboard versions for numbers and whatever else. You are correct. Pretty big task. or you just build the different layouts and connect all the button senders to the same controller, and you only show or hide the appropriate view based on the settings. all the underlying logic would be the same. Could be a fun project. but loads of scenarios you need to take into account. – The Lazy Coder Aug 22 '12 at 22:17
  • Not a perfect answer yet... but the closest... Probably what we'll head towards. – BadPirate Aug 27 '12 at 23:33
  • So I found the right answer! (see below - http://stackoverflow.com/a/12079125/285694) – BadPirate Oct 17 '12 at 00:30
9

I had the same issue and the only way i found that hides the dictation button is changing the keyboard type. For me changing it to email type seemed to be reasonable:

textField.keyboardType = UIKeyboardTypeEmailAddress;
Shahar Hajdu
  • 315
  • 2
  • 4
  • This is a simple, viable solution thanks. Note: If your UITextview is a property, you can likewise append the keyboard type, i.e. self.myTextView.keyboardType = UIKeyboard ETC; – timothykc Apr 22 '15 at 17:49
1

You could make a subclass of UITextField/UITextView that overrides insertDictationResult: to not insert anything.

This won't prevent the information being sent, but you could then display an alert informing them of the breech.

BarrettJ
  • 3,431
  • 2
  • 29
  • 26
  • Interesting solution. Do you know of anything that gets called before dictation starts, so the user can be alerted prior to saying anything that could leak Private information? – BadPirate Aug 22 '12 at 19:17
  • 1
    Unfortunately no. You get a recording ended notification (dictationRecordingDidEnd), but there's no way to cancel the transmission to the server (as my understanding is that it starts uploading as soon as the recording is started, anyway). – BarrettJ Aug 22 '12 at 20:49
0

This is a Swift 4 solution based on @BadPirate's hack. It will trigger the initial bell sound stating that dictation started, but the dictation layout will never appear on the keyboard.

This will not hide the dictation button from your keyboard: for that the only option seems to be to use an email layout with UIKeyboardType.emailAddress.


In viewDidLoad of the view controller owning the UITextField for which you want to disable dictation:

// Track if the keyboard mode changed to discard dictation
NotificationCenter.default.addObserver(self,
                                       selector: #selector(keyboardModeChanged),
                                       name: UITextInputMode.currentInputModeDidChangeNotification,
                                       object: nil)

Then the custom callback:

@objc func keyboardModeChanged(notification: Notification) {
    // Could use `Selector("identifier")` instead for idSelector but
    // it would trigger a warning advising to use #selector instead
    let idSelector = #selector(getter: UILayoutGuide.identifier)

    // Check if the text input mode is dictation
    guard
        let textField = yourTextField as? UITextField
        let mode = textField.textInputMode,
        mode.responds(to: idSelector),
        let id = mode.perform(idSelector)?.takeUnretainedValue() as? String,
        id.contains("dictation") else {
            return
    }

    // If the keyboard is in dictation mode, hide
    // then show the keyboard without animations
    // to display the initial generic keyboard
    UIView.setAnimationsEnabled(false)
    textField.resignFirstResponder()
    textField.becomeFirstResponder()
    UIView.setAnimationsEnabled(true)

    // Do additional update here to inform your
    // user that dictation is disabled
}
agirault
  • 2,509
  • 20
  • 24