MapKit gets [SwiftUI] Modifying state during view update, this will cause undefined behavior - swiftui

I've been playing around with MapKit to display a map at the top of View in SwiftUI, when an address is contained; however, I keep getting [SwiftUI] Modifying state during view update, this will cause undefined behavior. when the user rotates the view. Here's how I have my Map being rendered
HStack {
if let region = region {
MapView(region: region)
.frame(width: geo.size.width * 0.3, height: geo.size.height * 0.2)
.mask(RoundedRectangle(cornerRadius: 25))
.padding([.top, .leading], 15 )
AddressView(recipient: recipient)
}
Spacer()
.onAppear {
let addressString = String("\(recipient.addressLine1 ?? "One Apple Park Way") \(recipient.city ?? "Cupertino") \(recipient.state ?? "CA") \(recipient.zip ?? "95014") \(recipient.country ?? "")")
getLocation(from: addressString) { coordinates in
if let coordinates = coordinates {
self.region = MKCoordinateRegion(
center: coordinates,
span: MKCoordinateSpan(latitudeDelta: 0.005, longitudeDelta: 0.005))
}
}
}
}
The recipient is in CoreData and is passed into the view. Region is defined as follows: #State var region: MKCoordinateRegion?
Any suggestions on how to remove the error message?
here's the getLocation function
func getLocation(from address: String, completion: #escaping (_ location: CLLocationCoordinate2D?) -> Void) {
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(address) { (placemarks, _) in
guard let placemarks = placemarks,
let location = placemarks.first?.location?.coordinate else {
completion(nil)
return
}
completion(location)
}
}

My experience with Map is that some of these logs are internal and unavoidable, but I fixed about half of them like this:
#State var region: MKCoordinateRegion = .init(...)
// Fix for "Modifying state" logs
var regionBinding: Binding<MKCoordinateRegion> {
.init(
get: { region },
set: { newValue in DispatchQueue.main.async { region = newValue } }
)
}
And then pass regionBinding instead of $region

Related

Why is SwiftUI Map modifying the state of its calling struct, if no frame is assigned?

Setup:
My app uses a SwiftUI Map, essentially as
struct MapViewSWUI: View {
#Binding private var show_map_modal: Bool
#State private var region: MKCoordinateRegion
//…
init(show_map_modal: Binding<Bool>) {
self._show_map_modal = show_map_modal
self.region = // Some computed region
//…
var body: some View {
//…
Map(coordinateRegion: $region)
.frame(width: 400, height: 300) // Some frame for testing
}
}
Using this code, I can show the map modally without problems.
Problem:
If I out comment the .frame view modifier, I get the runtime error
Modifying state during view update, this will cause undefined behavior.
with the following stack frame:
Question:
Why is it in my case required to set a frame for the Map? This tutorial does it, but Apple's docs don't.
How to do it right?
PS:
I have read this answer to a similar question and tried to catch the error with a runtime breakpoint, but it does not show anything interesting:
I found an answer to another questions related to the same error, but it doesn't apply here.
EDIT:
Workaround found, but not understood:
My map is presented modally from another view. This view has a state var that controls the presentation:
#State private var show_map_modal = false
The body of the view consists of a HStack with some views, and a fullScreenCover view modifier is applied to the HStack:
var body: some View {
HStack {
// …
}
.fullScreenCover(isPresented: $show_map_modal) {
MapViewSWUI(show_map_modal: $show_map_modal, itemToBeDisplayed: viewItem)
.ignoresSafeArea(edges: [.leading, .trailing])
}
}
If the map is presented in this way, no run time error is raised.
However, if I include (as it was done up to now) .top or .bottom in the edge set, the run time error Modifying state during view update is raised.
I would be glad for any hint to the reason.
My guess is that the error is not related to the frame at all, but to the update of the region once the sheet is presented.
As you can see in my code, I update the region 3 seconds after presenting the seet. Then, the error shows up.
Could the be happening in your code?
struct ContentView: View {
#State private var show_map_modal = false
var body: some View {
Button {
show_map_modal.toggle()
} label: {
Text("Show me the map!")
}
.sheet(isPresented: $show_map_modal) {
MapViewSWUI(show_map_modal: $show_map_modal)
}
}
}
struct MapViewSWUI: View {
#Binding private var show_map_modal: Bool
#State private var region: MKCoordinateRegion
init(show_map_modal: Binding<Bool>) {
self._show_map_modal = show_map_modal
self.region = MKCoordinateRegion(
center: CLLocationCoordinate2D(
latitude: 51.507222,
longitude: -0.1275),
span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5)
)
}
var body: some View {
VStack(alignment: .trailing) {
Button("Done") {
show_map_modal.toggle()
}
.padding(10)
Map(coordinateRegion: $region)
}
// .frame(width: 400, height: 300) // Some frame for testing
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.region = MKCoordinateRegion(
center: CLLocationCoordinate2D(
latitude: 31.507222,
longitude: -1.1275),
span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5)
)
}
}
}
}

SwiftUI - showing Annotation with dynamic colors on Map with MapKit

hopefully someone can help me with my problem. I have an app where I show a Map with the current location of the user. Then I can tap on a button and it shows all gas stations around the location based on a certain radius. The stations are showed by little map pins. Now I want that the cheapest station has another color than the rest (yellow instead of red). The function for this is already written, but the problem is, that sometimes there is no map pin yellow or it is the wrong one which is yellow. The first tap on the button after the app starts is always good, but the following can be sporadic right or wrong. Here is my code.
Part of my MapView:
#ObservedObject var locationManager = LocationManager.shared
#EnvironmentObject var dataViewModel:DataViewModel
#EnvironmentObject var carViewModel:CarViewModel
#State private var radius: String = ""
#State private var showInput: Bool = false
var body: some View {
ZStack {
Map(coordinateRegion: $locationManager.region, interactionModes: .all, showsUserLocation: true, annotationItems: dataViewModel.annotations, annotationContent: { station in
MapAnnotation(coordinate: station.coordinate) {
if dataViewModel.annotations.count > 0 {
MapAnnotationView(dataViewModel: dataViewModel, station: station)
.onTapGesture {
dataViewModel.currentAnnotation = station
dataViewModel.showStationSheet = true
}
}
}
} )
.accentColor(Color.blue)
.ignoresSafeArea()
This is the function where I get my data from:
guard let url = URL(string: "https://creativecommons.tankerkoenig.de/json/list.php?lat=\(latitude)&lng=\(longitude)&rad=\(radius)&sort=dist&type=all&apikey=\(apiKey)") else { return }
URLSession.shared.dataTask(with: url) { data, response, error in
if error == nil {
if let data = data {
do {
let decodedResponse = try JSONDecoder().decode(HeadData.self, from: data)
DispatchQueue.main.async {
self.gasData.append(decodedResponse)
self.bestAnnotations = [Annotation]()
for bestStation in self.gasData[0].stations {
var id = UUID()
self.annoIDs.append(id)
var tempAnnotation = Annotation(id: id, name: bestStation.name, brand: bestStation.brand, street: bestStation.street, houseNumber: bestStation.houseNumber, postCode: bestStation.postCode, place: bestStation.place, distance: bestStation.dist, diesel: bestStation.diesel ?? 9.999, e5: bestStation.e5 ?? 9.999, e10: bestStation.e10 ?? 9.999, isOpen: bestStation.isOpen, address: bestStation.street, coordinate: CLLocationCoordinate2D(latitude: bestStation.lat, longitude: bestStation.lng))
self.bestAnnotations.append(tempAnnotation)
}
self.calculateBestAnnotation(activeCar: activeCar)
var i = 0
for station in self.gasData[0].stations {
var tempAnnotation = Annotation(id: self.annoIDs[i], name: station.name, brand: station.brand, street: station.street, houseNumber: station.houseNumber, postCode: station.postCode, place: station.place, distance: station.dist, diesel: station.diesel ?? 9.999, e5: station.e5 ?? 9.999, e10: station.e10 ?? 9.999, isOpen: station.isOpen, address: station.street, coordinate: CLLocationCoordinate2D(latitude: station.lat, longitude: station.lng))
i += 1
self.copiedAnnotations.append(tempAnnotation)
}
self.annotations = self.copiedAnnotations
}
} catch let jsonError as NSError {
DispatchQueue.main.async {
self.searchToastError = "Es konnten keine Daten gefunden werden."
self.presentSearchToast = true
}
}
return
}
}
}
.resume()
At first I am saving the decoded json response into an array and I calculate the cheapest gas station so that there is one element left in the array bestAnnotations. After that I append the data to the Annotation Array which is the data source of the Annotations on the MapView.
And then my MapAnnotationView looks like this:
#ObservedObject var locationManager = LocationManager.shared
#ObservedObject var dataViewModel:DataViewModel
#State var station: Annotation
var body: some View {
ZStack {
Circle()
.frame(width: 35, height: 35)
.foregroundColor(station.id == dataViewModel.bestAnnotations[0].id ? .yellow : .red)
Image(systemName: "mappin")
.resizable()
.scaledToFit()
.foregroundColor(.white)
.frame(width: 25, height: 25)
}
}
Hopefully someone can help me with the problem. Maybe there is there something wring with the Dispatch function?

SwiftUI: Trigger view update after user pans the map

I'm new to Swift/SwiftUI, and so please forgive me if this is trivial.
In an app I am attempting to retrieve the user's location, and fetch nearby websites from Wikipedia to display. A simple example is below:
//
// ContentView.swift
// MRE
//
// Created by Philipp Maier on 2/25/22.
//
import SwiftUI
import CoreLocationUI
import MapKit
import Foundation
struct ContentView: View {
enum ViewState {
case waiting, fetching
}
#State private var viewState = ViewState.waiting
#StateObject private var viewModel = LocationViewModel()
// some values to initialize
#State var listEntries = [
Geosearch(pageid: 45348219,
title: "Addison Apartments",
lat: 35.21388888888889,
lon: -80.84472222222222,
dist: 363.7 ),
Geosearch(pageid: 35914731,
title: "Midtown Park (Charlotte, North Carolina)",
lat: 35.2108,
lon: -80.8363,
dist: 1034.5 )
]
var body: some View {
NavigationView{
VStack{
ZStack(alignment: .leading){
Map(coordinateRegion: $viewModel.mapRegion, showsUserLocation: true)
.frame(height: 300)
LocationButton(.currentLocation, action: {
viewModel.requestAllowLocationPermission()
viewState = .fetching
print("Button Press: Latitude: \(viewModel.mapRegion.center.latitude), Longitude: \(viewModel.mapRegion.center.longitude)")
})
}
HStack{
Text("Latitude: \(viewModel.mapRegion.center.latitude), Longitude: \(viewModel.mapRegion.center.longitude)")
}
switch viewState {
case .waiting:
Text("Waiting for your location")
Spacer()
case .fetching:
List{
ForEach(listEntries) {location in
NavigationLink(destination: Text("Target") ) {
HStack(alignment: .top){
Text(location.title)
}
}
.task{
await fetchNearbyLandmarks(lat: viewModel.mapRegion.center.latitude, lon: viewModel.mapRegion.center.longitude)
}
}
}
}
}
}
}
func fetchNearbyLandmarks(lat: Double, lon: Double) async {
let urlString = "https://en.wikipedia.org/w/api.php?action=query&list=geosearch&gscoord=\(lat)%7C\(lon)&gsradius=5000&gslimit=25&format=json"
print(urlString)
guard let url = URL(string: urlString) else {
print("Bad URL: \(urlString)")
return
}
do {
let (data, _) = try await URLSession.shared.data(from: url)
let items = try JSONDecoder().decode(wikipediaResult.self, from: data)
listEntries = items.query.geosearch
print ("Loadingstate: Loaded")
} catch {
print ("Loadingstate: Failed")
}
}
}
final class LocationViewModel: NSObject, ObservableObject, CLLocationManagerDelegate {
#Published var mapRegion = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 40, longitude: -80.5), span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
let locationManager = CLLocationManager()
override init() {
super.init()
locationManager.delegate = self
}
func requestAllowLocationPermission() {
locationManager.requestLocation()
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let latestLocation = locations.first else {
print("Location Error #1")
return
}
DispatchQueue.main.async {
self.mapRegion = MKCoordinateRegion(center: latestLocation.coordinate, span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1))
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("Location Error #2:")
print(error.localizedDescription)
}
}
struct wikipediaResult: Codable {
let batchcomplete: String
let query: Query
}
struct Query: Codable {
let geosearch: [ Geosearch ]
}
struct Geosearch: Codable, Identifiable {
var id: Int { pageid }
let pageid: Int
let title: String
let lat: Double
let lon: Double
let dist: Double
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
The core function of the app works, in that once the user pushes the "Location" button, the list of Wikipedia pages underneath updates. I've also added to Text fields to track the center of the map as the user pans around.
Here's my issue: If I pan the map, the list of locations underneath does not update. However, once I click on a link to display the page, and hit the "back" button, the updated list is built "correctly", i.e. with the new coordinates.
I'm sure that I'm overlooking something simple, but how can I track panning of the user and dynamically adjust the list underneath "in real time"?
Thanks, Philipp
For being new to Swift/SwiftUI this is quite cool!
You want to refetch when the coordinates of your Map change. So you could use .onChange, e.g.on the ZStack of the Map:
.onChange(of: viewModel.mapRegion.center.latitude) { _ in
Task {
await fetchNearbyLandmarks(lat: viewModel.mapRegion.center.latitude, lon: viewModel.mapRegion.center.longitude)
}
}
Generally this works, but it might be fetching too often (on every change of latitude). So you might want to add some kind of delay, or try to figure out when the drag ended.

SwiftUI: Place and drag object immediately

I am attempting to place an object (a view of a square) on the screen and then drag it immediately. What I have achieved is the following:
I can drag existing objects that are already on the screen.
I can place new objects on the screen, but they do not drag along immediately. I need to lift my finger, and then tap on them again in order to drag.
How can I achieve the functionality: Place object on screen and immediately start dragging? I am missing something here. My code:
ContentView with square placement logic:
struct ContentView: View {
let screenSize = UIScreen.main.bounds
#State private var squares: [SquareView] = []
#State private var offsets = [CGSize](repeating: .zero, count: 300)
var body: some View {
GeometryReader { geo in
ForEach(squares, id: \.self) { square in
square
.position(x: square.startXLocation, y: square.startYLocation)
}
.ignoresSafeArea()
}
.onTouch(perform: updateLocation)
.onAppear {
for i in 0...2 {
let xLocation = Double.random(in: 50...(screenSize.width - 150))
let yLocation = Double.random(in: 50...(screenSize.height - 150))
let square = SquareView(sideLength: 40, number: i, startXLocation: xLocation, startYLocation: yLocation)
squares.append(square)
}
}
}
func updateLocation(_ location: CGPoint, type: TouchType) {
var square = SquareView(sideLength: 50, number: Int.random(in: 20...99), startXLocation: location.x, startYLocation: location.y)
if type == .started {
squares.append(square)
square.startXLocation = location.x
square.startYLocation = location.y
}
if type == .moved {
let newSquare = squares.last!
newSquare.offset = CGSize(width: location.x - newSquare.startXLocation, height: location.y - newSquare.startYLocation)
}
if type == .ended {
// Don't need to do anything here
}
}
}
The squares that I place with the logic to drag on the screen:
struct SquareView: View, Hashable {
let colors: [Color] = [.green, .red, .blue, .yellow]
let sideLength: Double
let number: Int
var startXLocation: Double
var startYLocation: Double
#State private var squareColor: Color = .yellow
#State var startOffset: CGSize = .zero
#State var offset: CGSize = .zero
var body: some View {
ZStack{
Rectangle()
.frame(width: sideLength, height: sideLength)
.foregroundColor(squareColor)
.onAppear {
squareColor = colors.randomElement()!
}
Text("\(number)")
} // ZStack
.offset(offset)
.gesture(
DragGesture()
.onChanged { gesture in
offset.width = gesture.translation.width + startOffset.width
offset.height = gesture.translation.height + startOffset.height
}
.onEnded { value in
startOffset.width = value.location.x
startOffset.height = value.location.y
}
)
}
static func ==(lhs: SquareView, rhs: SquareView) -> Bool {
return lhs.number == rhs.number
}
func hash(into hasher: inout Hasher) {
hasher.combine(number)
}
}
The struct used to detect the touch location on the screen (not relevant for the actual question, but necessary to reconstruct the program). Adapted from code by Paul Hudson, hackingwithswift.com:
// The types of touches users want to be notified about
struct TouchType: OptionSet {
let rawValue: Int
static let started = TouchType(rawValue: 1 << 0)
static let moved = TouchType(rawValue: 1 << 1)
static let ended = TouchType(rawValue: 1 << 2)
static let all: TouchType = [.started, .moved, .ended]
}
// Our UIKit to SwiftUI wrapper view
struct TouchLocatingView: UIViewRepresentable {
// A closer to call when touch data has arrived
var onUpdate: (CGPoint, TouchType) -> Void
// The list of touch types to be notified of
var types = TouchType.all
// Whether touch information should continue after the user's finger has left the view
var limitToBounds = true
func makeUIView(context: Context) -> TouchLocatingUIView {
// Create the underlying UIView, passing in our configuration
let view = TouchLocatingUIView()
view.onUpdate = onUpdate
view.touchTypes = types
view.limitToBounds = limitToBounds
return view
}
func updateUIView(_ uiView: TouchLocatingUIView, context: Context) {
}
// The internal UIView responsible for catching taps
class TouchLocatingUIView: UIView {
// Internal copies of our settings
var onUpdate: ((CGPoint, TouchType) -> Void)?
var touchTypes: TouchType = .all
var limitToBounds = true
// Our main initializer, making sure interaction is enabled.
override init(frame: CGRect) {
super.init(frame: frame)
isUserInteractionEnabled = true
}
// Just in case you're using storyboards!
required init?(coder: NSCoder) {
super.init(coder: coder)
isUserInteractionEnabled = true
}
// Triggered when a touch starts.
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let location = touch.location(in: self)
send(location, forEvent: .started)
}
// Triggered when an existing touch moves.
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let location = touch.location(in: self)
send(location, forEvent: .moved)
}
// Triggered when the user lifts a finger.
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let location = touch.location(in: self)
send(location, forEvent: .ended)
}
// Triggered when the user's touch is interrupted, e.g. by a low battery alert.
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let location = touch.location(in: self)
send(location, forEvent: .ended)
}
// Send a touch location only if the user asked for it
func send(_ location: CGPoint, forEvent event: TouchType) {
guard touchTypes.contains(event) else {
return
}
if limitToBounds == false || bounds.contains(location) {
onUpdate?(CGPoint(x: round(location.x), y: round(location.y)), event)
}
}
}
}
// A custom SwiftUI view modifier that overlays a view with our UIView subclass.
struct TouchLocater: ViewModifier {
var type: TouchType = .all
var limitToBounds = true
let perform: (CGPoint, TouchType) -> Void
func body(content: Content) -> some View {
content
.background(
TouchLocatingView(onUpdate: perform, types: type, limitToBounds: limitToBounds)
)
// .overlay(
// TouchLocatingView(onUpdate: perform, types: type, limitToBounds: limitToBounds)
// )
}
}
// A new method on View that makes it easier to apply our touch locater view.
extension View {
func onTouch(type: TouchType = .all, limitToBounds: Bool = true, perform: #escaping (CGPoint, TouchType) -> Void) -> some View {
self.modifier(TouchLocater(type: type, limitToBounds: limitToBounds, perform: perform))
}
}
// Finally, here's some example code you can try out.
struct ContentView1: View {
var body: some View {
VStack {
Text("This will track all touches, inside bounds only.")
.padding()
.background(.red)
.onTouch(perform: updateLocation)
Text("This will track all touches, ignoring bounds – you can start a touch inside, then carry on moving it outside.")
.padding()
.background(.blue)
.onTouch(limitToBounds: false, perform: updateLocation)
Text("This will track only starting touches, inside bounds only.")
.padding()
.background(.green)
.onTouch(type: .started, perform: updateLocation)
}
}
func updateLocation(_ location: CGPoint, type: TouchType) {
print(location, type)
}
}
A possible approach is to handle drag and creation in "area" (background container), while "item" views are just rendered at the place where needed.
Find below a simplified demo (used Xcode 13.2 / iOS 15.2), see also comments in code snapshot.
Note: tap detection in already "existed" item is an exercise for you.
extension CGPoint: Identifiable { // just a helper for demo
public var id: String { "\(x)-\(y)" }
}
struct TapAndDragDemo: View {
#State private var points: [CGPoint] = [] // << persistent
#State private var point: CGPoint? // << current
#GestureState private var dragState: CGSize = CGSize.zero
var body: some View {
Color.clear.overlay( // << area
Group {
ForEach(points) { // << stored `items`
Rectangle()
.frame(width: 24, height: 24)
.position(x: $0.x, y: $0.y)
}
if let curr = point { // << active `item`
Rectangle().fill(Color.red)
.frame(width: 24, height: 24)
.position(x: curr.x, y: curr.y)
}
}
)
.contentShape(Rectangle()) // << make area tappable
.gesture(DragGesture(minimumDistance: 0.0)
.updating($dragState) { drag, state, _ in
state = drag.translation
}
.onChanged {
point = $0.location // track drag current
}
.onEnded {
points.append($0.location) // push to stored
point = nil
}
)
}
}

ViewState does not changing with MapView

I'm currently trying to display an Mapview inside a sheet. The weird thing is, that the MapView is shown in the canvas. However in the Simulator it isn't updating the view. Can someone explain to me, what I'm doing wrong?
Here is my MapViewModel
class MapViewModel: ObservableObject {
enum ViewState {
case failed
case loading
case value([Checkpoint])
}
#Published var viewState: ViewState = .loading
#Published var region = MKCoordinateRegion()
func initMap(name: String, address: String) {
self.viewState = .loading
getLocation(from: address) { coordinate in
if let coordinate = coordinate {
let checkpoint = Checkpoint(title: name, coordinate: coordinate)
self.region = MKCoordinateRegion(center: coordinate, span: MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1))
var checkpoints: [Checkpoint] = []
checkpoints.append(checkpoint)
DispatchQueue.main.async {
withAnimation(.easeInOut) {
self.viewState = .value(checkpoints)
print("Done")
}
}
} else {
DispatchQueue.main.async {
self.viewState = .failed
}
}
}
}
func getLocation(from address: String, completion: #escaping (_ location: CLLocationCoordinate2D?)-> Void) {
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(address) { (placemarks, error) in
guard let placemarks = placemarks,
let location = placemarks.first?.location?.coordinate else {
completion(nil)
return
}
completion(location)
}
}
}
And here is my View
struct MapView: View {
#ObservedObject var mapVM = MapViewModel()
var name: String
var address: String
var body: some View {
switch mapVM.viewState {
case .loading:
VStack(alignment: .center) {
ProgressView()
}
.onAppear() {
mapVM.initMap(name: name, address: address)
}
case .value(let checkpoints):
VStack {
Map(coordinateRegion: $mapVM.region, annotationItems: checkpoints) { item in
MapAnnotation(coordinate: item.coordinate) {
VStack(spacing: 0) {
Image(systemName: "mappin.circle.fill")
.renderingMode(.original)
.font(.title)
Text(name)
.font(.caption)
.shadow(color: Color.systemBackground.opacity(0.5), radius: 1)
.frame(minWidth: 0, maxWidth: 150)
}
}
}
}
case .failed:
Text("Failed")
}
}
}
And here is how I implemented the MapView in my MainView
VStack(alignment: .center) {
MapView(name: tickerDetails.name, address: tickerDetails.hqAddress)
.frame(width: UIScreen.main.bounds.width - 30, height: 220)
.border(Color.systemGray4, cornerRadius: 10)
.padding(.leading)
}
Last but not least, here are two Screenshots how its displayed in the canvas and in the simulator:
The data for the preview is coming from an local stored JSON-File. The data for the simulator is fetched from the internet. However the data is loaded correctly, because the print statement is executed in MapViewModel (and the coordinates are also correct). It's just simply not updating the view with the data.
Edit
I implemented the MapView in a "normal" view and it works fine there. It seems to be, that the presented sheet can't work with view states...