HomeiOS DevelopmentSwiftUI scrollview habits in ChatGPT like UI

SwiftUI scrollview habits in ChatGPT like UI


I am attempting to mimmic the chat habits of in style AI chat assistants resembling ChatGPT, Le Chat or Perplexity in iOS with SwiftUI.

The principle a part of the habits I am attempting to mimmic is that if the consumer sends a message the query and reply pair are seen on the highest of the display screen. If there’s already an energetic query and reply pair which is seen that pair ought to scroll up so the brand new query and reply pair is seen. Scrolling down will nonetheless reveal all of the earlier pairs. Sending one other message will scroll the whole lot up once more so solely the brand new pair is seen.

This habits is working fairly properly, apart from the scroll view habits when a keyboard seems and there are already query and reply pairs which have been scrolled as much as solely present the most recent pair. As soon as the keyboard seems the scrollview will scroll down. However I would really like the scrollview to disregard the keyboard and never scroll in any respect to resize issues.

I’ve tried a number of issues, like including .ignoresSafeArea(.keyboard) or utilizing padding as a substitute of a body with a min top. However I’m kinda misplaced in what different choices I might attempt. Maybe utilizing one thing like Swift Introspect to entry the underlying UIScrollView, however I am additionally not sure about what properties to switch there to deal with this situation.

Under are a video exhibiting the difficulty and the code used to demo the difficulty and implementing my present chat habits.

The video beneath exhibits the difficulty:

SwiftUI scrollview habits in ChatGPT like UI

Code from the video:

struct DemoView: View {
    
    struct Message: Identifiable {
        
        let id: UUID
        let content material: String
    }
    
    @State non-public var messages: [Message] = []
    @State non-public var scrollPosition: UUID?
    @State non-public var message = ""
    
    var physique: some View {
        NavigationStack {
            VStack {
                GeometryReader { geometryProxy in
                    let scrollViewHeight = geometryProxy.measurement.top
                    
                    ScrollView {
                        VStack(alignment: .main, spacing: 10) {
                            ForEach(messages) { message in
                                VStack {
                                    VStack(alignment: .trailing) {
                                        Textual content(message.content material)
                                            .multilineTextAlignment(.main)
                                            .padding(.backside, 20)
                                            .body(maxWidth: .infinity, alignment: .trailing)
                                    }
                                    
                                    VStack(alignment: .main) {
                                        Textual content("Assistant reply")
                                            .multilineTextAlignment(.main)
                                            .padding(.backside, 20)
                                            .body(maxWidth: .infinity, alignment: .main)
                                    }
                                }
                                .body(
                                    minHeight: message.id == messages.final?.id ? scrollViewHeight : 0,
                                    alignment: .high
                                )
                            }
                        }
                        .body(maxWidth: .infinity)
                        .padding(.horizontal, 20)
                        .ignoresSafeArea(.keyboard, edges: .backside)
                    }
                    .scrollPosition(id: $scrollPosition, anchor: .backside)
                }
                .safeAreaInset(edge: .backside) {
                    VStack {
                        TextField("Ask something...", textual content: $message)
                            .multilineTextAlignment(.main)
                            .font(.physique)
                            .onSubmit {
                                guard !message.isEmpty else {
                                    return
                                }
                                
                                let id = UUID()
                                
                                messages.append(Message(id: id, content material: message))
                                message = ""
                                
                                withAnimation {
                                    scrollPosition = id
                                }
                            }
                    }
                    .contentShape(Rectangle())
                    .body(maxWidth: .infinity)
                    .padding(16)
                    .background(.grey)
                    .clipShape(RoundedRectangle(cornerRadius: 16, fashion: .steady))
                }
            }
            .padding(16)
        }
    }
}

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments