feat: add smooth CameraUpdate actions for supported maps
This commit is contained in:
parent
23b9e8ac7a
commit
f61bb8ef12
|
|
@ -1,5 +1,9 @@
|
|||
# Changelog
|
||||
|
||||
### 1.17.0
|
||||
|
||||
- **Add**: add smooth CameraUpdate actions for supported maps
|
||||
|
||||
### 1.16.2
|
||||
|
||||
- **Update**: `DefaultRecoverableJsonNetworkService` now supports error forwarding from its error handlers to initial requests.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = "LeadKit"
|
||||
s.version = "1.16.2"
|
||||
s.version = "1.17.0"
|
||||
s.summary = "iOS framework with a bunch of tools for rapid development"
|
||||
s.homepage = "https://github.com/TouchInstinct/LeadKit"
|
||||
s.license = "Apache License, Version 2.0"
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import TIMapUtils
|
|||
import MapKit
|
||||
import UIKit
|
||||
|
||||
open class AppleClusterPlacemarkManager<Model>: BasePlacemarkManager<MKAnnotationView, [ApplePlacemarkManager<Model>], MKCoordinateRegion>, MKMapViewDelegate {
|
||||
open class AppleClusterPlacemarkManager<Model>: BasePlacemarkManager<MKAnnotationView, [ApplePlacemarkManager<Model>], MKMapRect>, MKMapViewDelegate {
|
||||
public weak var mapViewDelegate: MKMapViewDelegate?
|
||||
|
||||
private let mapDelegateSelectors = NSObject.instanceMethodSelectors(of: MKMapViewDelegate.self)
|
||||
|
|
@ -139,20 +139,3 @@ open class AppleClusterPlacemarkManager<Model>: BasePlacemarkManager<MKAnnotatio
|
|||
return mapViewDelegate
|
||||
}
|
||||
}
|
||||
|
||||
private extension MKCoordinateRegion {
|
||||
static func from(coordinates: [CLLocationCoordinate2D]) -> MKCoordinateRegion {
|
||||
guard !coordinates.isEmpty else {
|
||||
return MKCoordinateRegion()
|
||||
}
|
||||
|
||||
let bbox = CoordinateBounds.from(coordinates: coordinates)
|
||||
|
||||
let span = MKCoordinateSpan(latitudeDelta: bbox.northEast.latitude - bbox.southWest.latitude,
|
||||
longitudeDelta: bbox.northEast.longitude - bbox.southWest.longitude)
|
||||
let center = CLLocationCoordinate2D(latitude: bbox.northEast.latitude - (span.latitudeDelta / 2),
|
||||
longitude: bbox.southWest.longitude + (span.longitudeDelta / 2))
|
||||
|
||||
return MKCoordinateRegion(center: center, span: span)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// 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 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.
|
||||
//
|
||||
|
||||
import MapKit
|
||||
import TIMapUtils
|
||||
|
||||
open class MKCameraUpdate: CameraUpdate, CameraUpdateFactory {
|
||||
private let action: CameraUpdateAction<CLLocationCoordinate2D, MKMapRect>
|
||||
|
||||
public init(action: CameraUpdateAction<CLLocationCoordinate2D, MKMapRect>) {
|
||||
self.action = action
|
||||
}
|
||||
|
||||
// MARK: - CameraUpdateFactory
|
||||
|
||||
public static func update(for action: CameraUpdateAction<CLLocationCoordinate2D, MKMapRect>) -> MKCameraUpdate {
|
||||
.init(action: action)
|
||||
}
|
||||
|
||||
// MARK: - CameraUpdate
|
||||
|
||||
private func update(map: MKMapView, animated: Bool = true) {
|
||||
switch action {
|
||||
case let .focus(target, zoom):
|
||||
map.set(zoomLevel: zoom,
|
||||
at: target,
|
||||
animated: animated)
|
||||
|
||||
case let .fit(bounds, insets):
|
||||
map.setVisibleMapRect(bounds,
|
||||
edgePadding: insets,
|
||||
animated: animated)
|
||||
|
||||
case .zoomIn:
|
||||
map.set(zoomLevel: map.zoomLevel + 1,
|
||||
at: map.centerCoordinate,
|
||||
animated: animated)
|
||||
|
||||
case .zoomOut:
|
||||
map.set(zoomLevel: map.zoomLevel - 1,
|
||||
at: map.centerCoordinate,
|
||||
animated: animated)
|
||||
}
|
||||
}
|
||||
|
||||
public func update(map: MKMapView) {
|
||||
update(map: map, animated: false)
|
||||
}
|
||||
|
||||
public func update(map: MKMapView, animationDuration: TimeInterval) {
|
||||
UIView.animate(withDuration: animationDuration) {
|
||||
self.update(map: map, animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// 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 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.
|
||||
//
|
||||
|
||||
import MapKit
|
||||
import TIMapUtils
|
||||
|
||||
public extension MKMapRect {
|
||||
static func from<C: Collection>(coordinates: C) -> MKMapRect where C.Element == CLLocationCoordinate2D {
|
||||
guard let bbox = CLCoordinateBounds.from(coordinates: coordinates) else {
|
||||
return MKMapRect.null
|
||||
}
|
||||
|
||||
let southWest = MKMapPoint(bbox.southWest)
|
||||
let northEast = MKMapPoint(bbox.northEast)
|
||||
|
||||
let origin = MKMapPoint(x: southWest.x,
|
||||
y: northEast.y)
|
||||
|
||||
let size = MKMapSize(width: northEast.x - southWest.x,
|
||||
height: southWest.y - northEast.y)
|
||||
|
||||
return MKMapRect(origin: origin, size: size)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// 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 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.
|
||||
//
|
||||
|
||||
import MapKit
|
||||
|
||||
// https://stackoverflow.com/a/15020534
|
||||
public extension MKMapView {
|
||||
var zoomLevel: Float {
|
||||
Float(log2(360 * ((frame.size.width / .expectedMapViewTileSize) / region.span.longitudeDelta))) + 1
|
||||
}
|
||||
|
||||
func set(zoomLevel: Float, at centerCoordinate: CLLocationCoordinate2D, animated: Bool = true) {
|
||||
let span = MKCoordinateSpan(latitudeDelta: 0,
|
||||
longitudeDelta: 360 / pow(2, Double(zoomLevel)) * frame.size.width / .expectedMapViewTileSize)
|
||||
setRegion(MKCoordinateRegion(center: centerCoordinate, span: span), animated: animated)
|
||||
}
|
||||
}
|
||||
|
||||
private extension CGFloat {
|
||||
static var expectedMapViewTileSize: CGFloat {
|
||||
256
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIAppleMapUtils'
|
||||
s.version = '1.16.2'
|
||||
s.version = '1.17.0'
|
||||
s.summary = 'Set of helpers for map objects clustering and interacting using Apple MapKit.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIFoundationUtils'
|
||||
s.version = '1.16.2'
|
||||
s.version = '1.17.0'
|
||||
s.summary = 'Set of helpers for Foundation framework classes.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// 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 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.
|
||||
//
|
||||
|
||||
import GoogleMapsUtils
|
||||
import TIMapUtils
|
||||
|
||||
extension GMSCameraUpdate: CameraUpdateFactory, CameraUpdate {
|
||||
// MARK: - CameraUpdateFactory
|
||||
|
||||
public static func update(for action: CameraUpdateAction<CLLocationCoordinate2D, GMSCoordinateBounds>) -> GMSCameraUpdate {
|
||||
switch action {
|
||||
case let .focus(target, zoom):
|
||||
return .setTarget(target, zoom: zoom)
|
||||
|
||||
case let .fit(bounds, insets):
|
||||
return .fit(bounds, with: insets)
|
||||
|
||||
case .zoomIn:
|
||||
return .zoomIn()
|
||||
|
||||
case .zoomOut:
|
||||
return .zoomOut()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CameraUpdate
|
||||
|
||||
public func update(map: GMSMapView) {
|
||||
map.moveCamera(self)
|
||||
}
|
||||
|
||||
public func update(map: GMSMapView, animationDuration: TimeInterval) {
|
||||
CATransaction.begin()
|
||||
CATransaction.setValue(animationDuration, forKey: kCATransactionAnimationDuration)
|
||||
map.animate(with: self)
|
||||
CATransaction.commit()
|
||||
}
|
||||
}
|
||||
|
|
@ -110,11 +110,7 @@ open class GoogleClusterPlacemarkManager<Model>: BasePlacemarkManager<GMSMarker,
|
|||
return false
|
||||
}
|
||||
|
||||
let bounds = placemarkManagers.reduce(GMSCoordinateBounds()) {
|
||||
$0.includingCoordinate($1.position)
|
||||
}
|
||||
|
||||
return tapHandler?(placemarkManagers, bounds) ?? false
|
||||
return tapHandler?(placemarkManagers, .from(coordinates: placemarkManagers.map { $0.position }) ?? .init()) ?? false
|
||||
}
|
||||
|
||||
open func clusterManager(_ clusterManager: GMUClusterManager, didTap clusterItem: GMUClusterItem) -> Bool {
|
||||
|
|
@ -131,3 +127,9 @@ open class GoogleClusterPlacemarkManager<Model>: BasePlacemarkManager<GMSMarker,
|
|||
nil // icon generation will be performed in GMUClusterRendererDelegate callback
|
||||
}
|
||||
}
|
||||
|
||||
extension GMSCoordinateBounds: CoordinateBounds {
|
||||
public static func of(southWest: CLLocationCoordinate2D, northEast: CLLocationCoordinate2D) -> Self {
|
||||
.init(coordinate: southWest, coordinate: northEast)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIGoogleMapUtils'
|
||||
s.version = '1.16.2'
|
||||
s.version = '1.17.0'
|
||||
s.summary = 'Set of helpers for map objects clustering and interacting using Google Maps SDK.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIKeychainUtils'
|
||||
s.version = '1.16.2'
|
||||
s.version = '1.17.0'
|
||||
s.summary = 'Set of helpers for Keychain classes.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// 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 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.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol CameraUpdate {
|
||||
associatedtype Map
|
||||
|
||||
func update(map: Map)
|
||||
func update(map: Map, animationDuration: TimeInterval)
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// 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 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.
|
||||
//
|
||||
|
||||
import UIKit.UIGeometry
|
||||
|
||||
public enum CameraUpdateAction<Location, BoundingBox> {
|
||||
case focus(target: Location, zoom: Float)
|
||||
case fit(bounds: BoundingBox, insets: UIEdgeInsets)
|
||||
case zoomIn
|
||||
case zoomOut
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// 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 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.
|
||||
//
|
||||
|
||||
public protocol CameraUpdateFactory {
|
||||
associatedtype Position
|
||||
associatedtype BoundingBox
|
||||
associatedtype Update: CameraUpdate
|
||||
|
||||
static func update(for action: CameraUpdateAction<Position, BoundingBox>) -> Update
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// 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 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.
|
||||
//
|
||||
|
||||
import CoreLocation
|
||||
|
||||
extension CLLocationCoordinate2D: Hashable {
|
||||
public static func == (lhs: CLLocationCoordinate2D, rhs: CLLocationCoordinate2D) -> Bool {
|
||||
rhs.latitude == lhs.latitude &&
|
||||
rhs.longitude == lhs.longitude
|
||||
}
|
||||
|
||||
public func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(latitude)
|
||||
hasher.combine(longitude)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// 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 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.
|
||||
//
|
||||
|
||||
// https://wiki.openstreetmap.org/wiki/Zoom_levels
|
||||
public enum CommonZoomLevels: UInt {
|
||||
case largestCountry = 3 // whole Russia (in horizontal orientation)
|
||||
case largeMetropolianArea = 9 // Moscow with suburb
|
||||
case metropolianArea = 10 // Saint-Petersburg with suburb
|
||||
case city = 11 // regional city or metropolian center
|
||||
case street = 16
|
||||
case building = 18
|
||||
|
||||
public var floatValue: Float {
|
||||
Float(rawValue)
|
||||
}
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
import CoreLocation
|
||||
|
||||
public struct CoordinateBounds {
|
||||
public struct CLCoordinateBounds: CoordinateBounds {
|
||||
public let southWest: CLLocationCoordinate2D
|
||||
public let northEast: CLLocationCoordinate2D
|
||||
|
||||
|
|
@ -30,22 +30,10 @@ public struct CoordinateBounds {
|
|||
self.southWest = southWest
|
||||
self.northEast = northEast
|
||||
}
|
||||
}
|
||||
|
||||
public extension CoordinateBounds {
|
||||
static func from<C: Collection>(coordinates: C) -> CoordinateBounds where C.Element == CLLocationCoordinate2D {
|
||||
guard let first = coordinates.first else {
|
||||
return .init(southWest: CLLocationCoordinate2D(),
|
||||
northEast: CLLocationCoordinate2D())
|
||||
}
|
||||
|
||||
let initialBox = CoordinateBounds(southWest: first, northEast: first)
|
||||
|
||||
return coordinates.dropFirst().reduce(initialBox) {
|
||||
CoordinateBounds(southWest: CLLocationCoordinate2D(latitude: min($0.southWest.latitude, $1.latitude),
|
||||
longitude: min($0.southWest.longitude, $1.longitude)),
|
||||
northEast: CLLocationCoordinate2D(latitude: max($0.northEast.latitude, $1.latitude),
|
||||
longitude: max($0.northEast.longitude, $1.longitude)))
|
||||
}
|
||||
public static func of(southWest: CLLocationCoordinate2D, northEast: CLLocationCoordinate2D) -> CLCoordinateBounds {
|
||||
.init(southWest: southWest, northEast: northEast)
|
||||
}
|
||||
}
|
||||
|
||||
extension CLLocationCoordinate2D: LocationCoordinate {}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// 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 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.
|
||||
//
|
||||
|
||||
import CoreLocation
|
||||
|
||||
public extension CoordinateBounds {
|
||||
static func from<C: Collection>(coordinates: C) -> Self? where C.Element == Coordinate {
|
||||
guard !coordinates.isEmpty else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var (northEastLon, northEastLat) = (-CLLocationDegrees.infinity, -CLLocationDegrees.infinity)
|
||||
var (southWestLon, southWestLat) = (CLLocationDegrees.infinity, CLLocationDegrees.infinity)
|
||||
|
||||
for coordinate in coordinates {
|
||||
southWestLon = min(southWestLon, coordinate.longitude)
|
||||
northEastLon = max(northEastLon, coordinate.longitude)
|
||||
southWestLat = min(southWestLat, coordinate.latitude)
|
||||
northEastLat = max(northEastLat, coordinate.latitude)
|
||||
}
|
||||
|
||||
return .of(southWest: Coordinate(latitude: southWestLat, longitude: southWestLon),
|
||||
northEast: Coordinate(latitude: northEastLat, longitude: northEastLon))
|
||||
}
|
||||
|
||||
var topLeft: Coordinate {
|
||||
Coordinate(latitude: northEast.latitude, longitude: southWest.longitude)
|
||||
}
|
||||
|
||||
var bottomRight: Coordinate {
|
||||
Coordinate(latitude: southWest.latitude, longitude: northEast.longitude)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// 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 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.
|
||||
//
|
||||
|
||||
public protocol CoordinateBounds {
|
||||
associatedtype Coordinate: LocationCoordinate
|
||||
|
||||
var southWest: Coordinate { get }
|
||||
var northEast: Coordinate { get }
|
||||
|
||||
static func of(southWest: Coordinate, northEast: Coordinate) -> Self
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// 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 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.
|
||||
//
|
||||
|
||||
import CoreLocation
|
||||
|
||||
public protocol LocationCoordinate {
|
||||
var latitude: CLLocationDegrees { get }
|
||||
var longitude: CLLocationDegrees { get }
|
||||
|
||||
init(latitude: CLLocationDegrees, longitude: CLLocationDegrees)
|
||||
}
|
||||
|
|
@ -22,8 +22,8 @@
|
|||
|
||||
import UIKit
|
||||
|
||||
open class BasePlacemarkManager<Placemark, Model, Position>: NSObject, PlacemarkManager {
|
||||
public typealias TapHandlerClosure = (Model, Position) -> Bool
|
||||
open class BasePlacemarkManager<Placemark, Model, Coordinate>: NSObject, PlacemarkManager {
|
||||
public typealias TapHandlerClosure = (Model, Coordinate) -> Bool
|
||||
public typealias IconProviderClosure = (Model) -> UIImage
|
||||
|
||||
public var tapHandler: TapHandlerClosure?
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIMapUtils'
|
||||
s.version = '1.16.2'
|
||||
s.version = '1.17.0'
|
||||
s.summary = 'Set of helpers for map objects clustering and interacting.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIMoyaNetworking'
|
||||
s.version = '1.16.2'
|
||||
s.version = '1.17.0'
|
||||
s.summary = 'Moya + Swagger network service.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TINetworking'
|
||||
s.version = '1.16.2'
|
||||
s.version = '1.17.0'
|
||||
s.summary = 'Swagger-frendly networking layer helpers.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TINetworkingCache'
|
||||
s.version = '1.16.2'
|
||||
s.version = '1.17.0'
|
||||
s.summary = 'Caching results of EndpointRequests.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIPagination'
|
||||
s.version = '1.16.2'
|
||||
s.version = '1.17.0'
|
||||
s.summary = 'Generic pagination component.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TISwiftUtils'
|
||||
s.version = '1.16.2'
|
||||
s.version = '1.17.0'
|
||||
s.summary = 'Bunch of useful helpers for Swift development.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TITableKitUtils'
|
||||
s.version = '1.16.2'
|
||||
s.version = '1.17.0'
|
||||
s.summary = 'Set of helpers for TableKit classes.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TITransitions'
|
||||
s.version = '1.16.2'
|
||||
s.version = '1.17.0'
|
||||
s.summary = 'Set of custom transitions to present controller. '
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIUIElements'
|
||||
s.version = '1.16.2'
|
||||
s.version = '1.17.0'
|
||||
s.summary = 'Bunch of useful protocols and views.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIUIKitCore'
|
||||
s.version = '1.16.2'
|
||||
s.version = '1.17.0'
|
||||
s.summary = 'Core UI elements: protocols, views and helpers.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// 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 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.
|
||||
//
|
||||
|
||||
import CoreLocation
|
||||
import YandexMapsMobile
|
||||
|
||||
public extension CLLocationCoordinate2D {
|
||||
init(ymkPoint: YMKPoint) {
|
||||
self.init(latitude: ymkPoint.latitude, longitude: ymkPoint.longitude)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// 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 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.
|
||||
//
|
||||
|
||||
import UIKit.UIGeometry
|
||||
|
||||
public extension UIEdgeInsets {
|
||||
static func +(lhs: UIEdgeInsets, rhs: UIEdgeInsets) -> UIEdgeInsets {
|
||||
UIEdgeInsets(top: lhs.top + rhs.top,
|
||||
left: lhs.left + rhs.left,
|
||||
bottom: lhs.bottom + rhs.bottom,
|
||||
right: lhs.right + rhs.right)
|
||||
}
|
||||
|
||||
static func *(lhs: UIEdgeInsets, rhs: CGFloat) -> UIEdgeInsets {
|
||||
UIEdgeInsets(top: lhs.top * rhs,
|
||||
left: lhs.left * rhs,
|
||||
bottom: lhs.bottom * rhs,
|
||||
right: lhs.right * rhs)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// 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 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.
|
||||
//
|
||||
|
||||
import YandexMapsMobile
|
||||
import TIMapUtils
|
||||
|
||||
extension YMKPoint: LocationCoordinate {}
|
||||
|
||||
extension YMKBoundingBox: CoordinateBounds {
|
||||
public static func of(southWest: YMKPoint, northEast: YMKPoint) -> Self {
|
||||
.init(southWest: southWest, northEast: northEast)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// 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 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.
|
||||
//
|
||||
|
||||
import YandexMapsMobile
|
||||
|
||||
public extension YMKScreenRect {
|
||||
func cameraPosition(in mapView: YMKMapView, insets: UIEdgeInsets) -> YMKCameraPosition? {
|
||||
let mapWindow: YMKMapWindow = mapView.mapWindow // mapWindow is IUO
|
||||
|
||||
let center = YMKScreenPoint(x: (topLeft.x + bottomRight.x) / 2,
|
||||
y: (topLeft.y + bottomRight.y) / 2)
|
||||
|
||||
guard let coordinatePoint = mapWindow.screenToWorld(with: center) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let mapInsets: UIEdgeInsets
|
||||
|
||||
if #available(iOS 11.0, *) {
|
||||
mapInsets = (insets + mapView.safeAreaInsets) * CGFloat(mapWindow.scaleFactor)
|
||||
} else {
|
||||
mapInsets = insets * CGFloat(mapWindow.scaleFactor)
|
||||
}
|
||||
|
||||
let size = CGSize(width: mapWindow.width(), height: mapWindow.height())
|
||||
|
||||
guard let minScale = minScale(for: size, insets: mapInsets) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let map = mapWindow.map
|
||||
|
||||
let newZoom = map.cameraPosition.zoom + log2(minScale)
|
||||
let minZoom = map.getMinZoom()
|
||||
let maxZoom = map.getMaxZoom()
|
||||
|
||||
return YMKCameraPosition(target: coordinatePoint,
|
||||
zoom: min(max(newZoom, minZoom), maxZoom),
|
||||
azimuth: 0,
|
||||
tilt: 0)
|
||||
}
|
||||
|
||||
private func minScale(for mapSize: CGSize, insets: UIEdgeInsets) -> Float? {
|
||||
let width = CGFloat(abs(bottomRight.x - topLeft.x))
|
||||
let height = CGFloat(abs(topLeft.y - bottomRight.y))
|
||||
|
||||
if width > 0 || height > 0 {
|
||||
let scaleX = mapSize.width / width - (insets.left + insets.right) / width
|
||||
let scaleY = mapSize.height / height - (insets.top + insets.bottom) / height
|
||||
|
||||
return Float(min(scaleX, scaleY))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// 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 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.
|
||||
//
|
||||
|
||||
import YandexMapsMobile
|
||||
|
||||
public extension YMKScreenRect {
|
||||
static func from<C: Collection>(coordinates: C,
|
||||
in mapWindow: YMKMapWindow) -> YMKScreenRect? where C.Element == YMKPoint {
|
||||
|
||||
guard let bbox = YMKBoundingBox.from(coordinates: coordinates) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let toScreenPoint = mapWindow.worldToScreen
|
||||
|
||||
guard let topLeft = toScreenPoint(bbox.topLeft),
|
||||
let bottomRight = toScreenPoint(bbox.bottomRight) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return YMKScreenRect(topLeft: topLeft, bottomRight: bottomRight)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
//
|
||||
// Copyright (c) 2022 Touch Instinct
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the Software), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// 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 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.
|
||||
//
|
||||
|
||||
import YandexMapsMobile
|
||||
import TIMapUtils
|
||||
|
||||
open class YMKCameraUpdate: CameraUpdate, CameraUpdateFactory {
|
||||
private let action: CameraUpdateAction<YMKPoint, [YMKPoint]>
|
||||
|
||||
public var animationType = YMKAnimation(type: .smooth, duration: 0.5)
|
||||
|
||||
public init(action: CameraUpdateAction<YMKPoint, [YMKPoint]>) {
|
||||
self.action = action
|
||||
}
|
||||
|
||||
// MARK: - CameraUpdateFactory
|
||||
|
||||
public static func update(for action: CameraUpdateAction<YMKPoint, [YMKPoint]>) -> YMKCameraUpdate {
|
||||
.init(action: action)
|
||||
}
|
||||
|
||||
open func cameraPosition(for action: CameraUpdateAction<YMKPoint, [YMKPoint]>,
|
||||
in mapView: YMKMapView) -> YMKCameraPosition {
|
||||
|
||||
let cameraPosition = mapView.mapWindow.map.cameraPosition
|
||||
|
||||
switch action {
|
||||
case let .focus(target, zoom):
|
||||
return YMKCameraPosition(target: target,
|
||||
zoom: zoom,
|
||||
azimuth: 0,
|
||||
tilt: 0)
|
||||
|
||||
case let .fit(bounds, insets):
|
||||
return YMKScreenRect.from(coordinates: bounds, in: mapView.mapWindow)?
|
||||
.cameraPosition(in: mapView, insets: insets) ?? cameraPosition
|
||||
|
||||
case .zoomIn:
|
||||
return YMKCameraPosition(target: cameraPosition.target,
|
||||
zoom: cameraPosition.zoom + 1,
|
||||
azimuth: cameraPosition.azimuth,
|
||||
tilt: cameraPosition.tilt)
|
||||
|
||||
case .zoomOut:
|
||||
return YMKCameraPosition(target: cameraPosition.target,
|
||||
zoom: cameraPosition.zoom - 1,
|
||||
azimuth: cameraPosition.azimuth,
|
||||
tilt: cameraPosition.tilt)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CameraUpdate
|
||||
|
||||
public func update(map: YMKMapView) {
|
||||
map.mapWindow.map.move(with: cameraPosition(for: action, in: map))
|
||||
}
|
||||
|
||||
public func update(map: YMKMapView, animationDuration: TimeInterval) {
|
||||
map.mapWindow.map.move(with: cameraPosition(for: action, in: map),
|
||||
animationType: YMKAnimation(type: .smooth, duration: Float(animationDuration)),
|
||||
cameraCallback: nil)
|
||||
}
|
||||
}
|
||||
|
|
@ -23,9 +23,11 @@
|
|||
import TIMapUtils
|
||||
import YandexMapsMobile
|
||||
import UIKit
|
||||
import CoreLocation
|
||||
|
||||
open class YandexClusterPlacemarkManager<Model>: BasePlacemarkManager<YMKCluster, [YandexPlacemarkManager<Model>], YMKBoundingBox>, YMKClusterListener, YMKClusterTapListener {
|
||||
public var placemarksMapping: Zip2Sequence<[YMKPlacemarkMapObject], [YandexPlacemarkManager<Model>]>?
|
||||
open class YandexClusterPlacemarkManager<Model>: BasePlacemarkManager<YMKCluster, [YandexPlacemarkManager<Model>], [YMKPoint]>, YMKClusterListener, YMKClusterTapListener {
|
||||
|
||||
public var placemarksMapping: [CLLocationCoordinate2D: [YandexPlacemarkManager<Model>]]?
|
||||
|
||||
public init(placemarkManagers: [YandexPlacemarkManager<Model>],
|
||||
iconProvider: @escaping IconProviderClosure,
|
||||
|
|
@ -45,14 +47,23 @@ open class YandexClusterPlacemarkManager<Model>: BasePlacemarkManager<YMKCluster
|
|||
tapHandler: tapHandler)
|
||||
}
|
||||
|
||||
open func addMarkers(to map: YMKMap, clusterRadius: Double = 60, minZoom: UInt = 15) {
|
||||
open func addMarkers(to map: YMKMap,
|
||||
clusterRadius: Double = 60,
|
||||
minZoom: UInt = CommonZoomLevels.street.rawValue) {
|
||||
|
||||
let clusterizedPlacemarkCollection = map.mapObjects.addClusterizedPlacemarkCollection(with: self)
|
||||
|
||||
let emptyPlacemarks = clusterizedPlacemarkCollection.addEmptyPlacemarks(with: dataModel.map { $0.position })
|
||||
|
||||
self.placemarksMapping = zip(emptyPlacemarks, dataModel)
|
||||
let placemarksZip = zip(emptyPlacemarks, dataModel)
|
||||
|
||||
placemarksMapping?.forEach { (placemark, manager) in
|
||||
// [(coordinate, placemarkManager)]
|
||||
let mappingSequence = placemarksZip.map { (CLLocationCoordinate2D(ymkPoint: $0.0.geometry), $0.1) }
|
||||
|
||||
// [coordinate: [placemarkManager]]
|
||||
self.placemarksMapping = Dictionary(grouping: mappingSequence) { $0.0 }.mapValues { $0.map { $0.1 } }
|
||||
|
||||
placemarksZip.forEach { (placemark, manager) in
|
||||
manager.configure(placemark: placemark)
|
||||
}
|
||||
|
||||
|
|
@ -73,12 +84,14 @@ open class YandexClusterPlacemarkManager<Model>: BasePlacemarkManager<YMKCluster
|
|||
return false
|
||||
}
|
||||
|
||||
return tapHandler(managers(in: cluster), .from(coordinates: managers(in: cluster).map { $0.position }))
|
||||
let managersInCluster = managers(in: cluster)
|
||||
|
||||
return tapHandler(managersInCluster, managersInCluster.map { $0.position })
|
||||
}
|
||||
|
||||
open func managers(in cluster: YMKCluster) -> [YandexPlacemarkManager<Model>] {
|
||||
cluster.placemarks.compactMap { placemark in
|
||||
placemarksMapping?.first { $0.0 == placemark }?.1
|
||||
cluster.placemarks.flatMap {
|
||||
placemarksMapping?[CLLocationCoordinate2D(ymkPoint: $0.geometry)] ?? []
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -89,21 +102,3 @@ open class YandexClusterPlacemarkManager<Model>: BasePlacemarkManager<YMKCluster
|
|||
placemark.appearance.setIconWith(iconProvider(managers(in: placemark)))
|
||||
}
|
||||
}
|
||||
|
||||
private extension YMKBoundingBox {
|
||||
static func from(coordinates: [YMKPoint]) -> YMKBoundingBox {
|
||||
guard let first = coordinates.first else {
|
||||
return YMKBoundingBox()
|
||||
}
|
||||
|
||||
let initialBox = YMKBoundingBox(southWest: first, northEast: first)
|
||||
|
||||
return coordinates.dropFirst().reduce(initialBox) {
|
||||
YMKBoundingBox(southWest: YMKPoint(latitude: min($0.southWest.latitude, $1.latitude),
|
||||
longitude: min($0.southWest.longitude, $1.longitude)),
|
||||
northEast: YMKPoint(latitude: max($0.northEast.latitude, $1.latitude),
|
||||
longitude: max($0.northEast.longitude, $1.longitude)))
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIYandexMapUtils'
|
||||
s.version = '1.16.2'
|
||||
s.version = '1.17.0'
|
||||
s.summary = 'Set of helpers for map objects clustering and interacting using Yandex Maps SDK.'
|
||||
s.homepage = 'https://github.com/TouchInstinct/LeadKit/tree/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
Loading…
Reference in New Issue