I would like to implement a done toolbar above the numpad keyboard when the textfield is tapped however the toolbar is not showing up for some reason.
The following code sample has been used:
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()
}
}
Make sure in your storyboard, TextField's property inspector doneAccessory property is set to ON
and O/P looks like
Related
I tried to add a toolbar for my UITextFiled, the keyboard is set to numpad. It is working but the button is not showing. I created an extension for my UITextfield
extension UITextField {
/// Adding a done button on the keyboard
func addDoneButtonOnKeyboard() {
let doneToolbar = UIToolbar(frame: CGRect(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(title: "Done", style: .done, target: self, action: #selector(self.doneButtonAction))
let items = [flexSpace, done]
doneToolbar.items = items
doneToolbar.sizeToFit()
self.inputAccessoryView = doneToolbar
}
/// Done button callback
#objc func doneButtonAction() {
self.resignFirstResponder()
}
}
and then I am calling this extension like this
private lazy var fromInputField: CoinpassInput = {
let input = CoinpassInput()
input.keyboardType = .decimalPad
input.addTarget(self, action: #selector(fromInputFieldDidChange), for: .editingChanged)
input.addDoneButtonOnKeyboard()
return input
}()
the toolbar is showing and working but the 'done; button is not showing. If I click on the right corner of the toolbar. the keyboard will hide. I dont know what I am missing why the button is not showing.
Try this code, with slight modifications:
func addDoneButtonOnKeyboard() {
let doneToolbar = UIToolbar(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 50))
doneToolbar.barStyle = .default
doneToolbar.barTintColor = .red
let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let done = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(self.doneButtonAction))
done.tintColor = .yellow
doneToolbar.setItems([flexSpace,done], animated: false)
doneToolbar.isUserInteractionEnabled = true
self.inputAccessoryView = doneToolbar
}
In my app, I have a button which when clicked should display time picker with toolbar on it. Most of examples I saw added toolbar as an inputAccessoryView on text field, but in my case I don't have a text field.
So, I created a custom view which has date time picker and toolbar and I am adding that view as a subview to my controller's view, but I don't see the custom view on the app.
Below is the controller code for button clicked :
func buttonClicked(date: Date) {
let timePicker = EditTimeHelper.createTimePickerAndToolbar(displayDate: date)
self.view.addSubview(timePicker)
}
Code for custom view in separate EditTimeHelper class:
static func createTimePickerAndToolbar(displayDate: Date) -> UIView {
let pickerView = UIView(frame: CGRect(x: 0, y: UIScreen.main.bounds.height - 300, width: UIScreen.main.bounds.width, height: 300))
let timePicker = createTimePicker(displayDate: displayDate)
pickerView.addSubview(timePicker)
let toolbar = createUIToolBar()
pickerView.addSubview(toolbar)
return pickerView
}
static func createTimePicker(displayDate: Date) -> UIDatePicker {
let timePicker:UIDatePicker = UIDatePicker()
timePicker.datePickerMode = UIDatePicker.Mode.time
timePicker.date = displayDate
timePicker.minuteInterval = 15
if #available(iOS 13.4, *) {
timePicker.preferredDatePickerStyle = .wheels
} else {
// Fallback on earlier versions where time picker is wheels style by default.
}
timePicker.addTarget(self, action: #selector(timeChanged(_:)), for: UIControl.Event.valueChanged)
timePicker.backgroundColor = .white
timePicker.frame = CGRect(x: 0, y: UIScreen.main.bounds.height - 200, width: UIScreen.main.bounds.width, height: 200)
return timePicker
}
private static func createUIToolBar() -> UIToolbar {
let pickerToolbar = UIToolbar()
pickerToolbar.autoresizingMask = .flexibleHeight
//customize the toolbar
pickerToolbar.barStyle = .default
pickerToolbar.barTintColor = UIColor.black
pickerToolbar.backgroundColor = UIColor.white
pickerToolbar.isTranslucent = false
pickerToolbar.frame = CGRect(x: 0, y: UIScreen.main.bounds.height - 300, width: UIScreen.main.bounds.width, height: 100)
// add buttons
let cancelButton = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(cancelBtnClicked(_:)))
cancelButton.tintColor = UIColor.white
let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneBtnClicked(_:)))
doneButton.tintColor = UIColor.white
//add the items to the toolbar
pickerToolbar.items = [cancelButton, flexSpace, doneButton]
return pickerToolbar
}
#objc func timeChanged(_ sender: UIDatePicker) {
}
#objc func cancelBtnClicked(_ button: UIBarButtonItem?) {
}
#objc func doneBtnClicked(_ button: UIBarButtonItem?) {
}
Any idea what am I doing wrong and not seeing custom view?
If I call EditTimeHelper.createTimePicker(displatDate: date), then I see the time picker, but I want to add toolbar on top of it.
When I debug this code, I do see time picker and toolbar as custom view's subviews, but I just don't see them on the app.
The reason why you can't see the picker and the tool bar is because you have positioned the time picker and the tool bar incorrectly. Notice these two lines:
timePicker.frame = CGRect(x: 0, y: UIScreen.main.bounds.height - 200, width: UIScreen.main.bounds.width, height: 200)
// and
pickerToolbar.frame = CGRect(x: 0, y: UIScreen.main.bounds.height - 300, width: UIScreen.main.bounds.width, height: 100)
Since these are subviews of the pickerView, the coordinates are relative to the top left corner of pickerView, not the top left corner of the screen. You should instead do
timePicker.frame = CGRect(x: 0, y: 100, width: UIScreen.main.bounds.width, height: 200)
// and
pickerToolbar.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 100)
Now you should see the tool bar and the time picker.
There are other problems with your code, however. First, timeChanged, cancelBtnClicked and doneBtnClicked won't be called. You have added self as the target for the bar button items and the picker, but since you are in a static method, self refers to the class itself. When the user presses the done button, it would try to call a method called doneBtnClicked on the class, rather than a particular instance. But the class doesn't have such a method! The doneBtnClicked you have declared is an instance method, available on instances only.
Second, you are giving these views fixed positions. This means that the layout will look very weird when the user rotates the screen. Just use AutoLayout!
You can make timeChanged, cancelBtnClicked and doneBtnClicked all static too, but a much better way is to just create a custom UIView subclass. Here is an example, as a starting point:
class TimePickerToolBarView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
let timePicker = createTimePicker()
addSubview(timePicker)
let toolBar = createUIToolBar()
addSubview(toolBar)
timePicker.translatesAutoresizingMaskIntoConstraints = false
toolBar.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
toolBar.heightAnchor.constraint(equalToConstant: 44),
toolBar.topAnchor.constraint(equalTo: topAnchor),
toolBar.leftAnchor.constraint(equalTo: leftAnchor),
toolBar.rightAnchor.constraint(equalTo: rightAnchor),
timePicker.leftAnchor.constraint(equalTo: leftAnchor),
timePicker.rightAnchor.constraint(equalTo: rightAnchor),
timePicker.bottomAnchor.constraint(equalTo: bottomAnchor),
timePicker.topAnchor.constraint(equalTo: toolBar.bottomAnchor),
])
}
private func createTimePicker() -> UIDatePicker {
let timePicker:UIDatePicker = UIDatePicker(frame: .zero)
timePicker.datePickerMode = UIDatePicker.Mode.time
timePicker.minuteInterval = 15
if #available(iOS 13.4, *) {
timePicker.preferredDatePickerStyle = .wheels
} else {
// Fallback on earlier versions where time picker is wheels style by default.
}
timePicker.addTarget(self, action: #selector(timeChanged(_:)), for: UIControl.Event.valueChanged)
timePicker.backgroundColor = .white
return timePicker
}
private func createUIToolBar() -> UIToolbar {
let pickerToolbar = UIToolbar(frame: .zero)
//customize the toolbar
pickerToolbar.barStyle = .default
pickerToolbar.barTintColor = UIColor.black
pickerToolbar.backgroundColor = UIColor.white
pickerToolbar.isTranslucent = false
// add buttons
let cancelButton = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(cancelBtnClicked(_:)))
cancelButton.tintColor = UIColor.white
let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneBtnClicked(_:)))
doneButton.tintColor = UIColor.white
//add the items to the toolbar
pickerToolbar.items = [cancelButton, flexSpace, doneButton]
return pickerToolbar
}
#objc func timeChanged(_ sender: UIDatePicker) {
}
#objc func cancelBtnClicked(_ button: UIBarButtonItem?) {
}
#objc func doneBtnClicked(_ button: UIBarButtonItem?) {
}
}
I'm trying to learn Swift and I've done on the view screens. But as you can understand more easily by checking the screenshot, when I enter a value into a text field, there isn't any done button showing up so I can not hide the keyboard from the screen. And that makes it impossible to press the submit button which is located bottom of the screen view.
Firstly, create a new Swift File. Add this to the file :
import Foundation
import UIKit
extension UIViewController{
func toolBar() -> UIToolbar{
let toolBar = UIToolbar()
toolBar.barStyle = .default
toolBar.isTranslucent = true
toolBar.barTintColor = UIColor.init(red: 0/255, green: 25/255, blue: 61/255, alpha: 1) //Write what you want for color
let space = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
var buttonTitle = "Done" //Or "Tamam"
var cancelButtonTitle = "Cancel" //Or "İptal" for Turkish
let doneButton = UIBarButtonItem(title: buttonTitle, style: .done, target: self, action: #selector(onClickDoneButton))
let cancelButton = UIBarButtonItem(title: cancelButtonTitle, style: .plain, target: self, action: #selector(onClickCancelButton))
doneButton.tintColor = .white
cancelButton.tintColor = .white
toolBar.setItems([cancelButton, space, doneButton], animated: false)
toolBar.isUserInteractionEnabled = true
toolBar.sizeToFit()
return toolBar
}
#objc func onClickDoneButton(){
view.endEditing(true)
}
#objc func onClickCancelButton(){
view.endEditing(true)
}
}
And add this toolbar to your textfield :
yourTextField.inputAccessoryView = toolBar()
Hope it helps...
You can add a toolbar as an input accessory :
let toolBar = UIToolbar()
toolBar.sizeToFit()
let button = UIBarButtonItem(title: "Done", style: .plain, target: self,
action: #selector(dismiss))
toolBar.setItems([button], animated: true)
toolBar.isUserInteractionEnabled = true
textField.inputAccessoryView = toolBar
Also you need to add dismiss method:
#objc func dismiss() {
view.endEditing(true)
}
Without switching back to UIKit Used the following library
Link: https://github.com/siteline/SwiftUI-Introspect
import SwiftUI
import Introspect
struct ContentView : View {
#State var text = ""
var body: some View {
TextField("placeHolder", text: $text)
.keyboardType(.default)
.introspectTextField { (textField) in
let toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: textField.frame.size.width, height: 44))
let flexButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.flexibleSpace, target: nil, action: nil)
let doneButton = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(textField.doneButtonTapped(button:)))
doneButton.tintColor = .systemPink
toolBar.items = [flexButton, doneButton]
toolBar.setItems([flexButton, doneButton], animated: true)
textField.inputAccessoryView = toolBar
}
}
extension UITextField {
#objc func doneButtonTapped(button:UIBarButtonItem) -> Void {
self.resignFirstResponder()
}
}
I want to add the Done button on the top of keyboard on right side in iOS for a textView.Please tell me how can i do that?
I want to achieve something similar to the above keyboard
Hope this help :)
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 action:#selector(yourTextViewDoneButtonPressed)];
keyboardToolbar.items = #[flexBarButton, doneBarButton];
self.yourTextView.inputAccessoryView = keyboardToolbar;
and then add yourTextViewDoneButtonPressed method
-(void)yourTextViewDoneButtonPressed
{
[self.yourTextView resignFirstResponder];
}
It can be done using storyboard:
Add UITextField to UIViewController view.
Add UIToolbar in the first level of UIViewController
Add UIBarButtonItem into the UIToolbar.
Connect UItoolbar to the code using IBOutlet.
Connect UIBarButtonItem to the code using IBAction (as didClick).
Make the UITextField will be delegating to UIViewController.
In the didClick function end editing (view.endEditing(true))
In the delegate function textFieldShouldBeginEditing should be: textField.inputAccessoryView = toolbar and returns true.
Swift 3:
func setDoneOnKeyboard() {
let keyboardToolbar = UIToolbar()
keyboardToolbar.sizeToFit()
let flexBarButton = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let doneBarButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(InsertStatusVC.dismissKeyboard))
keyboardToolbar.items = [flexBarButton, doneBarButton]
self.fullNameTextField.inputAccessoryView = keyboardToolbar
}
#objc func dismissKeyboard() {
view.endEditing(true)
}
This Solution For Swift3+
Add This line To your Project As a extension
Your Problem is solved.
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()
}
}
Before extension
After extension
Solution for Swift 4 and Swift 5 using a custom class. You can use this class in your Storyboard and .xib files.
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()
}
}
This Solution For Swift 4
override func viewDidLoad() {
super.viewDidLoad()
//init toolbar
let toolbar:UIToolbar = UIToolbar(frame: CGRect(x: 0, y: 0, width: self.view.frame.size.width, height: 30))
//create left side empty space so that done button set on right side
let flexSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
let doneBtn: UIBarButtonItem = UIBarButtonItem(title: “Done”, style: .done, target: self, action: Selector(“doneButtonAction”))
toolbar.setItems([flexSpace, doneBtn], animated: false)
toolbar.sizeToFit()
//setting toolbar as inputAccessoryView
self.textField1.inputAccessoryView = toolbar
self.textField2.inputAccessoryView = toolbar
}
func doneButtonAction() {
self.view.endEditing(true)
}
Add UIToolBar as custom view that will have UIBarButtonItem as Done button in it.
This is safer and cleaner way to add Done button to any Type of Keyboard. Create UIToolBar add Done Button to it and set inputAccessoryView of any UITextField or UITextView.
];
}
SWIFT 3
var ViewForDoneButtonOnKeyboard = UIToolbar()
ViewForDoneButtonOnKeyboard.sizeToFit()
var btnDoneOnKeyboard = UIBarButtonItem(title: "Done", style: .bordered, target: self, action: #selector(self.doneBtnFromKeyboardClicked))
ViewForDoneButtonOnKeyboard.items = [btnDoneOnKeyboard]
myTextField.inputAccessoryView = ViewForDoneButtonOnKeyboard
Function
#IBAction func doneBtnfromKeyboardClicked (sender: Any) {
print("Done Button Clicked.")
//Hide Keyboard by endEditing or Anything you want.
self.view.endEditing(true)
}
The answer from Ramis that allows the keyboard accessory view to be created using the Storyboard is a great strategy. It is 2017 after all! (Sorry, but I do not have enough points to upvote you.)
Let me add a little to the answer (to keep within the SO rules). I have a view with multiple textFields. After implementing each of Ramis' steps I attached the accessoryView to all of the textFields in this way:
-(void)textFieldDidBeginEditing:(UITextField *)textField
{
_activeTextField = textField;
[_activeTextField setInputAccessoryView:_iboKeyboardTools];
}
So easy compared with implementing all this in code! Just build the accessory view in Storyboard- and attach it again and again!
In my app when user clicks on UITextField he should be able to pick up a value from UIPickerView OR enter his own value using keyboard.
What's the best way of doing it in terms of user experience and ease of implementation?
UIPickerView with toolbar is already implemented.
I'd appreciate both advice on best way of doing it and example code of switching keyboard <-> pickerview.
I've tried adding a button "Show keyboard" on pickerview's toolbar and adding the following code:
func showKeyboard() {
selectedTextField.inputView = nil
}
But clicking this button doesn't do anything. Also I'm not sure it's a good way in terms of UX.
Here's the solution:
var useKeyboard:Bool = true
func showKeyboard() {
if useKeyboard {
useKeyboard = false
selectedTextField.inputView = nil
selectedTextField.reloadInputViews()
selectedTextField.keyboardAppearance = UIKeyboardAppearance.Default
selectedTextField.keyboardType = UIKeyboardType.Default
} else {
useKeyboard = true
selectedTextField.inputView = nil
selectedTextField.reloadInputViews()
createPicker(selectedTextField)
selectedTextField.resignFirstResponder()
selectedTextField.becomeFirstResponder()
}
}
// That's my custom picker - adjust whatever you need
func createPicker(sender: UITextField){
selectedTextField = sender
// Create picker view
var newPickerView: UIPickerView
newPickerView = UIPickerView(frame: CGRectMake(0, 200, view.frame.width, 300))
newPickerView.backgroundColor = .whiteColor()
// Only for UIPickerView
newPickerView.showsSelectionIndicator = true
newPickerView.delegate = self
newPickerView.dataSource = self
// Create toolbar
var toolBar = UIToolbar()
toolBar.barStyle = UIBarStyle.Default
toolBar.translucent = true
toolBar.tintColor = UIColor(red: 76/255, green: 217/255, blue: 100/255, alpha: 1)
toolBar.sizeToFit()
// Create buttons
var doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.Plain, target: self, action: "donePicker")
var spaceButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FlexibleSpace, target: nil, action: nil)
var cancelButton = UIBarButtonItem(title: "Cancel", style: UIBarButtonItemStyle.Plain, target: self, action: "cancelPicker")
var customButton = UIBarButtonItem(title: "Keyboard", style: UIBarButtonItemStyle.Plain, target: self, action: "showKeyboard")
// Assign buttons to toolbar
toolBar.setItems([cancelButton, spaceButton, customButton, doneButton], animated: false)
toolBar.userInteractionEnabled = true
// Add pickerview and toolbar to textfield
sender.inputView = newPickerView
sender.inputAccessoryView = toolBar
}
func donePicker() {
useKeyboard = true
selectedTextField.resignFirstResponder()
}
func cancelPicker() {
useKeyboard = true
selectedTextField.resignFirstResponder()
}