Skip to content

Commit

Permalink
Fix: recenter doesn't immediately take effect (#53)
Browse files Browse the repository at this point in the history
* Fix: recenter doesn't immediately take effect

Note: I only ever reproduced the bug in the context my app, which uses
this library via Ferrostar, while using the CoreLocationProvider, not
the SimulatedLocationProvider. But I expect this to be an issue for
anyone using this API.

To reproduce the bug in my app:

Setup: (all of this is working more or less as expected):

- While stationary (not sure if this is required)
- start a route
- see the user location puck and map centered on my location on the
  ground.
- pan the map off the route
- see the user location puck disappear, but you still see the smaller location
  indicator on the map. (as expected)
- see the "recenter me" button appear
- tap the "recenter me" button

Now here's the problem:

At this point I'm expecting the map to recenter with the user location
puck on my location on the ground.

But instead, I see the user location puck centered at the *current* map
position - where I'd previously panned to, *not* at my location on the ground.

Interestingly, if you'd repeat the process at this point - panning and
then re-tapping the "center me" button, it would *work* this second time.

---

The new behavior is definitely an improvement. However, if you've zoomed
way out while exploring the map away from your route, there is a
slightly noticeable two-part action while we wait for the new completion
block - first re-centering and then zooming. It's unquestionably better
than the current behavior though.

* Fixes from @hactar's branch

* Disable swiftformat andOperator (see #54)

* Fix inadvertent regression; update CHANGELOG

---------

Co-authored-by: Ian Wagner <[email protected]>
  • Loading branch information
michaelkirk and ianthetechie authored Oct 7, 2024
1 parent 9b306fc commit 899457f
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 49 deletions.
3 changes: 2 additions & 1 deletion .swiftformat
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@

# rules

--enable isEmpty
--enable isEmpty
--disable andOperator
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,25 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Version 0.2.0 - 2024-10-07

### Added

- `MLNMapViewCameraUpdating.setUserTrackingMode(_ mode: MLNUserTrackingMode, animated: Bool, completionHandler: (() -> Void)?)`
in [#53](https://github.com/maplibre/swiftui-dsl/pull/53).
Previously, you could only call `mapViewCameraUpdating.userTrackingMode = newMode`
without specifying `animated` or `completionHandler`.

### Fixed

- Fix broken animation when setting user tracking mode in [#53](https://github.com/maplibre/swiftui-dsl/pull/53).
For example, when tapping the "recenter" button in Ferrostar (which uses this
package), the map now immediately re-centers on the users current location,
whereas before you'd have to tap it twice. Note: the bug wasn't noticeable
when using the Ferrostar's SimulatedLocationProvider.
- Pitch range `.free` was being reset to `.freeWithinRange(0, 59.9999999)`
Fixed in in [#54](https://github.com/maplibre/swiftui-dsl/pull/54).

## Version 0.1.0 - 2024-09-21

This project has migrated from Stadia Maps to the MapLibre organization!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import Mockable
@Mockable
public protocol MLNMapViewCameraUpdating: AnyObject {
@MainActor var userTrackingMode: MLNUserTrackingMode { get set }
@MainActor func setUserTrackingMode(_ mode: MLNUserTrackingMode, animated: Bool, completionHandler: (() -> Void)?)

@MainActor var minimumPitch: CGFloat { get set }
@MainActor var maximumPitch: CGFloat { get set }
@MainActor var direction: CLLocationDirection { get set }
Expand Down
105 changes: 57 additions & 48 deletions Sources/MapLibreSwiftUI/MapViewCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,91 +98,100 @@ public class MapViewCoordinator<T: MapViewHostViewController>: NSObject, MLNMapV
mapView.minimumPitch = pitchRange.rangeValue.lowerBound
mapView.maximumPitch = pitchRange.rangeValue.upperBound
case let .trackingUserLocation(zoom: zoom, pitch: pitch, pitchRange: pitchRange, direction: direction):
mapView.userTrackingMode = .follow

if mapView.frame.size == .zero {
// On init, the mapView's frame is not set up yet, so manipulation via camera is broken,
// so let's do something else instead.
// Needs to be non-animated or else it messes up following

mapView.userTrackingMode = .follow

mapView.setZoomLevel(zoom, animated: false)
mapView.direction = direction

mapView.minimumPitch = pitch
mapView.maximumPitch = pitch
mapView.minimumPitch = pitchRange.rangeValue.lowerBound
mapView.maximumPitch = pitchRange.rangeValue.upperBound

} else {
let camera = mapView.camera
camera.heading = direction
camera.pitch = pitch

let altitude = MLNAltitudeForZoomLevel(
zoom,
pitch,
mapView.camera.centerCoordinate.latitude,
mapView.frame.size
)
camera.altitude = altitude
mapView.setCamera(camera, animated: animated)
mapView.setUserTrackingMode(.follow, animated: animated) {
mapView.minimumPitch = pitchRange.rangeValue.lowerBound
mapView.maximumPitch = pitchRange.rangeValue.upperBound
let camera = mapView.camera
camera.heading = direction
camera.pitch = pitch

let altitude = MLNAltitudeForZoomLevel(
zoom,
pitch,
mapView.camera.centerCoordinate.latitude,
mapView.frame.size
)
camera.altitude = altitude
mapView.setCamera(camera, animated: animated)
}
}
mapView.minimumPitch = pitchRange.rangeValue.lowerBound
mapView.maximumPitch = pitchRange.rangeValue.upperBound
case let .trackingUserLocationWithHeading(zoom: zoom, pitch: pitch, pitchRange: pitchRange):
mapView.userTrackingMode = .followWithHeading

if mapView.frame.size == .zero {
// On init, the mapView's frame is not set up yet, so manipulation via camera is broken,
// so let's do something else instead.
// Needs to be non-animated or else it messes up following

mapView.userTrackingMode = .followWithHeading
mapView.setZoomLevel(zoom, animated: false)
mapView.minimumPitch = pitch
mapView.maximumPitch = pitch
mapView.minimumPitch = pitchRange.rangeValue.lowerBound
mapView.maximumPitch = pitchRange.rangeValue.upperBound

} else {
let camera = mapView.camera

let altitude = MLNAltitudeForZoomLevel(
zoom,
pitch,
mapView.camera.centerCoordinate.latitude,
mapView.frame.size
)
camera.altitude = altitude
camera.pitch = pitch
mapView.setCamera(camera, animated: animated)
mapView.setUserTrackingMode(.followWithHeading, animated: animated) {
mapView.minimumPitch = pitchRange.rangeValue.lowerBound
mapView.maximumPitch = pitchRange.rangeValue.upperBound
let camera = mapView.camera

let altitude = MLNAltitudeForZoomLevel(
zoom,
pitch,
mapView.camera.centerCoordinate.latitude,
mapView.frame.size
)
camera.altitude = altitude
camera.pitch = pitch
mapView.setCamera(camera, animated: animated)
}
}

mapView.minimumPitch = pitchRange.rangeValue.lowerBound
mapView.maximumPitch = pitchRange.rangeValue.upperBound
case let .trackingUserLocationWithCourse(zoom: zoom, pitch: pitch, pitchRange: pitchRange):
mapView.userTrackingMode = .followWithCourse

if mapView.frame.size == .zero {
mapView.userTrackingMode = .followWithCourse
// On init, the mapView's frame is not set up yet, so manipulation via camera is broken,
// so let's do something else instead.
// Needs to be non-animated or else it messes up following

mapView.setZoomLevel(zoom, animated: false)
mapView.minimumPitch = pitch
mapView.maximumPitch = pitch
mapView.minimumPitch = pitchRange.rangeValue.lowerBound
mapView.maximumPitch = pitchRange.rangeValue.upperBound

} else {
let camera = mapView.camera

let altitude = MLNAltitudeForZoomLevel(
zoom,
pitch,
mapView.camera.centerCoordinate.latitude,
mapView.frame.size
)
camera.altitude = altitude
camera.pitch = pitch
mapView.setCamera(camera, animated: animated)
mapView.setUserTrackingMode(.followWithCourse, animated: animated) {
mapView.minimumPitch = pitchRange.rangeValue.lowerBound
mapView.maximumPitch = pitchRange.rangeValue.upperBound

let camera = mapView.camera

let altitude = MLNAltitudeForZoomLevel(
zoom,
pitch,
mapView.camera.centerCoordinate.latitude,
mapView.frame.size
)
camera.altitude = altitude
camera.pitch = pitch
mapView.setCamera(camera, animated: animated)
}
}

mapView.minimumPitch = pitchRange.rangeValue.lowerBound
mapView.maximumPitch = pitchRange.rangeValue.upperBound
case let .rect(boundingBox, padding):
mapView.setVisibleCoordinateBounds(boundingBox,
edgePadding: padding,
Expand Down

0 comments on commit 899457f

Please sign in to comment.