SwiftUI TextEditor搭配Apple Pencil长按崩溃问题求助
SwiftUI TextEditor Apple Pencil长按崩溃问题解决方案
问题描述
在SwiftUI中使用TextEditor时,若用Apple Pencil长按已有文本的中间位置,应用会抛出以下异常并崩溃:
Thread 1: "NSTextContentStorage: Inconsistent element cache state. Elements for range {0, 43} are already cached while trying to insert"
可通过以下最简代码复现问题:
import SwiftUI struct ContentView: View { @State private var text: String = "The quick brown fox jumps over the lazy dog" var body: some View { VStack { TextEditor(text: $text) .background(Color.init(red: 242/255, green: 242/255, blue: 242/255)) .border(.black) .frame(height: 200) } .padding() } }
解决方案
方案1:自定义封装UITextView(推荐)
SwiftUI的TextEditor底层使用TextKit 2,该问题是TextKit 2的缓存一致性bug导致。我们可以通过UIViewRepresentable封装原生UITextView(默认使用TextKit 1),彻底避开这个问题:
import SwiftUI import UIKit struct FixedTextView: UIViewRepresentable { @Binding var text: String func makeUIView(context: Context) -> UITextView { let textView = UITextView() // 匹配SwiftUI TextEditor的外观 textView.backgroundColor = UIColor(red: 242/255, green: 242/255, blue: 242/255, alpha: 1) textView.layer.borderColor = UIColor.black.cgColor textView.layer.borderWidth = 1 textView.font = UIFont.preferredFont(forTextStyle: .body) textView.text = text // 设置代理同步文本到Binding textView.delegate = context.coordinator return textView } func updateUIView(_ uiView: UITextView, context: Context) { if uiView.text != text { uiView.text = text } } func makeCoordinator() -> Coordinator { Coordinator(self) } class Coordinator: NSObject, UITextViewDelegate { var parent: FixedTextView init(_ parent: FixedTextView) { self.parent = parent } func textViewDidChange(_ textView: UITextView) { parent.text = textView.text } } } // 使用示例 struct ContentView: View { @State private var text: String = "The quick brown fox jumps over the lazy dog" var body: some View { VStack { FixedTextView(text: $text) .frame(height: 200) } .padding() } }
这个方案完全替代了SwiftUI的TextEditor,保留了文本编辑的核心功能,同时彻底规避了TextKit 2的崩溃问题。
方案2:使用Introspect库获取底层UITextView
如果你不想完全替换TextEditor,可以尝试用Introspect库获取其底层的UITextView,调整相关配置:
import SwiftUI import Introspect struct ContentView: View { @State private var text: String = "The quick brown fox jumps over the lazy dog" var body: some View { TextEditor(text: $text) .background(Color.init(red: 242/255, green: 242/255, blue: 242/255)) .border(.black) .frame(height: 200) .introspectTextView { textView in // 重置文本存储代理,缓解缓存冲突 textView.textStorage.delegate = nil // iOS 16+ 可强制关闭TextKit 2 if #available(iOS 16.0, *) { textView.isTextKit2Enabled = false } } } }
注意:Introspect的兼容性依赖于SwiftUI版本,若无法找到底层UITextView,优先使用方案1。
临时应急方案(不推荐)
如果需要快速临时修复,可以拦截Apple Pencil的长按手势,但这会影响正常的文本选择功能:
TextEditor(text: $text) .background(Color.init(red: 242/255, green: 242/255, blue: 242/255)) .border(.black) .frame(height: 200) .gesture(LongPressGesture().onEnded { _ in // 拦截手势,不触发系统默认交互 }, including: .all)
内容的提问来源于stack exchange,提问作者Mike R
相关产品推荐
相关产品推荐

