@@ -5,6 +5,8 @@ import UIKit
55final class AppleMapProviderAdapter : MapProviderAdapter {
66 private let mapViewDelegate = HybridMapViewDelegate ( )
77 private var isProgrammaticUpdate = false
8+ private var pendingProgrammaticUpdateIDs : [ Int ] = [ ]
9+ private var nextProgrammaticUpdateID = 0
810 private var isMapReady = false
911 private var hasDeliveredMapReady = false
1012 private var liveClusterTimer : Timer ?
@@ -210,34 +212,47 @@ final class AppleMapProviderAdapter: MapProviderAdapter {
210212 }
211213
212214 let edgePadding = padding? . toUIEdgeInsets ( ) ?? . zero
213- isProgrammaticUpdate = true
215+ let shouldAnimate = animated ?? true
216+ let updateID = beginProgrammaticUpdate ( )
214217 view. setVisibleMapRect (
215218 mapRect,
216219 edgePadding: edgePadding,
217- animated: animated ?? true
220+ animated: shouldAnimate
221+ )
222+ scheduleProgrammaticUpdateFallback (
223+ for: updateID,
224+ delay: shouldAnimate ? 1.0 : 0
218225 )
219- isProgrammaticUpdate = false
220226 }
221227
222228 func applyRegion( _ region: Region , animated: Bool = false ) {
223- isProgrammaticUpdate = true
229+ let updateID = beginProgrammaticUpdate ( )
224230 view. setRegion ( region. toMKCoordinateRegion ( ) , animated: animated)
225- isProgrammaticUpdate = false
231+ scheduleProgrammaticUpdateFallback (
232+ for: updateID,
233+ delay: animated ? 1.0 : 0
234+ )
226235 }
227236
228237 func updateMapCamera( _ camera: Camera , animated: Bool , duration: Double = 0 ) {
229- isProgrammaticUpdate = true
238+ let updateID = beginProgrammaticUpdate ( )
230239 let mapCamera = camera. toMKMapCamera ( )
231240
232241 if animated {
233- UIView . animate ( withDuration: duration) {
234- self . view. camera = mapCamera
235- }
242+ UIView . animate (
243+ withDuration: duration,
244+ animations: {
245+ self . view. camera = mapCamera
246+ } ,
247+ completion: { [ weak self] _ in
248+ self ? . endProgrammaticUpdate ( updateID)
249+ }
250+ )
251+ scheduleProgrammaticUpdateFallback ( for: updateID, delay: duration + 0.1 )
236252 } else {
237253 view. camera = mapCamera
254+ scheduleProgrammaticUpdateFallback ( for: updateID, delay: 0 )
238255 }
239-
240- isProgrammaticUpdate = false
241256 }
242257
243258 // Derived from MKCoordinateRegion (center + span). May differ from Android
@@ -254,6 +269,45 @@ final class AppleMapProviderAdapter: MapProviderAdapter {
254269 view. setRegion ( view. regionThatFits ( region) , animated: true )
255270 }
256271
272+ private func beginProgrammaticUpdate( ) -> Int {
273+ nextProgrammaticUpdateID += 1
274+ let updateID = nextProgrammaticUpdateID
275+ pendingProgrammaticUpdateIDs. append ( updateID)
276+ isProgrammaticUpdate = true
277+ return updateID
278+ }
279+
280+ private func endProgrammaticUpdate( _ updateID: Int ? = nil ) {
281+ if let updateID {
282+ guard let index = pendingProgrammaticUpdateIDs. firstIndex ( of: updateID) else {
283+ return
284+ }
285+ pendingProgrammaticUpdateIDs. remove ( at: index)
286+ } else if !pendingProgrammaticUpdateIDs. isEmpty {
287+ pendingProgrammaticUpdateIDs. removeFirst ( )
288+ }
289+
290+ isProgrammaticUpdate = !pendingProgrammaticUpdateIDs. isEmpty
291+ }
292+
293+ func endProgrammaticRegionChangeIfNeeded( ) {
294+ guard !pendingProgrammaticUpdateIDs. isEmpty else {
295+ return
296+ }
297+
298+ endProgrammaticUpdate ( )
299+ }
300+
301+ private func scheduleProgrammaticUpdateFallback( for updateID: Int , delay: TimeInterval ) {
302+ DispatchQueue . main. asyncAfter ( deadline: . now( ) + delay) { [ weak self] in
303+ guard let self else {
304+ return
305+ }
306+
307+ self . endProgrammaticUpdate ( updateID)
308+ }
309+ }
310+
257311 func startLiveClustering( ) {
258312 guard liveClusterTimer == nil else {
259313 return
@@ -340,6 +394,7 @@ final class AppleMapProviderAdapter: MapProviderAdapter {
340394 liveClusterTimer? . invalidate ( )
341395 liveClusterTimer = nil
342396 isProgrammaticUpdate = false
397+ pendingProgrammaticUpdateIDs. removeAll ( )
343398 isMapReady = false
344399 hasDeliveredMapReady = false
345400 onRegionChange = nil
0 commit comments