How to add a 'Done' button to numpad keyboard in iOS

73,665

Solution 1

This is a simple way of projecting a done button in iOS7 num-keypad. In the below delegate method of UITextField, add a notification for keyboard show.

-(void)textFieldDidBeginEditing:(UITextField *)textField {
    
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillShow:)
                                                 name:UIKeyboardWillShowNotification
                                               object:nil];
}

Now implement the method keyboardWillShow as below. Here we need to take extra care for iOS7.

- (void)keyboardWillShow:(NSNotification *)note {
    // create custom button
    UIButton *doneButton = [UIButton buttonWithType:UIButtonTypeCustom];
    doneButton.frame = CGRectMake(0, 163, 106, 53);
    doneButton.adjustsImageWhenHighlighted = NO;
    [doneButton setImage:[UIImage imageNamed:@"doneButtonNormal.png"] forState:UIControlStateNormal];
    [doneButton setImage:[UIImage imageNamed:@"doneButtonPressed.png"] forState:UIControlStateHighlighted];
    [doneButton addTarget:self action:@selector(doneButton:) forControlEvents:UIControlEventTouchUpInside];
    
    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"7.0")) {
        dispatch_async(dispatch_get_main_queue(), ^{
            UIView *keyboardView = [[[[[UIApplication sharedApplication] windows] lastObject] subviews] firstObject];
            [doneButton setFrame:CGRectMake(0, keyboardView.frame.size.height - 53, 106, 53)];
            [keyboardView addSubview:doneButton];
            [keyboardView bringSubviewToFront:doneButton];
            
            [UIView animateWithDuration:[[note.userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue]-.02
                                  delay:.0
                                options:[[note.userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] intValue]
                             animations:^{
                                 self.view.frame = CGRectOffset(self.view.frame, 0, 0);
                             } completion:nil];
        });
    } else {
        // locate keyboard view
        dispatch_async(dispatch_get_main_queue(), ^{
            UIWindow* tempWindow = [[[UIApplication sharedApplication] windows] objectAtIndex:1];
            UIView* keyboard;
            for(int i=0; i<[tempWindow.subviews count]; i++) {
                keyboard = [tempWindow.subviews objectAtIndex:i];
                // keyboard view found; add the custom button to it
                if([[keyboard description] hasPrefix:@"UIKeyboard"] == YES)
                    [keyboard addSubview:doneButton];
            }
        });
    }
}

Now add this macro to suitable header to detect the SYSTEM_VERSION

#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)

Solution 2

The much safer approach is to use a UIToolBar with Done Button as inputAccessoryView.


Sample Code :

UIToolbar *keyboardDoneButtonView = [[UIToolbar alloc] init];
[keyboardDoneButtonView sizeToFit];
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithTitle:@"Done"
                                                               style:UIBarButtonItemStyleBordered target:self
                                                              action:@selector(doneClicked:)];
[keyboardDoneButtonView setItems:[NSArray arrayWithObjects:doneButton, nil]];
txtField.inputAccessoryView = keyboardDoneButtonView;

Your -doneClicked method should look like this :

- (IBAction)doneClicked:(id)sender
{
    NSLog(@"Done Clicked.");
    [self.view endEditing:YES];
}

Sample Code Swift:

let keyboardDoneButtonView = UIToolbar.init()
keyboardDoneButtonView.sizeToFit()
let doneButton = UIBarButtonItem.init(barButtonSystemItem: UIBarButtonSystemItem.Done, 
                                                   target: self, 
                                                   action: Selector("doneClicked:")))    

keyboardDoneButtonView.items = [doneButton]
textFieldInput.inputAccessoryView = keyboardDoneButtonView

Your -doneClicked method should look like this :

func doneClicked(sender: AnyObject) {
  self.view.endEditing(true)
}

Solution 3

Even easier way:

Swift 3.0 and above:

func addDoneButton() {
    let keyboardToolbar = UIToolbar()
    keyboardToolbar.sizeToFit()
    let flexBarButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace,
        target: nil, action: nil)
    let doneBarButton = UIBarButtonItem(barButtonSystemItem: .done,
        target: view, action: #selector(UIView.endEditing(_:)))
    keyboardToolbar.items = [flexBarButton, doneBarButton]
    textField.inputAccessoryView = keyboardToolbar
}

Swift 2.3 and below:

func addDoneButton() {
    let keyboardToolbar = UIToolbar()
    keyboardToolbar.sizeToFit()
    let flexBarButton = UIBarButtonItem(barButtonSystemItem: .FlexibleSpace,
        target: nil, action: nil)
    let doneBarButton = UIBarButtonItem(barButtonSystemItem: .Done,
        target: view, action: #selector(UIView.endEditing(_:)))
    keyboardToolbar.items = [flexBarButton, doneBarButton]
    textField.inputAccessoryView = keyboardToolbar
}

Objective C:

- (void)addDoneButton {
    UIToolbar* keyboardToolbar = [[UIToolbar alloc] init];
    [keyboardToolbar sizeToFit];
    UIBarButtonItem *flexBarButton = [[UIBarButtonItem alloc]
    initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
    target:nil action:nil];
    UIBarButtonItem *doneBarButton = [[UIBarButtonItem alloc]
    initWithBarButtonSystemItem:UIBarButtonSystemItemDone
    target:self.view action:@selector(endEditing:)];
    keyboardToolbar.items = @[flexBarButton, doneBarButton];
    self.textField.inputAccessoryView = keyboardToolbar;
}

EDIT:

I've created a useful library called DCKit, which already have the toolbar out of the box:

Done toolbar above keyboard in iOS (with using DCKit library)

It also has many other cool features.

Solution 4

Just building on answers above with the Swift version since I had to translate it:

@IBOutlet weak var numberTextField: UITextField!

override func viewDidLoad() {
    addDoneButtonTo(numberTextField)
}

// MARK: Done for numberTextField

private func addDoneButtonTo(textField: UITextField) {
    let flexBarButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FlexibleSpace, target: nil, action: nil)
    let doneBarButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Done, target: self, action: "didTapDone:")
    let keyboardToolbar = UIToolbar()
    keyboardToolbar.sizeToFit()
    keyboardToolbar.items = [flexBarButton, doneBarButton]
    textField.inputAccessoryView = keyboardToolbar
}

func didTapDone(sender: AnyObject?) {
    numberTextField.endEditing(true)
}

Solution 5

You can add a button to the keyboard's input accessory view

myTextField.inputAccessoryView =_inputView;

input accessory view is a view that comes over the keyboard always and dismiss with the [textfield resignFirstResponder]

put done over the input view and perform resign first responder of the textfields.

Share:
73,665

Related videos on Youtube

George McKibbin
Author by

George McKibbin

Updated on May 12, 2021

Comments

  • George McKibbin
    George McKibbin almost 3 years

    So, the numpad keyboard doesn't come with a 'Done' or 'Next' button by default so I'd like to add one. In iOS 6 and below there were some tricks to add a button to the keyboard but they don't seem to be working in iOS 7.

    First I subscribe to the keyboard showing notification

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillShow:)
                                                 name:UIKeyboardWillShowNotification
                                               object:nil];
    

    Then I try to add a button when the keyboard shows up:

    - (void)keyboardWillShow:(NSNotification *)note 
    {
        // create custom button
        UIButton *doneButton = [UIButton buttonWithType:UIButtonTypeSystem];
        doneButton.frame = CGRectMake(0, 50, 106, 53);
        doneButton.adjustsImageWhenHighlighted = NO;
        [doneButton setTitle:@"Done" forState:UIControlStateNormal];
        [doneButton addTarget:self action:@selector(dismissKeyboard) forControlEvents:UIControlEventTouchUpInside];
    
        // locate keyboard view
        UIWindow* tempWindow = [[[UIApplication sharedApplication] windows] objectAtIndex:1];
        UIView* keyboard;
        for(int i=0; i<[tempWindow.subviews count]; i++) 
        {
            keyboard = [tempWindow.subviews objectAtIndex:i];
            // keyboard view found; add the custom button to it
            if([[keyboard description] hasPrefix:@"UIKeyboard"] == YES)
            [keyboard addSubview:doneButton];
        }
    }
    

    But the for loop doesn't run because it doesn't find any subviews. Any suggestions? I couldn't find any solutions for iOS7 so is there a different way I'm supposed to be doing this?

    Edit: Thanks for all the suggestions for toolbars guys but I'd rather not go down that route as I'm quite space poor (and it is kind of ugly).

    • Anil
      Anil over 10 years
      Tried this post? neoos.ch/blog/…
    • βhargavḯ
      βhargavḯ over 10 years
      @Anil That way of customizing UIKeyboard is prohibited by apple.
    • Praveen Matanam
      Praveen Matanam over 10 years
      Check with UIKeyboardDidShowNotification.
    • the1pawan
      the1pawan over 10 years
    • Anil
      Anil over 10 years
      @βḧäṙℊặṿῗ does Apple allow you to customize numberpads?
    • George McKibbin
      George McKibbin over 10 years
      @Anil That is the way I've already tried.
    • βhargavḯ
      βhargavḯ over 10 years
      @Anil I am using approach to set inputAccessoryView to textFields.
    • Vinodh
      Vinodh over 10 years
      Can you try github.com/simonbs/BSKeyboardControls which is the class which adds toolbar with next and previous button on top of keyboard
    • George McKibbin
      George McKibbin over 10 years
      I don't really want to add a toolbar, I want to put the button right on the keyboard.
    • Balram Tiwari
      Balram Tiwari over 10 years
      @Anil Please look at my answer below. That is a working solution for iOS7 & as well maintaining backward compatibility. Link to answer is here.
    • landonandrey
      landonandrey over 7 years
      See my answer here. Works on both iOS8/iOS9.
  • George McKibbin
    George McKibbin over 10 years
    I might have to end up doing this. I don't really like how much space it takes up.
  • Bhavin
    Bhavin over 10 years
    @GeorgeMcKibbin: The space should not be the issue here as it will take that space only while you are typing. Also, according to me this approach is far better than messing up with Keyboard that usually Apple does not like.
  • George McKibbin
    George McKibbin over 10 years
    Thanks, this is what I wanted :) Unfortunately If there was already a keyboard on screen and then you switch to a field that needs a numpad keyboard then keyBoardWillShow doesn't get called. But thanks, a step in the right direction haha.
  • govo
    govo about 10 years
    SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO why not NSFoundationVersionNumber>NSFoundationVersionNumber_iOS_6_0? And I test it, NSFoundationVersionNumber_iOS_5_0 is better
  • pronebird
    pronebird almost 10 years
    dispatch_async is not the most reliable method to hack into keyboard here. :(
  • Balram Tiwari
    Balram Tiwari almost 10 years
    @Andy : Kindly Provide more justification for unreliability of dispatch_async or else the most reliable method to use this with this approach. You are free to edit the answer.
  • pronebird
    pronebird almost 10 years
    @BalramTiwari I wish I could, but after hacking for a while it seems to me that Apple moved keyboard initialisation after all public notifications fired. I mean, generally my suggestion here is to stay away from hacking keyboard, even though I see the benefits from UX perspective.
  • pronebird
    pronebird almost 10 years
    @BalramTiwari dispatch_async solutions are easy to break in future because they depend on Runloop.
  • Mark McCorkle
    Mark McCorkle over 9 years
    It looks like to me you added a flex bar button to Bhavin's answer from 1 year ago as a new answer so I could see why someone down voted it. Maybe I missed something here as well?
  • Andrey Gordeev
    Andrey Gordeev over 9 years
    Yes, I don't use initWithTitle:@"Done", I use initWithBarButtonSystemItem:UIBarButtonSystemItemDone instead. This will return standard Apple's Done bar button. Moreover, it'll be already localized
  • Mark McCorkle
    Mark McCorkle over 9 years
    This should be added as an improvement (comment) to the previously correct answer IMO or expect down votes. A new answer should involve a different approach to the original question, not an improvement to an existing question. Nonetheless, thank you for the improvement. ;-)
  • Andrey Gordeev
    Andrey Gordeev over 9 years
    No, I don't think so. Comments are not supposed to be used for writing code :)
  • Hemant Chittora
    Hemant Chittora over 9 years
    in iOS8 this done button is not hiding, After dismissal of keyboard.
  • Chris
    Chris over 9 years
    When I do this, I only get the toolbar at the very bottom of my screen and the keyboard no longer appears. Thoughts?
  • Austin
    Austin over 9 years
    great answer, just one tidbit, arrayWithObjects is unspokenly deprecated in favor of literals: [NSArray arrayWithObjects:doneButton, nil] => @[doneButton]
  • Fred Faust
    Fred Faust about 9 years
    This works great but when an iPhone 6+ starts in portrait and then is rotated to horizontal the Done button no longer functions, any idea?
  • Radu Simionescu
    Radu Simionescu over 8 years
    this is the correct way of doing it. Overlaying a button over the keyboard is a bad idea because the layout can change with future versions of IOS.
  • SwiftArchitect
    SwiftArchitect about 8 years
    This answer, albeit clever, was bound to break.
  • Nazir
    Nazir about 8 years
    from iOS 8.0 UIBarButtonItemStyleBordered is deprecated use UIBarButtonItemStyleDone or UIBarButtonItemStylePlain
  • Arpit B Parekh
    Arpit B Parekh over 7 years
    I implemented your answer very similar to what I have to do. Thanks. But button do not come as an animated object, when keyboard show.