feat: current location marker and other ui settings to supported maps
This commit is contained in:
parent
5a74c342d9
commit
a5bc2dc8f0
|
|
@ -103,8 +103,16 @@ let package = Package(
|
|||
.target(name: "TINetworkingCache", dependencies: ["TIFoundationUtils", "TINetworking", "Cache"], path: "TINetworkingCache/Sources"),
|
||||
|
||||
// MARK: - Maps
|
||||
.target(name: "TIMapUtils", dependencies: [], path: "TIMapUtils/Sources"),
|
||||
.target(name: "TIAppleMapUtils", dependencies: ["TIMapUtils"], path: "TIAppleMapUtils/Sources"),
|
||||
|
||||
.target(name: "TIMapUtils",
|
||||
dependencies: [],
|
||||
path: "TIMapUtils/Sources",
|
||||
plugins: [.plugin(name: "TISwiftLintPlugin")]),
|
||||
|
||||
.target(name: "TIAppleMapUtils",
|
||||
dependencies: ["TIMapUtils"],
|
||||
path: "TIAppleMapUtils/Sources",
|
||||
plugins: [.plugin(name: "TISwiftLintPlugin")]),
|
||||
|
||||
// MARK: - Elements
|
||||
.target(name: "OTPSwiftView", dependencies: ["TIUIElements"], path: "OTPSwiftView/Sources"),
|
||||
|
|
|
|||
|
|
@ -24,9 +24,10 @@ import TIMapUtils
|
|||
import MapKit
|
||||
|
||||
open class AppleMapManager<DataModel>: BaseMapManager<MKMapView,
|
||||
ApplePlacemarkManager<DataModel>,
|
||||
AppleClusterPlacemarkManager<DataModel>,
|
||||
MKCameraUpdate> {
|
||||
ApplePlacemarkManager<DataModel>,
|
||||
AppleClusterPlacemarkManager<DataModel>,
|
||||
MKCameraUpdate,
|
||||
AppleMapUISettings> {
|
||||
|
||||
public typealias ClusteringIdentifier = String
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
//
|
||||
// Copyright (c) 2023 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 TIMapUtils
|
||||
import MapKit
|
||||
|
||||
open class AppleMapUISettings: BaseMapUISettings<MKMapView> {
|
||||
public var showCompassButton = false
|
||||
|
||||
override open func apply(to mapView: MKMapView) {
|
||||
super.apply(to: mapView)
|
||||
|
||||
mapView.showsUserLocation = showUserLocation
|
||||
mapView.isZoomEnabled = isZoomEnabled
|
||||
mapView.isPitchEnabled = isTiltEnabled
|
||||
mapView.isRotateEnabled = isRotationEnabled
|
||||
mapView.showsCompass = showCompassButton
|
||||
}
|
||||
}
|
||||
|
|
@ -24,9 +24,10 @@ import TIMapUtils
|
|||
import GoogleMaps
|
||||
|
||||
open class GoogleMapManager<DataModel>: BaseMapManager<GMSMapView,
|
||||
GooglePlacemarkManager<DataModel>,
|
||||
GoogleClusterPlacemarkManager<DataModel>,
|
||||
GMSCameraUpdate> {
|
||||
GooglePlacemarkManager<DataModel>,
|
||||
GoogleClusterPlacemarkManager<DataModel>,
|
||||
GMSCameraUpdate,
|
||||
GoogleMapUISettings> {
|
||||
|
||||
public init<IF: MarkerIconFactory, CIF: MarkerIconFactory>(map: GMSMapView,
|
||||
positionGetter: @escaping PositionGetter,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// Copyright (c) 2023 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 TIMapUtils
|
||||
import GoogleMaps
|
||||
|
||||
open class GoogleMapUISettings: BaseMapUISettings<GMSMapView> {
|
||||
public var showMyLocationButton = true
|
||||
|
||||
open override func apply(to mapView: GMSMapView) {
|
||||
super.apply(to: mapView)
|
||||
|
||||
mapView.isMyLocationEnabled = showUserLocation
|
||||
|
||||
mapView.settings.zoomGestures = isZoomEnabled
|
||||
mapView.settings.tiltGestures = isTiltEnabled
|
||||
mapView.settings.rotateGestures = isRotationEnabled
|
||||
mapView.settings.myLocationButton = showMyLocationButton
|
||||
}
|
||||
}
|
||||
|
|
@ -23,19 +23,19 @@
|
|||
import UIKit
|
||||
|
||||
public struct BorderDrawingOperation: DrawingOperation {
|
||||
public var frameableContentSize: CGSize
|
||||
public var frameableContentRect: CGRect
|
||||
public var border: CGFloat
|
||||
public var color: CGColor
|
||||
public var radius: CGFloat
|
||||
public var exteriorBorder: Bool
|
||||
|
||||
public init(frameableContentSize: CGSize,
|
||||
public init(frameableContentRect: CGRect,
|
||||
border: CGFloat,
|
||||
color: CGColor,
|
||||
radius: CGFloat,
|
||||
exteriorBorder: Bool) {
|
||||
|
||||
self.frameableContentSize = frameableContentSize
|
||||
self.frameableContentRect = frameableContentRect
|
||||
self.border = border
|
||||
self.color = color
|
||||
self.radius = radius
|
||||
|
|
@ -47,10 +47,10 @@ public struct BorderDrawingOperation: DrawingOperation {
|
|||
public func affectedArea(in context: CGContext?) -> CGRect {
|
||||
let margin = exteriorBorder ? border : 0
|
||||
|
||||
let width = frameableContentSize.width + margin * 2
|
||||
let height = frameableContentSize.height + margin * 2
|
||||
let width = frameableContentRect.width + margin * 2
|
||||
let height = frameableContentRect.height + margin * 2
|
||||
|
||||
return CGRect(origin: .zero,
|
||||
return CGRect(origin: frameableContentRect.origin,
|
||||
size: CGSize(width: width, height: height))
|
||||
}
|
||||
|
||||
|
|
@ -59,7 +59,8 @@ public struct BorderDrawingOperation: DrawingOperation {
|
|||
|
||||
let ctxSize = drawArea.size.ceiledContextSize
|
||||
|
||||
let ctxRect = CGRect(origin: .zero, size: CGSize(width: ctxSize.width, height: ctxSize.height))
|
||||
let ctxRect = CGRect(origin: frameableContentRect.origin,
|
||||
size: CGSize(width: ctxSize.width, height: ctxSize.height))
|
||||
|
||||
let widthDiff = CGFloat(ctxSize.width) - drawArea.width // difference between context width and real width
|
||||
let heightDiff = CGFloat(ctxSize.height) - drawArea.height // difference between context height and real height
|
||||
|
|
|
|||
|
|
@ -0,0 +1,163 @@
|
|||
//
|
||||
// Copyright (c) 2023 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 CoreGraphics
|
||||
|
||||
public struct CurrentLocationDrawingOperation: DrawingOperation {
|
||||
public enum Defaults {
|
||||
public static var iconSize: CGSize {
|
||||
CGSize(width: 27, height: 27)
|
||||
}
|
||||
|
||||
public static var borderWidth: CGFloat {
|
||||
2
|
||||
}
|
||||
}
|
||||
|
||||
public var iconSize: CGSize
|
||||
public var borderWidth: CGFloat
|
||||
public var mainColor: CGColor
|
||||
public var borderColor: CGColor
|
||||
public var backgroundColor: CGColor
|
||||
public var showHeadingArrow: Bool
|
||||
|
||||
private var innerCircleSize: CGSize {
|
||||
CGSize(width: innerBorderSize.width - borderWidth,
|
||||
height: innerBorderSize.height - borderWidth)
|
||||
}
|
||||
|
||||
private var innerBorderSize: CGSize {
|
||||
CGSize(width: outerBorderSize.width - borderWidth,
|
||||
height: outerBorderSize.height - borderWidth)
|
||||
}
|
||||
|
||||
private var outerBorderSize: CGSize {
|
||||
CGSize(width: iconSize.width - 8,
|
||||
height: iconSize.height - 8)
|
||||
}
|
||||
|
||||
private var innerCircleOrigin: CGPoint {
|
||||
CGPoint(x: (iconSize.width - innerCircleSize.width) / 2,
|
||||
y: (iconSize.height - innerCircleSize.height) / 2)
|
||||
}
|
||||
|
||||
private var innerBorderOrigin: CGPoint {
|
||||
CGPoint(x: (iconSize.width - innerBorderSize.width) / 2,
|
||||
y: (iconSize.height - innerBorderSize.height) / 2)
|
||||
}
|
||||
|
||||
private var outerBorderOrigin: CGPoint {
|
||||
CGPoint(x: (iconSize.width - outerBorderSize.width) / 2,
|
||||
y: (iconSize.height - outerBorderSize.height) / 2)
|
||||
}
|
||||
|
||||
private var iconRect: CGRect {
|
||||
CGRect(origin: .zero,
|
||||
size: iconSize)
|
||||
}
|
||||
|
||||
private var innerCircleRect: CGRect {
|
||||
CGRect(origin: innerCircleOrigin,
|
||||
size: innerCircleSize)
|
||||
}
|
||||
|
||||
private var innerBorderRect: CGRect {
|
||||
CGRect(origin: innerBorderOrigin,
|
||||
size: innerBorderSize)
|
||||
}
|
||||
|
||||
private var outerBorderRect: CGRect {
|
||||
CGRect(origin: outerBorderOrigin,
|
||||
size: outerBorderSize)
|
||||
}
|
||||
|
||||
public init(iconSize: CGSize = Defaults.iconSize,
|
||||
borderWidth: CGFloat = Defaults.borderWidth,
|
||||
mainColor: CGColor,
|
||||
borderColor: CGColor,
|
||||
showHeadingArrow: Bool = true) {
|
||||
|
||||
self.iconSize = iconSize
|
||||
self.borderWidth = borderWidth
|
||||
self.mainColor = mainColor
|
||||
self.borderColor = borderColor
|
||||
self.backgroundColor = mainColor.copy(alpha: 0.3) ?? mainColor
|
||||
self.showHeadingArrow = showHeadingArrow
|
||||
}
|
||||
|
||||
public func affectedArea(in context: CGContext? = nil) -> CGRect {
|
||||
iconRect
|
||||
}
|
||||
|
||||
public func apply(in context: CGContext) {
|
||||
let backgroundDrawing = SolidFillDrawingOperation(color: backgroundColor,
|
||||
ellipseRect: iconRect)
|
||||
|
||||
let innerCircleDrawing = SolidFillDrawingOperation(color: mainColor,
|
||||
ellipseRect: innerCircleRect)
|
||||
|
||||
let innerBorderDrawing = BorderDrawingOperation(frameableContentRect: innerBorderRect,
|
||||
border: borderWidth,
|
||||
color: borderColor,
|
||||
radius: min(innerBorderSize.width, innerBorderSize.height) / 2,
|
||||
exteriorBorder: false)
|
||||
|
||||
let outerBorderDrawing = BorderDrawingOperation(frameableContentRect: outerBorderRect,
|
||||
border: borderWidth,
|
||||
color: backgroundColor,
|
||||
radius: min(outerBorderSize.width, outerBorderSize.height) / 2,
|
||||
exteriorBorder: false)
|
||||
|
||||
// flip horizontally
|
||||
|
||||
context.translateBy(x: 0, y: iconSize.height)
|
||||
context.scaleBy(x: 1, y: -1)
|
||||
|
||||
backgroundDrawing.apply(in: context)
|
||||
|
||||
context.setFillColor(mainColor)
|
||||
context.setLineWidth(.zero)
|
||||
|
||||
guard showHeadingArrow else {
|
||||
innerCircleDrawing.apply(in: context)
|
||||
|
||||
innerBorderDrawing.apply(in: context)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
context.addLines(between: [
|
||||
CGPoint(x: innerBorderRect.minX, y: innerBorderRect.midY),
|
||||
CGPoint(x: iconRect.midX, y: iconRect.maxY),
|
||||
CGPoint(x: innerBorderRect.maxX, y: innerBorderRect.midY)
|
||||
])
|
||||
|
||||
context.drawPath(using: .fillStroke)
|
||||
|
||||
innerCircleDrawing.apply(in: context)
|
||||
|
||||
innerBorderDrawing.apply(in: context)
|
||||
|
||||
context.setBlendMode(.destinationAtop)
|
||||
outerBorderDrawing.apply(in: context)
|
||||
}
|
||||
}
|
||||
|
|
@ -113,7 +113,7 @@ open class DefaultClusterIconRenderer {
|
|||
open func borderDrawingOperation(iconSize: CGSize,
|
||||
cornerRadius: CGFloat) -> DrawingOperation {
|
||||
|
||||
BorderDrawingOperation(frameableContentSize: iconSize,
|
||||
BorderDrawingOperation(frameableContentRect: CGRect(origin: .zero, size: iconSize),
|
||||
border: borderWidth,
|
||||
color: border.color.cgColor,
|
||||
radius: cornerRadius,
|
||||
|
|
|
|||
|
|
@ -26,10 +26,12 @@ import UIKit.UIGeometry
|
|||
open class BaseMapManager<Map: AnyObject,
|
||||
PM: PlacemarkManager,
|
||||
CPM: PlacemarkManager,
|
||||
CUF: CameraUpdateFactory> where PM.Position: LocationCoordinate,
|
||||
PM.Position == CUF.Position,
|
||||
CUF.Update.Map == Map,
|
||||
CUF.BoundingBox == CPM.Position {
|
||||
CUF: CameraUpdateFactory,
|
||||
MS: MapUISettings> where PM.Position: LocationCoordinate,
|
||||
PM.Position == CUF.Position,
|
||||
CUF.Update.Map == Map,
|
||||
CUF.BoundingBox == CPM.Position,
|
||||
MS.MapView == Map {
|
||||
|
||||
public typealias PositionGetter = (PM.DataModel) -> PM.Position?
|
||||
|
||||
|
|
@ -55,6 +57,8 @@ open class BaseMapManager<Map: AnyObject,
|
|||
|
||||
public var cameraUpdateOnMarkersAdded: CUF.Update?
|
||||
|
||||
public var settings: MS?
|
||||
|
||||
public var cameraUpdateOnMarkerTap: CameraUpdateOnPointTap? = {
|
||||
CUF.update(for: .focus(target: $0, zoom: CommonZoomLevels.building.floatValue))
|
||||
}
|
||||
|
|
@ -109,4 +113,10 @@ open class BaseMapManager<Map: AnyObject,
|
|||
open func remove(clusterPlacemarkManager: CPM) {
|
||||
// override in subclass
|
||||
}
|
||||
|
||||
open func apply(settings: MS) {
|
||||
self.settings = settings
|
||||
|
||||
settings.apply(to: map)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
//
|
||||
// Copyright (c) 2023 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.
|
||||
//
|
||||
|
||||
open class BaseMapUISettings<MapView>: MapUISettings {
|
||||
public var showUserLocation = true
|
||||
|
||||
public var isZoomEnabled = true
|
||||
public var isTiltEnabled = false
|
||||
public var isRotationEnabled = false
|
||||
|
||||
public init() {
|
||||
}
|
||||
|
||||
open func apply(to mapView: MapView) {
|
||||
// override in subclass
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// Copyright (c) 2023 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 MapUISettings: AnyObject {
|
||||
associatedtype MapView
|
||||
|
||||
var isZoomEnabled: Bool { get set }
|
||||
var isTiltEnabled: Bool { get set }
|
||||
var isRotationEnabled: Bool { get set }
|
||||
|
||||
var showUserLocation: Bool { get set }
|
||||
|
||||
func apply(to mapView: MapView)
|
||||
}
|
||||
|
|
@ -24,9 +24,10 @@ import TIMapUtils
|
|||
import YandexMapsMobile
|
||||
|
||||
open class YandexMapManager<DataModel>: BaseMapManager<YMKMapView,
|
||||
YandexPlacemarkManager<DataModel>,
|
||||
YandexClusterPlacemarkManager<DataModel>,
|
||||
YMKCameraUpdate> {
|
||||
YandexPlacemarkManager<DataModel>,
|
||||
YandexClusterPlacemarkManager<DataModel>,
|
||||
YMKCameraUpdate,
|
||||
YandexMapUISettings> {
|
||||
|
||||
public init<IF: MarkerIconFactory, CIF: MarkerIconFactory>(map: YMKMapView,
|
||||
positionGetter: @escaping PositionGetter,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,135 @@
|
|||
//
|
||||
// Copyright (c) 2023 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 TIMapUtils
|
||||
import YandexMapsMobile
|
||||
|
||||
open class YandexMapUISettings: BaseMapUISettings<YMKMapView> {
|
||||
open class UserLocationObjectListener: NSObject, YMKUserLocationObjectListener {
|
||||
public var tintColor: UIColor {
|
||||
didSet {
|
||||
userLocationView?.accuracyCircle.fillColor = tintColor.withAlphaComponent(0.2)
|
||||
}
|
||||
}
|
||||
|
||||
public var currentLocationIcon: UIImage {
|
||||
didSet {
|
||||
updateCurrentLocationIcon()
|
||||
}
|
||||
}
|
||||
|
||||
private weak var userLocationView: YMKUserLocationView?
|
||||
|
||||
public init(tintColor: UIColor, currentLocationIcon: UIImage) {
|
||||
self.tintColor = tintColor
|
||||
self.currentLocationIcon = currentLocationIcon
|
||||
}
|
||||
|
||||
// MARK: - YMKUserLocationObjectListener
|
||||
|
||||
open func onObjectAdded(with view: YMKUserLocationView) {
|
||||
self.userLocationView = view
|
||||
|
||||
updateCurrentLocationIcon()
|
||||
|
||||
view.accuracyCircle.fillColor = tintColor.withAlphaComponent(0.2)
|
||||
}
|
||||
|
||||
open func onObjectRemoved(with view: YMKUserLocationView) {}
|
||||
|
||||
open func onObjectUpdated(with view: YMKUserLocationView, event: YMKObjectEvent) {}
|
||||
|
||||
private func updateCurrentLocationIcon() {
|
||||
userLocationView?.arrow.setIconWith(currentLocationIcon)
|
||||
|
||||
let style = YMKIconStyle(anchor: CGPoint(x: 0.5, y: 0.5) as NSValue,
|
||||
rotationType:YMKRotationType.rotate.rawValue as NSNumber,
|
||||
zIndex: 0,
|
||||
flat: true,
|
||||
visible: true,
|
||||
scale: 1,
|
||||
tappableArea: nil)
|
||||
|
||||
userLocationView?.pin.setIconWith(currentLocationIcon, style: style)
|
||||
}
|
||||
}
|
||||
|
||||
public weak var userLocationLayer: YMKUserLocationLayer?
|
||||
public weak var mapKit: YMKMapKit?
|
||||
public var userLocationObjectListener: UserLocationObjectListener?
|
||||
|
||||
public var tintColor: UIColor = .red {
|
||||
didSet {
|
||||
userLocationObjectListener?.tintColor = tintColor
|
||||
}
|
||||
}
|
||||
|
||||
public var showHeadingArrow: Bool = true {
|
||||
didSet {
|
||||
userLocationObjectListener?.currentLocationIcon = currentLocationIcon(showHeadingArrow: showHeadingArrow)
|
||||
}
|
||||
}
|
||||
|
||||
public init(mapKit: YMKMapKit = .sharedInstance()) {
|
||||
self.mapKit = mapKit
|
||||
}
|
||||
|
||||
open override func apply(to mapView: YMKMapView) {
|
||||
super.apply(to: mapView)
|
||||
|
||||
if showUserLocation {
|
||||
if userLocationLayer == nil {
|
||||
userLocationLayer = mapKit?.createUserLocationLayer(with: mapView.mapWindow)
|
||||
}
|
||||
|
||||
let currentLocationIcon = currentLocationIcon(showHeadingArrow: showHeadingArrow)
|
||||
|
||||
userLocationObjectListener = UserLocationObjectListener(tintColor: tintColor,
|
||||
currentLocationIcon: currentLocationIcon)
|
||||
} else {
|
||||
userLocationObjectListener = nil
|
||||
}
|
||||
|
||||
userLocationLayer?.setVisibleWithOn(showUserLocation)
|
||||
userLocationLayer?.setObjectListenerWith(userLocationObjectListener)
|
||||
|
||||
let map = mapView.mapWindow.map
|
||||
|
||||
map.isZoomGesturesEnabled = isZoomEnabled
|
||||
map.isTiltGesturesEnabled = isTiltEnabled
|
||||
map.isRotateGesturesEnabled = isRotationEnabled
|
||||
}
|
||||
|
||||
// MARK: - Subclass override
|
||||
|
||||
open func currentLocationIcon(showHeadingArrow: Bool = true) -> UIImage {
|
||||
let locationDrawingOperation = CurrentLocationDrawingOperation(mainColor: tintColor.cgColor,
|
||||
borderColor: UIColor.white.cgColor,
|
||||
showHeadingArrow: showHeadingArrow)
|
||||
|
||||
let renderer = UIGraphicsImageRenderer(bounds: locationDrawingOperation.affectedArea())
|
||||
|
||||
return renderer.image {
|
||||
locationDrawingOperation.apply(in: $0.cgContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue