iOS custom keyboard make a delete all button - ios

Currently my iOS custom keyboard has a delete all button. Here is its code when clicked:
func deletekeyPressed(sender: UIButton!) {
for _ in 1..<10000 {
(textDocumentProxy as UIKeyInput).deleteBackward()
}
}
The problem is, whenever I click the button there is a noticeable pause before all the text is deleted. I am pretty sure this is because I am calling deleteBackward() 10000 times.
Is there a faster way to delete all the text?

If you call deleteBackwards 10000 times, this means that 10000 deleteBackwards commands have to be serialized into some representation that can be transmitted via XPC, sent over to the host process, deserialized, and then applied. This could be the source of your delay.
I would batch your deleteBackwards calls into chunks that delete no more than the current document context you have available, then wait for the next textDidChange call, and if it is within X milliseconds of the last one, delete some more with the new context, as you can be fairly sure that the user didn't tap another text field so soon after pressing your button.
Below is some pseudocode that demonstrates what I mean.
var lastDeleteAllPressed: Double
let threshHold = 0.1 // you'll need to test on a real device to get a real value for this.
func textDidChange() {
//whatever else you need to do
if CACurrentMediaTime - lastDeleteAllPressed < threshHold && !proxy.isEmpty {
deleteKeyPressed()
}
}
func deleteKeyPressed() {
let length = proxy.contextBeforeInput.append(proxy.contextAfterInput).length
0...length.forEach { deleteBackwards() }
lastDeleteAllPressed = CACurrentMediaTime()
}
This approach has the benefit of not doing unnecessary work, and of working even if there are more than 10000 characters in the document the user is editing. The drawback is that it could fail if the host process takes unnaturally long to send back a textDidChange event.

Related

how to prevent button showing up for split second when view loads

So my goal is to smoothly load the viewController with no split second bugs. I have a function that is used to determine what buttons to show when the view loads based off a field in a Firestore document. Here is the function:
func determinePurchasedStatusVerification() {
db.collection("student_users/\(user?.uid)/events_bought").whereField("event_name", isEqualTo: selectedEventName!).whereField("isEventPurchased", isEqualTo: true).getDocuments { (querySnapshot, error) in
if let error = error {
print("\(error)")
} else {
guard let querySnap = querySnapshot?.isEmpty else { return }
if querySnap == true {
self.purchaseTicketButton.isHidden = false
self.viewPurchaseButton.isHidden = true
self.cancelPurchaseButton.isHidden = true
} else {
self.purchaseTicketButton.isHidden = true
self.viewPurchaseButton.isHidden = false
self.cancelPurchaseButton.isHidden = false
}
}
}
}
I call this function in the viewWillAppear() of the vc but when I instantiate to that vc, this is the result...
The extra purchase ticket button shows up for a split second. Even though it's very quick, you can still see it and it's just not something a user would need to see. It's also the other way around when you click on a cell that's not purchased, the two bottom buttons show up for a split second. I just want to know how I can prevent this quick bug and be able to display a smooth segue with no delays in the button hiding. Thanks.
getDocuments is an asynchronous function, meaning it doesn't call its callback function immediately -- it calls it when it gets data back from the server. It may seem like a split second just because your internet connection is fast and the Firebase servers are definitely fast, but it's a non-zero time for sure. And, someone with a slower connection might experience much more of a delay.
Unless your callback is getting called twice with different results (which seems doubtful), the only solution here is to make sure that your initial state has all of the buttons hidden (and maybe a loading indicator) and then show the buttons that you want once you get the data back (as you are right now). My guess is, though, that you have an initial state where the buttons are visible, which causes the flicker.

IOS Swift IBAction behaviour when combining actions

I have a custom keyboard extension which works as expected but I am coming across some odd behaviour which I can't explain. It is designed primarily for data input into Excel spreadsheets, so the fewer the keystrokes the better.
I have 2 IBActions.
Keypressed takes the value of the keypresses and inserts it into the current cell.
Returnpressed emulates the enter key which moves the cursor onto the next cell.
These work as described above, which is all good, but I am now trying to combine the actions, so that the user only has to press the first key and it inserts the text and then moves onto the next cell.
So when I simply extend the code in the Keypressed IBAction to include the code in the Returnpressed action, it simply inserts a carriage return into the text and stays in the same cell.
What am I missing please?
Here is a code snippet:
extension UIKeyInput{
func `return`() -> Void{
insertText("\n")
}
}
class KeyboardViewController: UIInputViewController, AVAudioPlayerDelegate {
#IBAction func KeyPressed(_ sender: Any) {
let string = (sender as AnyObject).titleLabel??.text
(textDocumentProxy as UIKeyInput).insertText("\(string!)")
**//THIS IS THE LINE THAT FIXED THIS FOR ME
textDocumentProxy.adjustTextPosition(byCharacterOffset: -1)**
self.EnterPressed(nil)
}
#IBAction func EnterPressed(_ sender: Any?) {
//default action for a return key press
textDocumentProxy.return()
}
I think you need to override the UITextInputDelegate textDidChange method (UIInputViewController implements UITextInputDelegate).It turns out that textDidChange is called when the text changes. And make the first responder to the next text field of your cell.
I managed to fudge this by determining what action s caused textDidChange to fire. It turns out that by simply adjusting the cursor portion, between inserting the text and firing the Return action works.
Not really sure how, but achieves what I want without the the user knowing it is a kludge and no overhead. I have changed the original code snippet to show the fix.

Position a button (of Twitter Digits) programmatically in Swift

a newbie question :)
I'm trying to use Twiiter Digits for authentication (by phone number) in my (first) iOS app.
It is easier for my to understand how to position a button programatically when it is a button that i create. but this 1 line of code confuses me.
to embed their action button i just need to add this part of code (see documentation):
override func viewDidLoad() {
let digitsButton = DGTAuthenticateButton(authenticationCompletion: { (session, error) in
// Inspect session/error objects
})
self.view.addSubview(digitsButton)
}
My problem is that the creation of this button is automatically and have a completion handler, so when\where exactly do i have the option to position (format) it?
Thanks.
You can always create your own custom button and use the methods of Digits to perform the same actions. For example :
func didTapButton(sender: AnyObject) {
let digits = Digits.sharedInstance()
digits.authenticateWithCompletion { (session, error) in
// Inspect session/error objects
}
}
Also if you want to continue to customize your Digits button and it's View Controllers , you can find more here here.
It might sound strange but it took me some time to realize i can\need to do it after the completion handler (inside the ViewDidLoad...).
Thanks for Letting me know i can use my own button with Digits.

Swift 2: Call a method after pressing Return in UITextField

I have a Text Field in a Tip Calculator. I want the Tip Calculator to update all the fields of my calculator.
With the current code that I have now, for whatever reason. It doesn't call calcTip() (or atleast it doesn't seem to) until I press return once, enter another (or the same) value and press return again.
I'm really close to having this app work exactly how I want it to, and I feel like I'm just 1 or 2 lines away.
My ViewController conforms to UITextFieldDelegate, and I declared amountBeforeTaxTextField.delegate = self in viewDidLoad.
TextShouldReturn:
func textFieldShouldReturn(textField: UITextField) -> Bool {
amountBeforeTaxTextField.clearsOnBeginEditing = true
amountBeforeTaxTextField.becomeFirstResponder()
calcTip()
amountBeforeTaxTextField.resignFirstResponder()
return true
}
calcTip():
func calcTip() {
let tip = tipCalc.calculateTip()
let total = tipCalc.calculateTotal()
let tax = tipCalc.calculateTax()
let perPerson = total / Float(Int(partySizeSlider.value))
tipCalc.tipPercentage = (tipPercentageSlider.value)
tipCalc.amountBeforeTax = (amountBeforeTaxTextField.text! as
NSString).floatValue
tipCalc.taxPercentage = (taxPercentageSlider.value)
resultLabel.text = String(format: "Tip $%.2f, Tax: $%.2f Total: $%.2f", tip, tax, total)
perPersonLabel.text = String(format: "Per-Person Total: $%.2f", perPerson)
}
Note: I have the same outcome with & without .becomeFirstResponder()
Thanks for reading.
So your textFieldShouldReturn function is called when you press the bottom right button on the keyboard, which can have various titles; such as "Go", "Return", "Done", or "Send".
Your first line, only accomplishes a UI effect; meaning when the user taps on the textfield to input text, there will be a little box that appears on the far right handside of the textfield that allows the user to clear the inputted text, if there is any. Sort of like a reset.
When you use becomeFirstResponder, you are pretty much calling another keyboard to pop up, so you aren't accomplishing anything by doing that.
When you use resignFirstResponder, you are hiding the keyboard.
What you want to do is remove "becomeFirstResponder" as well as the first line, because those don't do anything to help your problem. That should be the solution to the problem you are facing.

IOS Swift - Manually trigger textfield events

How do I trigger the events manually given the UITextField object?
textFieldShouldBeginEditing(_:)
textFieldDidBeginEditing(_:)
textFieldShouldEndEditing(_:)
textFieldDidEndEditing(_:)
In the current scenario. There is some logic which goes into the above callbacks. And there are times when I manually update a textfield
textfield.text = <new text>
And I would like the
textFieldDidBeginEditing
to be trigger, like (hopefully)
textfield.trigger("textFieldDidBeginEditing")
While my approach might not be entirely the right way, the above solution is what would most likely works for me. Although, If I can word my question differently, it would be:
How can I run some logic (code) when a UITextFields value (text) is changed via the UI or via textfield.text = <new value>
Brushing aside the situation you're trying to solve; regardless of wether this is the right approach; you can trigger UITextField events like this:
textField.sendActions(for: UIControl.Event.editingChanged)
To elaborate on my answer and my reasoning:
I don't think it is possible to trigger those kind of events "by hand" - neither should you be able to. It would just cause possibly more problems than it solves. E.g. what should happen with the caret for example, should it be displayed in the textfield?
Therefore I suggest you extract the logic into a second method and call that method from your current code and from the handler
Instead of
func textFieldDidEndEditing(textField: UITextField) {
// your logic is here
let k = 1 + 2
}
func yourCurrentCode() {
// somehow trigger the event "textFieldDidEndEditing"
}
do something like
func textFieldDidEndEditing() {
myLogic()
}
func yourCurrentCode() {
myLogic()
}
func myLogic() {
// your logic should be here
let k = 1 + 2
}
If you would like to be notified every time the value of the UITextField changes you should use the notification
UITextFieldTextDidChangeNotification

Resources