I am trying to create a resizable split view in SwiftUI where a Map is on top and a List(which will contain locations later) is on the bottom, separated by a draggable handle. The user should be able to drag the handle to smoothly resize both the map and the list. The current implementation is functional but nowhere near usable because the dragging animation is very laggy and jittery. Also at times, Xcode continuously prints the following error to the console during the drag: Publishing changes from within view updates is not allowed, this will cause undefined behavior. I believe the issue is being caused by the continuous view rendering loop with the resizing of the map & list with the map probably trying to render the appropriate region to account for the change in size. Here's what I have so far:
import SwiftUI
import MapKit
struct DraggableSplitMapView: View {
@State private var mapRegion = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 37.7749, longitude: -122.4194),
span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1)
)
@State private var mapHeight: CGFloat = 300
@GestureState private var dragOffset: CGSize = .zero
var body: some View {
NavigationView {
GeometryReader { geometry in
VStack(spacing: 0) {
Map(coordinateRegion: $mapRegion)
.frame(height: calculateMapHeight(geometry: geometry))
// Drag handle
ZStack {
Color.clear.frame(height: 12)
Capsule()
.fill(Color.secondary.opacity(0.5))
.frame(width: 40, height: 6)
}
.gesture(
DragGesture()
.updating($dragOffset) { value, state, _ in
state = value.translation
}
.onEnded { value in
let newHeight = self.mapHeight + value.translation.height
// Clamp the height from getting too big or small.
self.mapHeight = max(100, min(geometry.size.height - 150, newHeight))
}
)
// Dummy list
List(0..<50) { i in
Text("List Item \(i)")
}
.listStyle(.plain)
}
}
.navigationTitle("Draggable Map")
.navigationBarTitleDisplayMode(.inline)
}
}
private func calculateMapHeight(geometry: GeometryProxy) -> CGFloat {
let proposedHeight = self.mapHeight + self.dragOffset.height
// Ensure the height stays within reasonable bounds during the drag.
return max(100, min(geometry.size.height - 150, proposedHeight))
}
}
struct DraggableSplitMapView_Previews: PreviewProvider {
static var previews: some View {
DraggableSplitMapView()
}
}
The map will contain several hundred annotations displayed later so I think the issue will only get even worse with data bound to the map. How can I fix this issue and achieve smooth dragging while still using the native SwiftUI Map view? Is there a way to prevent the Map from writing back to its region binding during a drag gesture, or a better way to structure this view to avoid the conflict? Any help is appreciated.
