Skip to content

Instantly share code, notes, and snippets.

@cedricbahirwe
Created August 18, 2021 23:26
Show Gist options
  • Save cedricbahirwe/0ae256aa1d9f15b18559447bf440cda1 to your computer and use it in GitHub Desktop.
Save cedricbahirwe/0ae256aa1d9f15b18559447bf440cda1 to your computer and use it in GitHub Desktop.
A DraggableAnnotationView in MapBox
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