swiftuiswiftui-navigationlink

SwiftUI Navigation Link Arrow/Chevron modify position


I'm trying to recreate the messages app from Apple, I noticed that their chevron on the chat selection is at the top and not the center like how navigationLink gives by default.

Here is an image of Apple Messages:

Apple Image

Here is an image of my application:

enter image description here

import SwiftUI

struct ContentView: View {
    
    private var imageSize: CGFloat = 50
    var body: some View {
        NavigationStack {
            List(NavigationScreens.allCases, id: \.self) { screen in
                NavigationLink(value: screen) {
                    Label(title: {
                        MessageChat(name: "SwiftUI")
                            .padding(.leading, 15)
                            //.border(.red)
                    }, icon: {
                        HStack {
                            Image(systemName: "circle.fill")
                                .resizable()
                                .frame(width: 10, height: 10)
                            Image("image_test")
                                .resizable()
                                .frame(width: imageSize, height: imageSize)
                                .clipShape(Circle())
                        }
                        .padding(10)
                    })
                }
            }
            .navigationTitle("Messages")
            .listStyle(.plain)
            .scrollContentBackground(.hidden)
            .navigationDestination(for: NavigationScreens.self) { screen in
                switch screen {
                case .Chat, .Profile, .Settings:
                    ChatSelection()
                }
            }
        }
    }
}

I'm not entirely sure if this is the correct way of doing it or if a custom NavigationLink would be needed.

Message Chat Code:

struct MessageChat: View {
@State var contact: Contact

var body: some View {
    HStack {
        VStack(alignment: .leading, spacing: 4) {
            HStack {
                // MARK: Name
                Text(contact.name)
                    .font(.headline)
                    .lineLimit(1)
                    .foregroundStyle(Color.primary)
                
                Spacer()
                
                // MARK: Message Date
                Text("12/12/12")
                    .font(.subheadline)
                    .foregroundStyle(Color.gray)
            }
            .padding(.trailing, 20)
            
            // MARK: Message Description
            Text("Some random text long enough to have the multiple lines effect, just in case its not long enough")
                .lineLimit(2)
                .font(.subheadline)
                .foregroundStyle(Color.secondary)
                .frame(maxHeight: 40, alignment: .topLeading)
        }
    }
    .frame(maxWidth: .infinity, maxHeight: .infinity)
}

}


Solution

  • If you move the NavigationLink into the view MessageChat then you can get the styling you are after:

    struct MessageChat: View {
        let name: String
        let screen: NavigationScreens
    
        var body: some View {
            HStack {
                VStack(alignment: .leading, spacing: 4) {
                    NavigationLink(value: screen) {
                        HStack {
                            // ... name + message date, as before
                        }
                        .padding(.trailing, 20)
                    }
                    // ... description, as before
                }
            }
        }
    }
    

    The List then changes to something like:

    List(NavigationScreens.allCases, id: \.self) { screen in
        Label {
            MessageChat(name: "SwiftUI", screen: screen)
                .padding(.leading, 15)
        } icon: {
            HStack {
                // ... images, as before
            }
            .padding(10)
        }
    }
    

    Screenshot

    Surprisingly, this turns the whole row into a link! This is why the dot on the left is in the active button color (blue), instead of the default foreground color.

    Btw, you shouldn't be passing in a State variable as a parameter. So inside MessageChat, contact should either be a let or var variable, or a Binding, but not a State variable.