Просмотр чата Swiftui: клавиатура нажимает весь просмотр вместо корректировки области контентаIOS

Программируем под IOS
Ответить Пред. темаСлед. тема
Anonymous
 Просмотр чата Swiftui: клавиатура нажимает весь просмотр вместо корректировки области контента

Сообщение Anonymous »

Я строю интерфейс чата в Swiftui, похожий на приложение Chatgpt, и я хочу, чтобы клавиатура имела аналогичное поведение на imessage, когда пользователь набирает ящик ввода, является выше клавиатуры, прокрутки чата и заголовок остаются в вершина. Но у меня проблемы с обработкой клавиатуры. Когда появляется клавиатура, она нажимает весь вид вместо того, чтобы просто настраивать область содержания чата, сохраняя при этом заправленное заголовок и поле ввода над клавиатурой. />
  • Когда появляется клавиатура, весь вид (включая заголовок) поднимается < /li>
    Поле ввода иногда скрывается за клавиатурой < /li>
    Область прокрутки контента не регулируется правильно < /li>
    < /ul>
    code < /h2>
    Вот моя текущая реализация для Мой чат -раз
    (у меня также есть SideMenuview, который, кажется, тоже подталкивается) < /p>
    import SwiftUI

    struct ChatView: View {
    @State private var isMenuOpen = false
    @State private var showSettings = false
    @State private var showModelSelection = false
    @State private var dragOffset: CGFloat = 0
    @StateObject private var modelSelectionVM = ModelSelectionViewModel()
    @StateObject private var chatHistoryVM = ChatHistoryViewModel()
    @StateObject private var chatVM: ChatMessageViewModel
    @FocusState private var isTextFieldFocused: Bool

    private let menuWidth: CGFloat = UIScreen.main.bounds.width * 0.75

    init() {
    let modelVM = ModelSelectionViewModel()
    let historyVM = ChatHistoryViewModel()
    _modelSelectionVM = StateObject(wrappedValue: modelVM)
    _chatHistoryVM = StateObject(wrappedValue: historyVM)
    _chatVM = StateObject(wrappedValue: ChatMessageViewModel(modelSelectionVM: modelVM, chatHistoryVM: historyVM))
    }

    var body: some View {
    ZStack(alignment: .leading) {
    Color(.systemBackground)
    .ignoresSafeArea()

    // Dim background when menu is open
    if isMenuOpen {
    Color.black.opacity(0.4)
    .ignoresSafeArea()
    .onTapGesture { closeMenu() }
    }

    // Side Menu
    SideMenuView(isMenuOpen: $isMenuOpen,
    showSettings: $showSettings,
    dragOffset: $dragOffset,
    chatHistoryVM: chatHistoryVM,
    chatVM: chatVM)
    .offset(x: isMenuOpen ? 0 : -menuWidth)

    VStack(spacing: 0) {
    // **Fixed Header**
    HStack {
    Button(action: toggleMenu) {
    Image(systemName: "line.horizontal.3")
    .font(.title)
    .foregroundColor(.blue)
    }
    .frame(width: 44, height: 44)

    Spacer()

    Button(action: { showModelSelection = true }) {
    Text(modelSelectionVM.selectedModel?.name ?? "Select Model")
    .foregroundColor(modelSelectionVM.selectedModel == nil ? .red : .primary)
    }

    Spacer()

    Button(action: {
    if chatVM.messages.isEmpty {
    chatVM.isTemporaryChat.toggle()
    } else {
    chatHistoryVM.selectedChatId = nil
    chatVM.clearMessages()
    }
    }) {
    Image(systemName: chatVM.messages.isEmpty ?
    (chatVM.isTemporaryChat ? "timer.circle.fill" : "timer.circle") :
    "plus.circle.fill")
    .font(.title)
    .foregroundColor(chatVM.isTemporaryChat ? .orange : .blue)
    }
    .frame(width: 44, height: 44)
    }
    .padding(.horizontal)
    .padding(.top, 50)
    .frame(height: 50) // Fix header height
    .background(Color(.systemBackground))
    .zIndex(1) // Ensure header stays fixed

    // **Scrollable Messages**
    ScrollViewReader { proxy in
    ScrollView {
    LazyVStack(spacing: 8) {
    if chatVM.isTemporaryChat && chatVM.messages.isEmpty {
    Text("This chat won't appear in history, use or create memories, or be used to train our models. For safety purposes, we may keep a copy of this chat for up to 30 days.")
    .foregroundColor(.secondary)
    .multilineTextAlignment(.center)
    .padding()
    }

    ForEach(chatVM.messages) { message in
    ChatMessageView(message: message)
    .id(message.id)
    }
    }
    .padding(.bottom, 8)
    }
    .onChange(of: chatVM.messages.count) { _ in
    scrollToBottom(proxy: proxy)
    }
    }
    .padding(.bottom, 10)

    // **Input Field**
    VStack(spacing: 0) {
    Divider()
    HStack(spacing: 16) {
    TextField("Type a message...", text: $chatVM.currentInput)
    .padding(.horizontal, 16)
    .padding(.vertical, 12)
    .background(Color(.systemGray6))
    .cornerRadius(20)
    .focused($isTextFieldFocused)
    .submitLabel(.send)
    .onSubmit {
    chatVM.sendMessage()
    }

    Button(action: {
    chatVM.sendMessage()
    }) {
    Image(systemName: "arrow.up.circle.fill")
    .font(.system(size: 30))
    .foregroundColor(chatVM.currentInput.isEmpty || chatVM.isTyping ? .gray : .blue)
    }
    .disabled(chatVM.currentInput.isEmpty || chatVM.isTyping)
    }
    .padding(.horizontal)
    .padding(.vertical, 8)
    }
    .background(Color(.systemBackground))
    .ignoresSafeArea(.keyboard, edges: .bottom)
    }
    .frame(width: UIScreen.main.bounds.width)
    .background(Color(.systemBackground))
    .offset(x: dragOffset + (isMenuOpen ? menuWidth : 0))
    }
    .gesture(
    DragGesture()
    .onChanged { gesture in
    let translation = gesture.translation.width
    if isMenuOpen {
    dragOffset = Swift.min(0, translation)
    } else {
    dragOffset = Swift.max(0, translation)
    }
    }
    .onEnded { gesture in
    let translation = gesture.translation.width
    let velocity = gesture.velocity.width

    withAnimation(.spring(response: 0.3, dampingFraction: 0.9)) {
    if isMenuOpen {
    isMenuOpen = translation > -menuWidth / 2 || velocity > 500
    } else {
    isMenuOpen = translation > menuWidth / 2 || velocity > 500
    }
    dragOffset = 0
    }
    }
    )
    .overlay {
    if showSettings {
    SettingsView(isPresented: $showSettings)
    .transition(.move(edge: .bottom))
    }

    if showModelSelection {
    ModelSelectionView(isPresented: $showModelSelection, viewModel: modelSelectionVM)
    .transition(.move(edge: .bottom))
    }
    }
    }
    < /code>
    ожидаемое поведение < /h2>

    Заголовок должен оставаться фиксированным в верхней части < /li>
    Чат Область содержания должна настроить его размер и прокрутка, когда появляется клавиатура < /li>
    Поле ввода должно оставаться видимым и перемещаться с клавиатурой < /li>
    Аналогично тому, как imessage обрабатывает клавиатуру Внешний вид
    < /ul>
    что я попробовал < /h2>
    • Использование .IgnoressaFearea (.keyboard, края: .bottom) < / Code>
    • Добавление наблюдателей за клавиатурой и вручную настраивать позиции просмотра
    • Использование GeometryReader для управления макетом
    • Различные комбинации ZSTACK и VSTACK
    • Различные подходы с SafeAreainsets
    среда
  • ios 17+< /li>
    swiftui < /li>
    xcode 15.2
Вопрос
Как я могу правильно реализовать обработку клавиатуры в этом интерфейсе чата:
[*] Держите заголовок закрепленным в верхней части < /li>
Разрешить содержание чата регулировать и прокручивать надлежащим образом < /li>
Держите поле ввода видимым Выше клавиатуры
Поддерживайте плавную анимацию во время внешнего вида клавиатуры/исчезновения !

Подробнее здесь: https://stackoverflow.com/questions/794 ... ng-content
Реклама
Ответить Пред. темаСлед. тема

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «IOS»