How can I add a Number and Decimal keyboard into my Custom Keyboard on iOS8? - ios

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
}
}

Related

Swift how to detect if keyboard is fully visible or fully hidden only (and not partially visible)

This question is related to the following questions: Detect when keyboard is fully visible and prevent keyboard appearance handling code from adding extra offset for hidden element
I was initially using notifications like NSNotification.Name.UIKeyboardDidShow, NSNotification.Name.UIKeyboardDidHide hoping they would be triggered only once, hence enabling me to set a bool that the keyboard is fully displayed or that it is hidden.
But what I have noticed is that all the events: UIKeyboardWillShow, UIKeyboardDidShow, UIKeyboardWillHide, UIKeyboardDidHide, UIKeyboardWillChangeFrame, UIKeyboardDidChangeFrame are designed to trigger multiple times as the keyboard appears or disappears.
There seems to be no way to check if a keyboard is completely visible and not partially visible. All the answers I looked at listened to these notifications and did calculations to avoid the views from being hidden by the keyboard. But I could not find any way to see if a keyboard is fully displayed (or fully hidden)
I even looked into KeyboardObserver which makes it easier to observe keyboard events, But since it's still based on the default notifications, it's KeyboardEventType.didShow and KeyboardEventType.didHide are triggered multiple times as keyboard appears and disappears.
There should be a better way to tell if a keyboard is fully visible or not visible!
Here is an example of a property to check if the keyboard is available in the screen:
extension UIApplication {
var isKeyboardPresented: Bool {
if let keyboardWindowClass = NSClassFromString("UIRemoteKeyboardWindow"),
self.windows.contains(where: { $0.isKind(of: keyboardWindowClass) }) {
return true
}
else {
return false
}
}
}
if UIApplication.shared.isKeyboardPresented {
print("Keyboard presented") }
else {
print("Keyboard is not presented")
}

iOS: Good way to pass value from image button to func in the class

Initially I had two buttons with titles: "+" and "-". Both buttons are located in table cell. I have used protocol and delegate to pass value from button title to function call which depends on input params. If input parameter is "-" - decrease value, if "+" - increase.
But later I removed titles and replaced buttons with respective images. And here I faced an issue - I cannot call function properly because title is blank.
I have implemented the following workaround. I have set Accessibility Identifier for both buttons. For example for +:
And in cell class I used accessibilityIdentifier:
#IBAction func didPressOrderCellButton(_ sender: UIButton) {
cellDelegate?.didPressOrderCellButton(order: order!, menuItem: menuItem!, action: sender.accessibilityIdentifier!)
}
But... I have some doubts if it's proper way to do this. Will it create any issues in future if I decide to work with accessibility feature?
I do not want to use title and add code to ViewController class to hide it every time view is loaded or appeared. I want to avoid such solutions in my code because it's too difficult to support such solutions I believe.
I have tried to find another button identifier that I can use, but didn't succeed.
The simple approach would be just set the tag value inside your button and then check the same tag value and perform your operation Plus or Minus accordingly.
For instance:-
if sender.tag == 0 {
//Do minus
} else if sender.tag == 1 {
//Do plus
}

Append text to a UITextField with custom keyboard made out of buttons

I have a static numeric-keyboard made out of a bunch of buttons, I also have three UITextFields, textField1, textField2 and textField3 where I'm inputting the text using the static keyboard.
Here is the code I'm using to detect which textField is currently in focus and to input the content of the buttons. It kind of works but I don't like the fact that I have three IF statements and I'm not sure how to prevent the keyboard from appearing when a textField is tapped.
What would be the best way to implement this functionality?
#IBAction func appendKey(sender: AnyObject) {
let digit = sender.currentTitle!
if(textField1.isFirstResponder()){
textField1.text = textField1.text! + digit!
}else if(textField2.isFirstResponder()){
textField2.text = textField2.text! + digit!
}else if(textField3.isFirstResponder()){
textField3.text = textField3.text! + digit!
}
}
Thanks
If the standard keyboard is displaying then your custom keyboard isn't setup properly. Your custom keyboard should be the inputView of each UITextField. If you do that, the standard keyboard won't appear and yours will instead.
Your custom keyboard should be a separate class that handles all of it's own buttons. It appears you have everything in one view controller - all of the text fields, all of the buttons, and all of the button handling code. This is a bad approach. Create your custom keyboard class view. Put all of the code to handle and display the buttons in that custom view class. Create a single instance of this view in your view controller and assign the custom keyboard view instance to the inputView property of each text field.
In the custom keyboard class, listen for the UITextFieldTextDidBeginEditingNotification notification. This is how you keep track of the current text field. Your custom keyboard class should not have any specific reference to any text field other than track the current one. It should also ensure that the text field's inputView is itself.
In each button handler of the custom keyboard class, get the text you wish to append and then call the text field's insertText: method with the string. That's it. This will ensure the text is inserted and/or replaced based on the current selecting in the text field.

How to hide keyboard in Swift app during UI testing

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.

how to detect Number and Decimal Keyboard iOS

I'm developing a custom keyboard and based on Apple App Store Review guideline I have to provide a keypad for decimal and number pad :
Keyboard extensions must provide Number and Decimal keyboard types as described in the App Extension Programming Guide or they will be rejected
So I have to provide a keyboard for Decimal and Number Textfield, so how can I detect textfield type.
There is nothing about Number or Decimal Keyboard in App Extension Programming Guide so How detect which textfield I should load Decimal Keyboard or Number Keypad ?
You can get the current keyboard type before your view appears like so (in Swift).
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
// Setup keyboard manager to correct type
if let inputTraits = self.textDocumentProxy as? UITextInputTraits {
let keyboardType = inputTraits.keyboardType
let returnKeyType = inputTraits.returnKeyType
}
}
This from my project but reformatted for clarity. I also didn't compile it since it's such a simple example.
I've found it's good not to trust any extension API values until the view is visible or about to appear, but you could also try this in viewDidLoad()

Resources