Do the soft keyboard on a mobile device,So how to handle user clicks position,It is the first problem you encounter,in this problem,I have come a long way。
I touch a logical development since input method pocketed roughly classified into three stages,Now were Laijiangjiang design ideas,I hope this can help you。
The first generation of touch engine
Obviously,For a beginner,There's nothing better use of the control system,Full-featured,Speed is not slow,Improve the business logic,so,Pocketed generation of a first input message is treated with UIButton of TouchUpInside News。For a start, I do not even use the keyboard layout to build xib,Therefore, the direct use of @IBAction func buttonTouchUpInside(_ sender: UIButton) Such a statement,You understand at a glance,Right? Very easy to handle,Which the user clicks on the Button,Which is then passed in,Do not require additional judge,The system will seal the deal all。But soon encountered the first problem - press the button and the program will always perform a,So the UI will Caton,It appears clear from the candidate key is pressed to,Will be delayed。
To avoid interference business logic input (actually barrier) updates the UI,I read further information processing in the background,In fact you should always do this - never in the main thread processing business logic。
1 2 3 |
DispatchQueue.global(qos: .userInitiated).async { self.input(button:button) } |
But this has introduced another problem,When the user clicks the button too fast speed interval is too short,Key processing sequence will be disturbed,于是,I changed to synchronize asynchronous,Such business logic or processing in a background thread,But in strict accordance with the order of calls performed sequentially (This ensures that the user presses a key to the actual order processing)
1 2 3 |
DispatchQueue.global(qos: .userInitiated).sync { self.input(button:button) } |
Note that the first line,async → sync。
The second generation touch engine
The first generation of engine work for a long time,But eventually they encountered another problem:When the user clicks on a more accelerated time,Some background processing logic is not normal。
This is actually due UIButton The touch control mechanism itself caused by the conflict,When the user clicks the onscreen keyboard too fast,In fact, a short time while pressing two buttons,At this point the main thread itself is blocked each other,Only when users are off the screen with two fingers,The message will be sent,That is, @IBAction func buttonTouchUpInside(_ sender: UIButton) It is called twice immediately。In a very short time,Two consecutive calls,Although there is a strict order of,But each key messages are processed refresh candidate bar,This requires asynchronous UI changes,This leads to an abnormal number of processing logic - before the refresh is complete in UI,The next message has already started。
There are two ideas to solve,Either some business logic, and determines to not associated UI,Either find a way to allow the system to handle multiple users simultaneously press UIButton Case。 - apparently,We should start from the latter,于是,I had a key cache mechanism in the development of pocketed input X,It is no longer used TouchUpInside ,But TouchDown and TouchUp :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
var pendingKey:UIButton? @IBAction func keyboardKeyDown(_ sender:UIButton) { if sender == pendingKey {return} if let pending = pendingKey { keyboardKeyConfirm(pending) } pendingKey = sender } @IBAction func keyboardKeyUp(_ sender: UIButton) { guard let pending = pendingKey, sender.tag == pending.tag else {return} keyboardKeyConfirm(pending) pendingKey = nil } func keyboardKeyConfirm(_ sender:UIButton) { buttonTouchUpInside(sender) } |
note buttonTouchUpInside That is, the generation processing logic,Does not change,I had to write to let you know my thoughts change,So that when the user presses a key,Then cache it immediately,When the user then presses a key,If you already have cached,Then deal with it,And the new addition of cache;When you lift your finger,Then processing the key cache。As a result,,All keys are processed,And will not be TouchUpInside This signal is blocked - because I no longer use it。
Because of changes to the source signal acquisition (from TouchUpInside To TouchDown + TouchUp ),For users, a great "feel" changes。
The third-generation touch engine
The second generation actually work very well,But before the input X pocketed Added,Our internal testing discovered another problem - in fact, can not be said of the new find,Because it has always been there,That's "q" and "p" problem。Among the new iOS system,As if to avoid conflicts and system gestures,iOS for the edge of the screen 5 Pixels make a reservation processing,When you click on the edge of the screen when (a push-button when "q" or "p" point slightly aside), TouchDown This message is not immediately be triggered。
It will be delayed until the moment you lift your finger,And then TouchUp To send to your keyboard。This leads to the normal user when typing,I met the two positions,Always feel "Caton" a bit,Because the visual and audio feedback on,Indeed delayed until the moment you lift your finger, rather than pressing the trigger immediately。
If the app is among,You can do this:
1 2 3 |
override var preferredScreenEdgesDeferringSystemGestures: UIRectEdge { return [.left,.right] } |
But apparently,In this code does not take effect keyboard。In short,In order for the upcoming shelves Pocketed input X More competitive,I had to bite the bullet and continue to find ways。
After I tried a lot of programs,I finally found the controls can get a signal - UITapGestureRecognizer 。
Not use it to identify the standard click - this is also useless,It must be used func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool The proxy method,It can only cross the shield system,Get right TouchDown transfer,Rather than being delayed until the moment the user lifts the finger。
Correspondingly,I use UIView self func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) To get TouchUp behavior,As a result,,You can refer to the logic generation engine to achieve a:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
class TouchLayer:UIView,UIGestureRecognizerDelegate { var tapGR = UITapGestureRecognizer() init(keyboard:KeyboardViewController) { super.init(frame: CGRect.zero) tapGR.delegate = self self.addGestureRecognizer(tapGR) } var currentTouch:UITouch? override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { if let t = currentTouch { touchUpInside(t) } } func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { if let t = currentTouch { touchUpInside(t) } touchDown(touch) return true } } |
Other code not skip the table,After this operation,This TouchLayer Covering the top of the keyboard can be - of course,You can also in your Button Subclasses such operations,Then treatment alone。Here I am at the top of the keyboard cover,A unified process。
to this end,I had to keyboard business logic for a lot of adjustment and reconstruction until the end of last year ......,I wrote an article Pocketed X input method is how to deal with the problem of delayed edge of the screen。
In short,This is now the online version Pocketed input X The touch logic for use,Now,Every good。If shortcomings,Probably from the second generation of three generations of the cost of the upgrade is too big,It is almost off the grid and the X input method Classic One of the differences of emphasis。
Original article written by LogStudio:R0uter's Blog » How pocketed input method is a process of PUSH messages
Reproduced Please keep the source and description link:https://www.logcg.com/archives/3205.html
Often before the original cut will leave a window when the letter q in the input box is for this reason…
But this seems,iOS native input method is no problem in terms of UX,API and left with a third-party input API is probably not the same set? A little pit
The second generation set of logic a bit like doing ACT game processing input buffer ideas