Fairly new to iOS development so forgive me for asking something that might be quite obvious. As you all know the UITextField's keyboard with keyboardType set to .NumberPad looks like the following...
.NumberPad keyboard
What I would like to do is replace the empty space in the lower left corner with a minus sign. Is this possible or does one need to write an entire custom keyboard to achieve this?
Would really appreciate the help.
Add a toolbar to your textfield inputAccessoryView and when the textfield will become the responder then the keyboard will show the toolbar (Swift 3.0):
func addToolBar(){
let toolbar = UIToolbar(frame: CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: 44))
let minusButton = UIBarButtonItem(title: "-", style: .plain, target: self, action: #selector(toggleMinus))
toolbar.items = [minusButton]
theTextField.inputAccessoryView = toolbar
}
func toggleMinus(){
// Get text from text field
if var text = theTextField.text , text.isEmpty == false{
// Toggle
if text.hasPrefix("-") {
text = text.replacingOccurrences(of: "-", with: "")
}
else
{
text = "-\(text)"
}
// Set text in text field
theTextField.text = text
}
}
hope it helps.
Swift 5.2
Set up the UIToolbar as described above and then use an extension on UITextField:
import UIKit
extension UITextField {
func toggleMinus() {
guard let text = self.text, !text.isEmpty else { return }
self.text = String(text.hasPrefix("-") ? text.dropFirst() : "-\(text)")
}
}
Usage:
#objc func toggleMinus() {
yourTextField.toggleMinus()
}
Related
The app I'm coding is an event-based app. On the screen where you would create a new event I have a UITextField with a UIDatePicker set as its input view initialized as:
lazy var eventDateTxt: UITextField = {
let tf = UITextField()
tf.attributedPlaceholder = NSAttributedString(string: "Pick Your Event Date",
attributes: [NSAttributedString.Key.foregroundColor: UIColor.zipVeryLightGray])
tf.font = .zipBody
tf.borderStyle = .roundedRect
tf.tintColor = .white
tf.backgroundColor = .zipGray
tf.textColor = .white
tf.adjustsFontSizeToFitWidth = true
tf.minimumFontSize = 10.0;
let datePicker = UIDatePicker()
datePicker.datePickerMode = .dateAndTime
datePicker.minimumDate = Date()
tf.inputView = datePicker
datePicker.addTarget(self, action: #selector(dateChanged), for: .valueChanged)
return tf
}()
dateChanged looks like this
#objc func dateChanged(sender: UIDatePicker){
let dateFormat = DateFormatter()
dateFormat.dateStyle = .long
dateFormat.timeStyle = .short
eventDateTxt.text = dateFormat.string(from: sender.date)
event.startTime = sender.date
}
and although it doesn't matter, here is my code for the UITextFieldDelegate
extension NewEventViewController: UITextFieldDelegate {
func textFieldDidBeginEditing(_ textField: UITextField) {
if textField == titleText {
if textField.text == "Event Title" {
textField.text = ""
}
}
}
func textFieldDidEndEditing(_ textField: UITextField) {
if textField == titleText {
if textField.text == "" {
textField.text = "Event Title"
}
}
}
}
Now the problem: When I click the UITextField to open the DatePicker, it pops up as a small pop up view on the bottom of the screen and looks like this:
https://ibb.co/WBJ9GsN
note the very bottom of the image.
Now if you click the date at the bottom there it opens the DatePicker as expected and looks like this:
https://ibb.co/N7VvfX6
when doing so an error comes up in the consol
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x600002d79ea0 'UIView-bottom-readableContentGuide-constraint' UILayoutGuide:0x60000379d340'UIViewLayoutMarginsGuide'.bottom == UILayoutGuide:0x60000379d260'UIViewReadableContentGuide'.bottom (active)>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKitCore/UIView.h> may also be helpful.
2021-08-11 12:52:37.262846-0400 zip_official[93261:10730049] [DatePicker] UIDatePicker 0x7faa2732fe80 is being laid out below its minimum width of 280. This may not look like expected, especially with larger than normal font sizes.
Using the suggested breakpoint didn't lead me anywhere.
So my question: How do I make it so the DatePicker pops up immediately without the little bottom tab.
This date picker thing is a lot of pain in the back. It changed twice for last two iOS versions. I had the same issue earlier and what I did was:
Make the date picker stick to the bottom (with some constant to place it a little above safe area). In your case, I would give it a frame.
let datePicker = UIDatePicker(frame: CGRect(x: eventDateTxt.frame.minX, y: eventDateTxt.frame.minY, width: view.frame.width, height: 150))
Some pseudo code (for iOS 14 and higher I guess):
let datePicker = UIDatePicker()
datePicker.preferredDatePickerStyle = .wheels
If your iOS version < 14, then maybe do it with frames.
For better UI output
if #available(iOS 14.0, *) {
datePicker.preferredDatePickerStyle = .inline
} else if #available(iOS 13.4, *) {
datePicker.preferredDatePickerStyle = .wheels
}
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'd like to update the UIKeyboardAppearance within a ViewController. By this I mean let's say the VC loads with UIKeyboardAppearance.default. If I press a button, I'd like the keyboard to update to .dark and have the keyboard now show in that same VC as .dark.
As far as I can tell, iOS checks the value for UIKeyboardAppearance while loading the VC, and doesn't check again until it loads the VC again. Even if you change the value of UIKeyboardAppearance and hide/show the keyboard.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// creating a simple text box, and making the placeholder text the value of the keyboardAppearance
myTextBox.backgroundColor = UIColor.lightGray
myTextBox.frame = CGRect(x: 30, y: 200, width: 300, height: 50)
view.addSubview(myTextBox)
UITextField.appearance().keyboardAppearance = .dark
myTextBox.becomeFirstResponder()
myTextBox.placeholder = "Keybaoard Appearance is: \(UITextField.appearance().keyboardAppearance.rawValue)"
// a simple button to toggle the keyboardAppearance
toggleButton.frame = CGRect(x: 30, y: 300, width: 300, height: 50)
toggleButton.setTitle("Toggle Keyboard", for: .normal)
toggleButton.backgroundColor = UIColor.red
toggleButton.addTarget(self, action: #selector(toggleButtonFunction), for: .touchUpInside)
view.addSubview(toggleButton)
}
// toggles the keyboardAppearance. Hides the keyboard, and a second later shows it again.
#objc func toggleButtonFunction() {
if UITextField.appearance().keyboardAppearance == .dark {
UITextField.appearance().keyboardAppearance = .default
}
else {
UITextField.appearance().keyboardAppearance = .dark
}
myTextBox.resignFirstResponder()
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1), execute: {
self.myTextBox.becomeFirstResponder()
self.myTextBox.placeholder = "Keybaoard Appearance is: \(UITextField.appearance().keyboardAppearance.rawValue)"
})
}
let myTextBox = UITextField()
let toggleButton = UIButton()
}
I was hoping that after changing the UIKeyboardAppearance and hiding/showing the keyboard it would show with the new appearance (.dark or .default), but it continually shows with the same appearance until the VC is loaded again. You can see the value of UIKeyboardAppearance changes, but iOS seems to not check for that update until the VC loads again.
Is there any way to force a recheck within a VC?
Thanks for your help!
You can change the keyboard appearance of all text fields recursively on your screen (the allSubviewsOf(type:) extension is from this great answer by Mohammad Sadiq):
func changeTextFieldKeyboardAppearance() {
UITextField.appearance().keyboardAppearance = .dark
let textFields = view.allSubviewsOf(type: UITextField.self)
let firstResponder = textFields.first { $0.isFirstResponder }
firstResponder?.resignFirstResponder()
textFields.forEach { $0.keyboardAppearance = .dark }
firstResponder?.becomeFirstResponder()
}
[...]
extension UIView {
func allSubviewsOf<T: UIView>(type: T.Type) -> [T] {
var all = [T]()
func getSubview(view: UIView) {
if let aView = view as? T {
all.append(aView)
}
guard !view.subviews.isEmpty else {
return
}
view.subviews.forEach{ getSubview(view: $0) }
}
getSubview(view: self)
return all
}
}
If your view controller is embedded in a UITabBarController, you can trigger an update by changing its selectedIndex and changing it back to the original index immediately:
guard let tabBarController = tabBarController else {
return
}
let selectedIndex = tabBarController.selectedIndex
UITextField.appearance().keyboardAppearance = .dark
tabBarController.selectedIndex = selectedIndex == 1 ? 0 : 1
tabBarController.selectedIndex = selectedIndex
Thanks to Tamás for the answer!
It led me down the path to discover what I needed.
It looks like if you change the keyboardAppearance for UITextField
UITextField.appearance().keyboardAppearance = .dark
the system only checks on VC load. If you change it for each textField
myTextBox.keyboardAppearance = .dark
the system will check each time firstResponder changes and load the correct keyboard.
Thanks again Tamás!
Fairly new to iOS development so forgive me for asking something that might be quite obvious. As you all know the UITextField's keyboard with keyboardType set to .NumberPad looks like the following...
.NumberPad keyboard
What I would like to do is replace the empty space in the lower left corner with a minus sign. Is this possible or does one need to write an entire custom keyboard to achieve this?
Would really appreciate the help.
Add a toolbar to your textfield inputAccessoryView and when the textfield will become the responder then the keyboard will show the toolbar (Swift 3.0):
func addToolBar(){
let toolbar = UIToolbar(frame: CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: 44))
let minusButton = UIBarButtonItem(title: "-", style: .plain, target: self, action: #selector(toggleMinus))
toolbar.items = [minusButton]
theTextField.inputAccessoryView = toolbar
}
func toggleMinus(){
// Get text from text field
if var text = theTextField.text , text.isEmpty == false{
// Toggle
if text.hasPrefix("-") {
text = text.replacingOccurrences(of: "-", with: "")
}
else
{
text = "-\(text)"
}
// Set text in text field
theTextField.text = text
}
}
hope it helps.
Swift 5.2
Set up the UIToolbar as described above and then use an extension on UITextField:
import UIKit
extension UITextField {
func toggleMinus() {
guard let text = self.text, !text.isEmpty else { return }
self.text = String(text.hasPrefix("-") ? text.dropFirst() : "-\(text)")
}
}
Usage:
#objc func toggleMinus() {
yourTextField.toggleMinus()
}
I am trying to dismiss keyboard in text view using accessory views with done button but keyboard is not showing done button in it.Actually i have writen my code in textViewDidBeginEditing using inputAccessoryView.Here is my code.
func textViewDidBeginEditing(textView: UITextView) {
currentTextView = textView //This is to tell the current position in text view
var indexPath:NSIndexPath = NSIndexPath(forRow: 0, inSection: 2)
let cell:EventTableTableViewCell = EventTableview.cellForRowAtIndexPath(indexPath) as! EventTableTableViewCell
cell.messageTextView.autocorrectionType = UITextAutocorrectionType.No
let keyboardDoneButton = UIToolbar()
keyboardDoneButton.sizeToFit()
let item = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.Plain, target: self, action: Selector("doneButton"))
var toolbarButtons = [item]
//Put the buttons into the ToolBar and display the tool bar
keyboardDoneButton.setItems(toolbarButtons, animated: false)
keyboardDoneButton.userInteractionEnabled = true
//cell.messageTextView.inputAccessoryView = keyboardDoneButton
textView.inputAccessoryView = keyboardDoneButton
}
}
func doneButton()
{
UIApplication.sharedApplication().sendAction("resignFirstResponder", to:nil, from:nil, forEvent:nil)
}
_textView.returnKeyType = UIReturnKeyType.Done
Did you set the keyboard in IB?
Storyboard > Textfield > Attributes Inspector > Return Key > Done
Implement the delegate method:
- textfieldShouldReturn:
Found here: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UITextFieldDelegate_Protocol/#//apple_ref/occ/intfm/UITextFieldDelegate/textFieldShouldReturn: