There was many guys who meet problem with absence of Done button in numeric keyboard for iOS and there are many solutions to do it. I create this button but I cant find way to set focus on next text field by hit on this button. In my case there code that generates Table with TableViewCells. Thats why common idea to create reference to every Cell (anyway there are many cells). Every TableViewCell include ContentView and ContentView include TextField.
As result my custom Done button should behave like Return button in standard keyboard. By the way there method in textFieldShouldReturn that change focus but textFieldShouldReturn invoked on built-in Return button.
P.S. Im new in Swift.
Note: I have created a sample project where the next button functionality is implemented. You can find the project: https://github.com/nasirky/uitableview-form
You can add a toolbar with a done button to the UITextField inputAccessoryView. Here is an example
override func viewDidLoad() {
super.viewDidLoad()
textField.inputAccessoryView = getToolbar()
}
func getToolbar() -> UIToolbar {
let toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 40))
let doneButton = UIBarButtonItem(title: "Done", style: .plain, target: self, action:#selector(doneAction(_:)))
toolBar.setItems([doneButton], animated: false)
return toolBar
}
func doneAction(_ sender: UIBarButtonItem) {
/* Code for dismissing keyboard or moving focus to next UITextField */
}
After I carefully rechecked existing jump function I put it into textFieldDidEndEditing event. Inside Done function (doneAction in Nasir`s example) I put self.endEditing(true)
Related
I have a page where there is a UITextField that I add programmatically, I set the text type to number and add constraints and all that. Then I add a done button to the accessoryView of the text field and add a function to run when that done button is pressed. My problem is, when the page loads, I want the text field to be focused and the keyboard shown. I set the becomeFirstResponder on it, but when the page loads, the keyboard shows up for a split second then immediately disappears and the delegate methods are run.
I need to find a way to make the text field "active", "focused", whatever you want to call it when the page loads, and for the keyboard to be there and ready. I can't seem to find any help aside from call becomeFirstResponder on it, which only works for a split second.
Here is the code I am using to build the page and run everything, I simplified it to reduce clutter and read times, but if you need more info, please let me know and I will be happy to provide the full code...
class AgeViewController: UIViewController {
var selectedAge: Int = 0
var textInput: UITextField!
let settings = UserDefaults.standard
override func viewDidLoad() {
super.viewDidLoad()
createPage()
}
override func viewWillAppear(_ animated: Bool) {
textInput.becomeFirstResponder()
//I have tried this in both viewWillAppear and viewDidAppear
}
func createPage() {
textInput = UITextField()
textInput.font = .systemFont(ofSize: 50)
textInput.placeholder = "35"
textInput.borderStyle = .none
textInput.keyboardType = .numberPad
textInput.returnKey = .done
textInput.textAlignment = .right
addDoneButton()
textInput.delegate = self
view.addSubView(textInput)
//create a label and add it to the page
}
private fun addDoneButton() {
let doneToolbar: UIToolbar = 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 = UIBarButtonItem(title: "Done", style: .done, target: self, action: #selector(doneTapped))
let items = [flexSpace, done]
doneToolbar.sizeToFit()
textInput.inputAccessoryView = doneToolbar
}
#objc func doneTapped() {
textInput.resignFirstResponder()
}
}
extension AgeViewController: UITextFieldDelegate {
func textFieldDidBeginEditing(_ textField: UITextField) {
textField.textColor = UIColor(named: "text")!
}
func textFieldDidEndEditing(_ textField: UITextField, reason: UITextField.DidEndEditingReason) {
if textField.text != nil {
selectedAge = Int(textField.text!) ?? 35
settings.set(selectedAge, forKey: Strings.age)
} else {
textField.textColor = UIColor(named: "grayText")!
}
}
Like I said, the page loads, the keyboard shows up for a split second, then goes away and the delegate methods are called for didEndEditing. I don't understand why it isn't staying focused, I am calling becomeFirstResponder. I have tried calling textInput.becomeFirstResponder() in 3 different places, all with the same result. The first was right after I add the subview to the view, then I tried in viewDidAppear and finally in viewWillAppear, all have the same result, shows up for a split second, then goes away. Sorry for the long post, thank you for any help, I really appreciate it.
I have a viewController EditMessage which has two UITextFields (UITextView) which use the keyboard and they work great. This part is basic standard stuff. When the keyboard is displayed, I register a tag gesture for the entire view, so that if the user clicks anywhere else, I dismiss the keyboard:
self.view.addGestureRecognizer(UITapGestureRecognizer(target: self,
action: #selector(dismissKeyboard)))
In dismissKeyboard, this all works fine:
#objc func dismissKeyboard(sender: Any) {
self.view.endEditing(true)
}
However, I have a menu button(thumbnail image) implemented as a child view controller (UIViewController) on the same EditMessage view, which hijacks the screen via UIApplication.shared.keyWindow() to display an overlay and menu on the bottom of the screen. Built using the model/code from Brian Voong's YouTube channel to replicate a YouTube style slide in menu from the bottom. However, the keyboard is in the way. Since the child is a different view controller "endEditing" doesn't work (or maybe I am referencing the wrong view?).
class ButtonPickerController : UIViewController,
UIGestureRecognizerDelegate, UINavigationControllerDelegate {
var maxSize = CGFloat(60)
let thumbnail: UIImageView = {
let thumbnail = UIImageView()
thumbnail.backgroundColor = UIColor.lightGray
return thumbnail
}()
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(self.buttonTapped(sender:)))
tap.delegate = self
view.addGestureRecognizer(tap)
//view.backgroundColor = .yellow
view.contentMode = .scaleAspectFit
thumbnail.frame = CGRect(x: 0, y: 0, width: self.maxSize, height: self.maxSize)
setupSubviews()
}
Can someone point me in a good direction? This is my first question so hopefully I am asking properly.
I figured it out in the end. Thank you for the help. In my child view controller I did used the following statement when the button was tapped:
#objc func buttonTapped(sender: UITapGestureRecognizer? = nil) {
self.parent!.view.endEditing(true)
}
First, its not a good way to present overlays as UIViewController.
But a solution good be, to give the second viewcontroller a reference to the first one before viewDidLoad is called. Do you use Segues ? So in prepare would be the right place. In the second viewcontroller you create a property for the first one and then use this property as target when you create the UITapGestureRecognizer.
Another way is using a protocol and delegation.
This question already has answers here:
on single tap UIBarButton display toast and on double tap back action need to perform
(3 answers)
Closed 4 years ago.
I'm trying to implement a custom back button for every ViewController in my app. I want it to have two actions. If the button gets single tapped, it should give toast warning. If the button gets double tapped it should go back to ViewController.
How can I achieve this for only the backbutton in swift?
Try this to Work with bar Button with single and Double Tap
/// Tap Gesture to Handle Double Tap
let tapGesture : UITapGestureRecognizer = UITapGestureRecognizer.init(target: self, action: #selector(SourceVC.handleGesture(getsure:)))
tapGesture.numberOfTapsRequired = 2
/// Custom Button That With Handle two actions
let customButtonn : UIButton = UIButton(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
customButtonn.setTitleColor(UIColor.blue, for: .normal)
customButtonn.setTitle("button", for: .normal)
customButtonn.addTarget(self, action: #selector(SourceVC.navButton(sender:)), for: .touchUpInside)
customButtonn.addGestureRecognizer(tapGesture)
/// Create a Custom bar Button
let navLeftButton = UIBarButtonItem()
navLeftButton.customView = customButtonn
/// Assign it to navigation Bar
self.navigationItem.leftBarButtonItem = navLeftButton
My custom Button Actions in SourceVC Class
/// Single Tap
#objc func navButton(sender:UIButton){
print("navButtonTapped Once")
}
/// Double Tap
#objc func handleGesture(getsure:UITapGestureRecognizer){
print("navButtonTapped twice")
}
Simulator Output:
Console output:
Note - Doing this will move default back occur with the bar and you need to design it custom way so it appear like that as Shown in Simulator output - button appear without that back image
You cannot modify the actions and behavior for BackBarButtonItem, what you can do is create a your own UIBarButtonObject and assign it as your leftBarButtonItem while giving it your own selector
One thing to note is that this will remove the stock back button image.
Try this:
let doubleTap = UITapGestureRecognizer(target: self, action: #selector(actingFunction))
DoubleTap.numberOfTapsRequired = 2
barButton.addGestureRecognizer(doubleTap)
My tricky problem is :
I've 2 buttons with 2 UITextView.
Like you know, when I push an UITextView, keyboard appear. Well, this is not what I want.
I want to enable / disable the display's keyboard according a specific #IBAction taped.
The scenario is the following:
- First button (Keyboard icon) allow the user to display the keyboard to type something in one of the UITextView, with a FirstResponder init on the top one.
Second button (REC icon) allow the user to speak and display in pre-selected TextView but without displaying keyboard.
I already known that there is :
isUserInteractionEnabled
and
textViewDidBeginEditing
But it doesn't really fit well and/or fix my issue.
Here a screen to be more explicit (don't give a mind about the third green validate button, it's just for the .POST feature) :
Thanks for any help!
If I understand your problem correctly, You don't want the keyboard to appear when user taps on the textField but rather should come up only when user taps on Keyboard button and should dismiss on tapping other button.
All the posted answers mostly focus only on second part of showing keyboard and dismissing them on tapping button. Whats more tricky is preventing keyboard from appearing in when user taps on textField :)
Possible solutions you can try and their cons:
Solution 1:
Try setting textfield isEnabled = false sure this will prevent keyboard from appearing when user taps on textField but guess what Keyboard will not appear even on calling textfield.becomeFirstResponder() ahhh trouble :)
Solution 2:
implementing textFieldShouldBeginEditing of UITextField delegate and returing true or false based on whether used tapped on button or textField itself.
Sure it works but you will need to figure out way to tell textFieldShouldBeginEditing why was it triggered because of button or because of touch on textField again complications.
My Solution:
Use 2 textFields. One disable user interaction forever and use another textField which will never appear to user but will take care of showing keyboard when required.
Enough talk lets code :)
Step 1:
Lets say your textField which appears on screen is called textField
textfield.isEnabled = false
This will ensure whatever you do keyboard will not appear for this keyboard.
Step 2:
Create a temp textField of frame zero (which user can never tap :P )
tempTextField = UITextField(frame: CGRect.zero)
tempTextField.delegate = self
self.view.addSubview(tempTextField)
Step 3:
Now when user taps on show keyboard button, make your temp textField first responder
tempTextField.becomeFirstResponder()
and when user taps on other button resignFirstResponder for tempTextField.
tempTextField.resignFirstResponder()
Step 4:
But wait when user types nothing appears on my textField. Wait simply implement
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if textField == tempTextField {
self.textfield.text = (self.textfield.text ?? "") + string
}
return true
}
EDIT:
You don't really need two textFields, you can achieve the same with a UILabel and UITextField as well. I chose UITextField as I am not sure what are other requirements of yours!
Problem fixed. Hope it helps :)
Something like this should work:
class MyViewController: UIViewController, UITextViewDelegate {
#IBOutlet var yourTextView: UITextView
override func viewDidLoad() {
super.viewDidLoad()
yourTextView.delegate = self
}
#IBAction func showKeyboard(_ sender: UIButton) {
self.yourTextView.becomeFirstResponder()
}
#IBAction func hideKeyboard(_ sender: UIButton) {
self.yourTextView.resignFirstResponder()
}
}
You say
But it's not really working in my case.
Why?
You can disable the textfields interaction with (swift 3):
textField.isUserInteractionEnabled = false
Next, when you click on the keyboard button you can reenable the interaction so when the user click on one of the textfield the keyboard opens.
On the keyboard dismiss (you can see the UIKeyboardWillHideNotification callback notification) you can set the interaction to false.
function to dismiss KeyBoard when button is clicked:
#IBAction func hideKeyboard() {
textView.resignFirstResponder()
}
function to get keyboard to show up:
#IBAction func showKeyboard() {
textView.becomeFirstResponder()
}
Just copy and paste simple code for you accessory button embedded with keypad
func addKeyboardToolbar() {
let ViewForDoneButtonOnKeyboard = UIToolbar()
ViewForDoneButtonOnKeyboard.sizeToFit()
let button = UIButton.init(type: .custom)
button.setImage(UIImage.init(named: "login-logo"), for: UIControlState.normal)
button.addTarget(self, action:#selector(doneBtnfromKeyboardClicked), for:.touchUpInside)
button.frame = CGRect.init(x: 0, y: 0, width:UIScreen.main.bounds.width, height: 30) //CGRectMake(0, 0, 30, 30)
let barButton = UIBarButtonItem.init(customView: button)
ViewForDoneButtonOnKeyboard.items = [barButton]
postTextView.inputAccessoryView = ViewForDoneButtonOnKeyboard
}
#IBAction func doneBtnfromKeyboardClicked (sender: Any) {
self.contentView.endEditing(true)
}
I am using SWRevealViewController for side menu and IQKeyboardManagerSwift for keyboard.
When I am editing content in textfield and tries to open menu, the keyboard should automatically hide but I can't manage to do so.
How should this be done?
Capture the event of the opening menu. You can do this with SWRevealViewcontroller Delegate or by simply adding an #IBAction to the menu button.
In this method call .resignFirstResponder() for the element that needs the keyboard (like a textField):
textField.resignFirstResponder()
Of course you can call this function in every element that can have a keyboard to be sure to call the right one.
Since UIBarButtonItem doesn’t inherit from UIView or expose the item’s underlying view, this isn’t as easy as adding a gesture recognizer.
One plausible solution would be to [Step 1] define a custom view for the Side Menu Icon [Step 2]add the gesture to hide keyboard to it.
//gesture:tap anywehere to dismiss the keyboard
let tap = UITapGestureRecognizer(target:self.view,action:#selector(UIView.endEditing))
tap.cancelsTouchesInView = false
view.addGestureRecognizer(tap)
let customButton = UIButton(frame: CGRect.init(x: 0, y: 0, width: 20, height: 20))
customButton.setImage(UIImage(named: "menu"), for: .normal)
//hide keyboard gesture(tap gesture)
customButton.addGestureRecognizer(tap)
customButton.isUserInteractionEnabled = true
if self.revealViewController() != nil {
customButton.addTarget(self.revealViewController(), action: #selector(SWRevealViewController.revealToggle(_:)), for: .touchUpInside)
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
self.navigationItem.leftBarButtonItem?.customView = customButton
Please accept as answer if it works.(Worked for me)