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
|
1 Answer
Reset to default 4I 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
.safeAreaPadding(.top, 60)
to the detail view (for example, just before the.toolbar
modifier). – Benzy Neez Commented Mar 7 at 16:50Text("test")
and the problem still persists, I tried adding in different places still nothing – Pedro Cavaleiro Commented Mar 7 at 17:36