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 versuche, das Chat -Verhalten der beliebten AI -Chat -Assistenten wie Chatgpt, LE -Chat oder Verwirrung in iOS mit Swiftui zu mikeln. Wenn es bereits ein aktives Frage- und Antwortpaar gibt, das...
Ich versuche, das Chat -Verhalten der beliebten AI -Chat -Assistenten wie Chatgpt, LE -Chat oder Verwirrung in iOS mit Swiftui zu mikeln. Wenn es bereits ein aktives Frage- und Antwortpaar gibt, das...
Ich verwende einen benutzerdefinierten Modifikator namens AutoSheetDetentModifier, um die Größe eines Blatts automatisch basierend auf seinem Inhalt anzupassen.
Unter iOS 26 funktioniert es wie...
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 verwende Openais API in Python, und im Moment wird die Antwort erst zurückgegeben, nachdem die vollständige Antwort generiert wurde.
Beispiel:
response = client.chat.completions.create(
model=...