3

I have a messaging interface. When user types in to the texteditor it will be append to messagesDBArray and will be displayed in textview. Once new messages are there it should scroll to the bottom. But I'm having issues.

Errors: no errors

  1. RoundedRectangle background colour green overflows from corners (does not crop as rounded)
  2. TextEditor (not textview) is not transparent (so it can have rounded rectangle color underneath)
  3. proxy.scrollTo(id, anchor: .bottom) does not scrolls to the last message.
import SwiftUI
    
final class ViewModel: ObservableObject {
    @Published var messagesDBArray : [SingleMessageBubbleModel] = []
}
    
struct SingleMessageBubbleModel: Identifiable {
    let id = UUID()
    var text: String
    var received: Bool
    var timeStamp: Date
}
    
var messagesDBArray : [SingleMessageBubbleModel] = []
    
struct ContentView: View {
    @ObservedObject private var messageArrayObservedObject = ViewModel()
        
    @State private var showOnTheSpotMessaging: Bool = true
    @State var textTyped: String = ""
        
    var body: some View {
    VStack (alignment: .center) {    
        ZStack (alignment: .center) {      
            HStack () {
                RoundedRectangle(cornerRadius: 25, style: .continuous)
                    .stroke(Color.brown, lineWidth: 1)
                    .frame(width: 300, alignment: Alignment.top )
                    .padding([.bottom], 5)
                    .clipped()
                    .background(Color.green)        
            }
            HStack () {
                ScrollViewReader { proxy in
                    ScrollView {
                        LazyVStack {
                            ForEach(
                                messageArrayObservedObject.messagesDBArray,
                                id: \.id
                            ) {
                                message in MessageBubble(message: message)
                            }
                        }
                    }
                    .frame(alignment: .center)
                    .background(Color.clear)
                    .padding (.vertical, 5)
                    .padding (.horizontal,5)
                    .padding(.bottom, 5)
                    
                    .onChange(
                        of: messageArrayObservedObject.messagesDBArray.count
                    ) { id in
                        // When the lastMessageId changes, scroll to the bottom of the conversation
                        withAnimation {
                            proxy.scrollTo(id, anchor: .bottom)
                        }
                    }
                }
                .frame( height: 200, alignment: .center)
            }
            .frame(width: 295, alignment: Alignment.center )  
        } 
        HStack () {
            VStack {    
                ZStack (alignment: .center) {
                    HStack () {
                        RoundedRectangle(cornerRadius: 25, style: .continuous)
                            .stroke(Color.brown , lineWidth: 1)
                            .frame(width: 295, alignment: Alignment.top )
                            .padding([.bottom], 5)
                            .clipped()
                            .background(Color.green)
                            // .background(Color("#E5F2E4"))
                    }
                    HStack () {
                        TextEditor (text: $textTyped)
                            .frame(height: 200, alignment: .leading)
                            .padding(.horizontal, 10)
                            .background(.clear)
                        }
                    }
                    .frame(width: 290, alignment: Alignment.top )
                    .padding(.top, 5)
                }
            }   
        }
    }
    struct MessageBubble: View {
        var message: SingleMessageBubbleModel
        @State private var showTime = false
        
        var body: some View {
            VStack(alignment: message.received ? .leading : .trailing) {
                HStack {
                    Text(message.text)
                        .padding()
                        .background(message.received ? Color.gray : Color.blue)
                        .cornerRadius(30)
                }
                .frame(maxWidth: 300, alignment: message.received ? .leading : .trailing)
                .onTapGesture {
                    withAnimation {
                    showTime.toggle()
                }
            }  
        }
        .frame(maxWidth: .infinity, alignment: message.received ? .leading : .trailing)
        .padding(message.received ? .leading : .trailing)
        .padding(.horizontal, 4)
    }
}
Tyler2P
  • 2,324
  • 26
  • 22
  • 31
TheDummy
  • 65
  • 7

1 Answers1

0

for the first error you should use that code instead of your code where you make a background with RoundRectangle the same to your base rectangle and make the fill of that the color you want which is green

              RoundedRectangle(cornerRadius: 25, style: .continuous)
                    .stroke(Color.brown, lineWidth: 1).background(RoundedRectangle(cornerRadius: 25).fill(Color.green))
                     .frame(width: 300, alignment: Alignment.top )
                     .padding([.bottom], 5)
                     .clipped()

the second issue in your ContentView you should init your UITextView background color to clear and after that make your textEditor Color clear using that code

    init() {
       UITextView.appearance().backgroundColor = .clear
   }

and make your textEditor background clear

TextEditor (text: $textTyped)
                .frame(height: 200, alignment: .leading)
                .padding(.horizontal, 10)
                .background(Color.clear)

and the third issue is that i think you are using the array count but you should use the id of each message so when if we suppose that the last message-id is 728398 in your onChange

onChange(of: messageArrayObservedObject.messagesDBArray.count) { id in
                // When the lastMessageId changes, scroll to the bottom of the conversation
                withAnimation {
                    print("ididididid\(id)")
                    proxy.scrollTo(messageArrayObservedObject.messagesDBArray.last, anchor: .bottom)
                }
            }

your are using the ( messageArrayObservedObject.messagesDBArray.count )counts of messages like 5 message so you are scrolling to 5 not to the id of message which is 728398

belal medhat
  • 462
  • 1
  • 4
  • 18
  • For the 3rd one, it throws an error when I try to feed the id to a variable `lastMessageId` `ForEach(messageArrayObservedObject.messagesDBArray, id: \.id) { message in MessageBubble(message: message) lastMessageID = message.id }` Error thrown is `Type '()' cannot conform to 'View'` and how to retrieve the last item id in the array? – TheDummy Apr 21 '22 at 10:18
  • For the 2nd one with `init()`, it throws an error `Return from initializer without initializing all stored properties` – TheDummy Apr 21 '22 at 10:21
  • For the third try to use array.last it will get the last element on array – belal medhat Apr 21 '22 at 14:09
  • For the 2nd try to see that https://stackoverflow.com/questions/65865182/transparent-background-for-texteditor-in-swiftui it should help – belal medhat Apr 21 '22 at 14:13
  • `.onChange(of: messageArrayObservedObject.messagesDBArray.last?.id) { id in proxy.scrollTo(id, anchor: .bottom) }` doesn't work – TheDummy Apr 21 '22 at 15:05
  • should be like that. .onChange(of: messageArrayObservedObject.messagesDBArray) { id in proxy.scrollTo(messageArrayObservedObject.messagesDBArray.last, anchor: .bottom) – belal medhat Apr 22 '22 at 00:18
  • then it throws error : `Struct 'ScrollViewProxy' requires that 'SingleMessageBubbleModel' conform to 'Hashable'` – TheDummy Apr 22 '22 at 17:52
  • confirm your SingleMessageBubbleModel to Hashable protocol like that in struct SingleMessageBubbleModel : Hashable – belal medhat Apr 22 '22 at 18:50
  • error is gone but it still does not scroll to last item. – TheDummy Apr 23 '22 at 12:34