Skip to content

Commit

Permalink
Merge pull request #327 from Esri/mhd/Compass_updates
Browse files Browse the repository at this point in the history
SceneView support for Compass
  • Loading branch information
mhdostal authored May 9, 2023
2 parents 92bf62f + 79a43d1 commit adfe497
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 23 deletions.
38 changes: 36 additions & 2 deletions Documentation/Compass/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Compass:

## Key properties

`Compass` has the following initializer:
`Compass` has the following initializers:

```swift
/// Creates a compass with a rotation (0° indicates a direction toward true North, 90° indicates
Expand All @@ -25,6 +25,13 @@ Compass:
/// - rotation: The rotation whose value determines the heading of the compass.
/// - mapViewProxy: The proxy to provide access to map view operations.
public init(rotation: Double?, mapViewProxy: MapViewProxy)

/// Creates a compass with a rotation (0° indicates a direction toward true North, 90° indicates
/// a direction toward true West, etc.).
/// - Parameters:
/// - rotation: The rotation whose value determines the heading of the compass.
/// - action: The action to perform when the compass is tapped.
public init(rotation: Double?, action: @escaping () -> Void)
```

`Compass` has the following modifiers:
Expand All @@ -34,7 +41,7 @@ Compass:

## Behavior:

Whenever the map is not orientated North (non-zero bearing) the compass appears. When reset to north, it disappears. The `automaticallyHides` view modifier allows you to disable the auto-hide feature so that it is always displayed.
Whenever the map is not orientated North (non-zero bearing) the compass appears. When reset to north, it disappears. The `autoHideDisabled` view modifier allows you to disable the auto-hide feature so that it is always displayed.

When the compass is tapped, the map orients back to north (zero bearing).

Expand All @@ -59,4 +66,31 @@ var body: some View {
}
```

To add a `Compass` to a SceneView, use the initializer which takes an `action` argument to perform a custom action when the compass is tapped on.

```swift
@State private var scene = Scene(basemapStyle: .arcGISImagery)

@State private var viewpoint: Viewpoint?

var body: some View {
SceneViewReader { proxy in
SceneView(scene: scene)
.onViewpointChanged(kind: .centerAndScale) { viewpoint = $0 }
.overlay(alignment: .topTrailing) {
Compass(rotation: viewpoint?.rotation) {
if let viewpoint {
Task {
await proxy.setViewpoint(viewpoint.withRotation(.zero))
}
}
}
.padding()
}
}
}
```



To see it in action, try out the [Examples](../../Examples/Examples) and refer to [CompassExampleView.swift](../../Examples/Examples/CompassExampleView.swift) in the project.
46 changes: 34 additions & 12 deletions Sources/ArcGISToolkit/Components/Compass/Compass.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,29 @@ public struct Compass: View {
/// The width and height of the compass.
private var size: CGFloat = 44

/// An action to perform when the compass is tapped.
private var action: (() -> Void)?

/// Creates a compass with a heading based on compass directions (0° indicates a direction
/// toward true North, 90° indicates a direction toward true East, etc.).
/// - Parameters:
/// - heading: The heading of the compass.
/// - rotation: The rotation whose value determines the heading of the compass.
/// - mapViewProxy: The proxy to provide access to map view operations.
/// - action: The action to perform when the compass is tapped.
init(
heading: Double,
mapViewProxy: MapViewProxy? = nil
rotation: Double?,
mapViewProxy: MapViewProxy?,
action: (() -> Void)?
) {
let heading: Double
if let rotation {
heading = rotation.isZero ? .zero : 360 - rotation
} else {
heading = .nan
}
self.heading = heading
self.mapViewProxy = mapViewProxy
self.action = action
}

public var body: some View {
Expand All @@ -64,7 +76,11 @@ public struct Compass: View {
}
}
.onTapGesture {
Task { await mapViewProxy?.setViewpointRotation(0) }
if let mapViewProxy {
Task { await mapViewProxy.setViewpointRotation(0) }
} else if let action {
action()
}
}
.accessibilityLabel("Compass, heading \(Int(heading.rounded())) degrees \(CompassDirection(heading).rawValue)")
}
Expand All @@ -91,13 +107,19 @@ public extension Compass {
rotation: Double?,
mapViewProxy: MapViewProxy
) {
let heading: Double
if let rotation {
heading = rotation.isZero ? .zero : 360 - rotation
} else {
heading = .nan
}
self.init(heading: heading, mapViewProxy: mapViewProxy)
self.init(rotation: rotation, mapViewProxy: mapViewProxy, action: nil)
}

/// Creates a compass with a rotation (0° indicates a direction toward true North, 90° indicates
/// a direction toward true West, etc.).
/// - Parameters:
/// - rotation: The rotation whose value determines the heading of the compass.
/// - action: The action to perform when the compass is tapped.
init(
rotation: Double?,
action: @escaping () -> Void
) {
self.init(rotation: rotation, mapViewProxy: nil, action: action)
}

/// Define a custom size for the compass.
Expand All @@ -111,7 +133,7 @@ public extension Compass {
/// Specifies whether the ``Compass`` should automatically hide when the heading is 0.
/// - Parameter disable: A Boolean value indicating whether the compass should automatically
/// hide/show itself when the heading is `0`.
func autoHideDisabled(_ disable: Bool = true) -> some View {
func autoHideDisabled(_ disable: Bool = true) -> Self {
var copy = self
copy.autoHide = !disable
return copy
Expand Down
18 changes: 9 additions & 9 deletions Tests/ArcGISToolkitTests/CompassTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,33 +21,33 @@ final class CompassTests: XCTestCase {
/// is applied.
func testHiddenWithAutoHideOff() {
let compass1Heading = Double.zero
let compass1 = Compass(heading: compass1Heading)
.autoHideDisabled() as! Compass
let compass1 = Compass(rotation: compass1Heading, mapViewProxy: nil, action: nil)
.autoHideDisabled()
XCTAssertFalse(compass1.shouldHide(forHeading: compass1Heading))

let compass2Heading = 45.0
let compass2 = Compass(heading: compass2Heading)
.autoHideDisabled() as! Compass
let compass2 = Compass(rotation: compass2Heading, mapViewProxy: nil, action: nil)
.autoHideDisabled()
XCTAssertFalse(compass2.shouldHide(forHeading: compass2Heading))

let compass3Heading = Double.nan
let compass3 = Compass(heading: compass3Heading)
.autoHideDisabled() as! Compass
let compass3 = Compass(rotation: compass3Heading, mapViewProxy: nil, action: nil)
.autoHideDisabled()
XCTAssertFalse(compass3.shouldHide(forHeading: compass3Heading))
}

/// Verifies that the compass accurately indicates when it should be hidden.
func testHiddenWithAutoHideOn() {
let compass1Heading: Double = .zero
let compass1 = Compass(heading: compass1Heading)
let compass1 = Compass(rotation: compass1Heading, mapViewProxy: nil, action: nil)
XCTAssertTrue(compass1.shouldHide(forHeading: compass1Heading))

let compass2Heading = 45.0
let compass2 = Compass(heading: compass2Heading)
let compass2 = Compass(rotation: compass2Heading, mapViewProxy: nil, action: nil)
XCTAssertFalse(compass2.shouldHide(forHeading: compass2Heading))

let compass3Heading = Double.nan
let compass3 = Compass(heading: compass3Heading)
let compass3 = Compass(rotation: compass3Heading, mapViewProxy: nil, action: nil)
XCTAssertTrue(compass3.shouldHide(forHeading: compass3Heading))
}
}

0 comments on commit adfe497

Please sign in to comment.