I'm developing a custom keyboard for iOS 8 using Swift. I'm trying to write a function that inserts text around both sides of the cursor.
I have defined this function in my class that inherits from UIInputViewController
func nestedTag(tag: NSString) {
let proxy = self.textDocumentProxy as UITextDocumentProxy
proxy.insertText(tag)
proxy.insertText(tag)
proxy.insertText(" ")
proxy.adjustTextPositionByCharacterOffset(-1 * (1 + tag.length))
For example, if I call nestedTag
with the !!!
I'd expect to have the text field that the keyboard:
!!!<CURSOR>!!!<SPACE>
The issue is that if I call this function when focused on an empty text field with no input, I get this incorrect result.
!!!!!!<SPACE><CURSOR>
Additionally, if I call this function when focused on a populated text field, I get different incorrect behavior.
The text field before:
helloworld
The text field after:
hello w!!!!!! orld
In this example, the cursor did move 4 characters to the left, however this looked as if the call to adjustTextPositionByCharacterOffset
was called BEFORE the three calls to insertText
despite that not being the case (see code).
The documentation for both UITextDocumentProxy
methods seem to be pretty simple and self explanatory, and I do not seem to be seeing other people online having this problem. I can't seem to figure out what I am doing incorrectly to get these peculiar results. Thank you for your time.
I am struggling with it, but I have little found, so I share it
textDocumentProxy provides 6 methods
(insert
, move cursor
, delete
, afterInput
, beforeInput
, hasText
)
These 6 methods needs some time like async methods.
(Each methods needs different time)
ex) original sequence : move insert move
-> result : insert move move
So, I tried to wait some time before calling each method.
With wrapping each methods with semaphore
and dispatch_async
, and dispatch_after
,
we can wait them like sync methods.
inputQueue = dispatch_queue_create("happynewyear", 0);
inputQueue2 = dispatch_queue_create("happynewyear2", 0);
-(void)moveCursor:(NSInteger)num{
dispatch_async(inputQueue2, ^{
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
dispatch_after(delay(TIME_INTERVAL), inputQueue, ^{
[self.textDocumentProxy adjustTextPositionByCharacterOffset:num];
NSLog(@"moveCursor : %ld",(long)num);
dispatch_semaphore_signal(sema);
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
});
}
-(void)insertText:(NSString*)string{
dispatch_async(inputQueue2, ^{
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
dispatch_after(delay(TIME_INTERVAL), inputQueue, ^{
[self.textDocumentProxy insertText:string];
NSLog(@"insertText : %@",string);
dispatch_semaphore_signal(sema);
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
});
}
-(void)deleteBackward:(NSInteger)num{
dispatch_async(inputQueue2, ^{
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
dispatch_after(delay(TIME_INTERVAL), inputQueue, ^{
for (int i = 0; i<num; ++i) {
[self.textDocumentProxy deleteBackward];
}
NSLog(@"deleteBackward : %ld",(long)num);
dispatch_semaphore_signal(sema);
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
});
}
the delay functions...
inline static dispatch_time_t delay(double delay){
return dispatch_time(
DISPATCH_TIME_NOW,
delay * NSEC_PER_SEC
);
}
and I use TIME_INTERVAL
as 0.01
This trick makes your code keep the sequence.
I am trying other methods(afterInput, beforeInput ...) like above,
If I solve it, I will be back.