I am using UIKit MKMapView in SwiftUI to have more functionality.
I pass the map region to the UIViewRepresentable via Binding, so I can update the UIKit view from the SwiftUI view that uses that MKMapView.
The issue is that I have a button on top of the map that is responsible for centering the map back to the user location, but every time the map region changes, the updateUIView
function triggers, and the map centers it self, which leads to the map being stuck because whenever I move the map (i.e zoom, drag, rotate) the map centers itself.
How can I conditionally check if the button was clicked - and only then center the map?
updateUIView:
func updateUIView(_ uiView: MKMapView, context: Context) {
if let userLocation = uiView.annotations.first(where: { $0 is MKUserLocation }) {
uiView.setRegion(MKCoordinateRegion(center: userLocation.coordinate, span: MapDetails.defaultSpan), animated: true)
} // this gets triggered everytime map region changes.
}
UIViewRepresentable:
struct MapView: UIViewRepresentable {
@Binding var mapRegion: MKCoordinateRegion
@Binding var locations: [Location]
...
}
Main SwiftUI View (Where I use the MKMapView):
struct ExploreView: View {
@StateObject private var viewModel = MapViewModel()
@State private var didAppearOnce = false
@ViewBuilder
var body: some View {
NavigationView {
ZStack {
MapView(mapRegion: $viewModel.mapRegion, locations: $viewModel.locations)
.accentColor(.pink)
...
}
So my question is, is there any way to conditionally check whether the button was pressed and only then center the map inside updateUIView
?
CodePudding user response:
Leave updateUIView
empty, and add Combine in your viewModel
import Combine
class MapViewModel:ObservableObject {
@Published var buttonCenterPressed = false
var buttonCancellable: AnyCancellable?
}
In UIViewRepresentable
add subscriber in makeUIView
with mapView in parameters
struct MapView: UIViewRepresentable {
@Binding var mapRegion: MKCoordinateRegion
@Binding var locations: [Location]
var viewModel: MapViewModel
...
func makeUIView(context: Context) -> MKMapView {
let mapView = MKMapView(frame: .zero)
mapView.showsUserLocation = true
setSubscriber(mapView)
return mapView
}
func updateUIView(_ mapView: MKMapView, context: Context) {}
func setSubscriber(_ mapView: MKMapView){
viewModel.buttonCancellable = viewModel.$buttonCenterPressed.sink(receiveValue: { _ in
if let userLocation = mapView.annotations.first(where: { $0 is MKUserLocation }) {
mapView.setRegion(MKCoordinateRegion(center: userLocation.coordinate, span: MapDetails.defaultSpan), animated: true)
}
})
}
add viewModel
in parameter to the MapView
struct ExploreView: View {
@StateObject private var viewModel = MapViewModel()
@State private var didAppearOnce = false
@ViewBuilder
var body: some View {
NavigationView {
ZStack {
MapView(mapRegion: $viewModel.mapRegion, locations: $viewModel.locations, viewModel: viewModel)
.accentColor(.pink)
...
}
now you can implement action centering button and MapView
will be updated.
Button(action: {
viewModel.buttonCenterPressed.toggle()
}, label: {
Text("Center")
})