Swift 4中regionDidChange内Timer无法触发问题求助
解决MKMapView追踪恢复Timer不触发的问题
看起来你已经把暂停逻辑搞定了,控制台也输出了"tracking will resume",但Timer就是不干活——这大概率是Timer没有被正确加入RunLoop调度,或者RunLoop模式的问题导致Timer被阻塞了。我给你分析下原因和两种可靠的解决方案:
问题根源
- Timer未加入RunLoop:你用
Timer(timeInterval:...)创建的定时器,默认不会自动加入当前RunLoop。如果没有手动添加,系统根本不会调度它执行,哪怕你确认它不是nil也没用。 - RunLoop模式冲突:就算你把Timer加入了RunLoop的默认模式(
.default),当地图还处于交互状态(比如惯性滑动)时,主线程RunLoop会切换到UITrackingRunLoopMode,默认模式下的Timer会被暂停,直到交互完全结束,这也会导致Timer延迟甚至不触发。
解决方案一:修复Timer的RunLoop绑定
修改你在regionDidChangeAnimated里创建Timer的代码,把它加入主线程RunLoop的common模式(这个模式会包含默认模式和追踪模式,确保交互时Timer也能正常调度):
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) { let didMapTouchesEnd = mapTouchEnded() if (didMapTouchesEnd == true && self.shouldTrack == true) { print("tracking will resume") // 先销毁旧的Timer(如果有的话) self.resumeTimer?.invalidate() self.resumeTimer = Timer(timeInterval: self.countdownTime, target: self, selector: #selector(self.resumeTrack), userInfo: nil, repeats: false) // 关键:把Timer添加到主线程RunLoop的common模式 RunLoop.main.add(self.resumeTimer!, forMode: .common) } }
同时,记得在pauseTrack里完善Timer的清理逻辑,避免野指针:
func pauseTrack() { locationManager.stopUpdatingLocation() self.trackingMode = false self.shouldTrack = true // 清理Timer if let timer = self.resumeTimer { timer.invalidate() self.resumeTimer = nil } }
解决方案二:用GCD延迟执行替代Timer(更推荐)
Timer对RunLoop模式比较敏感,而GCD的asyncAfter不受RunLoop模式影响,还不用手动管理Timer的生命周期,代码更简洁安全:
把regionDidChangeAnimated里的Timer代码替换成:
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) { let didMapTouchesEnd = mapTouchEnded() if (didMapTouchesEnd == true && self.shouldTrack == true) { print("tracking will resume") // 用GCD延迟执行恢复逻辑,[weak self]避免循环引用 DispatchQueue.main.asyncAfter(deadline: .now() + self.countdownTime) { [weak self] in guard let self = self else { return } // 再次确认状态,避免用户在延迟期间又触发了暂停操作 if self.shouldTrack { self.resumeTrack() } } } }
这种方式你甚至可以删掉resumeTimer这个属性,不用再管Timer的销毁和绑定问题,可靠性更高。
额外优化建议
你通过遍历mapView子视图的手势识别器来判断用户交互的方式,其实不太稳定——苹果可能会在后续版本修改MKMapView的内部视图结构。如果要更可靠地判断用户是否在操作地图,可以考虑:
- 监听MKMapView的
gestureRecognizers状态(但要注意避免和系统手势冲突) - 或者在
regionWillChangeAnimated里结合mapView.isUserInteractionEnabled和animated参数来辅助判断
内容的提问来源于stack exchange,提问作者lunadiviner
相关产品推荐
相关产品推荐

