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.
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]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())
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)
.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 )
Ich verwende die Stylus -Erweiterung, um benutzerdefinierte CSS zu injizieren, die das versteckte Textproblem des Chatgpt -Seitenleiste beheben, indem das horizontale Scrollen aktiviert wird. Mein...
Ich habe eine Django -App, in der das Benutzerzeichen bei Google einsetzt und die Erlaubnis erteilt, auf Google -Geschäftsprofile zuzugreifen, um Bewertungen zu erstellen. Ich speichere diese Google...
Für eine Klasse versuche ich, Audio aus einer Chatgpt -API -Antwort zu streamen. Der folgende Code funktioniert hauptsächlich und ich bekomme eine gute Qualität, wenn ich die gespeicherte Datei...
Für eine Klasse versuche ich, Audio aus einer Chatgpt -API -Antwort zu streamen. Der folgende Code funktioniert hauptsächlich und ich bekomme eine gute Qualität, wenn ich die gespeicherte Datei...