SwiftUI TextField cut in half

Viewed 408

I am a novice when it comes to Swift Programming and am trying to learn how to build my own chat app via some Youtube tutorials. I've gotten pretty far but hit a snag. I don't understand why when I try to scroll up on this view in SwiftUI, the message is cut off halfway through. I thought it had something to do with padding, but padding should be set to all. Check out the screenshot below:

Swift text being cut off

I'm not sure what I'm doing wrong in my code as everything seems to work. My code is attached below. Any help would be appreciated!

import SwiftUI

struct ChatUIView: View {
    
    @State var message = ""
    //StateObject is the owner of the object...
    @StateObject var allMessages = Messages()
    
    var body: some View {
        VStack{
            
            ZStack{
                
                /*
                HStack{
                    Spacer()
                }*/
                
                VStack(spacing:5){
                    Text("Chat")
                        .fontWeight(.bold)
                }
                .foregroundColor(.white)
            }
            .padding(.all)
            
            //Spacer()
            VStack{
                
                //Spacer()
                //Displaying Message...
                
                ScrollView(.vertical, showsIndicators: false, content: {
                    
                    ScrollViewReader{reader in
                        
                        VStack(spacing: 20){
                            
                            ForEach(allMessages.messages){msg in
                                
                                //Chat Bubbles...
                                
                                ChatBubble(msg: msg)
                            
                                
                            }
                            //whenever new data is inserted, scroll to bottom...
                            .onChange(of: allMessages.messages) {(value) in
                                
                                //scrolling only user message...
                                
                                if value.last!.myMsg{
                                    reader.scrollTo(value.last?.id)
                                }
                                
                            }
                        }
                        .padding([.horizontal, .bottom])
                        .padding(.top, 25)
                    }
                })
                
                HStack(spacing:15){
                    
                    HStack(spacing: 15){
                        TextField("Message", text: self.$message)
                    }
                    .padding(.vertical, 12)
                    .padding(.horizontal)
                    .background(Color.black.opacity(0.06))
                    .clipShape(Capsule())
                    
                    
                    //send button
                    //hiding view...
                    if message != ""{
                        Button(action: {
                            
                            //appending message...
                            
                            //adding animation...
                            withAnimation(.easeIn){
                                allMessages.messages.append(Message(id: Date(), message: message, myMsg: false))
                            }
                            message = ""
                            
                        }, label: {
                            
                            Image("send")
                                .resizable()
                                .frame(width: 25, height: 25)
                                .rotationEffect(.init(degrees: 45))
                                .padding()
                                //.aspectRatio(contentMode: .fit)
                                //.font(.system(size: 0.5))
                                //.padding(.all)
                                .background(Color.black.opacity(0.07))
                                .clipShape(Circle())
                        })
                    }
                }
                .padding(.horizontal)
                .animation(.easeOut)
            }
            .padding(.bottom, UIApplication.shared.windows.first?.safeAreaInsets.bottom)
            .background(Color.white)
            .clipShape(RoundedShape())
        }
        //.edgesIgnoringSafeArea(.bottom)
        .background(Color.blue.edgesIgnoringSafeArea(.top))
    }
}

//Chat Bubbles...

struct ChatBubble : View {
    
    var msg : Message
    var body: some View{
        
        //Automatics scroll to bottom...
        //First assigning id to each row
        
        HStack(alignment: .top, spacing: 10){
            
            if msg.myMsg{
                
                //pushing msg to the left...
                
                //minimum space ...
                Spacer(minLength: 25)
                
                Text(msg.message)
                    .padding(.all)
                    .background(Color.black.opacity(0.06))
                    //.cornerRadius(15)
                    .clipShape(BubbleArrow(myMsg: msg.myMsg))
            } else {
                
                //pushing msg to the right...
                
                Text(msg.message)
                    .lineLimit(nil)
                    .foregroundColor(.white)
                    .padding(.all)
                    //.background(Color.black.opacity(0.06))
                    .background(Color.blue)
                    .clipShape(BubbleArrow(myMsg: msg.myMsg))
                
                Spacer(minLength: 25)
            }
        }
        .id(msg.id)
        //.padding(msg.myMsg ? .leading : .trailing, 55)
        //.padding(.vertical,10)
    }
}

// Bubble Arrow...

struct BubbleArrow : Shape {
    
    var myMsg : Bool
    
    
    func path(in rect: CGRect) -> Path {
        let path = UIBezierPath(roundedRect: rect, byRoundingCorners: myMsg ? [.topLeft, .bottomLeft, .bottomRight] : [.topRight, .bottomLeft, .bottomRight], cornerRadii: CGSize(width: 10, height: 10))
        
        return Path(path.cgPath)
    }
}


// Custom Rounded Shape...

struct RoundedShape : Shape {
    
    func path(in rect: CGRect) -> Path {
        let path = UIBezierPath(roundedRect: rect, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: 35, height: 35))
        
        return Path(path.cgPath)
    }
}

// Model Data For Message...

struct Message : Identifiable, Equatable {
    
    var id: Date
    var message: String
    var myMsg: Bool
    //var profilePic: String
    //var photo: Data?
    
}

class Messages: ObservableObject {
    
    @Published var messages : [Message] = []
    
    //sample data...
    init() {
        let strings = ["Hi!", "hello!", "How are you doing?!", "Fine, I just want to talk about life", "ok, I may be able to help with that", "This is awesome, thanks", "So what do you want to talk about?", "movies sound like a good topic. Let's start there!", "Ok, so tell me: What's you favorite movie?", "Definitely, interstellar for sure."]
        
        for i in 0..<strings.count{
            
            //simple logic for two sided message View...
            
            messages.append(Message(id: Date(), message: strings[i], myMsg: i % 2 == 0 ? false : true))
        }
    }
    
    func writeMessage(id: Date, msg: String, photo: Data?, myMsg: Bool){
        
        messages.append((Message(id: id, message: msg, myMsg: myMsg)))
        
        
    }
}

2 Answers

The problem is using .clipShape(RoundedShape()) on the VStack containing the ScrollView, TextField and Button. If you notice, it is not just that the message is cut in half, but that no other messages(there is one more) are showing passed that. The custom shape RoundedShape is not large enough to encompass all of the views. When I switch it to Rectangle, a built in shape, everything shows. I can't see why in the View Hierarchy, but it is the problem. I will leave it to someone else to determine what is happening with it.

When you are trying to debug your view hierarchy, start with your modifiers. Comment them out until something changes and then explore from there. Also, the orders of modifiers matter as using a modifier on a view gives you a whole new view. Try something as simple as putting .padding before a .font on Text and you will get an error. That is because a padded view has no modifier font, even though the modified view does have a font.

You may try using fixedSize:

Text(msg.message)
    .fixedSize(horizontal: false, vertical: true)
Related