Chatbot ui wie Chatgpt in iOS SwiftuiIOS

Programmierung für iOS
Anonymous
 Chatbot ui wie Chatgpt in iOS Swiftui

Post by Anonymous »

Ich arbeite an einer Chatbot -App und möchte eine Chat -Oberfläche implementieren, die der UI von Chatgpt ähnelt. Wenn ein Benutzer eine Nachricht sendet: < /strong> < /p>

Die vorherigen Nachrichten sollten oben aus der Ansicht herausziehen. < /Li>
Die neueste Nachricht des Benutzers sollte oben angezeigt werden. Natürlich - ohne Animationsstörungen oder abrupte Sprünge. Wenn dies der Fall ist, versuche ich, den Offset der Last "Benutzer" -Meldung auf 0 zu setzen. Ich kann ihn jedoch nicht genau oben auf dem Bildschirm positionieren. /> Sobald der Benutzer mit dem Scrollen beginnt, sollte IsNewMessage falsch werden und die Bildlaufansicht sollte sich normal verhalten.

Code: Select all

import SwiftUI

struct ChatView: View {
@State private var messages: [ChatMessage] = [
ChatMessage(text: "Hello! How can I assist you today?", isAssistant: true),
ChatMessage(text: "I need help with SwiftUI!", isAssistant: false),
ChatMessage(text: "Sure! What do you need help with in SwiftUI?", isAssistant: true)
]
@State private var scrollOffset: [UUID: CGFloat] = [:]
@State private var isNewMessage: Bool = false

var body: some View {
VStack {
ScrollViewReader { scrollProxy in
ScrollView {
VStack(alignment: .leading, spacing: 10) {
ForEach(messages) { message in
MessageView(
message: message,
messages: messages,
isNewMessage: $isNewMessage,
scrollOffset: $scrollOffset
)
.id(message.id)
}
}
.padding()
}
.onChange(of: isNewMessage) { newValue in
if newValue, let lastUserMessage = messages.last(where: { !$0.isAssistant }) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { // Wait for UI update
scrollProxy.scrollTo(lastUserMessage.id, anchor: .top) //  Move last user message to top
}
}
}
}

// Input Field
HStack {
TextField("Type a message...", text: .constant(""))
.textFieldStyle(RoundedBorderTextFieldStyle())

Button(action: sendMessage) {
Text("Send")
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
}
.padding()
}
}

private func sendMessage() {
let newMessage = ChatMessage(text: "How can I create a smooth scrolling chat UI like ChatGPT?", isAssistant: false)
messages.append(newMessage)
isNewMessage = true // Trigger auto-scroll

DispatchQueue.main.asyncAfter(deadline: .now() + 0.3){
let assistantMessage = ChatMessage(text: "Hold on, let me fetch the best answer for you!", isAssistant: true)
messages.append(assistantMessage)
}
}
}

//MARK: - MessageView
struct MessageView: View {
let message: ChatMessage
let messages: [ChatMessage]
@Binding var isNewMessage: Bool
@Binding var scrollOffset: [UUID: CGFloat]
@State private var safeAreaTop: CGFloat = 0

var body: some View {
ZStack(alignment: .topLeading) {
RoundedRectangle(cornerRadius: 20)
.foregroundColor(.white)

HStack(alignment: .top) {
if message.isAssistant {
MessageContentView(
message: message,
isNewMessage: $isNewMessage,
scrollOffset: $scrollOffset
)
.contentShape(Rectangle())

Spacer()
} else {
Spacer()
MessageContentView(
message: message,
isNewMessage: $isNewMessage,
scrollOffset: $scrollOffset
)
.contentShape(Rectangle())
}
}
.padding()
}

.background(GeometryReader { geometry in
Color.clear
.onAppear {
if safeAreaTop == 0 { // Capture safe area only once
let systemSafeArea = getSafeAreaTop()
let customNavBarHeight:  CGFloat = 40
safeAreaTop = systemSafeArea + customNavBarHeight
}

let messageOffset = geometry.frame(in: .global).minY - safeAreaTop

if isNewMessage, isLastUserMessage(message, messages: messages) {
scrollOffset[message.id] = 0 // Force last user message to offset 0
} else {
scrollOffset[message.id] = max(0, messageOffset)
}
}
.onChange(of: geometry.frame(in: .global).minY) { newValue in
let messageOffset = newValue - safeAreaTop

if isNewMessage, isLastUserMessage(message, messages: messages) {
scrollOffset[message.id] = 0 // Keep last user message at offset 0
} else {
scrollOffset[message.id] = max(0, messageOffset)
}
}
})

}

///  Check if the message is the last user message
private func isLastUserMessage(_ message: ChatMessage, messages: [ChatMessage]) -> Bool {
guard let lastUserMessage = messages.last(where: { !$0.isAssistant }) else {
return false
}
return lastUserMessage.id == message.id
}

/// Get Safe Area Top
private func getSafeAreaTop() -> CGFloat {
return (UIApplication.shared.connectedScenes.first as? UIWindowScene)?
.windows.first?.safeAreaInsets.top ?? 0
}

}

//MARK: - MessageContentView
struct MessageContentView: View {
let message: ChatMessage
@Binding var isNewMessage: Bool
@Binding var scrollOffset: [UUID: CGFloat]
var body: some View {
if message.isTyping {
TypingIndicatorView()
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.top, 10)
} else {
VStack(alignment: .leading, spacing: 5){
Text("Offset: \(scrollOffset[message.id] ?? 0, specifier: "%.2f")")
.font(.caption)
.foregroundColor(.gray)

Text(message.text ?? "")
.font(.body)
.padding(10) // Add padding inside the bubble
.background(
RoundedRectangle(cornerRadius: 10) // Rounded corners
.fill(message.isAssistant ? Color.gray : Color("appPrimaryColor").opacity(0.7)) // Different colors for sender & receiver
)

.foregroundColor(Color("appTextColor"))
.lineLimit(nil)
.fixedSize(horizontal: false, vertical: true)
}

}
}
}

struct ChatMessage: Identifiable {
let id = UUID()
let text: String
let isAssistant: Bool
var isTyping: Bool = false
}

#Preview{
ChatView()
}

//MARK: - TypingIndicatorView
struct TypingIndicatorView: View {
@State private var currentDot = 0
private let dotCount = 3
private let animationSpeed = 0.3 // Time between dots

var body: some View {
HStack(spacing: 4) {
ForEach(0..

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post