mapbox / mapbox-maps-ios

Interactive, thoroughly customizable maps for iOS powered by vector tiles and Metal
https://www.mapbox.com/mapbox-mobile-sdk
Other
462 stars 149 forks source link

Mapbox assert at start #2154

Closed Adraesh closed 5 months ago

Adraesh commented 5 months ago

Environment

Observed behavior and steps to reproduce

[Error]: Invalid size is used for setting the map view, fall back to the default size{64, 64}. MapboxMaps/MapView.swift:589: Assertion failed

func updateDrawableSize(to size: CGSize) {
        guard let metalView, !metalView.autoResizeDrawable else { return }

        metalView.bounds.size = size
        mapboxMap.size = size

        metalView.drawableSize = CGSize(width: size.width * pixelRatio, height: size.height * pixelRatio)
        // DrawableSize setter will recalculate `contentScaleFactor` if the new drawableSize doesn't fit into
        // the current bounds.size and scale.
        assert(metalView.contentScaleFactor == pixelRatio)

        // GL-Native will trigger update on `mapboxMap.size` update but it will come in the next frame.
        // To reduce glitches we can schedule repaint in the next frame to resize map texture.
        scheduleRepaint()
    }

Expected behavior

No crash should happen, just like on v11.1.0 of Mapbox SDK.

Our integration (works fine on 11.1.0:

ZStack {
                MapContentView()
                ...
}
import SwiftUI
import MapboxMaps

struct MapContentView: UIViewRepresentable {
    func makeUIView(context: UIViewRepresentableContext<MapContentView>) -> MapView {
        return MapManager.shared().mapView
    }

    func updateUIView(_ uiView: MapView, context: UIViewRepresentableContext<MapContentView>) {
    }
}

class MapManager: NSObject, ObservableObject, GestureManagerDelegate, UIColorPickerViewControllerDelegate {
private static var sharedMapManager: MapManager = {
        let mapManager = MapManager()

        return mapManager
    }()

    class func shared() -> MapManager {
        return self.sharedMapManager
    }

private override init() {
        super.init()

        self.instanciate()
        ...
    }
}
public func instanciate() -> Void {
        MapboxOptions.accessToken = "pk......";

        let centerCoordinate = CLLocationCoordinate2D(latitude: 46, longitude: 0)
        let camera = CameraOptions(center: centerCoordinate, zoom: 9)

        let mapInitOptions = MapInitOptions(cameraOptions: camera);
        self.mapView = MapView(frame: CGRect(x: 0, y: 0, width: 400, height: 400), mapInitOptions: mapInitOptions)
        self.mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]

        ...
    }
...

Thanks.
persidskiy commented 5 months ago

@Adraesh Hi, thank you for the report, we work on it.

Did you try to use the standard SwiftUI support from the sdk https://docs.mapbox.com/ios/maps/api/11.2.0/documentation/mapboxmaps/map? Would that help with the issue?

Adraesh commented 5 months ago

@persidskiy

Thank you for your prompte answer.

I am not quite sure how to implement the standard SwiftUI along with the architecture which we have already in place since more than 4 years now (before any mapbox swift ui support).

As you saw in my previous code, we have a singleton that holds a reference to the MapView in this way we can interact with the map wherever and whenever we want without being locked by a context.

Using this same approach how would you use the "standard SwiftUI support" and instanciate a Map and retaining a global reference of it?

Is there a way to instanciate a Map() with a MapView object or any related object?

I am missing something here.

persidskiy commented 5 months ago

@Adraesh

As you saw in my previous code, we have a singleton that holds a reference to the MapView in this way we can interact with the map wherever and whenever we want without being locked by a context.

Using this same approach how would you use the "standard SwiftUI support" and instanciate a Map and retaining a global reference of it?

The Map implementation doesn't allow you to use the singleton MapView, as this would break any guarantees on the Map API and generally is unwanted practice.

Is there a way to instanciate a Map() with a MapView object or any related object?

No, if you use Map view in SwiftUI, it wraps most of MapView functionality in declarative way. However, in some rare cases you can access the underlying MapboxMap and other objects available in MapProxy via MapReader https://docs.mapbox.com/ios/maps/api/11.2.0/documentation/mapboxmaps/swiftui-user-guide#Direct-access-to-the-underlying-map-implementation

persidskiy commented 5 months ago

Fixed here https://github.com/mapbox/mapbox-maps-ios/commit/3d59b1e3356151e7fa14311d05ec18fbe7fda899

Adraesh commented 5 months ago

@persidskiy Thank you very much.

Will check the 11.3.0-beta.1

Adraesh commented 5 months ago

It works perfectly. Thank you @persidskiy

feelingsonice commented 1 month ago

@persidskiy I'm also seeing this even though I'm not setting the map's size anywhere in my code nor am I using UIKit. Would you mind explain what this error means? Should I just ignore it since it's now a warning?