iOS13三指撤销与文案限长适配实例详解
iOS13适配中,三指撤销操作触发UITextField文案限长回调时,同步截断文本会破坏撤销栈状态导致崩溃。解决方案:将截断逻辑延迟到下一个runloop执行,数字键盘场景则在代理中直接拒绝超长输入,从源头避免崩溃。
正文
在适配iOS13时,遇到一个挺棘手的问题:UITextField输入中文时,三指撤销竟然直接crash了。如果你也在做iOS13适配,很可能碰上这个坑。

Bugly报错
NSInternalInconsistencyException setGroupIdentifier:: _NSUndoStack 0x1206532f0 is in invalid state, calling setGroupIdentifier with no begin group mark
堆栈信息
从堆栈信息能很清楚地看到,crash发生在撤销操作链中,核心是_NSUndoStack的状态异常。
CoreFoundation ___exceptionPreprocess + 220 libobjc.A.dylib objc_exception_throw + 56 Foundation -[_NSUndoStack groupIdentifier] Foundation -[NSUndoManager undoNestedGroup] + 240 UIKitCore -[UIUndoGestureInteraction undo:] + 72 UIKitCore -[UIKBUndoInteractionHUD performDelegateUndoAndUpdateHUDIfNeeded] + 96 UIKitCore -[UIKBUndoInteractionHUD controlActionUpInside:] + 152 UIKitCore -[UIApplication sendAction:to:from:forEvent:] + 96 xxxxx -[UIApplication(MemoryLeak) swizzled_sendAction:to:from:forEvent:] + 288 UIKitCore -[UIControl sendAction:to:from:forEvent:] + 240 UIKitCore -[UIControl _sendActionsForEvents:withEvent:] + 408 UIKitCore -[UIControl touchesEnded:withEvent:] + 520 UIKitCore -[UIWindow _sendTouchesForEvent:] + 2324 UIKitCore -[UIWindow sendEvent:] + 3352 UIKitCore -[UIApplication sendEvent:] + 336 UIKitCore ___dispatchPreprocessedEventFromEventQueue + 5880 UIKitCore ___handleEventQueueInternal + 4924 UIKitCore ___handleHIDEventFetcherDrain + 108 CoreFoundation ___CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 24 CoreFoundation ___CFRunLoopDoSource0 + 80 CoreFoundation ___CFRunLoopDoSources0 + 180 CoreFoundation ___CFRunLoopRun + 1080 CoreFoundation CFRunLoopRunSpecific + 464 GraphicsServices GSEventRunModal + 104 UIKitCore UIApplicationMain + 1936 xxxxx main + 148 libdyld.dylib _start + 4
问题定位
一时没什么头绪,最老实的办法就是一段一段注释代码。最终,矛头指向了下面这两个方法:
[self addTarget:observer
action:@selector(textChange:)
forControlEvents:UIControlEventEditingChanged];
- (void)textChange:(UITextField *)textField {
... ...
UITextRange *selectedRange = [textField markedTextRange];
if (!selectedRange || !selectedRange.start) {
if (destText.length > maxLength) {
textField.text = [destText substringToIndex:maxLength];
}
}
}
这段代码原本是用来限制文案长度的。问题在于,三指撤销操作会触发UIControlEventEditingChanged事件,从而执行textChange:。但此时即便输入法还存在高亮的markedText,markedTextRange也返回nil。紧接着,代码就会把textField.text给截断了。这一修改,直接破坏了撤销栈的内部状态,再继续执行撤销操作,crash就成了必然。
解决方案
既然问题是同步修改文本导致撤销栈混乱,那把截断操作延迟到下一个runloop执行就行了。方法很简单,将文案判长和截取的逻辑用dispatch_async扔到主队列:
- (void)textChange:(UITextField *)textField {
dispatch_async(dispatch_get_main_queue(), ^{
... ...
});
}
这样一来,截断操作就不会干扰当前正在进行的撤销流程了。
数字截断后 crash
中文输入的问题解决了,但数字键盘的场景还有坑。当UITextField限制了长度,超过最大长度后继续输入,再进行撤销操作同样会crash。而且上面的异步方案在这个场景下不奏效。目前比较稳妥的做法,是在UITextField的袋里回调中直接拦截输入:
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
/// 输入数字后截取字符串仍旧可以触发撤销操作导致crash, 在这里拦截一下
if (textField.keyboardType == UIKeyboardTypeNumberPad
&& range.location >= textField.tt_maxLength) {
return NO;
}
return YES;
}
简单粗暴,在超出长度后直接拒绝输入,从源头避免crash。
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
AWS RDS 数据库配置入门与基础操作指南
本文介绍了AWSRDS的基本概念与核心价值,即提供托管式关系数据库服务,简化运维。详细阐述了创建RDS实例的关键配置步骤,包括引擎选择、实例规格、存储与网络设置。最后,指导读者如何通过多种方式安全连接至数据库实例,并开始进行数据操作,为后续应用开发奠定基础。
PHP MVC中AJAX请求无法调用控制器方法的原因与解决方案
PHPMVC中AJAX请求返回整页HTML的常见原因是控制器方法未正确输出响应或未终止执行,导致框架渲染视图。解决方法是在控制器中设置JSON响应头、输出数据后调用exit()明确终止,同时前端使用小写url和dataType: "json "。
Go语言手动构造rsa.PublicKey:正确初始化大整数模数N完整指南
手动构造RSA公钥时,模数N为*big Int类型,不能直接使用超长十进制字面量,需通过SetString或UnmarshalText方法解析字符串。公钥指数E可直接赋值,推荐65537。生产环境应使用rsa GenerateKey生成密钥对,避免手动构造引发的安全和格式错误。
Go语言实现HTTP定时轮询监控多URL响应时间与状态检测
使用Go语言实现HTTP定时轮询监控,通过按行分割与Tab解析URL列表,避免闭包陷阱和nil指针,每个URL启动独立ticker安全并发请求,并配置超时控制与资源关闭,确保响应时间与状态码准确检测。
Tkinter中Label标签在主循环动态更新的正确方法
在Tkinter中正确动态更新标签的方法:将标签组件的textvariable参数绑定到一个StringVar变量,然后通过调用该变量的 set()方法更新其值,界面会自动刷新。这样避免直接修改text属性或调用update()。此做法实现数据与界面的解耦,代码更简洁,响应更及时,避免手动同步的闪烁,推荐做法。
- 热门数据榜
相关攻略
2026-07-10 06:41
2026-07-10 06:40
2026-07-10 06:39
2026-07-10 06:39
2026-07-10 06:39
2026-07-10 06:39
2026-07-10 06:39
2026-07-10 06:39
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程

