From a9b5c4be680a56e15e3f45d49181e8cae2de7684 Mon Sep 17 00:00:00 2001 From: Mateusz Zatorski Date: Wed, 18 Sep 2024 14:02:37 +0200 Subject: [PATCH 1/2] add preferredFramesPerSecond support --- .../rnmbx/components/mapview/RNMBXMapView.kt | 7 + .../components/mapview/RNMBXMapViewManager.kt | 6 +- .../RNMBXMapViewManagerDelegate.java | 3 + ios/RNMBX/RNMBXMapView.swift | 284 ++++++++++-------- ios/RNMBX/RNMBXMapViewComponentView.mm | 15 +- ios/RNMBX/RNMBXMapViewManager.m | 1 + 6 files changed, 176 insertions(+), 140 deletions(-) diff --git a/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapView.kt b/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapView.kt index 688f3a637..d390514c1 100644 --- a/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapView.kt +++ b/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapView.kt @@ -235,6 +235,7 @@ open class RNMBXMapView(private val mContext: Context, var mManager: RNMBXMapVie private val mFeatures = mutableListOf() private var mQueuedFeatures: MutableList? = ArrayList() private val mCameraChangeTracker = CameraChangeTracker() + private var mPreferredFrameRate: Int? = null private lateinit var mMap: MapboxMap private lateinit var mMapView: MapView @@ -651,6 +652,12 @@ open class RNMBXMapView(private val mContext: Context, var mManager: RNMBXMapVie changes.add(Property.LOCALIZE_LABELS) } + fun setReactPreferredFramesPerSecond(preferredFramesPerSecond: Int) { + if (this::mMapView.isInitialized) { + mMapView.setMaximumFps(preferredFramesPerSecond) + } + } + fun setReactStyleURL(styleURL: String) { mStyleURL = styleURL changes.add(Property.STYLE_URL) diff --git a/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapViewManager.kt b/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapViewManager.kt index 220cf5448..4df25e118 100644 --- a/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapViewManager.kt +++ b/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapViewManager.kt @@ -210,9 +210,9 @@ open class RNMBXMapViewManager(context: ReactApplicationContext, val viewTagReso mapView.setReactStyleURL(styleURL.asString()) } - @ReactProp(name = "preferredFramesPerSecond") @Suppress("UNUSED_PARAMETER") - fun setPreferredFramesPerSecond(mapView: RNMBXMapView, preferredFramesPerSecond: Int) { - //mapView.setReactPreferredFramesPerSecond(preferredFramesPerSecond); + @ReactProp(name = "preferredFramesPerSecond") + override fun setPreferredFramesPerSecond(mapView: RNMBXMapView, preferredFramesPerSecond: Dynamic) { + mapView.setReactPreferredFramesPerSecond(preferredFramesPerSecond.asInt()) } @ReactProp(name = "zoomEnabled") diff --git a/android/src/main/old-arch/com/facebook/react/viewmanagers/RNMBXMapViewManagerDelegate.java b/android/src/main/old-arch/com/facebook/react/viewmanagers/RNMBXMapViewManagerDelegate.java index 63e6d3d15..9cff5aa4f 100644 --- a/android/src/main/old-arch/com/facebook/react/viewmanagers/RNMBXMapViewManagerDelegate.java +++ b/android/src/main/old-arch/com/facebook/react/viewmanagers/RNMBXMapViewManagerDelegate.java @@ -103,6 +103,9 @@ public void setProperty(T view, String propName, @Nullable Object value) { case "mapViewImpl": mViewManager.setMapViewImpl(view, new DynamicFromObject(value)); break; + case "preferredFramesPerSecond": + mViewManager.setPreferredFramesPerSecond(view, new DynamicFromObject(value)); + break; default: super.setProperty(view, propName, value); } diff --git a/ios/RNMBX/RNMBXMapView.swift b/ios/RNMBX/RNMBXMapView.swift index a5b00ac27..23b4ad634 100644 --- a/ios/RNMBX/RNMBXMapView.swift +++ b/ios/RNMBX/RNMBXMapView.swift @@ -21,11 +21,11 @@ class InitWaiter { waiters.append(callback) } } - + func hasInited() -> Bool { return object != nil } - + /// call whan the object has inited, queued calls will be executed func onInit(_ object: Type) { self.object = object @@ -33,7 +33,7 @@ class InitWaiter { waiters = [] oldWaiters.forEach { $0(object) } } - + /// reset, calls will be queued again func reset() { self.object = nil @@ -46,14 +46,14 @@ class InitWaiter { */ public class RNMBXMapViewFactory { private static var factories: [String: RNMBXMapViewFactoryFunc] = [:]; - + static func get(_ id: String) -> RNMBXMapViewFactoryFunc? { if let id = id.split(separator: ":", maxSplits: 1).first { return factories[String(id)] } return nil } - + public static func register(_ id: String, factory: @escaping RNMBXMapViewFactoryFunc) { factories.updateValue(factory, forKey: id) } @@ -158,33 +158,33 @@ open class RNMBXMapView: UIView { var imageManager: ImageManager = ImageManager() var tapDelegate: IgnoreRNMBXMakerViewGestureDelegate? = nil - + var eventDispatcher: RCTEventDispatcherProtocol - + var reactOnPress : RCTBubblingEventBlock? var reactOnLongPress : RCTBubblingEventBlock? var reactOnMapChange : RCTBubblingEventBlock? - + @objc var onCameraChanged: RCTDirectEventBlock? - + var styleLoadWaiters = InitWaiter() var cameraWaiters = InitWaiter() - + var features: [FeatureEntry] = [] - + weak var reactCamera : RNMBXCamera? var images : [RNMBXImages] = [] var sources : [RNMBXInteractiveElement] = [] - + var handleMapChangedEvents = Set() - + var eventListeners : [Cancelable] = [] - + private var isPendingInitialLayout = true private var wasGestureActive = false private var isGestureActive = false - + var layerWaiters : [String:[(String) -> Void]] = [:] @objc @@ -192,21 +192,21 @@ open class RNMBXMapView: UIView { @objc public var mapViewImpl : String? = nil - + #if RNMBX_11 var cancelables = Set() #endif - + lazy var pointAnnotationManager : RNMBXPointAnnotationManager = { let result = RNMBXPointAnnotationManager(annotations: mapView.annotations, mapView: mapView) self._removeMapboxLongPressGestureRecognizer() return result }() - + lazy var calloutAnnotationManager : MapboxMaps.PointAnnotationManager = { return mapView.annotations.makePointAnnotationManager(id: "RNMBX-mapview-callouts") }() - + var _mapView: MapView! = nil func createMapView() -> MapView { if let mapViewImpl = mapViewImpl, let mapViewInstance = createAndAddMapViewImpl(mapViewImpl, self) { @@ -225,7 +225,7 @@ open class RNMBXMapView: UIView { _mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight] addSubview(_mapView) } - + _mapView.gestures.delegate = self setupEvents() afterMapViewAdded() @@ -245,7 +245,7 @@ open class RNMBXMapView: UIView { public var mapView : MapView! { get { return _mapView } } - + @available(*, deprecated, renamed: "withMapboxMap", message: "mapboxMap can be nil if the map initialization has not finished, use withMapboxMap instead") var mapboxMap: MapboxMap! { get { _mapView?.mapboxMap } @@ -263,7 +263,7 @@ open class RNMBXMapView: UIView { } else { addToMap = true } - + let entry = FeatureEntry(feature: mapComponent, view: subview, addedToMap: false) if (addToMap) { mapComponent.addToMap(self, style: style) @@ -278,7 +278,7 @@ open class RNMBXMapView: UIView { } } } - + @objc public func removeFromMap(_ subview: UIView) { if let mapComponent = subview as? RNMBXMapComponent { var entryIndex = features.firstIndex { $0.view == subview } @@ -302,7 +302,7 @@ open class RNMBXMapView: UIView { addToMap(subview) super.insertReactSubview(subview, at: atIndex) } - + @objc open override func removeReactSubview(_ subview: UIView!) { removeFromMap(subview) super.removeReactSubview(subview) @@ -312,15 +312,15 @@ open class RNMBXMapView: UIView { self.eventDispatcher = eventDispatcher super.init(frame: frame) } - + public required init (coder: NSCoder) { fatalError("not implemented") } - + func layerAdded (_ layer: Layer) { // TODO } - + func waitForLayerWithID(_ layerId: String, _ callback: @escaping (_ layerId: String) -> Void) { let style = mapView.mapboxMap.style; if style.layerExists(withId: layerId) { @@ -329,23 +329,23 @@ open class RNMBXMapView: UIView { layerWaiters[layerId, default: []].append(callback) } } - + @objc public override func layoutSubviews() { super.layoutSubviews() if let camera = reactCamera { if (isPendingInitialLayout) { isPendingInitialLayout = false; - + camera.initialLayout() } } } - - + + // MARK: - React Native properties let changes : PropertyChanges = PropertyChanges() var mapViewWaiters = InitWaiter() - + enum Property : String { case projection case localizeLabels @@ -362,7 +362,8 @@ open class RNMBXMapView: UIView { case onMapChange case styleURL case gestureSettings - + case preferredFramesPerSecond + func apply(_ map: RNMBXMapView) -> Void { switch self { case .projection: @@ -396,18 +397,20 @@ open class RNMBXMapView: UIView { map.applyPitchEnabled() case .gestureSettings: map.applyGestureSettings() + case .preferredFramesPerSecond: + map.applyPreferredFramesPerSecond() } } } - + func changed(_ property: Property) { changes.add(name: property.rawValue, update: property.apply) } - + func withMapView(callback: @escaping (_: MapView) -> Void) { mapViewWaiters.callOrWait(callback) } - + func withMapboxMap(callback: @escaping (_: MapboxMap) -> Void) { if let mapboxMap = _mapView?.mapboxMap { callback(mapboxMap) @@ -417,9 +420,9 @@ open class RNMBXMapView: UIView { } } } - + var projection: StyleProjection? - + @objc public func setReactProjection(_ value: String?) { if let value = value { projection = StyleProjection(name: value == "globe" ? .globe : .mercator) @@ -428,7 +431,7 @@ open class RNMBXMapView: UIView { } changed(.projection) } - + func applyProjection() { logged("RNMBXMapView.setReactProjection") { if let projection = projection { @@ -436,9 +439,26 @@ open class RNMBXMapView: UIView { } } } - + + var preferredFramesPerSecond: Int? = nil + + @objc public func setReactPreferredFramesPerSecond(_ value: Int) { + self.preferredFramesPerSecond = value + changed(.preferredFramesPerSecond) + } + + func applyPreferredFramesPerSecond() { + if let value = preferredFramesPerSecond { + if #available(iOS 15.0, *) { + self.mapView.preferredFrameRateRange = CAFrameRateRange(minimum: 1, maximum: Float(value)) + } else { + // Fallback on earlier versions + } + } + } + var locale: (layerIds: [String]?, locale: Locale)? = nil - + @objc public func setReactLocalizeLabels(_ value: NSDictionary?) { if let value = value { let localeString = value["locale"] as! String @@ -448,7 +468,7 @@ open class RNMBXMapView: UIView { } changed(.localizeLabels) } - + func applyLocalizeLabels() { onMapStyleLoaded { _ in logged("RNMBXMapView.\(#function)") { @@ -458,7 +478,7 @@ open class RNMBXMapView: UIView { } } } - + struct GestureSettings { var doubleTapToZoomInEnabled: Bool? = nil; var doubleTouchToZoomOutEnabled: Bool? = nil; @@ -473,9 +493,9 @@ open class RNMBXMapView: UIView { var simultaneousRotateAndPinchZoomEnabled: Bool? = nil; #endif } - + var gestureSettings = GestureSettings() - + @objc public func setReactGestureSettings(_ value: NSDictionary?) { if let value = value { @@ -526,11 +546,11 @@ open class RNMBXMapView: UIView { options.zoomAnimationAmount = zoomAnimationAmount.CGFloat }*/ gestureSettings = options - + changed(.gestureSettings) } } - + func applyGestureSettings() { if let gestures = self.mapView?.gestures { var options = gestures.options @@ -580,15 +600,15 @@ open class RNMBXMapView: UIView { } } } - + var attributionEnabled: OrnamentVisibility? = nil var attributionOptions: (position: OrnamentPosition, margins: CGPoint)? = nil - + @objc public func setReactAttributionEnabled(_ value: Bool) { attributionEnabled = value ? .visible : .hidden changed(.attribution) } - + func applyAttribution() { if let visibility = attributionEnabled { mapView.ornaments.options.attributionButton.visibility = visibility @@ -598,25 +618,25 @@ open class RNMBXMapView: UIView { mapView.ornaments.options.attributionButton.margins = options.margins } } - + @objc public func setReactAttributionPosition(_ position: [String: NSNumber]) { attributionOptions = self.getOrnamentOptionsFromPosition(position) changed(.attribution) } - + var logoEnabled: OrnamentVisibility? = nil var logoOptions: (position: OrnamentPosition, margins: CGPoint)? = nil - + @objc public func setReactLogoEnabled(_ value: Bool) { logoEnabled = value ? .visible : .hidden changed(.logo) } - + @objc public func setReactLogoPosition(_ position: [String: NSNumber]) { logoOptions = self.getOrnamentOptionsFromPosition(position) changed(.logo) } - + func applyLogo() { if let visibility = logoEnabled { mapView.ornaments.options.logo.visibility = visibility @@ -626,7 +646,7 @@ open class RNMBXMapView: UIView { mapView.ornaments.options.logo.margins = options.margins } } - + var compassEnabled: Bool = false var compassPosition: OrnamentPosition? = nil var compassMargins: CGPoint? = nil @@ -642,7 +662,7 @@ open class RNMBXMapView: UIView { compassFadeWhenNorth = value changed(.compass) } - + @objc public func setReactCompassPosition(_ position: [String: NSNumber]) { if let compassOptions = self.getOrnamentOptionsFromPosition(position) { compassPosition = compassOptions.position @@ -655,7 +675,7 @@ open class RNMBXMapView: UIView { compassPosition = toOrnamentPositon(Int(truncating: NSNumber(value: position))) changed(.compass) } - + @objc public func setReactCompassViewMargins(_ margins: CGPoint) { compassMargins = margins changed(.compass) @@ -665,21 +685,21 @@ open class RNMBXMapView: UIView { compassImage = image.isEmpty ? nil : image changed(.compass) } - + func applyCompass() { var visibility: OrnamentVisibility = .hidden if compassEnabled { visibility = compassFadeWhenNorth ? .adaptive : .visible } mapView.ornaments.options.compass.visibility = visibility - + if let position = compassPosition { mapView.ornaments.options.compass.position = position } if let margina = compassMargins { mapView.ornaments.options.compass.margins = margina } - + if let compassImage = compassImage { onMapStyleLoaded { map in let img = map.style.image(withId: compassImage) @@ -691,7 +711,7 @@ open class RNMBXMapView: UIView { self.mapView.ornaments.options.compass.image = nil } } - + func toOrnamentPositon(_ position: Int) -> OrnamentPosition { enum MapboxGLPosition : Int { case topLeft = 0 @@ -699,7 +719,7 @@ open class RNMBXMapView: UIView { case bottomLeft = 2 case bottomRight = 3 }; - + let glPosition = MapboxGLPosition(rawValue: position) switch glPosition { case .topLeft: @@ -718,19 +738,19 @@ open class RNMBXMapView: UIView { var scaleBarEnabled: Bool? = nil var scaleBarPosition: OrnamentPosition? = nil var scaleBarMargins: CGPoint? = nil - + @objc public func setReactScaleBarEnabled(_ value: Bool) { scaleBarEnabled = value changed(.scaleBar) } - + @objc public func setReactScaleBarPosition(_ position: [String: NSNumber]) { if let ornamentOptions = self.getOrnamentOptionsFromPosition(position) { scaleBarPosition = ornamentOptions.position scaleBarMargins = ornamentOptions.margins } } - + func applyScaleBar() { if let enabled = scaleBarEnabled { mapView.ornaments.options.scaleBar.visibility = enabled ? .visible : .hidden @@ -746,7 +766,7 @@ open class RNMBXMapView: UIView { @objc override public func didSetProps(_ props: [String]) { if (_mapView == nil) { let view = createMapView() - + mapViewWaiters.onInit(view) } changes.apply(self) @@ -771,7 +791,7 @@ open class RNMBXMapView: UIView { self.scrollEnabled = value changed(.scrollEnabled) } - + func applyScrollEnabled() { if let value = scrollEnabled { self.mapView.gestures.options.panEnabled = value @@ -784,14 +804,14 @@ open class RNMBXMapView: UIView { rotateEnabled = value changed(.rotateEnabled) } - + func applyRotateEnabled() { if let value = rotateEnabled { self.mapView.gestures.options.rotateEnabled = value } } - + var pitchEnabled: Bool? = nil @objc public func setReactPitchEnabled(_ value: Bool) { self.pitchEnabled = value @@ -820,21 +840,21 @@ open class RNMBXMapView: UIView { } } } - + func refreshComponentsBeforeStyleChange() { removeAllFeaturesFromMap(reason: .StyleChange) } - + func refreshComponentsAfterStyleChange(style: Style) { addFeaturesToMap(style: style) } - + var reactStyleURL: String? = nil @objc public func setReactStyleURL(_ value: String?) { self.reactStyleURL = value changed(.styleURL) } - + public func applyStyleURL() { var initialLoad = !self.styleLoadWaiters.hasInited() if !initialLoad { refreshComponentsBeforeStyleChange() } @@ -868,7 +888,7 @@ open class RNMBXMapView: UIView { let right = position["right"] let top = position["top"] let bottom = position["bottom"] - + if let left = left, let top = top { return (OrnamentPosition.topLeading, CGPoint(x: Int(truncating: left), y: Int(truncating: top))) } else if let right = right, let top = top { @@ -878,7 +898,7 @@ open class RNMBXMapView: UIView { } else if let bottom = bottom, let left = left { return (OrnamentPosition.bottomLeading, CGPoint(x: Int(truncating: left), y: Int(truncating: bottom))) } - + return nil } @@ -889,7 +909,7 @@ open class RNMBXMapView: UIView { } } } - + // MARK: - hooks for subclasses open func afterMapViewAdded() {} } @@ -906,7 +926,7 @@ extension RNMBXMapView { handler(self, mapEvent) }.store(in: &cancelables) } - + private func onNext(event: MapEventType, handler: @escaping (RNMBXMapView, T) -> Void) { let signal = event.method(self.mapView.mapboxMap) signal.observeNext { [weak self] (mapEvent) in @@ -962,7 +982,7 @@ extension RNMBXMapView { let event = RNMBXEvent(type:.mapIdle, payload: self.buildStateObject()); self.fireEvent(event: event, callback: self.reactOnMapChange) } - + self.wasGestureActive = false }) } @@ -978,11 +998,11 @@ extension RNMBXMapView { private func fireEvent(event: RNMBXEvent, callback: @escaping RCTBubblingEventBlock) { callback(event.toJSON()) } - + private func buildStateObject() -> [String: Any] { let cameraOptions = CameraOptions(cameraState: mapView.cameraState) let bounds = mapView.mapboxMap.coordinateBounds(for: cameraOptions) - + return [ "properties": [ "center": Point(mapView.cameraState.center).coordinates.toArray(), @@ -1000,7 +1020,7 @@ extension RNMBXMapView { "timestamp": timestamp() ] } - + private func timestamp(date: Date? = nil) -> Double { return (date ?? Date()).timeIntervalSince1970 * 1000 } @@ -1028,7 +1048,7 @@ extension RNMBXMapView { try result.toJSON() } } - + public func setupEvents() { self.onEvery(event: .mapLoadingError, handler: { (self, event) in let eventPayload : MapLoadingErrorPayload = event.payload @@ -1055,10 +1075,10 @@ extension RNMBXMapView { Logger.log(level: .error, message: "MapLoad error \(event)") } }) - + self.onEvery(event: .styleImageMissing) { (self, event) in let imageName = event.payload.id - + self.images.forEach { if $0.addMissingImageToStyle(style: self.mapboxMap.style, imageName: imageName) { return @@ -1088,10 +1108,10 @@ extension RNMBXMapView { let event = RNMBXEvent(type:.didFinishLoadingMap, payload: nil); self.fireEvent(event: event, callback: self.reactOnMapChange) }) - + self.onEvery(event: .styleLoaded, handler: { (self, event) in self.addFeaturesToMap(style: self.mapboxMap.style) - + if !self.styleLoadWaiters.hasInited(), let mapboxMap = self.mapboxMap { self.styleLoadWaiters.onInit(mapboxMap) } @@ -1110,7 +1130,7 @@ class IgnoreRNMBXMakerViewGestureDelegate : NSObject, UIGestureRecognizerDelegat init(originalDelegate: UIGestureRecognizerDelegate?) { self.originalDelegate = originalDelegate } - + func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool { return originalDelegate?.gestureRecognizerShouldBegin?(gestureRecognizer) ?? true } @@ -1118,7 +1138,7 @@ class IgnoreRNMBXMakerViewGestureDelegate : NSObject, UIGestureRecognizerDelegat func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return originalDelegate?.gestureRecognizer?(gestureRecognizer,shouldRecognizeSimultaneouslyWith: otherGestureRecognizer) ?? false } - + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOf otherGestureRecognizer: UIGestureRecognizer) -> Bool { return originalDelegate?.gestureRecognizer?(gestureRecognizer,shouldRequireFailureOf: otherGestureRecognizer) ?? false } @@ -1137,19 +1157,19 @@ class IgnoreRNMBXMakerViewGestureDelegate : NSObject, UIGestureRecognizerDelegat } return false } - + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { if let view = touch.view, isMarkerViewSubview(view) { return false } return originalDelegate?.gestureRecognizer?(gestureRecognizer,shouldReceive: touch) ?? true } - + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive press: UIPress) -> Bool { return originalDelegate?.gestureRecognizer?(gestureRecognizer,shouldReceive: press) ?? true } - + @available(iOS 13.4, *) func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive event: UIEvent) -> Bool { return originalDelegate?.gestureRecognizer?(gestureRecognizer,shouldReceive: event) ?? true @@ -1157,12 +1177,12 @@ class IgnoreRNMBXMakerViewGestureDelegate : NSObject, UIGestureRecognizerDelegat } extension RNMBXMapView { - + @objc public func setReactOnPress(_ value: @escaping RCTBubblingEventBlock) { self.reactOnPress = value changed(.onPress) } - + func applyOnPress() { let singleTapGestureRecognizer = self.mapView.gestures.singleTapGestureRecognizer @@ -1177,7 +1197,7 @@ extension RNMBXMapView { self.reactOnLongPress = value changed(.onLongPress) } - + func applyOnLongPress() { if (reactOnLongPress != nil) { let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(doHandleLongPress(_:))) @@ -1208,13 +1228,13 @@ extension RNMBXMapView: GestureManagerDelegate { DispatchQueue.main.async { if let source = sources.first { let hitbox = source.hitbox; - + let halfWidth = (hitbox["width"]?.doubleValue ?? RNMBXInteractiveElement.hitboxDefault) / 2.0; let halfHeight = (hitbox["height"]?.doubleValue ?? RNMBXInteractiveElement.hitboxDefault) / 2.0; let top = tapPoint.y - halfHeight; let left = tapPoint.x - halfWidth; - + let hitboxRect = CGRect(x: left, y: top, width: halfWidth * 2.0, height: halfHeight * 2.0) let options = RenderedQueryOptions( @@ -1222,7 +1242,7 @@ extension RNMBXMapView: GestureManagerDelegate { ) self.mapboxMap.queryRenderedFeatures(with: hitboxRect, options: options) { result in - + var newHits = hits var newTouchedSources = touchedSources; switch result { @@ -1244,10 +1264,10 @@ extension RNMBXMapView: GestureManagerDelegate { } } } - + func highestZIndex(sources: [RNMBXInteractiveElement]) -> RNMBXInteractiveElement? { var layersToSource : [String:RNMBXInteractiveElement] = [:] - + sources.forEach { source in source.getLayerIDs().forEach { layerId in if layersToSource[layerId] == nil { @@ -1258,9 +1278,9 @@ extension RNMBXMapView: GestureManagerDelegate { let orderedLayers = mapboxMap.style.allLayerIdentifiers return orderedLayers.lazy.reversed().compactMap { layersToSource[$0.id] }.first ?? sources.first } - - - + + + func _tapEvent(_ tapPoint: CGPoint) -> RNMBXEvent { let location = self.mapboxMap.coordinate(for: tapPoint) var geojson = Feature(geometry: .point(Point(location))); @@ -1271,7 +1291,7 @@ extension RNMBXMapView: GestureManagerDelegate { let event = RNMBXEvent(type:.tap, payload: logged("reactOnPress") { try geojson.toJSON() }) return event } - + @objc func doHandleTap(_ sender: UITapGestureRecognizer) { let tapPoint = sender.location(in: self) @@ -1284,7 +1304,7 @@ extension RNMBXMapView: GestureManagerDelegate { } let touchableSources = self.touchableSources() self.doHandleTapInSources(sources: touchableSources, tapPoint: tapPoint, hits: [:], touchedSources: []) { (hits, touchedSources) in - + if let source = self.highestZIndex(sources: touchedSources), source.hasPressListener, let onPress = source.onPress { @@ -1310,7 +1330,7 @@ extension RNMBXMapView: GestureManagerDelegate { ] ) self.fireEvent(event: event, callback: onPress) - + } else { if let reactOnPress = self.reactOnPress { self.fireEvent(event: self._tapEvent(tapPoint), callback: reactOnPress) @@ -1320,7 +1340,7 @@ extension RNMBXMapView: GestureManagerDelegate { } } } - + @objc func doHandleLongPress(_ sender: UILongPressGestureRecognizer) { let position = sender.location(in: self) @@ -1373,13 +1393,13 @@ extension RNMBXMapView: GestureManagerDelegate { public func gestureManager(_ gestureManager: GestureManager, didBegin gestureType: GestureType) { isGestureActive = true } - + public func gestureManager(_ gestureManager: GestureManager, didEnd gestureType: GestureType, willAnimate: Bool) { if !willAnimate { isGestureActive = false; } } - + public func gestureManager(_ gestureManager: GestureManager, didEndAnimatingFor gestureType: GestureType) { isGestureActive = false; } @@ -1411,7 +1431,7 @@ extension RNMBXMapView { guard let mapboxMap = mapboxMap else { fatalError("mapboxMap is null") } - + styleLoadWaiters.callOrWait(block) } } @@ -1419,7 +1439,7 @@ extension RNMBXMapView { extension RNMBXMapView { func setSourceVisibility(_ visible: Bool, sourceId: String, sourceLayerId: String?) -> Void { let style = self.mapboxMap.style - + style.allLayerIdentifiers.forEach { layerInfo in let layer = logged("setSourceVisibility.layer", info: { "\(layerInfo.id)" }) { try style.layer(withId: layerInfo.id) @@ -1452,12 +1472,12 @@ extension RNMBXMapView { class RNMBXPointAnnotationManager : AnnotationInteractionDelegate { weak var selected : RNMBXPointAnnotation? = nil private var draggedAnnotation: PointAnnotation? - + func annotationManager(_ manager: AnnotationManager, didDetectTappedAnnotations annotations: [Annotation]) { // We handle taps ourselfs // onTap(annotations: annotations) } - + func deselectCurrentlySelected(deselectAnnotationOnTap: Bool = false) -> Bool { if let selected = selected { selected.doDeselect(deselectAnnotationOnMapTap: deselectAnnotationOnTap) @@ -1466,15 +1486,15 @@ class RNMBXPointAnnotationManager : AnnotationInteractionDelegate { } return false } - + func onAnnotationClick(pointAnnotation: RNMBXPointAnnotation) { let oldSelected = selected var newSelected: RNMBXPointAnnotation? = pointAnnotation - + if (newSelected == oldSelected) { newSelected = nil } - + deselectCurrentlySelected() if let newSelected = newSelected { @@ -1482,7 +1502,7 @@ class RNMBXPointAnnotationManager : AnnotationInteractionDelegate { selected = newSelected } } - + func lookup(_ annotation: PointAnnotation) -> RNMBXPointAnnotation? { guard let userInfo = annotation.userInfo else { return nil @@ -1500,13 +1520,13 @@ class RNMBXPointAnnotationManager : AnnotationInteractionDelegate { #endif return nil } - + func onTap(annotations: [Annotation]) { guard annotations.count > 0 else { fatalError("didDetectTappedAnnotations: No annotations found") } - + for annotation in annotations { if let annotation = annotation as? PointAnnotation { if let pointAnnotation = lookup(annotation) { @@ -1515,7 +1535,7 @@ class RNMBXPointAnnotationManager : AnnotationInteractionDelegate { } } } - + func handleTap(_ tap: UITapGestureRecognizer, noAnnotationFound: @escaping (UITapGestureRecognizer) -> Void) { let layerId = manager.layerId guard let mapFeatureQueryable = mapView?.mapboxMap else { @@ -1557,10 +1577,10 @@ class RNMBXPointAnnotationManager : AnnotationInteractionDelegate { } } } - + var manager : MapboxMaps.PointAnnotationManager weak var mapView : MapView? = nil - + init(annotations: AnnotationOrchestrator, mapView: MapView) { manager = annotations.makePointAnnotationManager(id: "RNMBX-mapview-point-annotations") manager.delegate = self @@ -1571,7 +1591,7 @@ class RNMBXPointAnnotationManager : AnnotationInteractionDelegate { guard annotations.count > 0 else { fatalError("didDetectDraggedAnnotations: No annotations found") } - + for annotation in annotations { if let pointAnnotation = annotation as? PointAnnotation, let pt = lookup(pointAnnotation) { @@ -1607,7 +1627,7 @@ class RNMBXPointAnnotationManager : AnnotationInteractionDelegate { } } } - + // Used for handling panning to detect annotation dragging func handleLongPress(_ sender: UILongPressGestureRecognizer, noAnnotationFound: @escaping (UILongPressGestureRecognizer) -> Void) { let layerId = manager.layerId @@ -1624,7 +1644,7 @@ class RNMBXPointAnnotationManager : AnnotationInteractionDelegate { mapFeatureQueryable.queryRenderedFeatures( with: sender.location(in: sender.view), options: options) { [weak self] (result) in - + guard let self = self else { return } switch result { case .success(let queriedFeatures): @@ -1656,7 +1676,7 @@ class RNMBXPointAnnotationManager : AnnotationInteractionDelegate { guard var annotation = self.draggedAnnotation else { return } - + self.onDragHandler(self.manager, didDetectDraggedAnnotations: [annotation], dragState: .changed, targetPoint: targetPoint) let idx = self.manager.annotations.firstIndex { an in return an.id == annotation.id } @@ -1676,18 +1696,18 @@ class RNMBXPointAnnotationManager : AnnotationInteractionDelegate { return } } - + func remove(_ annotation: PointAnnotation) { manager.annotations.removeAll(where: {$0.id == annotation.id}) } - + #if RNMBX_11 var annotations = NSMapTable.init( keyOptions: .copyIn, valueOptions: .weakMemory ) #endif - + func add(_ annotation: PointAnnotation, _ rnmbxPointAnnotation: RNMBXPointAnnotation) { manager.annotations.append(annotation) manager.refresh() @@ -1695,15 +1715,15 @@ class RNMBXPointAnnotationManager : AnnotationInteractionDelegate { annotations.setObject(rnmbxPointAnnotation, forKey: annotation.id as NSString) #endif } - + func update(_ annotation: PointAnnotation) { let index = manager.annotations.firstIndex { $0.id == annotation.id } - + guard let index = index else { Logger.log(level: .warn, message: "RNMBX - PointAnnotation.refresh: annotation not found") return } - + manager.annotations[index] = annotation manager.refresh() } diff --git a/ios/RNMBX/RNMBXMapViewComponentView.mm b/ios/RNMBX/RNMBXMapViewComponentView.mm index 0e4b66acf..f193564fc 100644 --- a/ios/RNMBX/RNMBXMapViewComponentView.mm +++ b/ios/RNMBX/RNMBXMapViewComponentView.mm @@ -67,13 +67,13 @@ - (void)prepareView { _eventDispatcher = [[RNMBXMapViewEventDispatcher alloc] initWithComponentView:self]; _view = [[RNMBXMapView alloc] initWithFrame:_frame eventDispatcher:_eventDispatcher]; - + // just need to pass something, it won't really be used on fabric, but it's used to create events (it won't impact sending them) _view.reactTag = @-1; - + // capture weak self reference to prevent retain cycle __weak __typeof__(self) weakSelf = self; - + [_view setReactOnPress:^(NSDictionary* event) { __typeof__(self) strongSelf = weakSelf; @@ -135,7 +135,7 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared & { const auto &oldViewProps = static_cast(*oldProps); const auto &newViewProps = static_cast(*props); - + id attributionEnabled = RNMBXConvertFollyDynamicToId(newViewProps.attributionEnabled); if (attributionEnabled != nil) { _view.reactAttributionEnabled = attributionEnabled; @@ -214,6 +214,11 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared & _view.reactPitchEnabled = pitchEnabled; } + id preferredFramesPerSecond = RNMBXConvertFollyDynamicToId(newViewProps.preferredFramesPerSecond); + if (preferredFramesPerSecond != nil) { + _view.reactPreferredFramesPerSecond = preferredFramesPerSecond; + } + id projection = RNMBXConvertFollyDynamicToId(newViewProps.projection); if (projection != nil) { _view.reactProjection = projection; @@ -223,7 +228,7 @@ - (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared & if (localizeLabels != nil) { _view.reactLocalizeLabels = localizeLabels; } - + RNMBX_OPTIONAL_PROP_BOOL(deselectAnnotationOnTap); id styleURL = RNMBXConvertFollyDynamicToId(newViewProps.styleURL); diff --git a/ios/RNMBX/RNMBXMapViewManager.m b/ios/RNMBX/RNMBXMapViewManager.m index 934808825..4eb7084ed 100644 --- a/ios/RNMBX/RNMBXMapViewManager.m +++ b/ios/RNMBX/RNMBXMapViewManager.m @@ -25,6 +25,7 @@ @interface RCT_EXTERN_REMAP_MODULE(RNMBXMapView, RNMBXMapViewManager, RCTViewMan RCT_REMAP_VIEW_PROPERTY(scrollEnabled, reactScrollEnabled, BOOL) RCT_REMAP_VIEW_PROPERTY(rotateEnabled, reactRotateEnabled, BOOL) RCT_REMAP_VIEW_PROPERTY(pitchEnabled, reactPitchEnabled, BOOL) +RCT_REMAP_VIEW_PROPERTY(preferredFramesPerSecond, reactPreferredFramesPerSecond, NSInteger) RCT_EXPORT_VIEW_PROPERTY(deselectAnnotationOnTap, BOOL) RCT_REMAP_VIEW_PROPERTY(projection, reactProjection, NSString) From 7b9982ded62bb41a68b8d474fe8d28ce73983818 Mon Sep 17 00:00:00 2001 From: Mateusz Zatorski Date: Wed, 6 Nov 2024 15:30:03 +0100 Subject: [PATCH 2/2] add preferredFramesPerSecond to NativeProps --- src/specs/RNMBXMapViewNativeComponent.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/specs/RNMBXMapViewNativeComponent.ts b/src/specs/RNMBXMapViewNativeComponent.ts index 70f7e8a53..7dadbc9f2 100644 --- a/src/specs/RNMBXMapViewNativeComponent.ts +++ b/src/specs/RNMBXMapViewNativeComponent.ts @@ -79,6 +79,7 @@ export interface NativeProps extends ViewProps { onCameraChanged?: DirectEventHandler; mapViewImpl?: OptionalProp; + preferredFramesPerSecond?: OptionalProp; } export default codegenNativeComponent(