Home > Software engineering >  How to reset a SwiftUI MapKit coordinate region's center when navigating to a NavigationLink?
How to reset a SwiftUI MapKit coordinate region's center when navigating to a NavigationLink?

Time:04-26

Is there a way to update the MKCoordinateRegion of a MapView sheet/NavigationLink on appear?

I have a view that is being used to input some data to create a longitude/latitude combination from a street address:

Create.swift:

@State var longitude:Double = -122.677579; // Default longitude
@State var latitude:Double = 45.515519;    // Default latitude
@State var streetAddress:String = "";      // Empty string
@State private var isShowing = false;

var body:some View {
  VStack {
    /* Take in the street address string via TextField */
    Button(action:{validate()}, label:"Next")
  }
   .sheet(isPresented:$isShowing) {
     LocationPicker(longitude:self.longitude, latitude:self.latitude)
   };
}

func validate() {
  var isValid:Bool = false;
  /*
    1. convert the street address to longitude and latitude
    2. store long/lat in self.longitude, self.latitude
    3. set isValid to true
  */
  isShowing = true;
}

The flow:

  1. Accept a street address from the user
  2. clicking the 'next' button validates the street address by converting it to longitude/latitude
  3. Those longitude and latitude values are stored in the state variables
  4. isShowing is set to true, bringing up the .sheet, which contains a LocationPicker view.

LocationPicker.swift:

struct LocationPicker: View {
    @State var latitude:Double;
    @State var longitude:Double;
    var span:MKCoordinateSpan = MKCoordinateSpan(
        latitudeDelta: 0.009,
        longitudeDelta: 0.009
    )
    @State var region:MKCoordinateRegion;

    init(latitude:Double, longitude:Double) {
        _latitude = State(initialValue: latitude)
        _longitude = State(initialValue: longitude)
        _region = State(initialValue :MKCoordinateRegion(
            center: CLLocationCoordinate2D(latitude: latitude, longitude: longitude),
            span: self.span
            ))
    }

    var body:some View {
      Map(coordinateRegion: $region,
          showsUserLocation: false
      )
      /* ... */
    }
}

The issue I have with this configuration is that the .sheet's LocationPicker in Create.swift gets rendered when the Create.swift view gets rendered. So when I pass the longitude/latitude states into the sheet's LocationPicker, the default values are passed, and the centerpoint that appears when the map opens is the default coordinates rather than the coordinates that are generated by the streetAddress in validate().

I've attempted to use both an invisible NavigationLink and a .sheet in Create.swift, but both render at the same time as the parent. I tried using @Binding doubles for LocationPicker.swift:

struct LocationPicker: View {
    @Binding var latitude:Double;
    @Binding var longitude:Double;
    var span:MKCoordinateSpan = MKCoordinateSpan(
        latitudeDelta: 0.009,
        longitudeDelta: 0.009
    )
    @State var region:MKCoordinateRegion;

    init(latitude:Binding<Double>, longitude:Binding<Double>) {
        _latitude = latitude
        _longitude = longitude
        _region = State(initialValue :MKCoordinateRegion(
            center: CLLocationCoordinate2D(latitude: latitude, longitude: longitude),
            span: self.span
            ))
    }

    var body:some View {
      Map(coordinateRegion: $region,
          showsUserLocation: false
      )
      /* ... */
    }

However, CLLocationCoordinate2D does not accept Binding<Double> as a data type for latitude or longitude.

CodePudding user response:

When you pass a Binding in to an init, if you need to use the values contained in the Binding, you must use .wrappedValue to access the underlying values. In this case, you are passing in two Binding<Double> and are attempting to use them to create a CLLocationCoordinate2D, so the initializer would have to look like this:

init(latitude:Binding<Double>, longitude:Binding<Double>) {
    // Binding
    _latitude = latitude
    // Binding
    _longitude = longitude
    _region = State(initialValue :MKCoordinateRegion(
                                                 // not Bindings
        center: CLLocationCoordinate2D(latitude: latitude.wrappedValue, longitude: longitude.wrappedValue),
        span: self.span
        ))
}
  • Related