I have a ui::EditBox that I am using for user name entry, and I need to limit what the user can enter there. I do this in the delegate’s editBoxTextChanged() by editing the text via std::regex then setText() to the editBox. This works great; if the user enters an invalid character, it just doesn’t show on the edit box. The problem occurs when the user uses iOS’s “undo” functionality, such as pressing the UNDO button on the keyboard. If the last character the user tried to enter was removed by the regex, then the user presses UNDO, the app crashes with an NSRangeException:
2015-08-03 17:04:18.283 KidMade[41809:2606335] *** Terminating app due to uncaught exception ‘NSRangeException’, reason: ‘*** -[NSBigMutableString substringWithRange:]: Range {10, 1} out of bounds; string length 10’
*** First throw call stack:
(
0 CoreFoundation 0x04594746 __exceptionPreprocess + 182
1 libobjc.A.dylib 0x03338a97 objc_exception_throw + 44
2 CoreFoundation 0x0459466d +[NSException raise:format:] + 141
3 Foundation 0x0112d64a -[NSString substringWithRange:] + 145
4 UIKit 0x01e8cea4 -[NSTextStorage(UIKitUndoExtensions) _undoRedoAttributedSubstringFromRange:] + 158
5 UIKit 0x01e8dd53 -[_UITextUndoOperationTyping undoRedo] + 329
6 UIKit 0x01e8ce01 -[NSTextStorage(UIKitUndoExtensions) _undoRedoTextOperation:] + 33
7 Foundation 0x0127391e -[_NSUndoLightInvocation invoke] + 59
8 Foundation 0x01273f07 -[_NSUndoStack popAndInvoke] + 256
9 Foundation 0x012730ec -[NSUndoManager undoNestedGroup] + 547
10 Foundation 0x01272ec1 -[NSUndoManager undo] + 275
11 UIKit 0x10f4af51 -[NSUndoManagerAccessibility undo] + 50
12 UIKit 0x019c4a85 -[UIKeyboardLayoutStar continueFromInternationalActionForTouchUp:withActions:timestamp:interval:didLongPress:prevActions:executionContext:] + 652
13 UIKit 0x019c4194 -[UIKeyboardLayoutStar completeSendStringActionForTouchUp:withActions:timestamp:interval:didLongPress:prevActions:executionContext:] + 898
14 UIKit 0x019c397f -[UIKeyboardLayoutStar completeRetestForTouchUp:timestamp:interval:executionContext:] + 5948
15 UIKit 0x019c172d -[UIKeyboardLayoutStar touchUp:executionContext:] + 174
16 UIKit 0x10f345de -[UIKeyboardLayoutStarAccessibility touchUp:executionContext:] + 64
17 UIKit 0x01817e25 __28-[UIKeyboardLayout touchUp:]_block_invoke + 283
18 UIKit 0x01e183a2 -[UIKeyboardTaskQueue continueExecutionOnMainThread] + 404
19 UIKit 0x01e18a5f -[UIKeyboardTaskQueue addTask:] + 144
20 UIKit 0x01817d02 -[UIKeyboardLayout touchUp:] + 294
21 UIKit 0x01818a0e -[UIKeyboardLayout touchesEnded:withEvent:] + 539
22 UIKit 0x015facfa -[UIWindow _sendTouchesForEvent:] + 874
23 UIKit 0x015fb7d6 -[UIWindow sendEvent:] + 792
24 UIKit 0x015b96d1 -[UIApplication sendEvent:] + 242
25 UIKit 0x015c9b08 _UIApplicationHandleEventFromQueueEvent + 21484
26 UIKit 0x0159d337 _UIApplicationHandleEventQueue + 2300
27 CoreFoundation 0x044b606f CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION + 15
28 CoreFoundation 0x044abb7d __CFRunLoopDoSources0 + 253
29 CoreFoundation 0x044ab0d8 __CFRunLoopRun + 952
30 CoreFoundation 0x044aaa5b CFRunLoopRunSpecific + 443
31 CoreFoundation 0x044aa88b CFRunLoopRunInMode + 123
32 GraphicsServices 0x049412c9 GSEventRunModal + 192
33 GraphicsServices 0x04941106 GSEventRun + 104
34 UIKit 0x015a1106 UIApplicationMain + 1526
35 KidMade 0x00288ab0 main + 128
36 libdyld.dylib 0x07f6eac9 start + 1
37 ??? 0x00000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)
At this point I would settle for just disabling undo for that one usage of EditBox, but I do use it elsewhere that I would want to keep undo functioning.
[UPDATE] I figured out how to fully disable undo by adding the following to UIEditBoxImpl-ios.mm at about line 182:
- (void)textFieldDidBeginEditing:(UITextField *)sender {
CCLOG("textFieldDidBeginEditing...");
[sender.undoManager disableUndoRegistration];
}
This is a temporary solution and does not fix the actual problem of crashing when undo is pressed while functional.