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

SwiftUI:垂直TextField嵌套ScrollView时自动滚动异常问题求助

解决ScrollView内垂直TextField自动滚动到底部的问题

针对ScrollView中垂直TextField获取焦点后卡顿滚动到底部的问题,试试这几个方案:

方案1:用ScrollViewReader锁定滚动位置

通过ScrollViewReader监听TextField焦点状态,在获取焦点时手动维持当前滚动位置,阻断系统自动滚动逻辑:

import SwiftUI

struct ContentView: View {
    @State var longText: String = "Lorem ipsum dolor..."
    @State private var textFieldFocused = false
    @State private var keepScrollPosition: CGFloat = 0
    
    var body: some View {
        ScrollView {
            TextField("", text: $longText, axis: .vertical)
                .padding()
                .focused($textFieldFocused)
                .onChange(of: textFieldFocused) { isFocused in
                    if isFocused {
                        // 记录当前滚动位置并强制回调
                        withAnimation(.none) {
                            keepScrollPosition = UIScrollView.appearance().contentOffset.y
                        }
                    }
                }
        }
        .scrollDismissesKeyboard(.interactively)
        .onAppear {
            keepScrollPosition = UIScrollView.appearance().contentOffset.y
        }
        .onChange(of: keepScrollPosition) { pos in
            UIScrollView.appearance().setContentOffset(CGPoint(x: 0, y: pos), animated: false)
        }
    }
}

方案2:禁用底层UITextView的自动滚动行为

通过访问TextField对应的底层UITextView,关闭自动滚动相关属性。可以用第三方库Introspect快速实现,也可以自定义封装UIKit组件:

用Introspect库实现

import SwiftUI
import Introspect

struct ContentView: View {
    @State var longText: String = "Lorem ipsum dolor..."
    
    var body: some View {
        ScrollView {
            TextField("", text: $longText, axis: .vertical)
                .padding()
                .introspectTextView { textView in
                    // 禁用自动滚动到顶部
                    textView.*scrollsToTop* = false
                    // 关闭TextView内部滚动,确保它随内容垂直扩展
                    textView.*isScrollEnabled* = false
                }
        }
        .scrollDismissesKeyboard(.interactively)
    }
}

自定义UIViewRepresentable封装(无第三方依赖)

import SwiftUI

struct AutoExpandingTextView: UIViewRepresentable {
    @Binding var text: String
    
    func makeUIView(context: Context) -> UITextView {
        let textView = UITextView()
        textView.isScrollEnabled = false
        textView.scrollsToTop = false
        textView.font = UIFont.systemFont(ofSize: 17)
        textView.delegate = context.coordinator
        return textView
    }
    
    func updateUIView(_ uiView: UITextView, context: Context) {
        uiView.text = text
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    class Coordinator: NSObject, UITextViewDelegate {
        var parent: AutoExpandingTextView
        
        init(_ parent: AutoExpandingTextView) {
            self.parent = parent
        }
        
        func textViewDidChange(_ textView: UITextView) {
            parent.text = textView.text
            // 强制更新高度,确保TextView随内容扩展
            textView.invalidateIntrinsicContentSize()
        }
    }
}

// 使用示例
struct ContentView: View {
    @State var longText: String = "Lorem ipsum dolor..."
    
    var body: some View {
        ScrollView {
            AutoExpandingTextView(text: $longText)
                .padding()
        }
        .scrollDismissesKeyboard(.interactively)
    }
}

UIKit替代方案的表现

在UIKit中,将UITextView放入UIScrollView时,默认也会出现键盘弹出时自动滚动到TextView的行为,但控制逻辑更直接:

  • 直接设置UIScrollView的*scrollsToTop* = false
  • 在textViewDidBeginEditing代理方法中手动调整UIScrollView的contentOffset维持当前位置
  • 关闭UITextView的*isScrollEnabled*让它随内容自动扩展

这种场景下不会出现SwiftUI里的卡顿动画,因为可以更精细地控制滚动与布局的交互逻辑。


内容的提问来源于stack exchange,提问作者guiri_del_norte

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 00:45:19