私は数日間、SwiftUI を MKMapView で動作させることに取り組んできました。ここの投稿の助けを借りて
メイン(ContentView)SwiftUIビューでUIViewRepresentableとしてMKMapView要素にアクセスする
このようなオンラインの記事
https://www.hackingwithswift.com/books/ios-swiftui/advanced-mkmapview-with-swiftui
私は何とか機能させることができました。配置された円をタップして選択し、選択すると青色で強調表示されます。ただし、私が直面している問題は、円の配列に新しい円を追加するたびに、選択した円が失われることです。
ここにテストアプリのzipをアップロードしました(コードについてお詫び申し上げます。私のSwiftの知識はさびれており、SwiftUIとMapKitはさらにそうです)
アプリの構造は次のとおりです
- コンテンツ ビュー
- いくつかのボタンを持つ zstack に私の MapView を含むメインの SwiftUI ビュー
- 次の @States を含む
- @State private var circleRegions = MKCircle
- @State private var selectedCircleIndex : Int?
- @State private var selectedCircle : MKCircle? =なし
- マップビュー
- MKMapView をラップし、MKMapView コールバックを処理する Coordinator を持つ UIViewRepresentable
- 次の @Bindings をメインの ContentView 状態変数まで含めます
- @Binding var circleRegions : [MKCircle]
- @Binding var selectedCircleIndex : Int?
- @Binding var selectedCircle : MKCircle?
- コーディネーター
- 親プロパティとして MapView を持ち、mapTapped メソッドを含む必要な MKMapViewDelegate メソッドを実装するコーディネーター
MapView がその MakeUIView() を呼び出すと、MKMapView が作成され、そのマップ デリゲートとして Coordinator が設定され、次のようにタップ ジェスチャ認識エンジンが追加されます。
mapView.addGestureRecognizer(UITapGestureRecognizer(target: context.coordinator, action: #selector(context.coordinator.mapTapped(_:))))
この mapTapped 処理は、タップを処理するために MapView の親にすぐに行きます。
タップがワールド空間に処理されたら、それを circleRegions 配列の @Binding プロパティと照合して、領域の選択が変更されたかどうかを判断します。次に、selectedCircleIndex および selectedCircle @Binding プロパティを更新して選択を反映し、MKMapView のオーバーレイを強制的に再構築して、これらのオーバーレイ用の新しいレンダラーを作成できるようにします。この時点でレンダラーの色を更新して選択を反映させることができます。
これはすべて正常に機能しますが、オーバーレイを削除して再追加すると点滅しますが、世界の終わりではありません.すべてのオーバーレイを削除して再追加する以外に、レンダラーの色を更新するより良い方法を見つけることができませんでした.
問題は、円を選択して青色にし、マップを少しスクロールしてから、円ボタンを押して別の場所に新しい円を作成すると、両方の円が赤で表示され、最後に選択した円が表示されることです。青のまま「選択」されている必要があります (選択された円とインデックスは正しい) が、誤って赤で表示されます。
これをデバッグすると、コーディネーターの mapView(:rendererFor) MKMapViewDelegate 呼び出し中に MapView の circleRegions 配列 @Binding プロパティが無効に空になるようです。これにより、コードが円のインデックスを親の circleRegions と一致させることができなくなります。さらに紛らわしいのは、コードのこの時点にブレークポイントを配置すると、親の MapView が handleTap() を実行し、rebuildOverlays() を呼び出した時点までスタックを遡ることができるという事実です。この時点では、その circleRegions は正しいですが、レンダラー コールバックに到達するまでにはそれらは無効になっています。
Swift では、Coordinator の親 MapView がコール スタック内のこれらの異なる場所で同じインスタンスであることを確認するのが難しくなります。これは、MapView 自身または Coordinator の親のポインター値を異なる時点で簡単に確認できないためです。
この問題を試してデバッグまたは修正するために、動作を変更するための追加の構成をいくつか追加しました
- マップビュー
- dispatchMapViewUpdates = false にする
- これは、タップ ジェスチャ認識コールバック内で発生する rebuildOverlays() がすぐに実行されるか、メイン キューにディスパッチされるかを制御します
- dispatchMapViewUpdates = false にする
- コーディネーター
- let testWithIndex = true //これを更新して、インデックスまたはオプションの円でテストします
- マップ レンダラー デリゲート メソッドが選択したインデックスまたは選択した MKCircle を使用するかどうかを制御するのはどれですか? 親で、レンダリングされている円が選択されているかどうかを示します
悲しいことに、これらのオプションのいずれも問題を解決していないため、何が起こっているのか困惑しています。また、この問題はシミュレーターと同じようにデバイスでも発生することを確認したため、シミュレーターのみの SwiftUI の問題ではありません。
誰かに何かアイデアがあれば、私はそれを高く評価します。乾杯