+The MIT License (MIT)
+
+Copyright (c) 2022 Sven Tiigi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -7,13 +9,14 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/Package.swift b/Package.swift
index 1bb3773..7a7c64d 100644
--- a/Package.swift
+++ b/Package.swift
@@ -1,30 +1,25 @@
-// swift-tools-version:5.0
+// swift-tools-version:5.2
import PackageDescription
let package = Package(
name: "FlyoverKit",
platforms: [
- .iOS(.v10),
- .tvOS(.v10)
+ .iOS(.v13),
+ .tvOS(.v13)
],
products: [
.library(
name: "FlyoverKit",
- targets: ["FlyoverKit"]
- ),
+ targets: [
+ "FlyoverKit"
+ ]
+ )
],
- dependencies: [],
targets: [
.target(
name: "FlyoverKit",
- dependencies: [],
path: "Sources"
- ),
- .testTarget(
- name: "FlyoverKitTests",
- dependencies: ["FlyoverKit"],
- path: "Tests"
- ),
+ )
]
)
diff --git a/README.md b/README.md
index 31f70d9..5c6b2a3 100644
--- a/README.md
+++ b/README.md
@@ -1,65 +1,56 @@
+
+
-
+
+
+ FlyoverKit
+
+
-
-
-
-
+ A Swift Package to easily perform flyovers on a MKMapView.
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-`FlyoverKit` enables you to present stunning 360° flyover views on an `MKMapView` with zero effort while maintaining full configuration possibilities. `FlyoverKit` builds the core of [STLocationRequest](https://github.com/SvenTiigi/STLocationRequest) which enables you a way to request the user location services via a 3D 360° flyover MapView.
-
-## Example
-
-
-
-The example Application is an excellent way to see `FlyoverKit` in action. You get a brief look of the available configuration options and how they affect the flyover behaviour. Simply open the `FlyoverKit.xcodeproj` and run the `FlyoverKit-Example` scheme. Please keep in mind that the `SatelliteFlyover` and `HybridFlyover` will only work on a real iOS device.
-
-## Installation
-
-### CocoaPods
-
-FlyoverKit is available through [CocoaPods](http://cocoapods.org). To install
-it, simply add the following line to your Podfile:
+```swift
+import SwiftUI
+import FlyoverKit
-```bash
-pod 'FlyoverKit'
+struct ContentView: View {
+
+ var body: some View {
+ FlyoverMap(
+ coordinate: .init(
+ latitude: 37.8023,
+ longitude: -122.4057
+ )
+ )
+ }
+
+}
```
-### Carthage
-
-[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.
-
-To integrate FlyoverKit into your Xcode project using Carthage, specify it in your `Cartfile`:
+## Features
-```ogdl
-github "SvenTiigi/FlyoverKit"
-```
-
-Run `carthage update --platform iOS` to build the framework and drag the built `FlyoverKit.framework` into your Xcode project.
+- [x] Configurable flyovers on a MKMapView 🚁
+- [x] Easily start, stop and resume flyovers ⚙️
+- [x] Support for SwiftUI and UIKit 🧑🎨
+- [x] Runs on iOS and tvOS 📱 📺
-On your application targets’ “Build Phases” settings tab, click the “+” icon and choose “New Run Script Phase” and add the Framework path as mentioned in [Carthage Getting started Step 4, 5 and 6](https://github.com/Carthage/Carthage/blob/master/README.md)
+## Installation
### Swift Package Manager
@@ -67,247 +58,84 @@ To integrate using Apple's [Swift Package Manager](https://swift.org/package-man
```swift
dependencies: [
- .package(url: "https://github.com/SvenTiigi/FlyoverKit.git", from: "1.3.1")
+ .package(url: "https://github.com/SvenTiigi/FlyoverKit.git", from: "2.0.0")
]
```
-### Manually
+Or navigate to your Xcode project then select `Swift Packages`, click the “+” icon and search for `FlyoverKit`.
-If you prefer not to use any of the aforementioned dependency managers, you can integrate FlyoverKit into your project manually. Simply drag the `Sources` Folder into your Xcode project.
-
-## Usage
-`FlyoverKit` can be implemented in three different ways. Simply choose the most suitable type for your implementation. Please see the [Advanced](https://github.com/SvenTiigi/FlyoverKit#advanced) section in order to configure the `FlyoverKit` to your needs.
-
-### FlyoverCamera
-If you already have a `MKMapView` in your `Controller` and you want to add a flyover to this MapView, simply use the `FlyoverCamera`.
-
-```swift
-import FlyoverKit
-
-// Initialize the FlyoverCamera with an already existing MKMapView
-let flyoverCamera = FlyoverCamera(mapView: self.mapView)
-
-// Initialize a location via CLLocationCoordinate2D
-let eiffelTower = CLLocationCoordinate2D(latitude: 48.858370, longitude: 2.294481)
-
-// Start flyover
-flyoverCamera.start(flyover: eiffelTower)
-```
-
-### FlyoverMapView
-If you wish to show a MapView which is already preconfigured to perform a flyover on a given location, you should use the `FlyoverMapView`.
-
-```swift
-import FlyoverKit
+## Example
-// Initialize the FlyoverMapView
-let flyoverMapView = FlyoverMapView()
+Check out the example application to see FlyoverKit in action. Simply open the `Example/Example.xcodeproj` and run the "Example" scheme.
-// Initialize a location via CLLocation
-let eiffelTower = CLLocation(latitude: 48.858370, longitude: 2.294481)
+## Usage
-// Start flyover
-flyoverMapView.start(flyover: eiffelTower)
-```
+### SwiftUI
-### FlyoverMapViewController
-If you wish to present a `UIViewController` with an embedded `FlyoverMapView` to perform a flyover on a given location, `FlyoverMapViewController` is at your service.
+When using SwiftUI a `FlyoverMap` can be used to render and control a flyover.
```swift
-import FlyoverKit
-
-// Initialize a location via FlyoverAwesomePlace
-let eiffelTower = FlyoverAwesomePlace.parisEiffelTower
-
-// Initialize the FlyoverMapViewController with a Flyover object
-let controller = FlyoverMapViewController(flyover: eiffelTower)
-
-// Present controller
-self.present(controller, animated: true)
+FlyoverMap(
+ // Bool value if flyover is started or stopped
+ isStarted: true,
+ // The coordinate to perform the flyover on
+ coordinate: CLLocationCoordinate2D(
+ latitude: 37.8023,
+ longitude: -122.4057
+ ),
+ configuration: Flyover.Configuration(
+ // The animation curve
+ animationCurve: .linear,
+ // The altitude in meter
+ altitude: 900,
+ // The pitch in degree
+ pitch: 45.0,
+ // The heading step
+ headingStep: .incremented(by: 1.5)
+ ),
+ // The map type e.g. .standard, .satellite, .hybrid
+ mapType: .standard
+)
```
-## Advanced
-The `FlyoverKit` consists of three main components. The `Flyover` protocol, `FlyoverCamera` and `FlyoverMapView`.
-
-
-
-
-
-
-
-## Flyover Protocol
-The `flyover` protocol specifies a type with a `CLLocationCoordinat2D` property. It is used to perform a flyover on the given coordinate.
+### UIKit
```swift
-public protocol Flyover {
- var coordinate: CLLocationCoordinate2D { get }
-}
-```
-The `FlyoverKit` already implemented this protocol to various MapKit and CoreLocation types like `CLLocationCoordinate2D`, `CLLocation`, `MKMapPoint`, `MKMapItem`, `MKCoordinateRegion` and many [more](https://sventiigi.github.io/FlyoverKit/Extensions.html).
-
-You can apply the `Flyover` protocol to your own models to use them for a flyover.
+let flyoverMapView = FlyoverMapView()
-```swift
-struct Address {
- var name: String
- var coordinate: CLLocationCoordinate2D
+if flyoverMapView.isFlyoverStarted {
+ // ...
}
-extension Address: Flyover {}
-```
-
-### Operators
-In order to compare two `Flyover` types you can use the following two operators.
-
-#### Equatable operator (==)
-
-```swift
-self.flyover1 == self.flyover2
-```
-> Checks if the two given Flyover types are exactly the same via comparison of latitude and longitude.
-
-#### Rounded Equatable operator (~~)
-
-```swift
-self.flyover1 ~~ self.flyover2
-```
-> Checks if the two given Flyover types are nearly the same via comparison of rounded latitude and longitude.
-
-### FlyoverAwesomePlace
-The `FlyoverAwesomePlace` is a `Flyover` protocol compliant Enumeration with awesome locations and landmarks like `NewYorkStatueOfLiberty`, `GooglePlex`, `SydneyOperaHouse` and many [more](https://sventiigi.github.io/FlyoverKit/Enums/FlyoverAwesomePlace.html). You can use them in your own implementation or to explore different locations in the `FlyoverKitExample` Application.
-
-## FlyoverCamera
-The `FlyoverCamera` is responsible for manipulating the `MKMapView` camera and performs a 360° flyover animation via [UIViewPropertyAnimator](https://developer.apple.com/documentation/uikit/uiviewpropertyanimator).
-
-In order to initialize a `FlyoverCamera` object you need to pass a `MKMapView` (which reference will be weakly stored) and a `FlyoverCamera.Configuration` object.
-
-```swift
-// Initialize FlyoverCamera configuration
-let configuration = FlyoverCamera.Configuration(
- duration: 4.0,
- altitude: 600.0,
- pitch: 45.0,
- headingStep: 20.0
-)
-
-// Initialize FlyoverCamera
-let camera = FlyoverCamera(
- mapView: mapView,
- configuration: configuration
-)
-
-// Start Flyover
-camera.start(flyover: location)
-```
-### Configuration
-The `FlyoverCamera.Configuration` struct holds all specific flyover configuration values. Set the properties to get the right look and feel of the flyover as you need it to be.
-
-| Configuration | Description |
-| ------------- | ------------- |
-| duration | The flyover animation duration |
-| altitude | The altitude above the ground, measured in meters |
-| pitch | The viewing angle of the camera, measured in degrees |
-| headingStep | The direction step in degrees that is added to the MapViewCamera heading in each flyover iteration |
-| regionChangeAnimation | The region change animation that should be applied if a flyover has been started and the MapCamera has to change the region. Default is always `.none` which immediately present the place. If you wish that the region change should be performed via an animation you can set `.animated(duration: 1.5, curve: .easeIn)` |
-
-An excellent visualization of an `MKMapCamera` from [TechTopia](http://www.techotopia.com/index.php/An_iOS_9_MapKit_Flyover_Tutorial)
-
-
-
-
-
-
-
-### Configuration Theme
-If you don't want to set the properties yourself you can use a preconfigured configuration theme. Currently there are four themes available
-
-| Theme | Description |
-| ------------- | ------------- |
-| default | Default flyover configuration with configuration for a default flyover usage |
-| lowFlying | Flyover configuration with a low altitude and a high pitch. Simulates a low flying helicopter viewing angle |
-| farAway | Configuration with a high altitude and a normal pitch which results in a far away viewing angle |
-| giddy | A giddy configuration 🤢 which you shouldn't use in production. But it's fun 🤷♂️ 🤙|
-| astronautView | The view of an astronaut 🌎 |
-
-Furthermore, you can initialize a `FlyoverCamera` with a given `Theme`.
-
-```swift
-// Initialize FlyoverCamera
-let camera = FlyoverCamera(
- mapView: mapView,
+flyoverMapView.startFlyover(
+ at: CLLocationCoordinate2D(
+ latitude: 37.8023,
+ longitude: -122.4057
+ ),
configuration: .default
)
-```
-
-More themes coming soon... 👨💻
-
-## FlyoverMapView
-The `FlyoverMapView` is a preconfigured `MKMapView` with an embedded `FlyoverCamera`. The `FlyoverMapView` offers various convenience functions for example starting a flyover with a `MKAnnotation` object or setting a supported flyover `MapType`.
-```swift
-// Initialize with default configuration theme and standard MapType
-let flyoverMapView = FlyoverMapView()
+flyoverMapView.stopFlyover()
-// Or initialize by setting a different theme and the satelliteFlyover MapType
-let flyoverMapView = FlyoverMapView(configuration: .farAway, mapType: .satelliteFlyover)
+flyoverMapView.resumeFlyover()
```
-### MapType
-The `FlyoverMapView.MapType` specifies the three supported `MKMapType`'s.
+### Flyover
-| MapType | Description |
-| ------------- | ------------- |
-| standard | A street map that shows the position of all roads and shows 3D buildings |
-| satelliteFlyover | A satellite image of the area with road and road name information layered on top |
-| hybridFlyover | A hybrid satellite image with flyover data where available |
-
-### FlyoverMapViewController
-The `FlyoverMapViewController` is an easy and simple way to present a `UIViewController` with an embedded `FlyoverMapView`. You can use it if you just want to present a 360° flyover on a given `Flyover` type.
+A `Flyover` object represents the core object to start, stop and resume a flyover on an instance of `MKMapView`.
```swift
-// Initialize a location
-let eiffelTower = CLLocationCoordinate2DMake(48.858370, 2.294481)
-
-// Initialize the FlyoverMapViewController with a Flyover object
-let controller = FlyoverMapViewController(flyover: eiffelTower)
-
-// Present controller
-self.present(controller, animated: true)
-```
-
-Additionally you can initialize the `FlyoverMapViewController` with your own `Configuration` or `Configuration.Theme` and a `MapType`
-
-```swift
-// Initialize a location
-let eiffelTower = CLLocationCoordinate2DMake(48.858370, 2.294481)
-
-// Initialize a Configuration
-let configuration = FlyoverCamera.Configuration(
- duration: 4.0,
- altitude: 600.0,
- pitch: 45.0,
- headingStep: 20.0
-)
-
-// Initialize the FlyoverMapViewController
-let controller = FlyoverMapViewController(
- flyover: eiffelTower,
- configuration: configuration,
- mapType: .satelliteFlyover
+let flyover = Flyover(
+ mapView: self.mapView
)
-
-// Present controller
-self.present(controller, animated: true)
```
-
-## Contributing
-Contributions are very welcome 🙌 🤓
+> Note: The provided MKMapView is always weakly referenced
## License
```
FlyoverKit
-Copyright (c) 2020 Sven Tiigi
+Copyright (c) 2022 Sven Tiigi sven.tiigi@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/Sources/AwesomePlace/FlyoverAwesomePlace.swift b/Sources/AwesomePlace/FlyoverAwesomePlace.swift
deleted file mode 100644
index bd7ec96..0000000
--- a/Sources/AwesomePlace/FlyoverAwesomePlace.swift
+++ /dev/null
@@ -1,131 +0,0 @@
-//
-// FlyoverAwesomePlace.swift
-// FlyoverKit
-//
-// Created by Sven Tiigi on 22.02.18.
-// Copyright © 2018 Sven Tiigi. All rights reserved.
-//
-
-import CoreLocation
-
-// MARK: - FlyoverAwesomePlace
-
-/// Awesome 360° locations and landmarks like
-/// NewYorkStatueOfLiberty, GooglePlex, SydneyOperaHouse and many more.
-public enum FlyoverAwesomePlace: String, Codable, Equatable, Hashable, CaseIterable {
- // MARK: USA
- /// New York Statue of Liberty
- case newYorkStatueOfLiberty
- /// New York Manhatten
- case newYork
- /// San Francisco Golden Gate Bridge
- case sanFranciscoGoldenGateBridge
- /// New York Central Park
- case centralParkNY
- /// Google Plex
- case googlePlex
- /// Miami Beach
- case miamiBeach
- /// Laguna Beach
- case lagunaBeach
- /// Griffith Observatory
- case griffithObservatory
- /// Luxor Resort Las Vegas
- case luxorResortLasVegas
- /// Apple Headquarter
- case appleHeadquarter
- // MARK: Germany
- /// Berlin Brandenbuger Gate
- case berlinBrandenburgerGate
- /// Hamburg Townhall
- case hamburgTownHall
- /// Cologne Cathedral
- case cologneCathedral
- /// Munic Church
- case munichCurch
- /// Neuschwanstein Castle
- case neuschwansteinCastle
- /// Hamburg Elb-Philharmonic
- case hamburgElbPhilharmonic
- /// Muenster Castle
- case muensterCastle
- // MARK: Italy
- /// Colosseum Rom
- case romeColosseum
- /// Piazza Di Trevi
- case piazzaDiTrevi
- // MARK: Spain
- /// Sagrade Familia Spain
- case sagradaFamiliaSpain
- // MARK: England
- /// London Big Ben
- case londonBigBen
- /// London Eye
- case londonEye
- // MARK: Australia
- /// Opera House Sydney
- case sydneyOperaHouse
- // MARK: France
- /// Paris Eiffel Tower
- case parisEiffelTower
-}
-
-// MARK: - FlyoverAwesomePlace Flyover Extension
-
-extension FlyoverAwesomePlace: Flyover {
-
- /// The flyover coordinate
- public var coordinate: CLLocationCoordinate2D {
- switch self {
- case .newYorkStatueOfLiberty:
- return .init(latitude: 40.689249, longitude: -74.044500)
- case .newYork:
- return .init(latitude: 40.702749, longitude: -74.014120)
- case .sanFranciscoGoldenGateBridge:
- return .init(latitude: 37.826040, longitude: -122.479448)
- case .centralParkNY:
- return .init(latitude: 40.779269, longitude: -73.963201)
- case .googlePlex:
- return .init(latitude: 37.422001, longitude: -122.084109)
- case .miamiBeach:
- return .init(latitude: 25.791007, longitude: -80.148082)
- case .lagunaBeach:
- return .init(latitude: 33.543361, longitude: -117.792315)
- case .griffithObservatory:
- return .init(latitude: 34.118536, longitude: -118.300446)
- case .luxorResortLasVegas:
- return .init(latitude: 36.095511, longitude: -115.176072)
- case .appleHeadquarter:
- return .init(latitude: 37.332100, longitude: -122.029642)
- case .berlinBrandenburgerGate:
- return .init(latitude: 52.516275, longitude: 13.377704)
- case .hamburgTownHall:
- return .init(latitude: 53.550416, longitude: 9.992527)
- case .cologneCathedral:
- return .init(latitude: 50.941278, longitude: 6.958281)
- case .munichCurch:
- return .init(latitude: 48.138631, longitude: 11.573625)
- case .neuschwansteinCastle:
- return .init(latitude: 47.557574, longitude: 10.749800)
- case .hamburgElbPhilharmonic:
- return .init(latitude: 53.541227, longitude: 9.984075)
- case .muensterCastle:
- return .init(latitude: 51.963691, longitude: 7.611546)
- case .romeColosseum:
- return .init(latitude: 41.89021, longitude: 12.492231)
- case .piazzaDiTrevi:
- return .init(latitude: 41.900865, longitude: 12.483345)
- case .sagradaFamiliaSpain:
- return .init(latitude: 41.404024, longitude: 2.174370)
- case .londonBigBen:
- return .init(latitude: 51.500729, longitude: -0.124625)
- case .londonEye:
- return .init(latitude: 51.503324, longitude: -0.119543)
- case .sydneyOperaHouse:
- return .init(latitude: -33.857197, longitude: 151.215140)
- case .parisEiffelTower:
- return .init(latitude: 48.85815, longitude: 2.29452)
- }
- }
-
-}
diff --git a/Sources/Camera/FlyoverCamera+Configuration.swift b/Sources/Camera/FlyoverCamera+Configuration.swift
deleted file mode 100644
index ef53a18..0000000
--- a/Sources/Camera/FlyoverCamera+Configuration.swift
+++ /dev/null
@@ -1,100 +0,0 @@
-//
-// FlyoverCamera+Configuration.swift
-// FlyoverKit
-//
-// Created by Sven Tiigi on 21.02.18.
-// Copyright © 2018 Sven Tiigi. All rights reserved.
-//
-
-import CoreLocation
-
-// MARK: - Configuration
-
-public extension FlyoverCamera {
-
- /// The FlyoverCamera Configuration
- struct Configuration: Equatable, Hashable {
-
- /// The duration
- public var duration: TimeInterval
-
- /// The altitude above the ground, measured in meters
- public var altitude: CLLocationDistance
-
- /// The viewing angle of the camera, measured in degrees
- public var pitch: Double
-
- /// The heading step
- public var headingStep: CLLocationDirection
-
- /// The region change animation
- public var regionChangeAnimation: RegionChangeAnimation
-
- /// Default initializer
- ///
- /// - Parameters:
- /// - duration: The duration
- /// - altitude: The altitude
- /// - pitch: The pitch
- /// - headingStep: The heading step
- /// - regionChangeAnimation: The region change animation. Default value `none`
- public init(duration: TimeInterval,
- altitude: CLLocationDistance,
- pitch: Double,
- headingStep: Double,
- regionChangeAnimation: RegionChangeAnimation = .none) {
- self.duration = duration
- self.altitude = altitude
- self.pitch = pitch
- self.headingStep = headingStep
- self.regionChangeAnimation = regionChangeAnimation
- }
- }
-
-}
-
-// MARK: - Configuration Theme
-
-public extension FlyoverCamera.Configuration {
-
- /// Default configuration
- static let `default` = FlyoverCamera.Configuration(
- duration: 4.0,
- altitude: 600,
- pitch: 45.0,
- headingStep: 20.0
- )
-
- /// Low flying configuration
- static let lowFlying = FlyoverCamera.Configuration(
- duration: 4.0,
- altitude: 65,
- pitch: 80,
- headingStep: 20
- )
-
- /// Far away configuration
- static let farAway = FlyoverCamera.Configuration(
- duration: 4.0,
- altitude: 1330,
- pitch: 55,
- headingStep: 20
- )
-
- //// Giddy configuration
- static let giddy = FlyoverCamera.Configuration(
- duration: 0.0,
- altitude: 250,
- pitch: 80,
- headingStep: 50
- )
-
- /// Astronaut view configuration
- static let astronautView = FlyoverCamera.Configuration(
- duration: 20.0,
- altitude: 2000.0,
- pitch: 100.0,
- headingStep: 35.0
- )
-
-}
diff --git a/Sources/Camera/FlyoverCamera+RegionChangeAnimation.swift b/Sources/Camera/FlyoverCamera+RegionChangeAnimation.swift
deleted file mode 100644
index 0fb25f3..0000000
--- a/Sources/Camera/FlyoverCamera+RegionChangeAnimation.swift
+++ /dev/null
@@ -1,23 +0,0 @@
-//
-// FlyoverCamera+RegionChangeAnimation.swift
-// FlyoverKit
-//
-// Created by Sven Tiigi on 21.02.18.
-// Copyright © 2018 Sven Tiigi. All rights reserved.
-//
-
-import UIKit
-
-public extension FlyoverCamera {
-
- /// The RegionChangeAnimation Enum specifies if the
- /// switch/transition to a new coordinate should be animated or not
- enum RegionChangeAnimation: Equatable, Hashable {
- /// No animation should be applied
- case none
- /// Animation with given TimeInterval and AnimationCurve
- /// should be performed
- case animated(duration: TimeInterval, curve: UIView.AnimationCurve)
- }
-
-}
diff --git a/Sources/Camera/FlyoverCamera+State.swift b/Sources/Camera/FlyoverCamera+State.swift
deleted file mode 100644
index 275d1b5..0000000
--- a/Sources/Camera/FlyoverCamera+State.swift
+++ /dev/null
@@ -1,24 +0,0 @@
-//
-// FlyoverCamera+State.swift
-// FlyoverKit
-//
-// Created by Sven Tiigi on 28.02.18.
-// Copyright © 2018 Sven Tiigi. All rights reserved.
-//
-
-import Foundation
-
-public extension FlyoverCamera {
-
- /// The FlyoverCamera State
- ///
- /// - started: The FlyoverCamera is started and running
- /// - stopped: The FlyoverCamera is stopped (Initial-Value)
- enum State: String, Equatable, Hashable, CaseIterable {
- /// Started
- case started
- /// Stopped
- case stopped
- }
-
-}
diff --git a/Sources/Camera/FlyoverCamera.swift b/Sources/Camera/FlyoverCamera.swift
deleted file mode 100644
index efcbe79..0000000
--- a/Sources/Camera/FlyoverCamera.swift
+++ /dev/null
@@ -1,274 +0,0 @@
-//
-// FlyoverCamera.swift
-// FlyoverKit
-//
-// Created by Sven Tiigi on 21.02.18.
-// Copyright © 2018 Sven Tiigi. All rights reserved.
-//
-
-import MapKit
-
-// MARK: - FlyoverCamera
-
-/// The FlyoverCamera
-open class FlyoverCamera {
-
- // MARK: Public properties
-
- /// The configuration
- open var configuration: Configuration {
- willSet {
- // Force stop animation
- self.animator?.forceStopAnimation()
- }
- didSet {
- // Set MapCamera altitude
- self.mapCamera.altitude = self.configuration.altitude
- // Set MapCamera pitch
- self.mapCamera.pitch = CGFloat(self.configuration.pitch)
- // Restart flyover with current flyover
- self.performFlyover(self.flyover)
- }
- }
-
- /// Retrieve FlyoverCamera State
- open var state: State
-
- /// The animation curve
- open var curve: UIView.AnimationCurve = .linear
-
- // MARK: Private properties
-
- /// The MapView
- private weak var mapView: MKMapView?
-
- /// The MapView Camera
- private lazy var mapCamera: MKMapCamera = {
- let camera = MKMapCamera()
- camera.altitude = self.configuration.altitude
- camera.pitch = CGFloat(self.configuration.pitch)
- return camera
- }()
-
- /// The flyover
- private var flyover: Flyover?
-
- /// The UIViewPropertyAnimator
- private var animator: UIViewPropertyAnimator?
-
- // MARK: Initializer
-
- /// Default Initializer
- ///
- /// - Parameters:
- /// - mapView: The MapView
- /// - configuration: The Configuration. Default value: `.default` theme
- public init(mapView: MKMapView, configuration: Configuration = .default) {
- // Set MapView
- self.mapView = mapView
- // Set Configuration
- self.configuration = configuration
- // Initialize state
- self.state = .stopped
- // Add application will resign active observer
- NotificationCenter.default.addObserver(
- self,
- selector: #selector(self.applicationWillResignActive),
- name: UIApplication.willResignActiveNotification,
- object: nil
- )
- // Add application did become active observer
- NotificationCenter.default.addObserver(
- self,
- selector: #selector(self.applicationDidBecomeActive),
- name: UIApplication.didBecomeActiveNotification,
- object: nil
- )
- }
-
- /// Deinit
- deinit {
- // Stop
- self.stop()
- // Remove observer
- NotificationCenter.default.removeObserver(self)
- }
-
- // MARK: Public API
-
- /// Start flyover with FlyoverAble object and optional region change animation mode
- ///
- /// - Parameters:
- /// - flyover: The Flyover object (e.g. CLLocationCoordinate2D, CLLocation, MKMapPoint)
- open func start(flyover: Flyover) {
- // Set flyover
- self.flyover = flyover
- // Check if applicationState is not active
- if UIApplication.shared.applicationState != .active {
- // Return out of function
- return
- }
- // Change state
- self.state = .started
- // Stop current animation
- self.animator?.forceStopAnimation()
- // Set center coordinate
- self.mapCamera.centerCoordinate = flyover.coordinate
- // Check if duration is zero or the current mapView camera
- // equals nearly the same to the current flyover
- if self.mapView?.camera ~~ self.flyover {
- // Simply perform flyover as we still looking at the same coordinate
- self.performFlyover(flyover)
- } else if case .animated(let duration, let curve) = self.configuration.regionChangeAnimation, duration > 0 {
- // Apply StartAnimationMode animated
- // Initialize start animatior
- let startAnimator = UIViewPropertyAnimator(
- duration: duration,
- curve: curve,
- animations: { [weak self] in
- // Verify self is available
- guard let strongSelf = self else {
- // Self isn't available return out of function
- return
- }
- // Set MapView Camera
- strongSelf.mapView?.camera = strongSelf.mapCamera
- }
- )
- // Add completion
- startAnimator.setCompletion { [weak self] in
- // Start rotation
- self?.performFlyover(flyover)
- }
- // Start animation
- startAnimator.startAnimation()
- } else {
- // No animation should be applied
- // Set MapView Camera to look at the coordinate
- self.mapView?.camera = self.mapCamera
- // Perform flyover
- self.performFlyover(flyover)
- }
- }
-
- /// Stop flyover
- open func stop() {
- // Change state
- self.state = .stopped
- // Unwrap MapView Camera Heading and fractionComplete
- guard var heading = self.mapView?.camera.heading,
- let fractionComplete = self.animator?.fractionComplete else {
- // Force stop animation
- self.animator?.forceStopAnimation()
- // Clear animator
- self.animator = nil
- // Return out function
- return
- }
- // Force stop animation
- self.animator?.forceStopAnimation()
- // Initialize Animator with stop animation
- self.animator = UIViewPropertyAnimator(
- duration: 0,
- curve: self.curve,
- animations: { [weak self] in
- // Verify self is available
- guard let strongSelf = self else {
- // Self isn't available return out of function
- return
- }
- // Substract the headingStep from current heading to retrieve start value
- heading -= strongSelf.configuration.headingStep
- // Initialize the percentage of the compeleted heading step
- let percentageCompletedHeadingStep = Double(fractionComplete) * strongSelf.configuration.headingStep
- // Set MapCamera Heading
- strongSelf.mapCamera.heading = fmod(heading + percentageCompletedHeadingStep, 360)
- // Set MapView Camera
- strongSelf.mapView?.camera = strongSelf.mapCamera
- })
- // Start animation
- self.animator?.startAnimation()
- // Clear animator as animation is been handeled
- self.animator = nil
- }
-
- // MARK: Private API
-
- /// Perform flyover at the given Flyover coordinate
- ///
- /// - Parameter flyover: The Flyover object
- private func performFlyover(_ flyover: Flyover?) {
- // Unwrap Flyover
- guard let flyover = flyover else {
- // Flyover unavailable return out of function
- return
- }
- // Increase heading by heading step for mapCamera
- self.mapCamera.heading = fmod(self.mapCamera.heading + self.configuration.headingStep, 360)
- // Initialize UIViewPropertyAnimator
- self.animator = UIViewPropertyAnimator(
- duration: self.configuration.duration,
- curve: self.curve,
- animations: { [weak self] in
- // Verify self is available
- guard let strongSelf = self else {
- // Self isn't available return out of function
- return
- }
- // Update MapViewCamera
- strongSelf.mapView?.camera = strongSelf.mapCamera
- })
- // Add completion
- self.animator?.setCompletion { [weak self] in
- // Check if flyovers are nearly equal
- if self?.flyover ~~ flyover {
- // Invoke recursion
- self?.performFlyover(flyover)
- }
- }
- // Start Animation
- self.animator?.startAnimation()
- }
-
- /// UIApplicationWillResignActive notification handler
- @objc private func applicationWillResignActive() {
- // Check if current state is not stopped
- if self.state != .stopped {
- // Stop flyover as application is no longer active
- self.stop()
- } else {
- // Clear Flyover to prevent start on didBecomeActive
- self.flyover = nil
- }
- }
-
- /// UIApplicationDidBecomeActive notification handler
- @objc private func applicationDidBecomeActive() {
- // Start if flyover is available
- self.flyover.flatMap(self.start)
- }
-
-}
-
-// MARK: - UIViewPropertyAnimator Extension
-
-fileprivate extension UIViewPropertyAnimator {
-
- /// Force Stop Animation
- func forceStopAnimation() {
- // Stop animation without finishing
- self.stopAnimation(true)
- }
-
- /// Convenience function to set the completion with no parameter closure
- ///
- /// - Parameter completion: The completion closure
- func setCompletion(_ completion: @escaping () -> Void) {
- // Add completion
- self.addCompletion { _ in
- // Invoke closure
- completion()
- }
- }
-
-}
diff --git a/Sources/Flyover+Operators.swift b/Sources/Flyover+Operators.swift
deleted file mode 100644
index 464d7f3..0000000
--- a/Sources/Flyover+Operators.swift
+++ /dev/null
@@ -1,41 +0,0 @@
-//
-// Flyover+Operators.swift
-// FlyoverKit
-//
-// Created by Sven Tiigi on 28.02.18.
-// Copyright © 2018 Sven Tiigi. All rights reserved.
-//
-
-import Foundation
-
-infix operator ~~
-
-public extension Optional where Wrapped == Flyover {
-
- /// Compare two given Flyover types
- ///
- /// - Parameters:
- /// - lhs: The left hand side
- /// - rhs: The right hand side
- /// - Returns: Boolean if the two given Flyover's are exactly equal
- static func == (lhs: Flyover?, rhs: Flyover?) -> Bool {
- return lhs?.coordinate.latitude == rhs?.coordinate.latitude
- && lhs?.coordinate.longitude == rhs?.coordinate.longitude
- }
-
- /// Compare two given Flyover types via rounded latitude and longitude comparison
- ///
- /// - Parameters:
- /// - lhs: The left hand side
- /// - rhs: The right hand side
- /// - Returns: Boolean if the two given Flyover's are nearly equal (rounded)
- static func ~~ (lhs: Flyover?, rhs: Flyover?) -> Bool {
- guard let lhs = lhs?.coordinate, let rhs = rhs?.coordinate else {
- return false
- }
- let factor = 1000.0
- return round(lhs.latitude * factor) == round(rhs.latitude * factor)
- && round(lhs.longitude * factor) == round(rhs.longitude * factor)
- }
-
-}
diff --git a/Sources/Flyover.swift b/Sources/Flyover.swift
deleted file mode 100644
index ae922f1..0000000
--- a/Sources/Flyover.swift
+++ /dev/null
@@ -1,101 +0,0 @@
-//
-// Flyover.swift
-// FlyoverKit
-//
-// Created by Sven Tiigi on 21.02.18.
-// Copyright © 2018 Sven Tiigi. All rights reserved.
-//
-
-import MapKit
-
-// MARK: - Flyover-Protocol
-
-/// The Flyover Protocol
-public protocol Flyover {
- /// The flyover coordinate
- var coordinate: CLLocationCoordinate2D { get }
-}
-
-// MARK: - CoreLocation Framework Flyover Extensions
-
-extension CLLocationCoordinate2D: Flyover {
-
- /// The flyover coordinate
- public var coordinate: CLLocationCoordinate2D {
- return self
- }
-
-}
-
-extension CLCircularRegion: Flyover {
-
- /// The flyover coordinate
- public var coordinate: CLLocationCoordinate2D {
- return self.center
- }
-
-}
-
-extension CLLocation: Flyover { }
-
-// MARK: - MapKit Framework Flyover Extensions
-
-extension MKMapItem: Flyover {
-
- /// The flyover coordinate
- public var coordinate: CLLocationCoordinate2D {
- return self.placemark.coordinate
- }
-
-}
-
-extension MKMapView: Flyover {
-
- /// The flyover coordinate
- public var coordinate: CLLocationCoordinate2D {
- return self.centerCoordinate
- }
-
-}
-
-extension MKMapPoint: Flyover {}
-
-extension MKCoordinateRegion: Flyover {
-
- /// The flyover coordinate
- public var coordinate: CLLocationCoordinate2D {
- return self.center
- }
-
-}
-
-extension MKMapRect: Flyover {
-
- /// The flyover coordinate
- public var coordinate: CLLocationCoordinate2D {
- return self.origin.coordinate
- }
-
-}
-
-extension MKCoordinateSpan: Flyover {
-
- /// The flyover coordinate
- public var coordinate: CLLocationCoordinate2D {
- return .init(latitude: self.latitudeDelta, longitude: self.longitudeDelta)
- }
-
-}
-
-extension MKMapCamera: Flyover {
-
- /// /// The flyover coordinate
- public var coordinate: CLLocationCoordinate2D {
- return self.centerCoordinate
- }
-
-}
-
-extension MKShape: Flyover { }
-
-extension MKPlacemark: Flyover { }
diff --git a/Sources/Flyover/Flyover+Animator.swift b/Sources/Flyover/Flyover+Animator.swift
new file mode 100644
index 0000000..c3b8fd9
--- /dev/null
+++ b/Sources/Flyover/Flyover+Animator.swift
@@ -0,0 +1,80 @@
+import UIKit
+
+// MARK: - Flyover+Animator
+
+extension Flyover {
+
+ /// A Flyover Animator
+ final class Animator {
+
+ /// The UIViewPropertyAnimator
+ private var propertyAnimator: UIViewPropertyAnimator?
+
+ }
+
+}
+
+// MARK: - Start
+
+extension Flyover.Animator {
+
+ /// Start animations
+ /// - Parameters:
+ /// - duration: The duration of the animation, in seconds
+ /// - curve: The UIKit timing curve to apply to the animation
+ /// - animations: A closure containing the animations
+ /// - completion: A closure to execute when the animations ends
+ func start(
+ duration: TimeInterval,
+ curve: UIView.AnimationCurve,
+ animations: @escaping () -> Void,
+ completion: @escaping () -> Void
+ ) {
+ // Initialize a new property animator
+ let propertyAnimator = UIViewPropertyAnimator(
+ duration: duration,
+ curve: curve,
+ animations: animations
+ )
+ // Add completion
+ propertyAnimator.addCompletion { [weak self] finalPosition in
+ // Verify final position is end
+ guard finalPosition == .end && self?.propertyAnimator != nil else {
+ // Otherwise return out of function
+ return
+ }
+ // Invoke completion
+ completion()
+ }
+ // Start animation
+ propertyAnimator.startAnimation()
+ // Retain property animator
+ self.propertyAnimator = propertyAnimator
+ }
+
+}
+
+// MARK: - Stop
+
+extension Flyover.Animator {
+
+ /// Stop animations
+ /// - Returns: The completion percentage of the animation, if available
+ @discardableResult
+ func stop() -> CGFloat? {
+ // Verify a property animator is available
+ guard let propertyAnimator = self.propertyAnimator else {
+ // Otherwise return nil
+ return nil
+ }
+ // Retrieve the completion percentage of the animation
+ let fractionComplete = propertyAnimator.fractionComplete
+ // Stop animation
+ propertyAnimator.stopAnimation(true)
+ // Clear property animator reference
+ self.propertyAnimator = nil
+ // Return fraction completion percentage
+ return fractionComplete
+ }
+
+}
diff --git a/Sources/Flyover/Flyover+Configuration+Parameter.swift b/Sources/Flyover/Flyover+Configuration+Parameter.swift
new file mode 100644
index 0000000..4739070
--- /dev/null
+++ b/Sources/Flyover/Flyover+Configuration+Parameter.swift
@@ -0,0 +1,102 @@
+import Foundation
+import SwiftUI
+
+// MARK: - Flyover+Configuration+Parameter
+
+public extension Flyover.Configuration {
+
+ /// A Flyover Configuration Parameter
+ struct Parameter {
+
+ // MARK: Properties
+
+ /// A closure to update the Value to a new Value
+ private let update: (Value) -> Value
+
+ // MARK: Initializer
+
+ /// Creates a new instance of `Flyover.Configuration.Parameter`
+ /// - Parameter update: A closure to update the Value to a new Value
+ public init(
+ _ update: @escaping (Value) -> Value
+ ) {
+ self.update = update
+ }
+
+ /// Creates a new instance of `Flyover.Configuration.Parameter`
+ /// - Parameter value: The Value
+ public init(_ value: Value) {
+ self.init { _ in value }
+ }
+
+ // MARK: Call as Function
+
+ /// Call `Flyover.Configuration.Parameter` to retrieve/update its Value
+ /// - Parameter value: The current Value
+ /// - Returns: The new Value
+ public func callAsFunction(
+ _ value: Value
+ ) -> Value {
+ self.update(value)
+ }
+
+ }
+
+}
+
+// MARK: - ExpressibleByIntegerLiteral
+
+extension Flyover.Configuration.Parameter: ExpressibleByIntegerLiteral {
+
+ /// Creates a new instance of `Flyover.Configuration.Parameter`
+ /// - Parameter value: The Integer literal
+ public init(
+ integerLiteral value: Int
+ ) {
+ self.init(.init(value))
+ }
+
+}
+
+// MARK: - ExpressibleByFloatLiteral
+
+extension Flyover.Configuration.Parameter: ExpressibleByFloatLiteral where Value: _ExpressibleByBuiltinFloatLiteral {
+
+ /// Creates a new instance of `Flyover.Configuration.Parameter`
+ /// - Parameter value: The Value literal
+ public init(
+ floatLiteral value: Value
+ ) {
+ self.init(value)
+ }
+
+}
+
+// MARK: - Increment
+
+public extension Flyover.Configuration.Parameter {
+
+ /// Increment the Parameter Value by a given Value
+ /// - Parameter value: The Value that should be incremented
+ static func increment(by value: Value) -> Self {
+ .init { $0 + value }
+ }
+
+}
+
+// MARK: - Restricted
+
+public extension Flyover.Configuration.Parameter {
+
+ /// A restricted Parameter in a given Range
+ /// - Parameters:
+ /// - parameter: The Parameter
+ /// - range: The Range
+ static func restricted(
+ _ parameter: Self,
+ in range: ClosedRange
+ ) -> Self {
+ .init { max(min(parameter($0), range.upperBound), range.lowerBound) }
+ }
+
+}
diff --git a/Sources/Flyover/Flyover+Configuration.swift b/Sources/Flyover/Flyover+Configuration.swift
new file mode 100644
index 0000000..88511ae
--- /dev/null
+++ b/Sources/Flyover/Flyover+Configuration.swift
@@ -0,0 +1,65 @@
+import CoreLocation
+import UIKit
+
+// MARK: - Flyover+Configuration
+
+public extension Flyover {
+
+ /// A Flyover Configuration
+ struct Configuration {
+
+ // MARK: Properties
+
+ /// The animation duration in seconds
+ public var animationDuration: TimeInterval
+
+ /// The animation curve
+ public var animationCurve: UIView.AnimationCurve
+
+ /// The altitude above the ground, measured in meters
+ public var altitude: Parameter
+
+ /// The viewing angle of the camera, measured in degrees
+ public var pitch: Parameter
+
+ /// The heading of the camera (measured in degrees) relative to true north
+ public var heading: Parameter
+
+ // MARK: Initializer
+
+ /// Creates a new instance of `Flyover.Configuration`
+ /// - Parameters:
+ /// - animationDuration: The animation duration in seconds. Default value `1`
+ /// - animationCurve: The animation curve. Default value `.linear`
+ /// - altitude: The altitude above the ground, measured in meters
+ /// - pitch: The viewing angle of the Flyover, measured in degrees
+ /// - heading: The heading of the Flyover, measured in degrees, relative to true north
+ public init(
+ animationDuration: TimeInterval = 1,
+ animationCurve: UIView.AnimationCurve = .linear,
+ altitude: Parameter,
+ pitch: Parameter,
+ heading: Parameter
+ ) {
+ self.animationDuration = animationDuration
+ self.animationCurve = animationCurve
+ self.altitude = altitude
+ self.pitch = pitch
+ self.heading = heading
+ }
+ }
+
+}
+
+// MARK: - Flyover+Configuration+default
+
+public extension Flyover.Configuration {
+
+ /// A default Flyover Configuration
+ static let `default` = Self(
+ altitude: 2000,
+ pitch: 50,
+ heading: .increment(by: 1.5)
+ )
+
+}
diff --git a/Sources/Flyover/Flyover+Context.swift b/Sources/Flyover/Flyover+Context.swift
new file mode 100644
index 0000000..1e08f28
--- /dev/null
+++ b/Sources/Flyover/Flyover+Context.swift
@@ -0,0 +1,34 @@
+import CoreLocation
+import Foundation
+
+// MARK: - Flyover+Context
+
+extension Flyover {
+
+ /// A Flyover Context
+ struct Context {
+
+ /// The CLLocationCoordinate2D
+ let coordinate: CLLocationCoordinate2D
+
+ /// The Configuration
+ let configuration: Configuration
+
+ }
+
+}
+
+// MARK: - Flyover+Context+matches(with:)
+
+extension Flyover.Context {
+
+ /// Retrieve a Bool value whether a given Coordinate matches with the current one
+ /// - Parameter coordinate: The Coordinate to check
+ func matches(
+ with coordinate: CLLocationCoordinate2D
+ ) -> Bool {
+ self.coordinate.latitude == coordinate.latitude
+ && self.coordinate.longitude == coordinate.longitude
+ }
+
+}
diff --git a/Sources/Flyover/Flyover.swift b/Sources/Flyover/Flyover.swift
new file mode 100644
index 0000000..5b3f571
--- /dev/null
+++ b/Sources/Flyover/Flyover.swift
@@ -0,0 +1,182 @@
+import MapKit
+
+// MARK: - Flyover
+
+/// A Flyover
+public final class Flyover {
+
+ // MARK: Properties
+
+ /// The property animator
+ private let animator = Animator()
+
+ /// Bool value whether Flyover is currently started or stopped
+ public private(set) var isStarted = false
+
+ /// The map view
+ public weak var mapView: MKMapView?
+
+ /// The Context
+ private var context: Context?
+
+ // MARK: Initializer
+
+ /// Creates a new instance of `Flyover`
+ /// - Parameters:
+ /// - mapView: The MKMapView (weakly referenced)
+ public init(
+ mapView: MKMapView
+ ) {
+ self.mapView = mapView
+ }
+
+}
+
+// MARK: - Start
+
+public extension Flyover {
+
+ /// Start Flyover
+ /// - Parameters:
+ /// - coordinate: The Coordinate
+ /// - configuration: The Flyover Configuration. Default value `.default`
+ @discardableResult
+ func start(
+ at coordinate: CLLocationCoordinate2D,
+ configuration: Configuration = .default
+ ) -> Bool {
+ // Verify the map view is available and the given coordinate is valid
+ guard let mapView = self.mapView, CLLocationCoordinate2DIsValid(coordinate) else {
+ // Stop
+ self.stop()
+ // Otherwise return false as Flyover could not be started
+ return false
+ }
+ // Check if coordinate has changed
+ if self.context?.matches(with: coordinate) == false {
+ // Change camera to new coordinate without animation
+ mapView.camera = .init(
+ lookingAtCenter: coordinate,
+ fromDistance: mapView.camera.centerCoordinateDistance,
+ pitch: mapView.camera.pitch,
+ heading: mapView.camera.heading
+ )
+ }
+ // Set Context
+ self.context = .init(
+ coordinate: coordinate,
+ configuration: configuration
+ )
+ // Verify is not started
+ guard !self.isStarted else {
+ // Otherwise return out of function
+ return self.isStarted
+ }
+ // Start animation
+ let isStarted = self.startAnimation()
+ // Update is started state
+ self.isStarted = isStarted
+ // Return is started
+ return isStarted
+ }
+
+}
+
+// MARK: - Start Animation
+
+private extension Flyover {
+
+ /// Start Animation
+ @discardableResult
+ func startAnimation() -> Bool {
+ // Verify map view and context are available
+ guard let mapView = self.mapView,
+ let context = self.context else {
+ // Otherwise return false as animation could not be started
+ return false
+ }
+ // Start animation
+ self.animator.start(
+ duration: context.configuration.animationDuration,
+ curve: context.configuration.animationCurve,
+ animations: { [weak mapView] in
+ // Verify map view is available
+ guard let mapView = mapView else {
+ // Otherwise return out of function
+ return
+ }
+ // Update heading
+ mapView.camera = .init(
+ lookingAtCenter: context.coordinate,
+ fromDistance: context.configuration.altitude(mapView.camera.centerCoordinateDistance),
+ pitch: context.configuration.pitch(mapView.camera.pitch),
+ heading: fmod(
+ context.configuration.heading(mapView.camera.heading),
+ 360
+ )
+ )
+ },
+ completion: { [weak self] in
+ // Restart animation
+ self?.startAnimation()
+ }
+ )
+ // Return true as animation was started succesfully
+ return true
+ }
+
+}
+
+// MARK: - Resume
+
+public extension Flyover {
+
+ /// Resume Flyover with the latest coordiante and configuration, if available
+ /// - Returns: A Bool value if the Flyover could be resumed
+ @discardableResult
+ func resume() -> Bool {
+ // Verify is not started
+ guard !self.isStarted else {
+ // Otherwise return false as Flyover can not be resumed
+ return false
+ }
+ // Restart animation
+ return self.startAnimation()
+ }
+
+}
+
+// MARK: - Stop
+
+public extension Flyover {
+
+ /// Stop Flyover
+ func stop() {
+ // Disable is started
+ self.isStarted = false
+ // Verify MapView and PropertyAnimator are available
+ guard let fractionComplete = self.animator.stop(),
+ let mapView = self.mapView,
+ let context = self.context else {
+ // Return out of function
+ return
+ }
+ // Switch on map type
+ switch mapView.mapType {
+ case .standard, .mutedStandard:
+ // Set heading
+ mapView.camera.heading = fmod(
+ {
+ let heading = mapView.camera.heading
+ let headingDelta = (context.configuration.heading(heading) - heading)
+ return (heading - headingDelta) + (fractionComplete * headingDelta)
+ }(),
+ 360
+ )
+ default:
+ // Re-apply the heading to stop any ongoing animations
+ mapView.camera.heading = mapView.camera.heading
+ }
+ }
+
+}
diff --git a/Sources/FlyoverMap/FlyoverMap.swift b/Sources/FlyoverMap/FlyoverMap.swift
new file mode 100644
index 0000000..742a540
--- /dev/null
+++ b/Sources/FlyoverMap/FlyoverMap.swift
@@ -0,0 +1,88 @@
+import MapKit
+import SwiftUI
+
+// MARK: - FlyoverMap
+
+/// A FlyoverMap
+public struct FlyoverMap {
+
+ // MARK: Properties
+
+ /// Bool value if Flyover is started
+ private let isStarted: Bool
+
+ /// The Coordinate
+ private let coordinate: CLLocationCoordinate2D
+
+ /// The Flyover Configuration
+ private let configuration: Flyover.Configuration
+
+ /// The MapType
+ private let mapType: MKMapType
+
+ /// A closure to update the underlying FlyoverMapView
+ private let updateMapView: ((FlyoverMapView) -> Void)?
+
+ // MARK: Initializer
+
+ /// Creates a new instance of `FlyoverMap`
+ /// - Parameters:
+ /// - isStarted: Bool value if Flyover is started. Default value `true`
+ /// - coordinate: The Coordinate
+ /// - configuration: The Flyover Configuration. Default value `.default`
+ /// - mapView: The MapType. Default value `.standard`
+ /// - updateMapView: A closure to update the underlying FlyoverMapView. Default value `nil`
+ public init(
+ isStarted: Bool = true,
+ coordinate: CLLocationCoordinate2D,
+ configuration: Flyover.Configuration = .default,
+ mapType: MKMapType = .standard,
+ updateMapView: ((FlyoverMapView) -> Void)? = nil
+ ) {
+ self.isStarted = isStarted
+ self.coordinate = coordinate
+ self.configuration = configuration
+ self.mapType = mapType
+ self.updateMapView = updateMapView
+ }
+
+}
+
+// MARK: - UIViewRepresentable
+
+extension FlyoverMap: UIViewRepresentable {
+
+ /// Make MKMapView
+ /// - Parameter context: The Context
+ public func makeUIView(
+ context: Context
+ ) -> FlyoverMapView {
+ .init()
+ }
+
+ /// Update MKMapView
+ /// - Parameters:
+ /// - flyoverMapView: The FlyoverMapView
+ /// - context: The Context
+ public func updateUIView(
+ _ flyoverMapView: FlyoverMapView,
+ context: Context
+ ) {
+ // Update map type
+ flyoverMapView.mapType = self.mapType
+ // Update map view if needed
+ self.updateMapView?(flyoverMapView)
+ // Check if is started
+ if self.isStarted {
+ // Start Flyover
+ flyoverMapView.startFlyover(
+ at: self.coordinate,
+ configuration: self.configuration
+ )
+ } else {
+ // Stop Flyover
+ flyoverMapView.stopFlyover()
+ }
+ }
+
+}
diff --git a/Sources/FlyoverMapView/FlyoverMapView.swift b/Sources/FlyoverMapView/FlyoverMapView.swift
new file mode 100644
index 0000000..4970fe9
--- /dev/null
+++ b/Sources/FlyoverMapView/FlyoverMapView.swift
@@ -0,0 +1,128 @@
+import MapKit
+
+// MARK: - FlyoverMapView
+
+/// A Flyover capable `MKMapView`
+open class FlyoverMapView: MKMapView {
+
+ // MARK: Properties
+
+ /// The Flyover
+ public private(set) lazy var flyover = Flyover(mapView: self)
+
+ /// The type of data displayed by the map view.s
+ open override var mapType: MKMapType {
+ get {
+ super.mapType
+ }
+ set {
+ super.mapType = {
+ switch newValue {
+ case .satellite:
+ return .satelliteFlyover
+ case .hybrid:
+ return .hybridFlyover
+ default:
+ return newValue
+ }
+ }()
+ }
+ }
+
+ // MARK: Initializer
+
+ /// Creates a new instance of `FlyoverMapView`
+ /// - Parameter frame: The frame rectangle for the view, measured in points
+ public override init(
+ frame: CGRect
+ ) {
+ super.init(frame: frame)
+ #if !os(tvOS)
+ self.showsCompass = false
+ #endif
+ self.pointOfInterestFilter = .excludingAll
+ self.showsBuildings = true
+ self.showsScale = false
+ self.isZoomEnabled = false
+ self.isPitchEnabled = false
+ self.isScrollEnabled = false
+ self.isRotateEnabled = false
+ }
+
+ /// Initializer with NSCoder is unavailable
+ @available(*, unavailable, message: "Initializer with NSCoder is unavailable")
+ public required init?(coder: NSCoder) { nil }
+
+}
+
+// MARK: - FlyoverMapView+init(mapType:)
+
+public extension FlyoverMapView {
+
+ /// Creates a new instance of `FlyoverMapView`
+ /// - Parameter flyoverMapType: The FlyoverMapType
+ convenience init(
+ mapType: MKMapType
+ ) {
+ self.init(frame: .zero)
+ self.mapType = mapType
+ }
+
+}
+
+// MARK: - Is Flyover Started
+
+public extension FlyoverMapView {
+
+ /// Bool value whether Flyover is currently started or stopped
+ var isFlyoverStarted: Bool {
+ self.flyover.isStarted
+ }
+
+}
+
+// MARK: - Start Flyover
+
+public extension FlyoverMapView {
+
+ /// Start Flyover
+ /// - Parameters:
+ /// - coordinate: The Coordinate
+ /// - configuration: The Flyover Configuration. Default value `.default`
+ @discardableResult
+ func startFlyover(
+ at coordinate: CLLocationCoordinate2D,
+ configuration: Flyover.Configuration = .default
+ ) -> Bool {
+ self.flyover.start(
+ at: coordinate,
+ configuration: configuration
+ )
+ }
+
+}
+
+// MARK: - Resume Flyover
+
+public extension FlyoverMapView {
+
+ /// Resume Flyover with the latest coordiante and configuration, if available
+ /// - Returns: A Bool value if the Flyover could be resumed
+ @discardableResult
+ func resumeFlyover() -> Bool {
+ self.flyover.resume()
+ }
+
+}
+
+
+// MARK: - Stop Flyover
+
+public extension FlyoverMapView {
+
+ /// Stop Flyover
+ func stopFlyover() {
+ self.flyover.stop()
+ }
+
+}
diff --git a/Sources/MapView/FlyoverMapView+MapType.swift b/Sources/MapView/FlyoverMapView+MapType.swift
deleted file mode 100644
index 0863517..0000000
--- a/Sources/MapView/FlyoverMapView+MapType.swift
+++ /dev/null
@@ -1,65 +0,0 @@
-//
-// FlyoverMapView+MapType.swift
-// FlyoverKit
-//
-// Created by Sven Tiigi on 21.02.18.
-// Copyright © 2018 Sven Tiigi. All rights reserved.
-//
-
-import MapKit
-
-// MARK: - MapType
-
-public extension FlyoverMapView {
-
- /// The FlyoverMapView supported MapType
- enum MapType: String, Equatable, Hashable, CaseIterable {
- /// Standard
- case standard
- /// Satellite Flyover
- case satelliteFlyover
- /// Hybrid Flyover
- case hybridFlyover
- }
-
-}
-
-// MARK: - MapType RawRepresentable
-
-extension FlyoverMapView.MapType: RawRepresentable {
-
- /// Associated type RawValue as MKMapType
- public typealias RawValue = MKMapType
-
- /// RawRepresentable initializer
- ///
- /// - Parameters:
- /// - rawValue: The MapType
- public init?(rawValue: RawValue) {
- // Switch on rawValue
- switch rawValue {
- case .standard:
- self = .standard
- case .satelliteFlyover:
- self = .satelliteFlyover
- case .hybridFlyover:
- self = .hybridFlyover
- default:
- return nil
- }
- }
-
- /// The MKMapType
- public var rawValue: RawValue {
- // Switch on self
- switch self {
- case .standard:
- return .standard
- case .satelliteFlyover:
- return .satelliteFlyover
- case .hybridFlyover:
- return .hybridFlyover
- }
- }
-
-}
diff --git a/Sources/MapView/FlyoverMapView.swift b/Sources/MapView/FlyoverMapView.swift
deleted file mode 100644
index aa8afaa..0000000
--- a/Sources/MapView/FlyoverMapView.swift
+++ /dev/null
@@ -1,118 +0,0 @@
-//
-// FlyoverMapView.swift
-// FlyoverKit
-//
-// Created by Sven Tiigi on 21.02.18.
-// Copyright © 2018 Sven Tiigi. All rights reserved.
-//
-
-import MapKit
-
-// MARK: - FlyoverMapView
-
-/// The FlyoverMapView
-open class FlyoverMapView: MKMapView {
-
- // MARK: Properties
-
- /// The FlyoverCamera
- open lazy var flyoverCamera: FlyoverCamera = {
- return FlyoverCamera(mapView: self)
- }()
-
- /// The FlyoverMapView MapType
- open var flyoverMapType: MapType? {
- get {
- // Return MapType constructed with MKMapType otherwise return standard
- return MapType(rawValue: self.mapType)
- }
- set {
- // Set mapType rawValue
- newValue.flatMap { self.mapType = $0.rawValue }
- }
- }
-
- /// The FlyoverCamera Configuration computed property for easy access
- open var configuration: FlyoverCamera.Configuration {
- get {
- // Return FlyoverCamera configuration
- return self.flyoverCamera.configuration
- }
- set {
- // Set new value
- self.flyoverCamera.configuration = newValue
- }
- }
-
- /// Retrieve FlyoverCamera state
- open var state: FlyoverCamera.State {
- // Return FlyoverCamera state property
- return self.flyoverCamera.state
- }
-
- // MARK: Initializer
-
- /// Default initializer with flyover configuration and map type
- ///
- /// - Parameters:
- /// - configuration: The flyover configuration. Default value: `.default` theme
- /// - mapType: The map type. Default value: `.standard` type
- public init(configuration: FlyoverCamera.Configuration = .default,
- mapType: MapType = .standard) {
- super.init(frame: .zero)
- // Set the configuration
- self.flyoverCamera.configuration = configuration
- // Set flyover map type
- self.flyoverMapType = mapType
- // Hide compass on iOS
- #if os(iOS)
- self.showsCompass = false
- #endif
- // Show buildings
- self.showsBuildings = true
- }
-
- /// Initializer with NSCoder (not supported) returns nil
- required public init?(coder aDecoder: NSCoder) {
- return nil
- }
-
- /// Deinit
- deinit {
- // Stop FlyoverCamera
- self.stop()
- }
-
- // MARK: Convenience start/stop functions
-
- /// Start flyover with MKAnnotation and the optional region change animation mode.
- ///
- /// - Parameters:
- /// - annotation: The MKAnnotation
- open func start(annotation: MKAnnotation) {
- // Disable userInteraction
- self.isUserInteractionEnabled = false
- // Start flyover with annotation coordinate
- self.start(flyover: annotation.coordinate)
- }
-
- /// Start flyover with FlyoverAble and the optional region change animation mode.
- ///
- /// - Parameters:
- /// - flyover: The Flyover object (e.g. CLLocationCoordinate2D, CLLocation, MKMapPoint)
- open func start(flyover: Flyover) {
- // Disable userInteraction
- self.isUserInteractionEnabled = false
- // Start flyover
- self.flyoverCamera.start(flyover: flyover)
- }
-
- /// Stop Flyover
- open func stop() {
- // Stop FlyoverCamera
- self.flyoverCamera.stop()
- // Enable userInteraction
- self.isUserInteractionEnabled = true
- }
-
-}
diff --git a/Sources/MapView/FlyoverMapViewController.swift b/Sources/MapView/FlyoverMapViewController.swift
deleted file mode 100644
index 4e2ae6c..0000000
--- a/Sources/MapView/FlyoverMapViewController.swift
+++ /dev/null
@@ -1,65 +0,0 @@
-//
-// FlyoverMapViewController.swift
-// FlyoverKit
-//
-// Created by Sven Tiigi on 21.02.18.
-// Copyright © 2018 Sven Tiigi. All rights reserved.
-//
-
-import UIKit
-
-// MARK: - FlyoverMapViewController
-
-/// The FlyoverMapViewController
-open class FlyoverMapViewController: UIViewController {
-
- // MARK: Properties
-
- /// The flyover MapView
- open var flyoverMapView: FlyoverMapView
-
- /// The flyover
- open var flyover: Flyover {
- didSet {
- // Start Flyover
- self.flyoverMapView.start(flyover: flyover)
- }
- }
-
- // MARK: Initializer
-
- /// Default initializer with flyover configuration and map type
- ///
- /// - Parameters:
- /// - flyover: The flyover object
- /// - configuration: The flyover configuration. Default value: `.default` theme
- /// - mapType: The map type. Default value `.standard` type
- public init(flyover: Flyover,
- configuration: FlyoverCamera.Configuration = .default,
- mapType: FlyoverMapView.MapType = .standard) {
- self.flyoverMapView = FlyoverMapView(configuration: configuration, mapType: mapType)
- self.flyover = flyover
- super.init(nibName: nil, bundle: nil)
- self.flyoverMapView.start(flyover: flyover)
- }
-
- /// Initializer with NSCoder. Returns nil
- required public init?(coder aDecoder: NSCoder) {
- return nil
- }
-
- /// Deinit
- deinit {
- // Stop flyover
- self.flyoverMapView.stop()
- }
-
- // MARK: View-Lifecycle
-
- /// LoadView
- open override func loadView() {
- // Set FlyoverMapView as underlying view
- self.view = self.flyoverMapView
- }
-
-}
diff --git a/Tests/BaseTests.swift b/Tests/BaseTests.swift
deleted file mode 100644
index 80ffd30..0000000
--- a/Tests/BaseTests.swift
+++ /dev/null
@@ -1,71 +0,0 @@
-//
-// BaseTests.swift
-// FlyoverKitTests
-//
-// Created by Sven Tiigi on 24.02.18.
-// Copyright © 2018 Sven Tiigi. All rights reserved.
-//
-
-import CoreLocation
-@testable import FlyoverKit
-import XCTest
-
-// MARK: - BaseTests
-
-/// The BaseTests
-class BaseTests: XCTestCase {
-
- /// The timeout value while waiting
- /// that an expectation is fulfilled
- let expectationTimeout: TimeInterval = 10.0
-
- /// Random Double Value
- var randomDouble: Double {
- let random = Double(arc4random()) / 0xFFFFFFFF
- return random * (80 - 10) + 10
- }
-
- /// Random CLLocationCoordinate2D
- var randomCoordinate: CLLocationCoordinate2D {
- return CLLocationCoordinate2D(latitude: self.randomDouble, longitude: self.randomDouble)
- }
-
- /// SetUp
- override func setUp() {
- super.setUp()
- // Disable continueAfterFailure
- self.continueAfterFailure = false
- }
-
- /// Perform test with expectation
- ///
- /// - Parameters:
- /// - name: The expectation name
- /// - execution: The test execution
- func performTest(_ expectationName: String, _ timeout: TimeInterval? = nil,
- _ execution: (XCTestExpectation) -> Void) {
- // Create expectation with function name
- let expectation = self.expectation(description: expectationName)
- // Perform test execution with expectation
- execution(expectation)
- // Wait for expectation been fulfilled with custom or default timeout
- self.waitForExpectations(timeout: timeout.flatMap { $0 } ?? self.expectationTimeout, handler: nil)
- }
-
-}
-
-// MARK: - XCTestCase AssertFlyover
-
-extension XCTestCase {
-
- /// Assert that two given Flyover objects are equal by comparing latitude and longitude
- ///
- /// - Parameters:
- /// - flyover1: The first Flyover object
- /// - flyover2: The second Flyover object
- func XCTAssertFlyover(_ flyover1: Flyover, _ flyover2: Flyover) {
- // Assert Flyover
- XCTAssertTrue(flyover1 == flyover2)
- }
-
-}
diff --git a/Tests/FlyoverCameraTests.swift b/Tests/FlyoverCameraTests.swift
deleted file mode 100644
index bb200c0..0000000
--- a/Tests/FlyoverCameraTests.swift
+++ /dev/null
@@ -1,79 +0,0 @@
-//
-// FlyoverCameraTests.swift
-// FlyoverKitTests
-//
-// Created by Sven Tiigi on 24.02.18.
-// Copyright © 2018 Sven Tiigi. All rights reserved.
-//
-
-@testable import FlyoverKit
-import MapKit
-import XCTest
-
-class FlyoverCameraTests: BaseTests {
-
- func testConfigurationEquatable() {
- let duration = 1.0
- let altitude = 2.0
- let pitch = 3.0
- let headingStep = 4.0
- let configuration1 = FlyoverCamera.Configuration(
- duration: duration,
- altitude: altitude,
- pitch: pitch,
- headingStep: headingStep
- )
- let configuration2 = FlyoverCamera.Configuration(
- duration: duration,
- altitude: altitude,
- pitch: pitch,
- headingStep: headingStep
- )
- XCTAssertEqual(configuration1, configuration2)
- }
-
- func testFlyoverCameraDefaultInitializerConfiguration() {
- let mapView = MKMapView()
- let flyoverCamera = FlyoverCamera(mapView: mapView)
- XCTAssertEqual(flyoverCamera.configuration, .default)
- }
-
- func testFlyoverCameraConfigurationTheme() {
- let mapView = MKMapView()
- let configurationThemes: [FlyoverCamera.Configuration] = [
- .default,
- .lowFlying,
- .farAway,
- .giddy,
- .astronautView
- ]
- configurationThemes.forEach { (theme) in
- let flyoverCamera = FlyoverCamera(mapView: mapView, configuration: theme)
- XCTAssertEqual(flyoverCamera.configuration, theme)
- }
- }
-
- func testFlyoverCameraStartStop() {
- let mapView = MKMapView()
- let flyoverCamera = FlyoverCamera(mapView: mapView)
- XCTAssertFalse(flyoverCamera.state == .started)
- flyoverCamera.start(flyover: FlyoverAwesomePlace.parisEiffelTower)
- XCTAssertTrue(flyoverCamera.state == .started)
- flyoverCamera.stop()
- XCTAssertFalse(flyoverCamera.state == .started)
- flyoverCamera.configuration.regionChangeAnimation = .animated(duration: 0.1, curve: .linear)
- flyoverCamera.configuration.duration = 0.1
- flyoverCamera.start(flyover: FlyoverAwesomePlace.parisEiffelTower)
- self.performTest(#function) { (expectation) in
- DispatchQueue.main.asyncAfter(deadline: .now() + 1.0, execute: {
- XCTAssertTrue(flyoverCamera.state == .started)
- flyoverCamera.start(flyover: FlyoverAwesomePlace.googlePlex)
- XCTAssertTrue(flyoverCamera.state == .started)
- flyoverCamera.stop()
- XCTAssertFalse(flyoverCamera.state == .started)
- expectation.fulfill()
- })
- }
- }
-
-}
diff --git a/Tests/FlyoverMapViewTests.swift b/Tests/FlyoverMapViewTests.swift
deleted file mode 100644
index a287aa3..0000000
--- a/Tests/FlyoverMapViewTests.swift
+++ /dev/null
@@ -1,102 +0,0 @@
-//
-// FlyoverMapViewTests.swift
-// FlyoverKitTests
-//
-// Created by Sven Tiigi on 24.02.18.
-// Copyright © 2018 Sven Tiigi. All rights reserved.
-//
-
-@testable import FlyoverKit
-import MapKit
-import XCTest
-
-class FlyoverMapViewTests: BaseTests {
-
- // MARK: FlyoverMapView
-
- func testFlyoverMapViewDefaultInitializer() {
- let mapView = FlyoverMapView()
- XCTAssertEqual(mapView.configuration, .default)
- XCTAssertEqual(mapView.flyoverMapType, .standard)
- }
-
- func testFlyoverMapViewConvenienceInitializer() {
- let theme: FlyoverCamera.Configuration = .farAway
- let mapType: FlyoverMapView.MapType = .satelliteFlyover
- let mapView = FlyoverMapView(configuration: theme, mapType: mapType)
- XCTAssertEqual(mapView.configuration, theme)
- XCTAssertEqual(mapView.flyoverMapType, mapType)
- }
-
- func testFlyoverMapViewStartStop() {
- let mapView = FlyoverMapView()
- XCTAssertFalse(mapView.state == .started)
- mapView.start(flyover: FlyoverAwesomePlace.appleHeadquarter)
- XCTAssertTrue(mapView.state == .started)
- mapView.stop()
- XCTAssertFalse(mapView.state == .started)
- let annotation = MKPointAnnotation()
- annotation.coordinate = self.randomCoordinate
- mapView.start(annotation: annotation)
- XCTAssertTrue(mapView.state == .started)
- mapView.stop()
- XCTAssertFalse(mapView.state == .started)
- }
-
- func testFlyoverMapViewMapType() {
- let standard: FlyoverMapView.MapType = .standard
- let satelliteFlyover: FlyoverMapView.MapType = .satelliteFlyover
- let hybridFlyover: FlyoverMapView.MapType = .hybridFlyover
- XCTAssertEqual(MKMapType.standard, standard.rawValue)
- XCTAssertEqual(MKMapType.satelliteFlyover, satelliteFlyover.rawValue)
- XCTAssertEqual(MKMapType.hybridFlyover, hybridFlyover.rawValue)
- XCTAssertEqual([
- MKMapType.standard,
- MKMapType.satelliteFlyover,
- MKMapType.hybridFlyover
- ].map(FlyoverMapView.MapType.init).count, 3)
- XCTAssertNil(FlyoverMapView.MapType.init(rawValue: .satellite))
- let mapView = FlyoverMapView()
- mapView.flyoverMapType = satelliteFlyover
- XCTAssertNotNil(mapView.flyoverMapType)
- XCTAssertEqual(mapView.flyoverMapType, satelliteFlyover)
- XCTAssertEqual(mapView.mapType, .satelliteFlyover)
- mapView.mapType = .satellite
- XCTAssertNil(mapView.flyoverMapType)
- }
-
- func testFlyoverMapViewConfigurationUpdate() {
- let flyoverMapView = FlyoverMapView()
- let configuration: FlyoverCamera.Configuration = .farAway
- flyoverMapView.configuration = configuration
- XCTAssertEqual(configuration, flyoverMapView.configuration)
- }
-
- // MARK: FlyoverMapViewController
-
- func testFlyoverMapViewController() {
- var flyover = FlyoverAwesomePlace.appleHeadquarter
- let controller = FlyoverMapViewController(flyover: flyover)
- XCTAssertFlyover(flyover, controller.flyover)
- XCTAssertEqual(controller.view, controller.flyoverMapView)
- XCTAssertTrue(controller.flyoverMapView.state == .started)
- XCTAssertEqual(controller.flyoverMapView.configuration, .default)
- XCTAssertEqual(controller.flyoverMapView.flyoverMapType, .standard)
- flyover = FlyoverAwesomePlace.googlePlex
- controller.flyover = flyover
- XCTAssertFlyover(flyover, controller.flyover)
- XCTAssertTrue(controller.flyoverMapView.state == .started)
- }
-
- func testFlyoverMapViewControllerThemeInitializer() {
- let flyover = FlyoverAwesomePlace.googlePlex
- let controller = FlyoverMapViewController(
- flyover: flyover,
- configuration: .farAway, mapType: .satelliteFlyover
- )
- XCTAssertFlyover(flyover, controller.flyover)
- XCTAssertEqual(controller.flyoverMapView.configuration, .farAway)
- XCTAssertEqual(controller.flyoverMapView.flyoverMapType, .satelliteFlyover)
- }
-
-}
diff --git a/Tests/FlyoverProtocolTests.swift b/Tests/FlyoverProtocolTests.swift
deleted file mode 100644
index 4d93cc0..0000000
--- a/Tests/FlyoverProtocolTests.swift
+++ /dev/null
@@ -1,96 +0,0 @@
-//
-// FlyoverProtocolTests.swift
-// FlyoverKitTests
-//
-// Created by Sven Tiigi on 24.02.18.
-// Copyright © 2018 Sven Tiigi. All rights reserved.
-//
-
-import CoreLocation
-@testable import FlyoverKit
-import MapKit
-import XCTest
-
-class FlyoverProtocolTests: BaseTests {
-
- func testFlyoverOperator() {
- let flyover1: Flyover = self.randomCoordinate
- let flyover2: Flyover? = flyover1
- let flyover3: Flyover = self.randomCoordinate
- let flyover4: Flyover? = nil
- let flyover5: Flyover? = flyover3
- XCTAssertTrue(flyover1 == flyover2)
- XCTAssertTrue(flyover1 ~~ flyover2)
- XCTAssertFalse(flyover1 == flyover4)
- XCTAssertFalse(flyover2 ~~ flyover4)
- XCTAssertFalse(flyover1 == flyover3)
- XCTAssertFalse(flyover2 ~~ flyover3)
- XCTAssertFalse(flyover3 == flyover4)
- XCTAssertFalse(flyover3 ~~ flyover4)
- XCTAssertTrue(flyover3 == flyover5)
- XCTAssertTrue(flyover3 ~~ flyover5)
- }
-
- func testFlyoverCLLocationCoordinate2D() {
- let coordinate = self.randomCoordinate
- XCTAssertFlyover(coordinate, coordinate.coordinate)
- }
-
- func testFlyoverCLCircularRegion() {
- let coordinate = self.randomCoordinate
- let region = CLCircularRegion(center: coordinate, radius: 300, identifier: "")
- XCTAssertFlyover(coordinate, region)
- }
-
- func testFlyoverMKMapItem() {
- let coordinate = self.randomCoordinate
- let mapItem = MKMapItem(placemark: MKPlacemark(coordinate: coordinate))
- XCTAssertFlyover(coordinate, mapItem)
- }
-
- func testFlyoverMKMapPoint() {
- let mockValue = (x: self.randomDouble, y: self.randomDouble)
- let mapPoint = MKMapPoint(x: mockValue.x, y: mockValue.y)
- let coordinate = mapPoint.coordinate
- XCTAssertFlyover(mapPoint, coordinate)
- }
-
- func testFlyoverMKCoorindateRegionAndSpan() {
- let coordinate = self.randomCoordinate
- let span = MKCoordinateSpan(latitudeDelta: coordinate.latitude, longitudeDelta: coordinate.longitude)
- let region = MKCoordinateRegion(center: coordinate, span: span)
- XCTAssertFlyover(coordinate, region)
- XCTAssertFlyover(coordinate, span)
- }
-
- func testFlyoverMapView() {
- let mapView = MKMapView()
- let coordinate = mapView.centerCoordinate
- XCTAssertFlyover(coordinate, mapView)
- }
-
- func testFlyoverMKMapRect() {
- let mockValue = (x: self.randomDouble, y: self.randomDouble)
- let mapPoint = MKMapPoint(x: mockValue.x, y: mockValue.y)
- let coordinate = mapPoint.coordinate
- let rect = MKMapRect(origin: mapPoint, size: MKMapSize(width: mockValue.x, height: mockValue.y))
- XCTAssertFlyover(coordinate, rect)
- }
-
- func testFlyoverMKMapCamera() {
- let coordinate = self.randomCoordinate
- let camera = MKMapCamera()
- camera.centerCoordinate = coordinate
- XCTAssertFlyover(coordinate, camera)
- }
-
- func testFlyoverAwesomePlaceCases() {
- for place in FlyoverAwesomePlace.allCases {
- XCTAssertFlyover(place, place.coordinate)
- }
- for place in FlyoverAwesomePlace.iterate() {
- XCTAssertFlyover(place, place.coordinate)
- }
- }
-
-}
diff --git a/fastlane/.env b/fastlane/.env
deleted file mode 100644
index ef9bcc7..0000000
--- a/fastlane/.env
+++ /dev/null
@@ -1 +0,0 @@
-FASTLANE_SKIP_UPDATE_CHECK=1
diff --git a/fastlane/Fastfile b/fastlane/Fastfile
deleted file mode 100644
index 2b28672..0000000
--- a/fastlane/Fastfile
+++ /dev/null
@@ -1,98 +0,0 @@
-fastlane_version "2.120.0"
-
-default_platform :ios
-
-platform :ios do
-
- desc "Release a new version of FlyoverKit"
- lane :release do |options|
- # Ensure Git status is clean
- ensure_git_status_clean
- # Ensure Git branch is master
- ensure_git_branch(branch: 'master')
- # Perform Dependency-Manager compatibility tests
- compatibilityTests
- # Perform Tests
- tests
- # Retrieve Version from options
- version = options[:version]
- # Increment Version
- increment(version: version)
- # Add Git Tag
- add_git_tag(tag: version)
- # Push Git Tag
- push_git_tags()
- # Push Git commit
- push_to_git_remote()
- # Pod push / Pod trunk
- pod_push()
- end
-
- desc "Increment Version"
- lane :increment do |options|
- # Retrieve Version from options
- version = options[:version]
- # Set Podspec version
- version_bump_podspec(
- path: "FlyoverKit.podspec",
- version_number: version
- )
- # Set Framework plist version
- set_info_plist_value(
- path: "Configs/FlyoverKit.plist",
- key: "CFBundleShortVersionString",
- value: version
- )
- # Set Framework Tests plist version
- set_info_plist_value(
- path: "Configs/FlyoverKitTests.plist",
- key: "CFBundleShortVersionString",
- value: version
- )
- # Set Example plist version
- set_info_plist_value(
- path: "Example/Resources/Info.plist",
- key: "CFBundleShortVersionString",
- value: version
- )
- # Commit modified files
- git_commit(
- path: [
- "FlyoverKit.podspec",
- "Configs/FlyoverKit.plist",
- "Configs/FlyoverKitTests.plist",
- "Example/Resources/Info.plist"
- ],
- message: "FlyoverKit Version #{version} 🚀"
- )
- end
-
- desc "Runs tests"
- lane :tests do
- # Perform iOS Tests
- scan(
- project: "FlyoverKit.xcodeproj",
- scheme: "FlyoverKit-iOS",
- clean: true
- )
- # Perform tvOS Tests
- scan(
- project: "FlyoverKit.xcodeproj",
- scheme: "FlyoverKit-tvOS",
- clean: true
- )
- end
-
- desc "Run Dependency-Manager compatibility tests"
- lane :compatibilityTests do
- # Carthage build to ensure Carthage compatibility
- carthage(
- command: "build",
- no_skip_current: true,
- cache_builds: true
- )
- # Pod lib lint to ensure CocoaPods compatibility
- pod_lib_lint(allow_warnings: true)
- end
-
-end