I'm having trouble with the caret of UITextfields.
Whenever I tap on the textfield the caret doesn't show, but otherwise the textfield is working properly. The problem is similar to the problem described here (already tried the solution written there as well).
I noticed that whenever I load a viewcontroller and tap on a textfield (or programatically make it the first responder) it does show a caret, but as soon as I tap another textfield or make a textfield resign its first responder status no textfield will show its caret anymore (unless I go back and reload the viewcontroller again).
Got no clue as of why this is happening. I am using a custom font throughout the app. But I'm not sure if that's what making the caret disappear, as I've used custom fonts before with no problem whatsoever.
EDIT:
I think I've at least found why the problem occurs. It is related to me overriding the becomeFirstResponder and resignFirstResponder.
My code looks like:
extension UITextField {
open override func becomeFirstResponder() -> Bool {
let willBecomeResponder = super.becomeFirstResponder()
if willBecomeResponder {
backgroundColor = .red
layer.borderColor = .blue
}
return willBecomeResponder
}
open override func resignFirstResponder() -> Bool {
let willResignResponder = super.resignFirstResponder()
if willResignResponder {
backgroundColor = .blue
layer.borderColor = .red
}
return willResignResponder
}
}
Overriding those methods in the extension makes the caret disappear.
I'm thinking this most likely happens because UITextField itself does some...'caret-management' in it's own implementation of those methods.
More so because even return super.becomeFirstResponder() and no custom code in the overridden method makes the caret disappear.
My question therefore is; how can one solve this problem without making a custom UITextField subclass?
Calling super obviously just calls the UIResponder's implementation, but the docs specifically mention:
becomeFirstResponder()
You can override this method in your custom responders to update your object's state or perform some action such as highlighting the selection. If you override this method, you must call super at some point in your implementation.
So I need to call super I guess.
Alright I solved my problem by overriding isFirstResponder and altering the textfield based on the super.isFirstResponder there.
Looks like:
open override var isFirstResponder: Bool {
get {
let responder = super.isFirstResponder
backgroundColor = (responder) ? .red : .blue
layer.borderColor = (responder) ? .blue : .red
return responder
}
}
I did noticed a lot of calls being sent to isFirstResponder, so maybe this isn't the most efficient way (if altering the textfield is a heavy op).
When I use default Security text Entry in UITextField in Swift Language after type type text once UITextField.
Once loss focus from UITextField after try to edit Secure text then UITextField is first reset and after it start put new text in UITextField.
How to edit old Secure Text without Storing data into Any Kind of String object
I'd suggest to create a custom UITextField class and override become​First​Responder() method do add your desired functionality:
You can override this method in your custom responders to update your
object's state or perform some action such as highlighting the
selection. If you override this method, you must call super at some
point in your implementation.
The custom Class should be similar to:
class CustomSecureTextField: UITextField {
override func becomeFirstResponder() -> Bool {
super.becomeFirstResponder()
if !isSecureTextEntry { return true }
if let currentText = text { insertText(currentText) }
return true
}
}
The logic of the implementation of becomeFirstResponder as follows:
By default, the secured-entry text field clears the inserted text when it is become first responder text, so what's happening in CustomSecureTextField that if the text field is secured-entry, it will re-insert the current inserted text -after clearing it-, but you have to make sure that the text field input is secured (that's the purpose of adding if !isSecureTextEntry { return true }) or the text will be duplicated (re-inserted) each time the text field becomes first responder.
Output:
Note that both of text fields are types of CustomSecureTextField:
This answer helped me to figure out this problem.
textField.isSecureTextEntry = true
following property not gonna work if you make testField isSecureTextEntrysecure property makes true .
textField.clearsOnBeginEditing = false
There is an issue with the #Ahmd F solution when you simply tap on the field it will automatically add the text to the field I have resolved that in the below code thanks
override open func becomeFirstResponder() -> Bool {
super.becomeFirstResponder()
if !isSecureTextEntry { return true}
if let currrentText = text {
self.text = ""
insertText(currrentText)
}
return true
}
I just started with UI testing in Xcode 7 and hit this problem:
I need to enter text into a textfield and then click a button. Unfortunately this button is hidden behind the keyboard which appeared while entering text into the textfield. Xcode is trying to scroll to make it visible but my view isn't scrollable so it fails.
My current solution is this:
let textField = app.textFields["placeholder"]
textField.tap()
textField.typeText("my text")
app.childrenMatchingType(.Window).elementBoundByIndex(0).tap() // hide keyboard
app.buttons["hidden button"].tap()
I can do this because my ViewController is intercepting touches:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
view.endEditing(false)
super.touchesBegan(touches, withEvent: event)
}
I am not really happy about my solution, is there any other way how to hide the keyboard during UI testing?
If you have set up your text fields to resign FirstResponder (either via textField.resignFirstResponder() or self.view.endEditing(true)) in the textFieldShouldReturn() delegate method, then
textField.typeText("\n")
will do it.
Swift 5 helper function
func dismissKeyboardIfPresent() {
if app.keyboards.element(boundBy: 0).exists {
if UIDevice.current.userInterfaceIdiom == .pad {
app.keyboards.buttons["Hide keyboard"].tap()
} else {
app.toolbars.buttons["Done"].tap()
}
}
}
Based on a question to Joe's blog, I have an issue in which after a few runs on simulator the keyboards fails to hide using this piece of code:
XCUIApplication().keyboard.buttons["Hide keyboard"]
So, I changed it to: (thanks Joe)
XCUIApplication().keyboard.buttons["Hide keyboard"]
let firstKey = XCUIApplication().keys.elementBoundByIndex(0)
if firstKey.exists {
app.typeText("\n")
}
What I try to do here is detecting if the keyboard stills open after tap the hide button, if it is up, I type a "\n", which in my case closes the keyboard too.
This also happens to be tricky, because sometimes the simulator lost the focus of the keyboard typing and this might make the test fail, but in my experience the failure rate is lower than the other approaches I've taken.
I hope this can help.
I always use this to programmatically hide the keyboard in Swift UITesting:
XCUIApplication().keyboards.buttons["Hide keyboard"].tap()
XCUIApplication().toolbars.buttons["Done"].tap()
With Swift 4.2, you can accomplish this now with the following snippet:
let app = XCUIApplication()
if app.keys.element(boundBy: 0).exists {
app.typeText("\n")
}
The answer to your question lies not in your test code but in your app code. If a user cannot enter text using the on-screen software keyboard and then tap on the button, you should either make the test dismiss the keyboard (as a user would have to, in order to tap on the button) or make the view scrollable.
Just make sure that the keyboard is turned off in the simulator before running the tests.
Hardware->Keyboard->Connect Hardware Keyboard.
Then enter your text using the paste board
textField.tap()
UIPasteboard.generalPasteboard().string = "Some text"
textField.doubleTap()
app.menuItems["paste"].tap()
I prefer to search for multiple elements that are possibly visible to tap, or continue, or whatever you want to call it. And choose the right one.
class ElementTapHelper {
///Possible elements to search for.
var elements:[XCUIElement] = []
///Possible keyboard element.
var keyboardElement:XCUIElement?
init(elements:[XCUIElement], keyboardElement:XCUIElement? = nil) {
self.elements = elements
self.keyboardElement = keyboardElement
}
func tap() {
let keyboard = XCUIApplication().keyboards.firstMatch
if let key = keyboardElement, keyboard.exists {
let frame = keyboard.frame
if frame != CGRect.zero {
key.forceTap()
return
}
}
for el in elements {
if el.exists && el.isHittable {
el.forceTap()
return
}
}
}
}
extension XCUIElement {
///If the element isn't hittable, try and use coordinate instead.
func forceTap() {
if self.isHittable {
self.tap()
return
}
//if element isn't reporting hittable, grab it's coordinate and tap it.
coordinate(withNormalizedOffset: CGVector(dx:0, dy:0)).tap()
}
}
It works well for me. This is how I would usually use it:
let next1 = XCUIApplication().buttons["Next"]
let keyboardNext = XCUIApplication().keyboards.firstMatch.buttons["Next"]
ElementTapHelper(elements: [next1], keyboardElement: keyboardNext).tap()
Nice thing about this is you can provide multiple elements that could be tapped, and it searches for keyboard element first.
Another benefit of this is if you are testing on real devices the keyboard opens by default. So why not just press the keyboard button?
I only use this helper when there are multiple buttons that do the same thing, and some may be hidden etc.
If you are using IQKeyboardManager you can easily do this:
app.toolbars.buttons["Done"].tap()
This way you capture the "Done" button in the keyboard toolbar and hide the keyboard. It also works for different localizations.
I am implementing a message composer as like iMessages.
enablesReturnKeyAutomatically of UITextView is not working.
I have set it as textView.enablesReturnKeyAutomatically = YES;
but when I tap on numeric keypad and start typing something and sent using my send button. the textView got empty but the send button (return button of UITextView) is still enabled.
enablesReturnKeyAutomatically seems to only affect the uikeyboard, which in this case is a numeric keypad. The default numeric keypad does not have a return key, so there is nothing to enable/disable.
It sounds like you have a separate send button in the view. A workaround is inside - textView:shouldChangeTextInRange:replacementText: calculate what the new string will be with the replacementText and test if this new String is equal to #"". If so, then sendButton.enabled = NO, else sendButton.enabled = YES.
Remember to set the UITextViewDelegate and to trim the new string of white space.
I created a custom keyboard in Swift but it was rejected from the App Store because: "keyboard extension does not include Number and Decimal types".
How can I add these two keyboards easily?
I tried to rebuild the original view but it doesn't work properly. I'm sure there is a solution to create 2 or 3 different views and switch between them.
How can I switch between keyboard types when the keyboard type changes?
You can detect changes to the keyboard type in textDidChange. You need to get the UITextDocumentProxy then detect the proxy's keyboardType, and then you can do whatever layout changes needed.
override func textDidChange(_ textInput: UITextInput?) {
// Called when the document context is changed - theme or keyboard type changes
let proxy = self.textDocumentProxy as UITextDocumentProxy
if proxy.keyboardType == UIKeyboardType.numberPad
|| proxy.keyboardType == UIKeyboardType.decimalPad {
//add code here to display number/decimal input keyboard
}
}