iPadOS 16下SwiftUI TextEditor长文本输入跳转至顶部问题
iPadOS 16 下 TextEditor 输入时自动跳转至顶部的异常问题解决
问题概述
在iPadOS 16系统中,SwiftUI的TextEditor组件存在异常行为:当用户输入新字符时,文本会自动跳转至顶部。该问题仅在输入5段及以上的多段落长文本时触发,严重影响长文本的撰写体验。
问题演示

复现代码
以下代码可复现该问题,粘贴5段及以上Lorem Ipsum文本后输入字符即可触发:
class EditorViewModel: ObservableObject { @Published var text: String = "" } struct EditorView: View { @ObservedObject var viewModel: EditorViewModel var body: some View { VStack { TextEditor(text: $viewModel.text) .frame(height: 80) .padding() .background(Color.red) Spacer() } } } struct ContentView: View { var body: some View { EditorView( viewModel: EditorViewModel() ) } }
解决方案
方案1:用UIViewRepresentable封装UITextView替代TextEditor
这是最可靠的解决方式,直接使用UIKit的UITextView规避SwiftUI组件的系统bug:
struct FixedTextView: UIViewRepresentable { @Binding var text: String func makeUIView(context: Context) -> UITextView { let textView = UITextView() textView.backgroundColor = .red textView.textContainerInset = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16) textView.delegate = context.coordinator return textView } func updateUIView(_ uiView: UITextView, context: Context) { // 保留光标位置,避免文本更新导致跳转 if uiView.text != text { let cursorPosition = uiView.selectedRange uiView.text = text uiView.selectedRange = cursorPosition } } 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 } } } // 替换原EditorView中的TextEditor struct EditorView: View { @ObservedObject var viewModel: EditorViewModel var body: some View { VStack { FixedTextView(text: $viewModel.text) .frame(height: 80) Spacer() } } }
方案2:临时规避:控制文本更新时机
通过本地文本绑定+状态控制,减少因@Published更新导致的组件重绘跳转,适合快速适配场景:
class EditorViewModel: ObservableObject { @Published var text: String = "" private(set) var isUserTyping = false func syncText(_ newText: String) { isUserTyping = true text = newText // 延迟标记输入结束,避免频繁更新触发跳转 DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { self.isUserTyping = false } } } struct EditorView: View { @ObservedObject var viewModel: EditorViewModel @State private var localText = "" var body: some View { VStack { TextEditor(text: $localText) .frame(height: 80) .padding() .background(Color.red) .onChange(of: localText) { newValue in if !viewModel.isUserTyping { viewModel.syncText(newValue) } } .onAppear { localText = viewModel.text } Spacer() } } }
说明
- 方案1彻底规避了SwiftUI TextEditor的系统bug,功能和原生TextEditor一致,推荐优先使用。
- 方案2是临时折中方案,可能存在极少数边缘情况(如快速连续输入),适合无法大规模修改代码的场景。
内容的提问来源于stack exchange,提问作者Pocheshire
相关产品推荐
相关产品推荐

