NEW: Start my new Ultimate Portfolio App course with a free Hacking with Swift+ trial! >>

SOLVED: Icons in NavigationView Title, Apple Messages style

Forums > SwiftUI

Hi all,

I was wondering if there is a way to embed icons into the NavigationView title bar, inline with the title, the way the stock Messages app has it. I've seen .navigationBarItems(trailing:), but that puts the icons on a line above the title, like in the stock Phone app. My initial idea was passing an HStack to the .navigationBarTitle(title:) modifier and adding the title, a Spacer() and my icon, but that requires that title is a Text, not any View, eliminating that option. Is there a way to replicate the Messages style in SwiftUI?

EDIT: For reference, here is an image of what I'm aiming to achieve, vs what I'm able to achieve.

Thanks a lot, jakcharvat

   

I have hacked some code together for you. I know that there probably is a way more elegant way of doing this but it works. It does need a lot of tweeking but hopefully it will start you on your way. If anyone can improve on this or show the correct way of doing it, i'd be interested in learning how.

import SwiftUI

struct ContentView: View {
    @State private var midY: CGFloat = 0.0
    @State private var headerText = "Contacts"
    var body: some View {
        NavigationView {
            List {
                HStack {
                    //text
                    HeaderView(headerText: self.headerText, midY: $midY)
                        .frame(height: 40, alignment: .leading)
                        .padding(.top, 5)
                        .offset(x: -45)

                    HStack {
                        //button 1
                        Button(action: {
                            self.action1()
                        }) {
                            Image(systemName: "ellipsis.circle")
                                .font(.largeTitle)
                        }

                        //button 2
                        Button(action: {
                            self.action2()
                        }) {
                            Image(systemName: "pencil.circle")
                                .font(.largeTitle)
                        }
                    }.padding(EdgeInsets(top: 5, leading: 0, bottom: 0, trailing: 16))
                    .foregroundColor(.blue)

                } .frame(height: 40, alignment: .leading)
                    .opacity(self.midY < 70 ? 0.0 : 1.0)
                    .frame(alignment: .bottom)

                ForEach(0..<100){ count in
                    Text("Row \(count)")
                }

            }
            .navigationBarTitle(self.midY < 70 ? Text(self.headerText) : Text(""), displayMode: .inline)
            .navigationBarItems(trailing: self.midY < 70 ? HStack {
                //button 1
                Button(action: {
                    self.action1()
                }) {
                    Image(systemName: "ellipsis.circle")
                    .frame(width: 20, height: 20)
                }

                //button 2
                Button(action: {
                    self.action2()
                }) {
                    Image(systemName: "pencil.circle")
                    .frame(width: 20, height: 20)
                }
            }
            :
                HStack {
                    //button 1
                    Button(action: {
                        self.action1()
                    }) {
                        Image(systemName: "ellipsis.circle")
                        .frame(width: 0, height: 0)
                    }

                    //button 2
                    Button(action: {
                        self.action2()
                    }) {
                        Image(systemName: "pencil.circle")
                        .frame(width: 0, height: 0)

                    }
                }
            )
        }
    }

    func action1() {
        print("do action 1...")
    }

    func action2() {
        print("do action 2...")
    }
}

struct HeaderView: View {
    let headerText: String
    @Binding var midY: CGFloat
    var body: some View {
        GeometryReader { geometry -> Text in
            let frame = geometry.frame(in: CoordinateSpace.global)

            withAnimation(.easeIn(duration: 0.25)) {
                DispatchQueue.main.async {
                   self.midY = frame.midY
                }
            }

            return Text(self.headerText)
                .bold()
                .font(.largeTitle)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

2      

Hi, thanks for the quick reply. Definitely not as clean as I was hoping (I hoped for some out-of-the-box implementation I was missing), but it's definitely a good start. Thank you very much, I'll see if I can improve on it a bit (especially since the Navbar title is sitting under the Navbar itself right now and a few animations).

   

There is a simpler way by using navigation bar items. That can be leading or trailing the title. Try this and let me know if it worked .navigationBarItems(leading: Image("your image name").resizable().frame(width:40, height:40, alignment: .center).cornerRadius(20))

   

Hacking with Swift is sponsored by Instabug

SPONSORED Catch bugs as soon as they happen and know exactly why a crash occurred. Instabug's SDK grabs all the logs they need to fix bugs, crashes and performance issues in minutes instead of days. Get screenshots, device details, network logs, repro steps, and tons of other critical insights needed to resolve issues and prioritize product backlogs straight from your dashboard. It only takes a minute to integrate!

Get started now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.