ios - Custom Title in Navigation Bar in SwiftUI - Stack Overflow

In my SwiftUI detail view I have this code (part of it)import SwiftUIimport MapKitimport Kingfisher

In my SwiftUI detail view I have this code (part of it)

import SwiftUI
import MapKit
import Kingfisher
import BottomSheet

struct VehiclePositionView: View {
    @Environment(\.isPresented) var isPresented
    @Environment(\.colorScheme) var colorScheme
    @EnvironmentObject var themeProvider: ThemeProvider
    
    @State var vehicle: VehicleModel
    
    @State private var bottomSheetPosition: BottomSheetPosition = .dynamicBottom
    @State private var cameraPosition = MapCameraPosition.region(
        MKCoordinateRegion(
            center: CLLocationCoordinate2D(latitude: 0, longitude: 0),
            span: MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05)
        )
    )
    
    let span: (CGFloat, CGFloat) = (0.005, 0.005)
    let imgUrl = URL(string: ".jpg")!
    
    var body: some View {
        Map(position: $cameraPosition) {
            Annotation(vehicle.plate, coordinate: location) {
                Image(systemName: "car")
                    .foregroundColor(themeProvider.accentColor)
                    .background(
                        Circle()
                            .fill(Color.white)
                            .frame(width: 30, height: 30)
                            .shadow(color: getVehicleStatusColor(for: vehicle), radius: 3)
                    )
            }
        }
        .mapStyle(.standard(elevation: .realistic, showsTraffic: true))
        .onAppear {
            NotificationCenter.notify(.toggleBottomBar)
            cameraPosition = .region(
                MKCoordinateRegion(
                    center: CLLocationCoordinate2D(latitude: vehicle.latitude, longitude: vehicle.longitude),
                    span: MKCoordinateSpan(latitudeDelta: span.0, longitudeDelta: span.1)
                )
            )
        }
        .onChange(of: isPresented) {
            if !isPresented {
                NotificationCenter.notify(.toggleBottomBar)
            }
        }
        .toolbar {
            ToolbarItem(placement: .principal) {
                VStack(spacing: 3) {
                    ZStack {
                        KFImage(imgUrl)
                            .fade(duration: 0.25)
                            .resizable()
                            .aspectRatio(contentMode: .fill)
                            .frame(width: 50, height: 50)
                            .clipShape(Circle())
                            .shadow(
                                color: colorScheme == .dark ? Color.white.opacity(0.7) : Color.black.opacity(0.3),
                                radius: 5
                            )
                        Circle()
                            .fill(getVehicleStatusColor(for: vehicle))
                            .frame(width: 12, height: 12)
                            .offset(x: 18, y: 20)
                    }
                    Text(vehicle.plate)
                    Text("\(vehicle.brand) \(vehicle.model)")
                        .font(.footnote)
                        .foregroundStyle(themeProvider.secondary)
                }
                //.padding(.top, 50)
            }
        }
        .toolbarBackground(.thinMaterial, for: .navigationBar)
        .toolbarRole(.editor)
        .toolbarVisibility(.visible, for: .navigationBar)
        .bottomSheet(bottomSheetPosition: $bottomSheetPosition, switchablePositions: [.dynamicTop, .dynamicBottom]) {
            VStack(alignment: .leading, spacing: 8) {
                Text(vehicle.plate)
                    .font(.title2)
                    .bold()
                    .foregroundStyle(themeProvider.primary)
                Text("\(vehicle.brand) \(vehicle.model)")
                    .font(.callout)
                    .foregroundStyle(themeProvider.secondary)
            }
            .padding(.horizontal)
        } mainContent: {
            
        }        
    }
    
    private var location: CLLocationCoordinate2D {
        return CLLocationCoordinate2D(latitude: vehicle.latitude, longitude: vehicle.longitude)
    }
    
}

I was expecting to get the following output, and this is exactly what I get in the Xcode preview

But on a real device/simulator I get this weird behaviour

As you can see, when the transition starts it does appear as expected but when the animation ends it clips the toolbar item

On the parent view I'm using a NavigationStack, no special configuration. On the detail (this view) the configuration for the toolbar/navbar is provided in this post. I'm targeting iOS 18

UPDATE

After some debugging I was able to narrow down the issue to this part of the code in the parent view

            .navigationTitle("vehicles")
            .searchable(text: $searchField, isPresented: $searchVisible, placement: .navigationBarDrawer, prompt: "search")
            .toolbar {
                ToolbarItemGroup(placement: .topBarTrailing) {
                    Button() { searchVisible = true } label: { Image(systemName: "magnifyingglass") }
                    Menu {
                        Picker(selection: $vehicleState) {
                            ForEach(VehicleState.allCases, id:\.self) { choice in
                                Text(LocalizedStringKey(choice.rawValue))
                            }
                        } label: {
                            Text("Select a state")
                        }
                    } label: {
                        if vehicleState == .all {
                            Image(systemName: "line.3.horizontal.decrease.circle")
                        } else {
                            Image(systemName: "line.3.horizontal.decrease.circle.fill")
                        }
                    }
                }
            }

Full RootView code

NavigationStack {
            List(appState.vehicles.filter {
                (
                    $0.model.lowercased().contains(searchField.lowercased()) ||
                    $0.plate.lowercased().contains(searchField.lowercased()) ||
                    $0.brand.lowercased().contains(searchField.lowercased()) ||
                    $0.driverName.lowercased().contains(searchField.lowercased()) ||
                    (vehicleLocations[$0.id]?.lowercased().contains(searchField.lowercased()) ?? false) ||
                    searchField.isEmpty
                ) && (
                    vehicleState == .all ||
                    (vehicleState == .off && !$0.ignition) ||
                    (vehicleState == .running && $0.ignition && $0.speed >= 5) ||
                    (vehicleState == .idle && $0.ignition && $0.speed < 5)
                )
            }) { vehicle in
                NavigationLink(destination: VehiclePositionView(vehicle: vehicle)) {
                    VehicleCell(vehicle: vehicle, locatedCallback: {
                        vehicleLocations[vehicle.id] = $0
                    })
                }
            }
            .safeAreaInset(edge: .bottom) {
                Spacer()
                    .frame(height: navbarHeight + 10)
            }
            .navigationBarTitleDisplayMode(.large)
            .navigationTitle("vehicles")
            .searchable(text: $searchField, isPresented: $searchVisible, placement: .navigationBarDrawer, prompt: "search")
            .toolbar {
                ToolbarItemGroup(placement: .topBarTrailing) {
                    Button() { searchVisible = true } label: { Image(systemName: "magnifyingglass") }
                    Menu {
                        Picker(selection: $vehicleState) {
                            ForEach(VehicleState.allCases, id:\.self) { choice in
                                Text(LocalizedStringKey(choice.rawValue))
                            }
                        } label: {
                            Text("Select a state")
                        }
                    } label: {
                        if vehicleState == .all {
                            Image(systemName: "line.3.horizontal.decrease.circle")
                        } else {
                            Image(systemName: "line.3.horizontal.decrease.circle.fill")
                        }
                    }
                }
            }
        }

In my SwiftUI detail view I have this code (part of it)

import SwiftUI
import MapKit
import Kingfisher
import BottomSheet

struct VehiclePositionView: View {
    @Environment(\.isPresented) var isPresented
    @Environment(\.colorScheme) var colorScheme
    @EnvironmentObject var themeProvider: ThemeProvider
    
    @State var vehicle: VehicleModel
    
    @State private var bottomSheetPosition: BottomSheetPosition = .dynamicBottom
    @State private var cameraPosition = MapCameraPosition.region(
        MKCoordinateRegion(
            center: CLLocationCoordinate2D(latitude: 0, longitude: 0),
            span: MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05)
        )
    )
    
    let span: (CGFloat, CGFloat) = (0.005, 0.005)
    let imgUrl = URL(string: "https://clientes.gesfrota.pt/Gesfrota_Images/1/Viaturas/viatura_14563.jpg")!
    
    var body: some View {
        Map(position: $cameraPosition) {
            Annotation(vehicle.plate, coordinate: location) {
                Image(systemName: "car")
                    .foregroundColor(themeProvider.accentColor)
                    .background(
                        Circle()
                            .fill(Color.white)
                            .frame(width: 30, height: 30)
                            .shadow(color: getVehicleStatusColor(for: vehicle), radius: 3)
                    )
            }
        }
        .mapStyle(.standard(elevation: .realistic, showsTraffic: true))
        .onAppear {
            NotificationCenter.notify(.toggleBottomBar)
            cameraPosition = .region(
                MKCoordinateRegion(
                    center: CLLocationCoordinate2D(latitude: vehicle.latitude, longitude: vehicle.longitude),
                    span: MKCoordinateSpan(latitudeDelta: span.0, longitudeDelta: span.1)
                )
            )
        }
        .onChange(of: isPresented) {
            if !isPresented {
                NotificationCenter.notify(.toggleBottomBar)
            }
        }
        .toolbar {
            ToolbarItem(placement: .principal) {
                VStack(spacing: 3) {
                    ZStack {
                        KFImage(imgUrl)
                            .fade(duration: 0.25)
                            .resizable()
                            .aspectRatio(contentMode: .fill)
                            .frame(width: 50, height: 50)
                            .clipShape(Circle())
                            .shadow(
                                color: colorScheme == .dark ? Color.white.opacity(0.7) : Color.black.opacity(0.3),
                                radius: 5
                            )
                        Circle()
                            .fill(getVehicleStatusColor(for: vehicle))
                            .frame(width: 12, height: 12)
                            .offset(x: 18, y: 20)
                    }
                    Text(vehicle.plate)
                    Text("\(vehicle.brand) \(vehicle.model)")
                        .font(.footnote)
                        .foregroundStyle(themeProvider.secondary)
                }
                //.padding(.top, 50)
            }
        }
        .toolbarBackground(.thinMaterial, for: .navigationBar)
        .toolbarRole(.editor)
        .toolbarVisibility(.visible, for: .navigationBar)
        .bottomSheet(bottomSheetPosition: $bottomSheetPosition, switchablePositions: [.dynamicTop, .dynamicBottom]) {
            VStack(alignment: .leading, spacing: 8) {
                Text(vehicle.plate)
                    .font(.title2)
                    .bold()
                    .foregroundStyle(themeProvider.primary)
                Text("\(vehicle.brand) \(vehicle.model)")
                    .font(.callout)
                    .foregroundStyle(themeProvider.secondary)
            }
            .padding(.horizontal)
        } mainContent: {
            
        }        
    }
    
    private var location: CLLocationCoordinate2D {
        return CLLocationCoordinate2D(latitude: vehicle.latitude, longitude: vehicle.longitude)
    }
    
}

I was expecting to get the following output, and this is exactly what I get in the Xcode preview

But on a real device/simulator I get this weird behaviour

As you can see, when the transition starts it does appear as expected but when the animation ends it clips the toolbar item

On the parent view I'm using a NavigationStack, no special configuration. On the detail (this view) the configuration for the toolbar/navbar is provided in this post. I'm targeting iOS 18

UPDATE

After some debugging I was able to narrow down the issue to this part of the code in the parent view

            .navigationTitle("vehicles")
            .searchable(text: $searchField, isPresented: $searchVisible, placement: .navigationBarDrawer, prompt: "search")
            .toolbar {
                ToolbarItemGroup(placement: .topBarTrailing) {
                    Button() { searchVisible = true } label: { Image(systemName: "magnifyingglass") }
                    Menu {
                        Picker(selection: $vehicleState) {
                            ForEach(VehicleState.allCases, id:\.self) { choice in
                                Text(LocalizedStringKey(choice.rawValue))
                            }
                        } label: {
                            Text("Select a state")
                        }
                    } label: {
                        if vehicleState == .all {
                            Image(systemName: "line.3.horizontal.decrease.circle")
                        } else {
                            Image(systemName: "line.3.horizontal.decrease.circle.fill")
                        }
                    }
                }
            }

Full RootView code

NavigationStack {
            List(appState.vehicles.filter {
                (
                    $0.model.lowercased().contains(searchField.lowercased()) ||
                    $0.plate.lowercased().contains(searchField.lowercased()) ||
                    $0.brand.lowercased().contains(searchField.lowercased()) ||
                    $0.driverName.lowercased().contains(searchField.lowercased()) ||
                    (vehicleLocations[$0.id]?.lowercased().contains(searchField.lowercased()) ?? false) ||
                    searchField.isEmpty
                ) && (
                    vehicleState == .all ||
                    (vehicleState == .off && !$0.ignition) ||
                    (vehicleState == .running && $0.ignition && $0.speed >= 5) ||
                    (vehicleState == .idle && $0.ignition && $0.speed < 5)
                )
            }) { vehicle in
                NavigationLink(destination: VehiclePositionView(vehicle: vehicle)) {
                    VehicleCell(vehicle: vehicle, locatedCallback: {
                        vehicleLocations[vehicle.id] = $0
                    })
                }
            }
            .safeAreaInset(edge: .bottom) {
                Spacer()
                    .frame(height: navbarHeight + 10)
            }
            .navigationBarTitleDisplayMode(.large)
            .navigationTitle("vehicles")
            .searchable(text: $searchField, isPresented: $searchVisible, placement: .navigationBarDrawer, prompt: "search")
            .toolbar {
                ToolbarItemGroup(placement: .topBarTrailing) {
                    Button() { searchVisible = true } label: { Image(systemName: "magnifyingglass") }
                    Menu {
                        Picker(selection: $vehicleState) {
                            ForEach(VehicleState.allCases, id:\.self) { choice in
                                Text(LocalizedStringKey(choice.rawValue))
                            }
                        } label: {
                            Text("Select a state")
                        }
                    } label: {
                        if vehicleState == .all {
                            Image(systemName: "line.3.horizontal.decrease.circle")
                        } else {
                            Image(systemName: "line.3.horizontal.decrease.circle.fill")
                        }
                    }
                }
            }
        }
Share edited Mar 10 at 12:15 Pedro Cavaleiro asked Mar 7 at 16:16 Pedro CavaleiroPedro Cavaleiro 9597 silver badges23 bronze badges 4
  • The header is overflowing the safe area, so it may be the content of the page which is covering it. Try adding .safeAreaPadding(.top, 60) to the detail view (for example, just before the .toolbar modifier). – Benzy Neez Commented Mar 7 at 16:50
  • Unfortunately it didn't work. I even removed all the detail code, leaving only the one posted here plus your modifier and replaced by Text("test") and the problem still persists, I tried adding in different places still nothing – Pedro Cavaleiro Commented Mar 7 at 17:36
  • I have tried to reproduce the problem using the code in the question, but wasn't successful. It would be good if you could update the code to make it complete, so that it can be used to reproduce the problem when run in isolation. – Benzy Neez Commented Mar 7 at 18:00
  • @BenzyNeez I updated with the entire code, let me know if you want the main view as well – Pedro Cavaleiro Commented Mar 7 at 18:12
Add a comment  | 

1 Answer 1

Reset to default 4

I was able to reproduce the problem by reducing the detail view to just Map().

It helps if you set the navigation bar title display mode to .large:

var body: some View {
    Map()
        .navigationBarTitleDisplayMode(.large) // 

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744918394a4600977.html

相关推荐

  • ios - Custom Title in Navigation Bar in SwiftUI - Stack Overflow

    In my SwiftUI detail view I have this code (part of it)import SwiftUIimport MapKitimport Kingfisher

    1天前
    40

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信