Created
August 18, 2021 23:26
-
-
Save cedricbahirwe/0ae256aa1d9f15b18559447bf440cda1 to your computer and use it in GitHub Desktop.
A DraggableAnnotationView in MapBox
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class DraggableAnnotationView: MGLAnnotationView { | |
var didSetNewCoord: (CLLocationCoordinate2D) -> Void = { _ in } | |
init(reuseIdentifier: String, size: CGFloat, didSetCoord: @escaping(CLLocationCoordinate2D) -> Void) { | |
self.didSetNewCoord = didSetCoord | |
super.init(reuseIdentifier: reuseIdentifier) | |
// `isDraggable` is a property of MGLAnnotationView, disabled by default. | |
isDraggable = true | |
// This property prevents the annotation from changing size when the map is tilted. | |
scalesWithViewingDistance = false | |
// Begin setting up the view. | |
frame = CGRect(x: 0, y: 0, width: size, height: size) | |
// If UIKit Preference | |
configurePin(size: size) | |
// If SwiftUI Preference | |
let controller = UIHostingController(rootView: LocationPinView()) | |
controller.view.frame = frame | |
controller.view.backgroundColor = .clear | |
addSubview(controller.view) | |
layer.opacity = 0.8 | |
} | |
private func configurePin(size: CGFloat) { | |
backgroundColor = UIColor(custom: .murugoBlue).withAlphaComponent(0.35) | |
// Use CALayer’s corner radius to turn this view into a circle. | |
layer.cornerRadius = size / 2 | |
layer.borderWidth = 50 | |
layer.borderColor = UIColor(custom: .murugoBlue).withAlphaComponent(0.15).cgColor | |
layer.shadowColor = UIColor.black.cgColor | |
layer.shadowOpacity = 0.1 | |
let imgView = UIImageView(image: UIImage(named: "pinpoint")!) | |
imgView.frame.size = CGSize(width: 40, height: 60) | |
imgView.center = center | |
imgView.center.y = imgView.center.y - 15 | |
imgView.contentMode = .scaleAspectFit | |
addSubview(imgView) | |
} | |
// These two initializers are forced upon us by Swift. | |
override init(frame: CGRect) { | |
super.init(frame: frame) | |
} | |
required init?(coder aDecoder: NSCoder) { | |
fatalError("init(coder:) has not been implemented") | |
} | |
// Custom handler for changes in the annotation’s drag state. | |
override func setDragState(_ dragState: MGLAnnotationViewDragState, animated: Bool) { | |
super.setDragState(dragState, animated: animated) | |
switch dragState { | |
case .starting: | |
print("Starting", terminator: "") | |
startDragging() | |
case .dragging: | |
print(".", terminator: "") | |
case .ending, .canceling: | |
print("Ending") | |
endDragging() | |
case .none: | |
break | |
@unknown default: | |
fatalError("Unknown drag state") | |
} | |
} | |
// When the user interacts with an annotation, animate opacity and scale changes. | |
func startDragging() { | |
UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0, options: [], animations: { | |
self.layer.opacity = 0.65 | |
self.transform = CGAffineTransform.identity.scaledBy(x: 1.2, y: 1.2) | |
}, completion: nil) | |
// Initialize haptic feedback generator and give the user a light thud. | |
let hapticFeedback = UIImpactFeedbackGenerator(style: .light) | |
hapticFeedback.impactOccurred() | |
} | |
func endDragging() { | |
transform = CGAffineTransform.identity.scaledBy(x: 1.5, y: 1.5) | |
UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0, options: [], animations: { | |
self.layer.opacity = 0.8 | |
self.transform = CGAffineTransform.identity.scaledBy(x: 1, y: 1) | |
}, completion: {_ in | |
guard let annot = self.annotation else { | |
return | |
} | |
self.didSetNewCoord(annot.coordinate) | |
}) | |
// Give the user more haptic feedback when they drop the annotation. | |
let hapticFeedback = UIImpactFeedbackGenerator(style: .light) | |
hapticFeedback.impactOccurred() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment