如何基于Sheet中UIViewController切换更新MKMapView
可扩展的Sheet-地图同步架构方案
方案一:协议驱动的职责分离(推荐)
核心思路是让每个Sheet内的ViewController自主定义「进入时的地图操作」和「离开时的地图操作」,MainViewController仅需统一调用协议方法,无需关心具体VC类型。
步骤1:定义地图交互协议
创建协议声明Sheet VC对地图的操作需求:
protocol SheetMapEffectable: AnyObject { // 当该VC成为Sheet顶部时执行的地图操作 func applyMapEffects() // 当该VC从Sheet顶部离开时执行的地图操作 func removeMapEffects() }
步骤2:让Sheet内的VC遵守协议
以NearbyBusStopsVC为例,实现协议逻辑:
extension NearbyBusStopsVC: SheetMapEffectable { func applyMapEffects() { MapManager.shared.addBusStopAnnotations() } func removeMapEffects() { MapManager.shared.removeBusStopAnnotations() } }
注:WelcomeVC若无需地图操作,可空实现或不遵守协议
步骤3:简化MainViewController的回调逻辑
移除所有if-else判断,直接通过协议调用:
extension MainViewController: SheetControllerDelegate { func didChangeViewInSheet(from previousVC: UIViewController, to currentVC: UIViewController) { // 先处理上一个VC的地图清理 (previousVC as? SheetMapEffectable)?.removeMapEffects() // 再处理当前VC的地图应用 (currentVC as? SheetMapEffectable)?.applyMapEffects() } }
优势
- 完全符合开闭原则:新增Sheet VC时,仅需让其遵守协议并实现对应方法,无需修改MainVC或SheetController代码
- 职责清晰:每个VC自主管理对应地图逻辑,避免MainVC沦为庞大的逻辑集合
- 扩展性极强:支持任意数量的Sheet VC,无需新增判断分支
方案二:基于状态枚举的集中管理
若希望将地图操作逻辑集中在MainVC统一管控,可抽象Sheet页面状态为枚举,替代直接判断VC类型的方式。
步骤1:定义Sheet状态枚举
enum SheetPageState { case welcome case nearbyBusStops // 新增页面时在此添加对应case }
步骤2:给VC绑定状态标识
通过扩展为每个Sheet VC映射对应的状态:
extension UIViewController { var sheetPageState: SheetPageState? { switch self { case is WelcomeVC: return .welcome case is NearbyBusStopsVC: return .nearbyBusStops // 新增VC时在此添加映射 default: return nil } } }
步骤3:基于状态处理地图操作
extension MainViewController: SheetControllerDelegate { func didChangeViewInSheet(from previousVC: UIViewController, to currentVC: UIViewController) { let prevState = previousVC.sheetPageState let currState = currentVC.sheetPageState // 清理上一个状态的地图效果 switch prevState { case .nearbyBusStops: removeBusStopAnnotations() default: break } // 应用当前状态的地图效果 switch currState { case .nearbyBusStops: addBusStopAnnotations() default: break } } }
优势
- 地图操作逻辑集中,便于统一管控和调试
- 相比直接判断VC类型,枚举方式更清晰,减少冗余代码
- 新增页面时仅需添加枚举case和对应逻辑
额外优化建议
- 封装地图操作逻辑:将地图标注、移除等操作封装为
MapManager单例,避免MainViewController暴露过多细节,进一步降低耦合:
class MapManager { static let shared = MapManager() private let mapView: MKMapView // 持有地图视图实例 func addBusStopAnnotations() { /* 具体实现 */ } func removeBusStopAnnotations() { /* 具体实现 */ } }
- 优化SheetController的回调可靠性:当前通过
willSet获取上一个VC的方式存在连续操作时的风险,可在present和dismiss方法中直接记录前后VC,再回调代理:
class SheetController { // ... 原有代码 ... func present(_ viewController: UIViewController) { // ... 原有代码 ... let previousVC = viewControllers.last viewControllers.append(viewController) delegate?.didChangeViewInSheet(from: previousVC, to: viewController) } func dismiss(_ onCompletion: (() -> Void)? = nil) { let poppedVC = viewControllers.removeLast() let currentVC = viewControllers.last // ... 原有代码 ... delegate?.didChangeViewInSheet(from: poppedVC, to: currentVC) } }
内容的提问来源于stack exchange,提问作者User95797654974
相关产品推荐
相关产品推荐

