Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 88 additions & 17 deletions ios/RNMBX/RNMBXCamera.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,46 @@ public enum RemovalReason {
case ViewRemoval, StyleChange, OnDestroy, ComponentChange, Reorder
}

public protocol RNMBXMapComponent: AnyObject {
/// Base protocol for all map components
public protocol RNMBXMapComponentProtocol: AnyObject {
func waitForStyleLoad() -> Bool
}

/// Default implementation: most components don't need to wait for style load
extension RNMBXMapComponentProtocol {
public func waitForStyleLoad() -> Bool {
return false
}
}

/// Protocol for components that can work without direct MapView access
public protocol RNMBXMapComponent: RNMBXMapComponentProtocol {
func addToMap(_ map: RNMBXMapView, style: Style)
func removeFromMap(_ map: RNMBXMapView, reason: RemovalReason) -> Bool

func waitForStyleLoad() -> Bool
}

/// Protocol for components that require a valid MapView instance for both add and remove operations.
/// Use this protocol when your component needs to interact with the native MapView directly.
/// The MapView parameter is guaranteed to be non-nil when these methods are called.
///
/// This protocol inherits from RNMBXMapComponent to ensure compatibility with existing code,
/// but provides default implementations of the base protocol methods that throw errors,
/// forcing implementers to use the mapView-aware versions.
public protocol RNMBXMapAndMapViewComponent: RNMBXMapComponent {
func addToMap(_ map: RNMBXMapView, mapView: MapView, style: Style)
func removeFromMap(_ map: RNMBXMapView, mapView: MapView, reason: RemovalReason) -> Bool
}

/// Default implementations for RNMBXMapAndMapViewComponent that prevent accidental use of base protocol methods
extension RNMBXMapAndMapViewComponent {
public func addToMap(_ map: RNMBXMapView, style: Style) {
Logger.error("CRITICAL: addToMap(_:style:) called on RNMBXMapAndMapViewComponent. Use addToMap(_:mapView:style:) instead. Component: \(type(of: self))")
}

public func removeFromMap(_ map: RNMBXMapView, reason: RemovalReason) -> Bool {
Logger.error("CRITICAL: removeFromMap(_:reason:) called on RNMBXMapAndMapViewComponent. Use removeFromMap(_:mapView:reason:) instead. Component: \(type(of: self))")
return false
}
}

enum CameraMode: Int {
Expand Down Expand Up @@ -85,7 +120,7 @@ class CameraUpdateQueue {
open class RNMBXMapComponentBase : UIView, RNMBXMapComponent {
private weak var _map: RNMBXMapView! = nil
private var _mapCallbacks: [(RNMBXMapView) -> Void] = []

weak var map : RNMBXMapView? {
return _map;
}
Expand All @@ -103,28 +138,64 @@ open class RNMBXMapComponentBase : UIView, RNMBXMapComponent {
_mapCallbacks.append(callback)
}
}

public func waitForStyleLoad() -> Bool {
return false
}


public func addToMap(_ map: RNMBXMapView, style: Style) {
_mapCallbacks.forEach { callback in
callback(map)
}
_mapCallbacks = []
_map = map
}

public func removeFromMap(_ map: RNMBXMapView, reason: RemovalReason) -> Bool {
_mapCallbacks = []
_map = nil
return true
}
}

/// Base class for components that require MapView to be non-nil
open class RNMBXMapAndMapViewComponentBase : UIView, RNMBXMapAndMapViewComponent {
private weak var _map: RNMBXMapView! = nil
private var _mapCallbacks: [(RNMBXMapView) -> Void] = []

weak var map : RNMBXMapView? {
return _map;
}

func withMapView(_ callback: @escaping (_ mapView: MapView) -> Void) {
withRNMBXMapView { mapView in
callback(mapView.mapView)
}
}

func withRNMBXMapView(_ callback: @escaping (_ map: RNMBXMapView) -> Void) {
if let map = _map {
callback(map)
} else {
_mapCallbacks.append(callback)
}
}

// Uses default implementation from RNMBXMapComponentProtocol extension

public func addToMap(_ map: RNMBXMapView, mapView: MapView, style: Style) {
_mapCallbacks.forEach { callback in
callback(map)
}
_mapCallbacks = []
_map = map
}

public func removeFromMap(_ map: RNMBXMapView, mapView: MapView, reason: RemovalReason) -> Bool {
_mapCallbacks = []
_map = nil
return true
}
}

@objc(RNMBXCamera)
open class RNMBXCamera : RNMBXMapComponentBase {
open class RNMBXCamera : RNMBXMapAndMapViewComponentBase {
var cameraAnimator: BasicCameraAnimator?
let cameraUpdateQueue = CameraUpdateQueue()

Expand Down Expand Up @@ -519,18 +590,18 @@ open class RNMBXCamera : RNMBXMapComponentBase {
_updateCamera()
}

public override func addToMap(_ map: RNMBXMapView, style: Style) {
super.addToMap(map, style: style)
public override func addToMap(_ map: RNMBXMapView, mapView: MapView, style: Style) {
super.addToMap(map, mapView: mapView, style: style)
map.reactCamera = self
}
public override func removeFromMap(_ map: RNMBXMapView, reason: RemovalReason) -> Bool {

public override func removeFromMap(_ map: RNMBXMapView, mapView: MapView, reason: RemovalReason) -> Bool {
if (reason == .StyleChange) {
return false
}

map.mapView.viewport.removeStatusObserver(self)
return super.removeFromMap(map, reason:reason)
mapView.viewport.removeStatusObserver(self)
return super.removeFromMap(map, mapView: mapView, reason: reason)
}

@objc public func moveBy(x: Double, y: Double, animationMode: NSNumber?, animationDuration: NSNumber?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
Expand Down
42 changes: 18 additions & 24 deletions ios/RNMBX/RNMBXCustomLocationProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,77 +3,71 @@ import MapboxMaps
let TAG = "RNMBXCustomLocationProvider"

@objc
public class RNMBXCustomLocationProvider: UIView, RNMBXMapComponent {
public class RNMBXCustomLocationProvider: UIView, RNMBXMapAndMapViewComponent {
var map: RNMBXMapView? = nil

let changes : PropertyChanges<RNMBXCustomLocationProvider> = PropertyChanges()

enum Property: String {
case coordinate
case heading

func apply(locationProvider: RNMBXCustomLocationProvider) {
switch self {
case .coordinate: locationProvider.applyCoordinate()
case .heading: locationProvider.applyHeading()
}
}
}

@objc
public var coordinate: [Double] = [] {
didSet { changed(.coordinate) }
}

@objc
public var heading: NSNumber = 0.0 {
didSet { changed(.heading) }
}

func changed(_ property: Property) {
changes.add(name: property.rawValue, update: property.apply)
}

@objc
override public func didSetProps(_ props: [String]) {
if customLocationProvider != nil {
changes.apply(self)
}
}

var customLocationProvider: CustomLocationProvider? = nil
#if RNMBX_11
#else
var defaultLocationProvider: LocationProvider?
#endif

public func addToMap(_ map: RNMBXMapView, style: Style) {
public func addToMap(_ map: RNMBXMapView, mapView: MapView, style: Style) {
self.map = map
if let mapView = map.mapView {
installCustomeLocationProviderIfNeeded(mapView: mapView)
changes.apply(self)
}
installCustomeLocationProviderIfNeeded(mapView: mapView)
changes.apply(self)
}

private func applyCoordinate() {
updateCoordinate(latitude: coordinate[1], longitude: coordinate[0])
}

private func applyHeading() {
updateHeading(heading: heading.doubleValue)
}

public func removeFromMap(_ map: RNMBXMapView, reason: RemovalReason) -> Bool {
if let mapView = map.mapView {
removeCustomLocationProvider(mapView: mapView)
}

public func removeFromMap(_ map: RNMBXMapView, mapView: MapView, reason: RemovalReason) -> Bool {
removeCustomLocationProvider(mapView: mapView)
self.map = nil
return true
}

public func waitForStyleLoad() -> Bool {
false
}
// Uses default implementation from RNMBXMapComponentProtocol extension (returns false)
}

#if RNMBX_11
Expand Down
5 changes: 1 addition & 4 deletions ios/RNMBX/RNMBXImages.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,8 @@ open class RNMBXImages : UIView, RNMBXMapComponent {
}

// MARK: - RNMBXMapComponent
// Uses default implementation from RNMBXMapComponentProtocol extension (returns false)

public func waitForStyleLoad() -> Bool {
return false
}

public func addToMap(_ map: RNMBXMapView, style: Style) {
self.style = style
imageManager = map.imageManager
Expand Down
37 changes: 22 additions & 15 deletions ios/RNMBX/RNMBXInteractiveElement.swift
Original file line number Diff line number Diff line change
@@ -1,63 +1,70 @@
import MapboxMaps

@objc
public class RNMBXInteractiveElement : UIView, RNMBXMapComponent {
public class RNMBXInteractiveElement : UIView, RNMBXMapAndMapViewComponent {
weak var map : RNMBXMapView? = nil
weak var mapView : MapView? = nil

static let hitboxDefault = 44.0

@objc public var draggable: Bool = false

@objc public var hasPressListener: Bool = false

@objc public var hitbox : [String:NSNumber] = [
"width": NSNumber(value: hitboxDefault),
"height": NSNumber(value: hitboxDefault)
]

@objc public var id: String! = nil {
willSet {
if id != nil && newValue != id {
Logger.log(level:.warn, message: "Changing id from: \(optional: id) to \(optional: newValue), changing of id is not supported")
if let map = map { removeFromMap(map, reason: .ComponentChange) }
if let map = map, let mapView = mapView {
removeFromMap(map, mapView: mapView, reason: .ComponentChange)
}
}
}
didSet {
if oldValue != nil && oldValue != id {
if let map = map { addToMap(map, style: map.mapboxMap.style) }
if let map = map, let mapView = mapView {
addToMap(map, mapView: mapView, style: mapView.mapboxMap.style)
}
}
}
}

@objc public var onDragStart: RCTBubblingEventBlock? = nil

@objc public var onPress: RCTBubblingEventBlock? = nil

func getLayerIDs() -> [String] {
return []
}

func isDraggable() -> Bool {
return draggable
}

func isTouchable() -> Bool {
return hasPressListener
}
// MARK: - RNMBXMapComponent
public func addToMap(_ map: RNMBXMapView, style: Style) {

// MARK: - RNMBXMapAndMapViewComponent
public func addToMap(_ map: RNMBXMapView, mapView: MapView, style: Style) {
if (self.id == nil) {
Logger.log(level: .error, message: "id is required on \(self) but not specified")
}
self.map = map
self.mapView = mapView
}

public func removeFromMap(_ map: RNMBXMapView, reason: RemovalReason) -> Bool {
public func removeFromMap(_ map: RNMBXMapView, mapView: MapView, reason: RemovalReason) -> Bool {
self.map = nil
self.mapView = nil
return true
}

public func waitForStyleLoad() -> Bool {
return true
}
Expand Down
Loading
Loading