首页 > 解决方案 > 在 SwiftUI 中制作聊天应用程序:如何让 ScrollView 在键盘出现时保持原位?

问题描述

我正在 SwiftUI 中制作聊天应用程序。这是我想要的效果:在 Telegram 或 Whatsapp 中打开任何聊天,点击输入框。键盘向上滑动时,聊天内容向上滑动。因此,如果您正在查看底部消息,您仍然可以看到它。

我无法在 SwiftUI 中获得这种效果。键盘向上滑动不滑动聊天内容:

import SwiftUI

struct SlidingKeyboardTest: View {
    @State var inputText = "Placeholder"
    
    var body: some View {
        VStack {
            ScrollView {
                LazyVStack {
                    ForEach(1...100, id: \.self) { id in
                        HStack {
                            Spacer()
                            Text("message \(id)")
                            Spacer()
                        }
                    }
                }
            }
            TextEditor(text: $inputText)
                .frame(height: 50)
        }
        .background(LinearGradient(gradient: Gradient(colors: [.white, .blue, .white]), startPoint: .top, endPoint: .bottom))
        .onTapGesture { UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) }
    }
}

struct SlidingKeyboardTest_Previews: PreviewProvider {
    static var previews: some View {
        SlidingKeyboardTest()
    }
}

任何想法如何获得这种效果?

标签: swiftui

解决方案


您需要使用Introspect来接收对 UIScrollView 的访问并监听键盘高度的变化。

这是一个代码:

import Combine
import Introspect
import SwiftUI

struct SlidingKeyboardTest: View {
    @State var inputText = "Placeholder"
    @State var keyboardHeight = CGFloat(0)
    @State var scrollView: UIScrollView? = nil

    var body: some View {
        VStack {
            ScrollView {
                LazyVStack {
                    ForEach(1 ... 100, id: \.self) { id in
                        HStack {
                            Spacer()
                            Text("message \(id)")
                            Spacer()
                        }
                    }
                }
            }.introspectScrollView {
                scrollView = $0
            }
            TextEditor(text: $inputText)
                .frame(height: 50)
        }.onReceive(Publishers.keyboardHeight) { height in
            if height > 0 {
                self.scrollView!.setContentOffset(CGPoint(x: 0, y: self.scrollView!.contentOffset.y + height), animated: true)
            } else {
                self.scrollView!.contentOffset.y = max(self.scrollView!.contentOffset.y - keyboardHeight, 0)
            }

            keyboardHeight = height
        }

        .background(LinearGradient(gradient: Gradient(colors: [.white, .blue, .white]), startPoint: .top, endPoint: .bottom))
        .onTapGesture { UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) }
    }
}

struct SlidingKeyboardTest_Previews: PreviewProvider {
    static var previews: some View {
        SlidingKeyboardTest()
    }
}

推荐阅读