You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 03:05:48