简体   繁体   中英

How to show Profile Icon next to Large Navigation Bar Title in SwiftUI?

I am developing an App that supports multiple Profiles. I really like the way Apple displays the Profile Icon next to the Large Navigation Bar Title in all their Apps. See the Screenshot below:

截图示例

My Question is the following:

  • Is it possible to achieve this in SwiftUI ? And if so, how?
  • If it's not possible in pure SwiftUI , how can I achieve it including UIKit Code?

Thanks for your help.

I solved this by using SwiftUI-Introspect , to "Introspect underlying UIKit components from SwiftUI".

Here is an example of a view:

struct ContentView: View {

    @State private var lastHostingView: UIView!
    
    var body: some View {
        NavigationView {
            ScrollView {
                ForEach(1 ... 50, id: \.self) { index in
                    Text("Index: \(index)")
                }
                .frame(maxWidth: .infinity)
            }
            .navigationTitle("Large title")
            .introspectNavigationController { navController in
                let bar = navController.navigationBar
                let hosting = UIHostingController(rootView: BarContent())
                
                guard let hostingView = hosting.view else { return }
                // bar.addSubview(hostingView)                                          // <--- OPTION 1
                // bar.subviews.first(where: \.clipsToBounds)?.addSubview(hostingView)  // <--- OPTION 2
                hostingView.backgroundColor = .clear
                
                lastHostingView?.removeFromSuperview()
                lastHostingView = hostingView
                
                hostingView.translatesAutoresizingMaskIntoConstraints = false
                NSLayoutConstraint.activate([
                    hostingView.trailingAnchor.constraint(equalTo: bar.trailingAnchor),
                    hostingView.bottomAnchor.constraint(equalTo: bar.bottomAnchor, constant: -8)
                ])
            }
        }
    }
}

Bar content & profile picture views:

struct BarContent: View {
    var body: some View {
        Button {
            print("Profile tapped")
        } label: {
            ProfilePicture()
        }
    }
}

struct ProfilePicture: View {
    var body: some View {
        Circle()
            .fill(
                LinearGradient(
                    gradient: Gradient(colors: [.red, .blue]),
                    startPoint: .topLeading,
                    endPoint: .bottomTrailing
                )
            )
            .frame(width: 40, height: 40)
            .padding(.horizontal)
    }
}

The .frame(width: 40, height: 40) & hostingView.bottomAnchor constant will need to be adjusted to your needs.

And the results for each option (commented in the code):

Option 1 Option 2
View sticks when scrolled View disappearing underneath on scroll
结果 - 选项 1 结果 - 选项 2

Without NavigationView

I done this with pure SwiftUI. You have to replace the Image("Profile") line with your own image (maybe from Assets or from base64 data with UIImage).

HStack {
    Text("Apps")
        .font(.largeTitle)
        .fontWeight(.bold)
    
    Spacer()
    
    Image("Profile")
        .resizable()
        .scaledToFit()
        .frame(width: 40, height: 40)
        .clipShape(Circle())
}
.padding(.all, 30)

This products following result: 结果显示大粗体字“应用程序”,然后是我的示例个人资料图片

With NavigationView

Let's assume that you have NavigationView and inside that there's only ScrollView and .navigationTitle . You can add that profile image there by using overlay.

NavigationView {
    ScrollView {
        //your content here
    }
    .overlay(
        ProfileView()
            .padding(.trailing, 20)
            .offset(x: 0, y: -50)
    , alignment: .topTrailing)

    .navigationTitle(Text("Apps"))
}

Where ProfileView could be something like this:

struct ProfileView: View {
    var body: some View {
        Image("Profile")
            .resizable()
            .scaledToFit()
            .frame(width: 40, height: 40)
            .clipShape(Circle())
    }
}

The result will be like this...

上面代码的结果

...which is pretty close to the App Store:

显示 App Store 行为的 gif

Here is a possible solution. I added an HStack view which contains title and image as a navigationBarItems

在此处输入图像描述

struct ContentView: View {
  
    var body: some View {
        GeometryReader { geo in
        
        NavigationView{
            VStack{
                ScrollView{
                    Text("Contents")
                    
                    Spacer()
                    Text("Contents2")
                    
                    Spacer()
                    
                    Text("Contents3")
                }
                
                
            }.navigationBarItems(leading: HStack {
                Text("Apps")
                    .font(.largeTitle)
                    .fontWeight(.bold)
                
                Spacer().frame(width: geo.size.width * 0.6)
                
                Image(systemName: "person.crop.circle")
                    .resizable()
                    .frame(width: 40, height: 40)
                    
            })
        }
        
    
        
       
                        
    }
    }
  
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM