98

It works fine with the default keyboard, but I cant get it working with the numpad.

Any ideas?

Dejan Skledar
  • 11,280
  • 7
  • 44
  • 70
  • see this link http://www.weheartswift.com/make-custom-keyboard-ios-8-using-swift/ – Deepesh Feb 05 '15 at 08:11
  • 2
    I am not interested in creating a new keyboard, I just want to add a button to the UIToolBar, and then add it to the textField as an inputAccessoryView, like here: http://stackoverflow.com/a/11382044/3207979, but i havent figured it yet, how to do it in swift.. – Dejan Skledar Feb 05 '15 at 08:19
  • Instead of custom Keyboard or Toolbar, we can achieve this by adding a custom button to the default keyboard. Refer this [answer](https://stackoverflow.com/a/47286917/756941). – Nazik Nov 14 '17 at 13:23
  • For solution using storyboard [https://stackoverflow.com/a/37896351/1465582](https://stackoverflow.com/a/37896351/1465582) – Irshad Mohamed Aug 29 '18 at 19:43

12 Answers12

119

As far as I know, you can't add the Done button on the keyboard part; you'd have add a inputAccessoryView to the UITextField or UITextView (if that's what you're using).

Check the documentation for more info.

Edit: Check this question for an example on how to do that.

Edit 2: Similar example in Swift.

Edit 3: Code from edit 2, as link may expire.

override func viewDidLoad()
{
    super.viewDidLoad()

    //--- add UIToolBar on keyboard and Done button on UIToolBar ---//
    self.addDoneButtonOnKeyboard()
}

//--- *** ---//

func addDoneButtonOnKeyboard()
{
    var doneToolbar: UIToolbar = UIToolbar(frame: CGRectMake(0, 0, 320, 50))
    doneToolbar.barStyle = UIBarStyle.BlackTranslucent

    var flexSpace = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FlexibleSpace, target: nil, action: nil)
    var done: UIBarButtonItem = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.Done, target: self, action: Selector("doneButtonAction"))

    var items = NSMutableArray()
    items.addObject(flexSpace)
    items.addObject(done)

    doneToolbar.items = items
    doneToolbar.sizeToFit()

    self.textView.inputAccessoryView = doneToolbar
    self.textField.inputAccessoryView = doneToolbar

}

func doneButtonAction()
{
    self.textViewDescription.resignFirstResponder()
}

Swift 4.2

func addDoneButtonOnKeyboard(){
        let doneToolbar: UIToolbar = UIToolbar(frame: CGRect.init(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 50))
        doneToolbar.barStyle = .default

        let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
        let done: UIBarButtonItem = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(self.doneButtonAction))

        let items = [flexSpace, done]
        doneToolbar.items = items
        doneToolbar.sizeToFit()

        txtMobileNumber.inputAccessoryView = doneToolbar
    }

    @objc func doneButtonAction(){
        txtMobileNumber.resignFirstResponder()
    }
Vinod Kumar
  • 3,375
  • 1
  • 17
  • 35
nikolovski
  • 4,049
  • 1
  • 31
  • 37
  • Works Perfectly. Quick and easy implementation. Thanks! – Octavio Antonio Cedeño May 26 '17 at 12:42
  • 5
    You can change to this and you wont have to localize the button: let done: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.done, target: self, action: #selector(self.doneButtonAction)) – Zeezer Jun 10 '19 at 15:38
78

Swift 5 solution using @IBInpectable and extension. A dropdown will appear under the Attribute Inspector in the Interface Builder for every UITextField in the project.

extension UITextField{    
    @IBInspectable var doneAccessory: Bool{
        get{
            return self.doneAccessory
        }
        set (hasDone) {
            if hasDone{
                addDoneButtonOnKeyboard()
            }
        }
    }

    func addDoneButtonOnKeyboard()
    {
        let doneToolbar: UIToolbar = UIToolbar(frame: CGRect.init(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 50))
        doneToolbar.barStyle = .default

        let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
        let done: UIBarButtonItem = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(self.doneButtonAction))

        let items = [flexSpace, done]
        doneToolbar.items = items
        doneToolbar.sizeToFit()

        self.inputAccessoryView = doneToolbar
    }

    @objc func doneButtonAction()
    {
        self.resignFirstResponder()
    }
}
Dejan Atanasov
  • 1,202
  • 10
  • 13
60

A Swift-3 Version (of Marko's solution - which worked for me)

(in my case, I have a UITextField identified as textfield)

func addDoneButtonOnKeyboard() {
    let doneToolbar: UIToolbar = UIToolbar(frame: CGRect(x: 0, y: 0, width: 320, height: 50))
    doneToolbar.barStyle       = UIBarStyle.default        
    let flexSpace              = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil)
    let done: UIBarButtonItem  = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.done, target: self, action: #selector(ViewController.doneButtonAction))

    var items = [UIBarButtonItem]()
    items.append(flexSpace)
    items.append(done)

    doneToolbar.items = items
    doneToolbar.sizeToFit()

    self.textfield.inputAccessoryView = doneToolbar
}

func doneButtonAction() {
    self.textfield.resignFirstResponder()
}
Adam Stoller
  • 933
  • 8
  • 16
15

You can add toolbar with Done button to keyboard. InputAccessoryView property of textfield can be used to set this toolbar.

Below is code that I’ve used in my case

//Add done button to numeric pad keyboard
 let toolbarDone = UIToolbar.init()
 toolbarDone.sizeToFit()
 let barBtnDone = UIBarButtonItem.init(barButtonSystemItem: UIBarButtonSystemItem.Done,
         target: self, action: #selector(VerifyCardViewController.doneButton_Clicked(_:)))

 toolbarDone.items = [barBtnDone] // You can even add cancel button too
 txtCardDetails3.inputAccessoryView = toolbarDone

Screenshot

Jayprakash Dubey
  • 35,723
  • 18
  • 170
  • 177
8

The simplest solution I could find - works with Swift 4.2 - hope this helps :)

extension UITextField {

   func addDoneButtonOnKeyboard() {
       let keyboardToolbar = UIToolbar()
       keyboardToolbar.sizeToFit()
       let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace,
           target: nil, action: nil)
       let doneButton = UIBarButtonItem(barButtonSystemItem: .done,
           target: self, action: #selector(resignFirstResponder))
       keyboardToolbar.items = [flexibleSpace, doneButton]
       self.inputAccessoryView = keyboardToolbar
   }
}

Then you can handle the done action in the textFieldDidEndEditing delegate method or just add a custom method to the extension and set it in the selector of the doneButton.

Mitemmetim
  • 520
  • 3
  • 12
6

If your Done button is supposed to just close the numberpad, then the simplest version would be to call resignFirstResponder as the selector like this:

UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.Done, target: textField, action: #selector(UITextField.resignFirstResponder))

This code works in iOS9 and Swift 2, I am using it in my app:

func addDoneButtonOnNumpad(textField: UITextField) {

  let keypadToolbar: UIToolbar = UIToolbar()

  // add a done button to the numberpad
  keypadToolbar.items=[
    UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.Done, target: textField, action: #selector(UITextField.resignFirstResponder)),
    UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FlexibleSpace, target: self, action: nil)
  ]
  keypadToolbar.sizeToFit()
  // add a toolbar with a done button above the number pad
  textField.inputAccessoryView = keypadToolbar
}
Cookie Ninja
  • 1,156
  • 15
  • 29
Coder1224
  • 1,785
  • 2
  • 17
  • 21
3

First of all let me tell you one thing that You Can ADD A Done Button On Your Default KeyBoard. Actually today I just went through this problem and solved. Ready Made Code, just place this on your .swift class. I am using Xcode 7 and testing this using iPad Retina(ios 9).

Anyway no more talk here is the code.

  //Creating an outlet for the textfield
  @IBOutlet weak var outletTextFieldTime: UITextField!

  //Adding the protocol of UITextFieldDelegate      
  class ReservationClass: UIViewController, UITextFieldDelegate { 

  func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
  //Making A toolbar        
  let keyboardDoneButtonShow = UIToolbar(frame: CGRectMake(0, 0,  self.view.frame.size.width, self.view.frame.size.height/17))
  //Setting the style for the toolbar
    keyboardDoneButtonShow.barStyle = UIBarStyle .BlackTranslucent        
  //Making the done button and calling the textFieldShouldReturn native method for hidding the keyboard.
  let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.Done, target: self, action: Selector("textFieldShouldReturn:"))
  //Calculating the flexible Space.
    let flexSpace = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FlexibleSpace, target: nil, action: nil)
  //Setting the color of the button.
    item.tintColor = UIColor .yellowColor()
  //Making an object using the button and space for the toolbar        
  let toolbarButton = [flexSpace,doneButton]
  //Adding the object for toolbar to the toolbar itself
  keyboardDoneButtonShow.setItems(toolbarButton, animated: false)
  //Now adding the complete thing against the desired textfield
    outletTextFieldTime.inputAccessoryView = keyboardDoneButtonShow
    return true

}

//Function for hidding the keyboard.
func textFieldShouldReturn(textField: UITextField) -> Bool {
    self.view.endEditing(true)
    return false
   }
}

Which is giving me the solution as...

enter image description here

One more thing I just wanna tell you that this is the working solution which I have used on my recent project. I have posted this because there is no working solution available for this problem. Working Code and explanation as promised.

EDIT :-

Making it more professional...If you are following the above code then also it will give you the working solution but here I am trying to make it more Professional. Please notice the code MOD. I left the previous unused code with quote.

Changes..

  1. Creating an Individual Button object
  2. Setting it to the toolbar
  3. Reducing the size of the toolbar which was created previously
  4. Positioning the custom button to the left of the screen using FlexSpace and NegativeSpace.

    func textFieldShouldBeginEditing(textField: UITextField) -> Bool {
    let keyboardDoneButtonShow = UIToolbar(frame: CGRectMake(200,200, self.view.frame.size.width,30))
    
    // self.view.frame.size.height/17
    keyboardDoneButtonShow.barStyle = UIBarStyle .BlackTranslucent
    let button: UIButton = UIButton()
    button.frame = CGRectMake(0, 0, 65, 20)
    button.setTitle("Done", forState: UIControlState .Normal)
    button.addTarget(self, action: Selector("textFieldShouldReturn:"), forControlEvents: UIControlEvents .TouchUpInside)
    button.backgroundColor = UIColor .clearColor()
    // let doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.Done, target: self, action: Selector("textFieldShouldReturn:"))
    let doneButton: UIBarButtonItem = UIBarButtonItem()
    doneButton.customView = button
    let negativeSpace = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FixedSpace, target: nil, action: nil)
    negativeSpace.width = -10.0
    let flexSpace = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FlexibleSpace, target: nil, action: nil)
    //doneButton.tintColor = UIColor .yellowColor()
    let toolbarButton = [flexSpace,doneButton,negativeSpace]
    keyboardDoneButtonShow.setItems(toolbarButton, animated: false)
    outletTextFieldTime.inputAccessoryView = keyboardDoneButtonShow
    return true
    }
    

It is now giving me the following...Moreover try to match the pictures and find the changes.

enter image description here

Thanks.

Hope this helped. Sorry for the long answer.

onCompletion
  • 6,500
  • 4
  • 28
  • 37
3

I would use a library calles IQKeyboardManagerSwift.

You simplay have to insert one line of code into AppDelegates didFinishWithLaunching method and it applies all necessary features to all TextFields.

import IQKeyboardManagerSwift

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    //Following line is all you need and it applies for all TextFields
    IQKeyboardManager.shared.enable = true
    return true
}
ilija.trkulja
  • 147
  • 2
  • 9
3

Because above answers are old in Xcode 11 and above initial toolBar like UIToolbar() will throw a breaking constraint on console every time like here you run so use UIToolbar(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: 35)) instead

Sample code

            let toolBar =  UIToolbar(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: 35))
            toolBar.barStyle = .default
            toolBar.sizeToFit()

            // Adding Button ToolBar
            let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action: #selector(doneButtonTapped))
            toolBar.items = [doneButton]
            toolBar.isUserInteractionEnabled = true
            textField.inputAccessoryView = toolBar
SuryaKantSharma
  • 1,113
  • 12
  • 27
2

According to Marko Nikolovski answer

Here is objective-C answer :

- (void)ViewDidLoad {
[self addDoneButtonOnKeyboard];
}

- (void)addDoneButtonOnKeyboard {
UIToolbar *toolBarbutton = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 320, 50)];
toolBarbutton.barStyle = UIBarStyleBlackTranslucent;

UIBarButtonItem *barBtnItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIBarButtonItem *done = [[UIBarButtonItem alloc] initWithTitle:@"Done" style:UIBarButtonItemStyleDone target:self action:@selector(doneButtonAction)];

NSMutableArray *items = [[NSMutableArray alloc] init];
[items addObject:barBtnItem];
[items addObject:done];

toolBarbutton.items = items;
[toolBarbutton sizeToFit];

self.timeoutTextField.inputAccessoryView = toolBarbutton;
}

- (void)doneButtonAction {
[self.view endEditing:YES];
}
dicle
  • 1,122
  • 1
  • 12
  • 40
2

You can create a custom class. I'm using Swift 4:

class UITextFieldWithDoneButton: UITextField {
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.addDoneButtonOnKeyboard()
    }

    fileprivate func addDoneButtonOnKeyboard() {
        let doneToolbar: UIToolbar = UIToolbar(frame: CGRect.init(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 50))
        doneToolbar.barStyle = .default

        let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
        let done: UIBarButtonItem = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(self.doneButtonAction))

        let items = [flexSpace, done]
        doneToolbar.items = items
        doneToolbar.sizeToFit()

        self.inputAccessoryView = doneToolbar
    }

    @objc fileprivate func doneButtonAction() {
        self.resignFirstResponder()
    }
}
Kirill
  • 738
  • 10
  • 26
  • 1
    This worked a treat for me. Just copied and pasted to the bottom of the .swift file, then used: textfieldOutput.addDoneButtonOnKeyboard() into ViewDidLoad. Thank you! – David_2877 Sep 19 '20 at 21:36
1

simple and easy way to add "DONE" button on any keyboard is add universal library

IQKeyboardManager

add below code in appdelegate at application(_ application: UIApplication, didFinishLaunchingWithOptions.

IQKeyboardManager.shared.enable = true
IQKeyboardManager.shared.enableAutoToolbar = true
IQKeyboardManager.shared.keyboardDistanceFromTextField = 15
IQKeyboardManager.shared.shouldResignOnTouchOutside = true