Я боролся за то, чтобы SwiftUI работал с MKMapView уже несколько дней. С помощью сообщений здесь
Доступ к элементам MKMapView как UIViewRepresentable в основном (ContentView) представлении SwiftUI
и подобных статьях в Интернете
https://www.hackingwithswift.com/books/ios-swiftui/advanced-mkmapview-with-swiftui
Мне удалось заставить что-то работать. Коснитесь, чтобы выбрать помещенные круги, и выделите их синим цветом, когда они выбраны. Однако проблема, с которой я сталкиваюсь, заключается в том, что выбранный круг теряется всякий раз, когда я добавляю новый круг в массив.
Я загрузил zip тестового приложения для этого здесь ( Извинения за код, мои знания Swift ржавый и мой SwiftUI и MapKit тем более )
Приложение имеет следующую структуру
- ContentView
- Основное представление SwiftUI, содержащее мой MapView в zstack с некоторыми кнопками
- , включая следующие @States
- @ State private var circleRegions = MKCircle
- @ State private var selectedCircleIndex: Int?
- @ Государственный частный var selectedCircle: MKCircle? = nil
- MapView
- UIViewRepresentable, который упаковывает MKMapView и имеет координатор для обработки обратных вызовов MKMapView
- , включая следующие @ привязки к основным переменным состояния ContentView
- @ Binding var circleRegions: [MKCircle]
- @ Binding var selectedCircleIndex: Int?
- @ Binding var selectedCircle: MKCircle?
- Координатор
- Координатор, имеющий MapView в качестве родительского свойства и реализующий необходимые методы MKMapViewDelegate, включая наш метод mapTapped
Когда MapView вызывает свой MakeUIView (), он создает MKMapView и устанавливает координатор в качестве отображаемого делегата и добавляет распознаватель жестов касания следующим образом
mapView.addGestureRecognizer(UITapGestureRecognizer(target: context.coordinator, action: #selector(context.coordinator.mapTapped(_:))))
Эта обработка mapTapped выполняется непосредственно до родительского элемента MapView для обработки касания.
После того, как касание перешло в мировое пространство, я проверяю его по circleRegions свойство @Binding массива, чтобы определить, был ли выбран регион. Затем я обновляю свойства selectedCircleIndex и selectedCircle @Binding, чтобы отразить выделение, а затем принудительно перестраиваю наложения MKMapView, чтобы обеспечить создание новых средств визуализации для этих наложений, после чего я могу обновить цвет средств визуализации, чтобы отразить выбор.
Все это прекрасно работает, хотя наложения действительно мигают, когда они удаляются и повторно добавляются, но это не конец света, я не смог найти лучшего способа обновления цвета рендерера, кроме удаления и повторного добавления. добавив все наложения.
Проблема в том, что если я выберу кружок, сделав его синим, а затем немного прокрутите карту, а затем нажмите кнопку кружка, чтобы создать новый кружок в другом месте, то у меня останутся оба круга, отображающиеся красным цветом. последний выбранный, который должен быть синим, все еще "выбран" (выбранный круг и индекс верны), но неправильно отображается красным.
При отладке кажется, что свойство @Binding массива circleRegions в MapView становится недопустимо пустым во время вызов координатора mapView (: rendererFor) MKMapViewDelegate. Который останавливает мой код от сопоставления индекса круга с кругомRegions родителя. Еще более запутанным является тот факт, что если вы поместите точку останова в этот момент в коде, вы сможете go увеличить размер стека до того момента, когда родительский MapView был handleTap (), и вызывая rebuildOverlays (). На этом этапе его circleRegions верны, но к тому времени, когда мы нажимаем на обратный вызов рендерера, они становятся недействительными.
Swift затрудняет понимание того, что родительский MapView координатора является одним и тем же экземпляром в этих разных местах вызова стека, так как вы не можете легко смотреть на значения указателя самого MapView или родителя-координатора в разное время.
Чтобы попытаться отладить или исправить эту проблему, я добавил несколько дополнительных настроек, позволяющих изменить поведение
- MapView
- let dispatchMapViewUpdates = false
- который контролирует, выполняется ли rebuildOverlays () внутри функции обратного вызова распознавателя жестов касания немедленно или отправляется обратно в основную очередь
- Координатор
- let testWithIndex = true // обновить это, чтобы проверить с индексом или необязательным кружком
- , который контролирует, использует ли метод делегата средства отображения карты выбранный индекс или выбранный MKCircle? на родителя, чтобы сказать, выбран ли визуализируемый круг
К сожалению, ни один из этих вариантов не решает проблему, которая просто оставляет меня озадаченным относительно того, что происходит. Я также подтвердил, что эта проблема возникает на устройстве так же, как и в симуляторе, поэтому проблема не только в Simulator SwiftUI.
Если у кого-то есть какие-либо идеи, я определенно буду признателен. Приветствия