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

iPadOS 16下SwiftUI TextEditor长文本输入跳转至顶部问题

iPadOS 16 下 TextEditor 输入时自动跳转至顶部的异常问题解决

问题概述

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

问题演示

TextEditor跳转顶部异常演示

复现代码

以下代码可复现该问题,粘贴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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 13:35:18