SwiftUI:避免使用 MKMapView+UIViewRepresentable 在 TabView 中重新创建/重新渲染视图

Posted

技术标签:

【中文标题】SwiftUI:避免使用 MKMapView+UIViewRepresentable 在 TabView 中重新创建/重新渲染视图【英文标题】:SwiftUI: avoid recreating/rerendering view in TabView with MKMapView+UIViewRepresentable 【发布时间】:2019-09-25 07:30:17 【问题描述】:

我有一个带有三个选项卡的 TabView,其中一个包含这样实现的地图视图:

struct MapView: UIViewRepresentable 
    let region: MKCoordinateRegion
    let animatedRegion: Bool

    func makeUIView(context: Context) -> MKMapView 
        let mapView = MKMapView(frame: .zero)
        mapView.delegate = context.coordinator
        mapView.showsUserLocation = true
        mapView.setRegion(region, animated: animatedRegion)
        return mapView
    

    func makeCoordinator() -> Coordinator 
        Coordinator(self)
    

    func updateUIView(_ mapView: MKMapView, context: Context) 

    

    class Coordinator: NSObject, MKMapViewDelegate 
        var control: MapView

        init(_ control: MapView) 
            self.control = control
        
    

标签视图是这样实现的:

TabView(selection: $selection) 
    MapView(/* params */)
        .tabItem 
            Image(systemName: "1.square.fill")
            Text("map")
        .tag(1)
    Text("Screen #2")
        .tabItem 
            Image(systemName: "2.square.fill")
            Text("2")
        .tag(2)
    Text("Screen #3")
        .tabItem 
            Image(systemName: "3.square.fill")
            Text("3")
        .tag(3)

问题是每次我从其他两个选项卡之一切换回地图选项卡时都会执行 makeUIView(:context) 方法。当我切换到另一个选项卡时,似乎底层的 MKMapView 实例被释放,然后当我切换回来时它被重新创建。在 UIKit 中,它不会像那样重新渲染整个视图。我做错了什么,或者我可以做些什么来确保在我切换回来时保留底层的 MKMapView 实例,这样它就不必每次都重新创建它?

【问题讨论】:

【参考方案1】:

您需要为每个选项卡存储MKMapView 的实例并在离开视图之前销毁,因为 SwiftUI 在需要渲染时会销毁 MapView,例如绑定变量已更改。

struct MapView: UIViewRepresentable 
    @Binding var currentTab: Int
    static private var mapViews = [Int: MKMapView]()

    let region: MKCoordinateRegion
    let animatedRegion: Bool

    func makeUIView(context: Context) -> MKMapView 
        guard MapView.mapViews[currentTab] != nil else  return MapView.mapViews[currentTab]! 

        let mapView = MKMapView(frame: .zero)
        mapView.delegate = context.coordinator
        mapView.showsUserLocation = true
        mapView.setRegion(region, animated: animatedRegion)

        MapView.mapViews[currentTab] = mapView

        return mapView
    

    func makeCoordinator() -> Coordinator 
        Coordinator(self)
    

    func updateUIView(_ mapView: MKMapView, context: Context) 

    

    class Coordinator: NSObject, MKMapViewDelegate 
        var control: MapView

        init(_ control: MapView) 
            self.control = control
        
    

【讨论】:

太棒了,我对您的代码进行了稍微修改的版本,符合我的需要,效果很好。 pastebin.com/pyYStaHn 我会将mapView.setRegion(region, animated: animatedRegion) 移动到updateUIView,因为如果region 发生更改,那么 setRegion 将不会运行。【参考方案2】:

使用环境对象。总是更新它并在 TabView MapView(coordinates: Object.coordinates)

中传递它

【讨论】:

以上是关于SwiftUI:避免使用 MKMapView+UIViewRepresentable 在 TabView 中重新创建/重新渲染视图的主要内容,如果未能解决你的问题,请参考以下文章

SwiftUI 上的按钮点击调用 MKMapView.setRegion

如何在 SwiftUI 中获取 MKMapView 方向

无法将点击手势识别器添加到 SwiftUI MKMapView UIViewRepresentable

如何从 MKMapView didSelect 注释更新封装的 SwiftUI 视图

小屏幕设备上的 UI 损坏 - SwiftUI

SwiftUI 官方教程