Я работаю над приложением Chatbot и хочу реализовать интерфейс чата, похожий на пользовательский интерфейс CHATGPT. Когда пользователь отправляет сообщение: < /strong> < /p>
Предыдущие сообщения должны выходить из виду вверху. < /Li>
Последнее сообщение пользователя должно появиться вверх Загрузите естественно - без каких -либо анимационных сбоев или резких прыжков. Если это так, я пытаюсь установить последнее смещение «Пользовательское» пузырькового пузыря на 0. Однако я не могу установить его точно в верхней части экрана. Видимый.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..
Подробнее здесь: https://stackoverflow.com/questions/794 ... os-swiftui
Chatbot ui нравится чат в ios swiftui ⇐ IOS
Программируем под IOS
1741510096
Anonymous
Я работаю над приложением Chatbot и хочу реализовать интерфейс чата, похожий на пользовательский интерфейс CHATGPT. Когда пользователь отправляет сообщение: < /strong> < /p>
Предыдущие сообщения должны выходить из виду вверху. < /Li>
Последнее сообщение пользователя должно появиться вверх Загрузите естественно - без каких -либо анимационных сбоев или резких прыжков. Если это так, я пытаюсь установить последнее смещение «Пользовательское» пузырькового пузыря на 0. Однако я не могу установить его точно в верхней части экрана. Видимый.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..
Подробнее здесь: [url]https://stackoverflow.com/questions/79486243/chatbot-ui-like-chatgpt-in-ios-swiftui[/url]
Ответить
1 сообщение
• Страница 1 из 1
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...
Мобильная версия