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
Related
I wanted to use a custom UIMenuController in WKWebView.
First, I wanted to get rid of the default menu (Copy, Look up, Share), but for some reason I don't know, but it hasn't disappeared.
override open func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
switch action {
case #selector(highlightHandler):
return true
default:
return false
}
}
func enableCustomMenu() {
let memo = UIMenuItem(title: "메모", action: #selector(highlightHandler))
UIMenuController.shared.menuItems = [memo]
UIMenuController.shared.update()
}
#objc func highlightHandler(sender: UIMenuItem) { }
I tried using the code above to remove the default menuItems and add custom menuItems called "메모", but it didn't.
How can I show only the items I want called "메모"?
canPerformAction() cannot reject an option in most cases. It can only tell the system that the class it's being called in is willing to provide the needed function. Returning False just says "I can't do that one", and then the next item in the responder chain is called and eventually something is found that says "Yes, I can do that". Having said that, it seems that I get a different result if I override this function on the item that is the first responder. In that case, False actually seems to disable the command. So if you can implement canPerformAction() on the first-responder, do that. If not...
Basically you have to temporarily break the responder chain. You do that by overriding the UIResponder "next" variable so that it conditionally returns nil when you want the chain broken. You don't want it to leave it broken for long or bad things will happen. Anything that was approved by the FirstResponder or things in the responder chain between First and you will still be approved, but that will stop approval of things after you in the chain.
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.
I am new to swift and new to iOS development. I am currently struggling to understand why delegates are used to handle events that happen in the UI.
So far, in the tutorial I am working through, I have only ever done things like the following code segment to handle UI events:
#IBAction func fahrenheitFieldEditingChanged(_ textField: UITextField) {
if let text = textField.text, let value = Double(text) {
fahrenheitValue = Measurement(value: value, unit: .fahrenheit)
} else {
fahrenheitValue = nil
}
}
But now the tutorial I am working through is having me use a delegate to to handle another UI event from the same text field. Why is it done this way? What is the point of using delegates rather than just write actions to handle the events?
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
let existingTextHasDecimalSeparator = textField.text?.range(of: ".")
let replacementTextHasDecimalSeparator = string.range(of: ".")
if existingTextHasDecimalSeparator != nil, replacementTextHasDecimalSeparator != nil {
return false
} else {
return true
}
}
Take a look at UITextViewDelegate. It'll become easier to understand.
Responding to Editing Notifications
func textViewShouldBeginEditing(UITextView)
Asks the delegate if editing should begin in the specified text view.
func textViewDidBeginEditing(UITextView)
Tells the delegate that editing of the specified text view has begun.
func textViewShouldEndEditing(UITextView)
Asks the delegate if editing should stop in the specified text view.
func textViewDidEndEditing(UITextView)
Tells the delegate that editing of the specified text view has ended.
Responding to Text Changes
func textView(UITextView, shouldChangeTextIn: NSRange, replacementText: String)
Asks the delegate whether the specified text should be replaced in the text view.
func textViewDidChange(UITextView)
Tells the delegate that the text or attributes in the specified text view were changed by the user.
There is more, but I won't copy/paste anymore you can see yourself. What you see here are things that you can tune in if you want.
If you want you can tune in and get the callback of when the user starts typing. Or you can tune in and get the callback of when the user ended his editing.
How can you find out when the user stoped or started using IBAction?!
Once* you set yourself as a delegate (textViewInstance.delegate = self, you can then choose to get any of these callbacks.
*To be 100% accurate, you need to do that, but also adopt the UITextViewDelegate protocol and then conform to it using the mentioned delegate callbacks
Delegate is a design pattern that allows you to easily customize the behavior of several objects in one central object.
Actions are merely user interactions.
It's not possible to replace delegate callbacks with actions. For instance, if you have a WebView which fails to load, then how does it inform your object using an action?
In your example, an action cannot return properties like shouldChangeCharactersIn and replacementString. You need a delegate for that.
Read more...
Delegate pattern (Apple docs)
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.
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.