Below my question is the code i'm using in xCode 10 to place a "Done" button on the pop up keyboard.
I have a second ViewController but don't know how to use this code on this second one. The errors displayed in my second ViewController are:
Invalid redeclaration of 'doneAccessory'
Invalid redeclaration of 'addDoneButtonOnKeyboard()'
Invalid redeclaration of 'doneButtonAction()'
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()
}
}
Please add this UITexfield extension to your helper class. Its good practice having all your extensions in the helper class.
You need not set anything about your second view controller. You need to open your storyboard and select the text field where you want the done button. Find the image to set the done button then you will not face any issue.
Related
I want to set share button on rightbarbuttonitem of navigation controller.
I don't want to add custom images , I want use share button image provided by Xcode.This is how I do in storyboard.
I set Style , and set System_Item as Action.
Now the question is how do I set System_Item programatically, if I create barbuttonitem programatically ?
let shareButton = UIBarButtonItem(title: "",
style: .plain,
target: self,
action: #selector(shareAction(sender:)))
shareButton.tintColor = AppColor.barButtonColor
navigationItem.rightBarButtonItem = shareButton
you need to use UIBarButtonSystemItemAction option to get share action directly
let share = UIBarButtonItem(barButtonSystemItem: .action, target: self, action: #selector(shareAction(sender:)))
shareButton.tintColor = AppColor.barButtonColor
navigationItem.rightBarButtonItem = share
and handle the action as like
#objc func shareAction(sender: UIBarButtonItem) {
}
You can create an extension to do this ->
public extension UIViewController {
func setRightBarButtonItem(tintColor: UIColor = AppColor.barButtonColor) {
let button = UIButton(type: .system)
button.tintColor = tintColor
button.setImage(UIImage(.action), for: .normal) // <-- Set system icon to button
button.translatesAutoresizingMaskIntoConstraints = false
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tap))
button.addGestureRecognizer(tapGesture)
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: button)
}
#objc func tap() {
// Do stuff
}
}
Then in your ViewController
override func viewDidLoad() {
super.viewDidLoad()
setRightBarButtonItem()
// OR
setRightBarButtonItem(tintColor: .blue)
}
UIImage extension to use system icons without version control
extension UIImage {
public convenience init?(_ systemItem: UIBarButtonItem.SystemItem) {
guard let sysImage = UIImage.imageFrom(systemItem: systemItem)?.cgImage else {
return nil
}
self.init(cgImage: sysImage)
}
private class func imageFrom(systemItem: UIBarButtonItem.SystemItem) -> UIImage? {
let sysBarButtonItem = UIBarButtonItem(barButtonSystemItem: systemItem, target: nil, action: nil)
let toolBar = UIToolbar()
toolBar.setItems([sysBarButtonItem], animated: false)
toolBar.snapshotView(afterScreenUpdates: true)
if let buttonView = sysBarButtonItem.value(forKey: "view") as? UIView{
for subView in buttonView.subviews where subView is UIButton {
let button = subView as! UIButton
let image = button.imageView!.image!
return image
}
}
return nil
}
}
I'm using two textField which is of numPad keyboard type. I have added Next button for the first textField and Done button for the second textField in the numPad keyboard. Using the following code.
import UIKit
class SecurityPinSettingsVC: UIViewController, UITextFieldDelegate
{
#IBOutlet weak var textField1: UITextField!
#IBOutlet weak var textField2: UITextField!
override func viewDidLoad()
{
super.viewDidLoad()
self.textField1.delegate = self
self.textField2.delegate = self
self.textField1.textAlignment = NSTextAlignment.center
self.textField2.textAlignment = NSTextAlignment.center
addNextButtonOnKeyboard()
addDoneButtonOnKeyboard()
}
func addNextButtonOnKeyboard()
{
let nextToolbar: UIToolbar = UIToolbar(frame: CGRect(x: 0,y: 0, width: 320, height: 50))
nextToolbar.barStyle = UIBarStyle.default
let flexSpace = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil)
let next: UIBarButtonItem = UIBarButtonItem(title: "Next", style: UIBarButtonItemStyle.done, target: self, action: #selector(ViewController.doneButtonAction))
var items = [UIBarButtonItem]()
items.append(flexSpace)
items.append(next)
nextToolbar.items = items
nextToolbar.sizeToFit()
self.textField1.inputAccessoryView = nextToolbar
}
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 ddone: UIBarButtonItem = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.done, target: self, action: #selector(ViewController.doneButtonAction))
var items = [UIBarButtonItem]()
items.append(flexSpace)
items.append(ddone)
doneToolbar.items = items
doneToolbar.sizeToFit()
self.textField2.inputAccessoryView = doneToolbar
}
func doneButtonAction()
{
//self.textField2.becomeFirstResponder()
//self.textField2.resignFirstResponder()
}
}
Both buttons are successfully added. The problem is, I could not create separate action methods for Next button and Done button. It accepts only doneButtonAction method for both buttons, which I have created and used in previous viewControllers. So, How can I get the textField id in the doneButtonAction method, in-order to know which button is clicked. So that, by clicking on Next button I want to make textField2 to become first responder, and by clicking Done button I want textField2 to resign first responder.
Like in ordinary textField, textFieldShouldReturn method will do this job easily, where the textField object will be passed to that function as an argument.
Here, how can we pass the textField object to the doneButtonAction method.
I'm using Xcode 8.2, Swift 3.0
Thanks in Advance.
you can get text field which are become responder
if textField1.isFirstResponder {
// textfield 1 is becomeResponder
}
How to add button above the keyboard like this one in Stack Exchange app? And when you long press the text in UITextView How to add "Select" and "Select All"?
The first question, you can set textField's inputAccessoryView to your custom view, this can customize the keyboard's header.
The result:
You can do it like below;
first, you should instance the view you want to add above the keyboard.
class ViewController : UIViewController {
#IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
textField.inputAccessoryView = Bundle.main.loadNibNamed("CustomAccessoryView", owner: self, options: nil)?.first as! UIView?
In your CustomAccessoryView, you can set the action of the button:
import UIKit
class CustomAccessoryView: UIView {
#IBAction func clickLoveButton(_ sender: UIButton) {
print("Love button clicked")
}
}
I would recommend to create a toolbar for your UITextField's accessoryView property.
The idea is to add this toolbar once, before the textfield would show for the first time. Therefore, we assign the delegate to self, and override the textFieldShouldBeginEditing delegate call with our implementation to add the accessoryView.
Here is a simple example, how can u achieve it:
import UIKit
class ViewController: UIViewController {
// your `UITextfield` instance
// Don't forget to attach it from the IB or create it programmaticly
#IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Assign the delegate to self
textField.delegate = self
}
}
// MARK: Create extension to conform to UITextfieldDelegate
extension ViewController: UITextFieldDelegate {
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
setupTextFieldsAccessoryView()
return true
}
func setupTextFieldsAccessoryView() {
guard textField.inputAccessoryView == nil else {
print("textfields accessory view already set up")
return
}
// Create toolBar
let toolBar: UIToolbar = UIToolbar(frame:CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: 44))
toolBar.barStyle = UIBarStyle.black
toolBar.isTranslucent = false
// Add buttons as `UIBarButtonItem` to toolbar
// First add some space to the left hand side, so your button is not on the edge of the screen
let flexsibleSpace: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.flexibleSpace, target: nil, action: nil) // flexible space to add left end side
// Create your first visible button
let doneButton: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.done, target: self, action: #selector(didPressDoneButton))
// Note, that we declared the `didPressDoneButton` to be called, when Done button has been pressed
toolBar.items = [flexsibleSpace, doneButton]
// Assing toolbar as inputAccessoryView
textField.inputAccessoryView = toolBar
}
func didPressDoneButton(button: UIButton) {
// Button has been pressed
// Process the containment of the textfield or whatever
// Hide keyboard
textField.resignFirstResponder()
}
}
This should be your output:
You'll have to use the inputAccessoryView of your textfield.
you can put the code snippet below in your viewDidLoad():
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: 60))
button.backgroundColor = UIColor.blue
button.setTitle("NEXT", for: .normal)
button.setTitleColor(UIColor.white, for: .normal)
button.addTarget(self, action: #selector(self. yourButton), for: .touchUpInside)
numtextField.inputAccessoryView = button
}
#objc func nextButton()
{
print("do something")
}
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
}
func doneBtnfromKeyboardClicked (){
self.contentView.endEditing(true)
}
to add a toolbar with a done button which dismisses the keyboard above a UITextField you can write a UITextField extension with the following function:
public func addAccessoryView() {
let doneButton = UIBarButtonItem.init(barButtonSystemItem: UIBarButtonSystemItem.Done, target: self, action: "resignFirstResponder")
let flexSpace: UIBarButtonItem = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FlexibleSpace, target: self, action: nil)
let toolbar = UIToolbar()
toolbar.barStyle = UIBarStyle.Default
toolbar.translucent = true
toolbar.tintColor = Color.blue
toolbar.sizeToFit()
toolbar.setItems([flexSpace, doneButton], animated: false)
toolbar.userInteractionEnabled = true
self.inputAccessoryView = toolbar
}
you can then call the function in your textfield like this:
textfield.addAccessoryView()
So I am using a UIPickerView as the first responder of a UITextField. I set this as shows:
var myPickerView = UIPickerView()
myTextField.inputView = myPickerView
I was not having issues with this until I added a UITapGestureRecognizer (that would allow me to click outside of the picker view to dismiss it) to the view like so:
let dismissUnitPickerGesture = UITapGestureRecognizer(target: self, action: "hidePicker")
dismissUnitPickerGesture.delegate = self
dismissUnitPickerGesture.cancelsTouchesInView = false
hidePicker func:
#IBAction func hidePicker(){
myTextField.resignFirstResponder()
//I don't think this code is relevant to the problem, but I included it just in case
if materialSelectedOption == "Aluminum Cans" {
amountTextField.placeholder = "Number of Cans"
unitCell.hidden = true
}
}
Now it takes up to five clicks on the UITextField on the simulator to open the picker view, which is frustrating and obviously not good for an app. I'm pretty sure it has something to do with the tap gesture, but I could be wrong.
If there are any other questions you guys have please let me know.
adding a UITapGestureRecognizer to just detect that a tap is registered outside the pickerView and hide it is kind of wrong!
you can use the touchesBegan function and call self.view.endEditing(true) like so:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.view.endEditing(true)
//or use textField.endEditing(true)
}
I Suggest you to add tool bar with "Done" and "Clear" items instead gesture.
The following code you can use:
func textFieldDidBeginEditing(sender: UITextField) {{
let toolBar = UIToolbar(frame: CGRectMake(0, self.myTextField.frame.size.height/6, self.myTextField.frame.size.width, 40.0))
toolBar.layer.position = CGPoint(x: self.myTextField.frame.size.width/2, y: self.myTextField.frame.size.height-20.0)
toolBar.barStyle = UIBarStyle.BlackTranslucent
toolBar.tintColor = UIColor.whiteColor()
let defaultButton = UIBarButtonItem(title: "Clear", style: UIBarButtonItemStyle.Plain, target: self, action: #selector(CustomTimePickerTextView.clearTime(_:)))
let doneButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Done, target: self, action: #selector(CustomTimePickerTextView.donePressed(_:)))
let flexSpace = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FlexibleSpace, target: self, action: nil)
let textButton = UIBarButtonItem(title: self.placeholder, style: UIBarButtonItemStyle.Plain, target: nil, action: nil)
toolBar.setItems([defaultButton,flexSpace,textButton,flexSpace,doneButton], animated: true)
self.myTextField.inputAccessoryView = toolBar
}
func clearTime(sender:UIBarButtonItem) {
//
}
func donePressed(sender: UIBarButtonItem) {
self.myTextField.resignFirstResponder()
}
So I have an view controller with a form like so:
When I use the back and forward buttons on the input accessory view, everything works fine. However, in simulator when I use the tab key on the keyboard to traverse the text fields, and then segue back to the previous view controller, I get the following:
and then this in the console:
I look for zombie objects in Instruments and find this:
but that doesn't provide much help. Any idea why this would ONLY happen when tab is pressed on the keyboard in simulator? It isn't an issue on the device and I can't reproduce it there but I feel like a zombie object needs to be address either way.
and here is my code for that view controller:
in viewDidLoad i'm calling setupInputAccessoryView
func setupInputAccessoryView(){
// Create a button bar for the number pad
let keyboardDoneButtonView = UIToolbar()
keyboardDoneButtonView.sizeToFit()
// Setup the buttons to be put in the toolbar.
let item4 = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("endEditingNow") )
let item = UIBarButtonItem(title: " < ", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("previousField"))
let item2 = UIBarButtonItem(title: " >", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("nextField"))
let item3 = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FlexibleSpace, target: self, action: nil)
if let font = UIFont(name: "Helvetica", size: 17) {
item4.setTitleTextAttributes([NSFontAttributeName: font], forState: UIControlState.Normal)
}
if let font2 = UIFont(name: "Helvetica", size: 26){
item.setTitleTextAttributes([NSFontAttributeName: font2], forState: UIControlState.Normal)
item2.setTitleTextAttributes([NSFontAttributeName: font2], forState: UIControlState.Normal)
}
//Add buttons to toolbar and add as an inputAccessoryView
var toolbarButtons = [item, item2, item3, item4]
keyboardDoneButtonView.setItems(toolbarButtons, animated: false)
keyBoardToolBar = keyboardDoneButtonView
}
and then in textFieldDidBeginEditing I have:
func textFieldDidBeginEditing(textField: UITextField) {
currentTextField = textField
textField.inputAccessoryView = keyBoardToolBar
if (textField == stateField){
statePicker.frame = CGRectZero
statePicker.delegate = self
statePicker.dataSource = self
textField.inputView = statePicker
}else if (textField == countryField){
countryPicker.frame = CGRectZero
countryPicker.delegate = self
countryPicker.dataSource = self
textField.inputView = countryPicker
}
UIView.animateWithDuration(0.5, animations: { () -> Void in
self.scrollView.contentOffset.y = textField.center.y - 30
})
}
and then my selectors for the toolbar buttons
func endEditingNow(){
self.view.endEditing(true)
}
func nextField(){
var nextTag = currentTextField!.tag + 1
self.view.viewWithTag(nextTag)?.becomeFirstResponder()
}
func previousField(){
var nextTag = currentTextField!.tag - 1
self.view.viewWithTag(nextTag)?.becomeFirstResponder()
}