You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于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和对应逻辑

额外优化建议

  1. 封装地图操作逻辑:将地图标注、移除等操作封装为MapManager单例,避免MainViewController暴露过多细节,进一步降低耦合:
class MapManager {
    static let shared = MapManager()
    private let mapView: MKMapView // 持有地图视图实例
    
    func addBusStopAnnotations() { /* 具体实现 */ }
    func removeBusStopAnnotations() { /* 具体实现 */ }
}
  1. 优化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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 09:44:51