How to do animation in custom annotation pin when update annotation view - ios

I want to do animation like "when scroll collection view cell the annotation pin goes Up/Down at the end of scrolling. But how to do animation like annotation pin goes Up when start scrolling and annotation pin goes Down when scrolling end in collection view
//code --> For Scrolling
func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
if scrollView == collectionView {
NSLog("page collection %d",Int(scrollView.contentOffset.x/scrollView.frame.size.width))
self.UpdateMapAnotationPin(Int(scrollView.contentOffset.x/scrollView.frame.size.width))
}
}
// -->When Update Pin
func UpdateMapAnotationPin(vIndex : Int) {
if self.mapAnnotations.count != 0 {
let info = self.mapAnnotations[vIndex]
let aView = map.viewForAnnotation(info)
info.imageName = "ic_map_pin1"
info.tagPin = vIndex
aView?.image = UIImage(named: info.imageName)
if aView != nil {
self.animationWithView(aView!)
}
}
}
// --> For animation
func animationWithView(mkView : MKAnnotationView) {
let point:MKMapPoint = MKMapPointForCoordinate(mkView.annotation!.coordinate);
let endFrame:CGRect = mkView.frame;
mkView.frame = CGRectMake(mkView.frame.origin.x, mkView.frame.origin.y - 20, mkView.frame.size.width, mkView.frame.size.height);
let delay = 0.03
UIView.animateWithDuration(0.5, delay: delay, options: UIViewAnimationOptions.CurveLinear, animations:{() in
mkView.frame = endFrame
}, completion:{(Bool) in
UIView.animateWithDuration(0.05, delay: 0.0, options: UIViewAnimationOptions.CurveLinear, animations:{() in
mkView.transform = CGAffineTransformMakeScale(1.0, 1.0) }, completion: {(Bool) in
UIView.animateWithDuration(0.3, delay: 0.0, options: UIViewAnimationOptions.CurveLinear, animations:{() in
mkView.transform = CGAffineTransformIdentity
}, completion: nil)
})
})
}

I think you want to animated like AirBnb app annotations.
You have to select the pin by calling it's viewforAnimation method
Steps
1.Make annotation using by assigning the custom id like this
class GridAnnotation: NSObject ,MKAnnotation{
var title: String?
var coordinate: CLLocationCoordinate2D
var info: String
var index: String
init(title: String, coordinate: CLLocationCoordinate2D, info: String,index: String) {
self.title = title
self.coordinate = coordinate
self.info = info
self.index = index
}
}
override func viewDidLoad() {
super.viewDidLoad()
let annotationStart = GridAnnotationStart(title: "", coordinate: firstLocation.coordinate, info: "\(zoneCreateModal.id)",index: "\(0)")
self.mapVw.addAnnotation(annotationSta
rt)
}
2.Get all annotations
let arrAllAnnotations = self.mapVw.annotations.filter { $0 !== self.mapVw.userLocation }
for someAnnotation in annotationsToRemove {
let strId = "Your current id"
if someAnnotation.isKind(of: AnnotationModal.self) {
let annotaion = someAnnotation as! AnnotationModal
if annotaion.info == strId {
//Call view for animation
self.mapVw.selectAnnotation(someAnnotation, animated: true)
self.mapVw.view(for: someAnnotation)
}
}
}
5.Set type of new selected annoatation in viewForAnnotation
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if (annotation.isKind(of: MKUserLocation.self)) {
return nil
}
if annotation.isKind(of: GridAnnotationZoomModal.self) {
let anView = MKAnnotationView(annotation: annotation, reuseIdentifier: "landingPoints")
let annotationInfo = annotation as! GridAnnotationZoomModal
imgView.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
if anView.isSelected == true {
imgView.image = UIImage(named: "Your selected image name")
}else{
imgView.image = UIImage(named: "Your image not selected name")
}
anView.isDraggable = false
anView.isEnabled = true
anView.isUserInteractionEnabled = true
anView.tag = Int(annotationInfo.index)!
anView.addSubview(imgView)
anView.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
anView.centerOffset = CGPoint(x: 0,y: -15)
return anView
}
return nil
}

Related

User select one annotation on MapKit

I do a app with MapKit and I want the user can select an annotation (clic on) and then open a new view controller.
I do it with the method :
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
performSegue(withIdentifier: "showClic", sender: nil)
}
But this code open the VC when there a cluster of annotation.
And I want to do when there is a cluster of annotation a zoom with the camera and open the VC only when only one annotation is selected.
func fitMapViewToAnnotaionList(annotations: [MKPointAnnotation], userLocation: CLLocationCoordinate2D) -> Void {
let mapEdgePadding = UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20)
var zoomRect:MKMapRect = MKMapRectNull
for index in 0..<annotations.count {
let annotation = annotations[index]
let aPoint:MKMapPoint = MKMapPointForCoordinate(annotation.coordinate)
let rect:MKMapRect = MKMapRectMake(aPoint.x, aPoint.y, 0.1, 0.1)
if MKMapRectIsNull(zoomRect) {
zoomRect = rect
} else {
zoomRect = MKMapRectUnion(zoomRect, rect)
}
}
let aPoint:MKMapPoint = MKMapPointForCoordinate(userLocation)
let rect:MKMapRect = MKMapRectMake(aPoint.x, aPoint.y, 0.1, 0.1)
if MKMapRectIsNull(zoomRect) {
zoomRect = rect
} else {
zoomRect = MKMapRectUnion(zoomRect, rect)
}
mapView.setVisibleMapRect(zoomRect, edgePadding: mapEdgePadding, animated: true)
}

iOS Mapbox Updating the map while dragging annotation reverts the annotation to original coordinates

I'm trying to update some map components while dragging an annotations like highlighting a specific MGLPolygon and panning the map if the annotation is already dragged near the edge. I will use the later for this problem.
I tried the code https://docs.mapbox.com/ios/maps/examples/draggable-views/ and added some lines. Here's the exact copy with my changes.
import Mapbox
// Example view controller
class ViewController: UIViewController, MGLMapViewDelegate {
var mapView: MGLMapView!
override func viewDidLoad() {
super.viewDidLoad()
mapView = MGLMapView(frame: view.bounds)
mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
mapView.styleURL = MGLStyle.streetsStyleURL
mapView.tintColor = .darkGray
mapView.zoomLevel = 1
mapView.delegate = self
view.addSubview(mapView)
// Specify coordinates for our annotations.
let coordinates = [
CLLocationCoordinate2D(latitude: 0, longitude: -70),
CLLocationCoordinate2D(latitude: 0, longitude: -35),
CLLocationCoordinate2D(latitude: 0, longitude: 0),
CLLocationCoordinate2D(latitude: 0, longitude: 35),
CLLocationCoordinate2D(latitude: 0, longitude: 70)
]
// Fill an array with point annotations and add it to the map.
var pointAnnotations = [MGLPointAnnotation]()
for coordinate in coordinates {
let point = MGLPointAnnotation()
point.coordinate = coordinate
point.title = "To drag this annotation, first tap and hold."
pointAnnotations.append(point)
}
mapView.addAnnotations(pointAnnotations)
}
// MARK: - MGLMapViewDelegate methods
// This delegate method is where you tell the map to load a view for a specific annotation. To load a static MGLAnnotationImage, you would use `-mapView:imageForAnnotation:`.
func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -> MGLAnnotationView? {
// This example is only concerned with point annotations.
guard annotation is MGLPointAnnotation else {
return nil
}
// For better performance, always try to reuse existing annotations. To use multiple different annotation views, change the reuse identifier for each.
if let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "draggablePoint") {
return annotationView
} else {
let dav = DraggableAnnotationView(reuseIdentifier: "draggablePoint", size: 50)
dav.mapView = mapView
return dav
}
}
func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -> Bool {
return true
}
}
// MGLAnnotationView subclass
class DraggableAnnotationView: MGLAnnotationView {
var mapView: MGLMapView!
var screen: CGRect!
var mapBounds: CGRect!
init(reuseIdentifier: String, size: CGFloat) {
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)
backgroundColor = .darkGray
// Use CALayer’s corner radius to turn this view into a circle.
layer.cornerRadius = size / 2
layer.borderWidth = 1
layer.borderColor = UIColor.white.cgColor
layer.shadowColor = UIColor.black.cgColor
layer.shadowOpacity = 0.1
screen = UIScreen.main.bounds
mapBounds = CGRect(
x: screen.origin.x + 20,
y: screen.origin.y + 20,
width: screen.size.width - 40,
height: screen.size.height - 40)
}
// 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:
let pointCoordinate = self.mapView.convert(center, toCoordinateFrom: nil)
if mapBounds.contains(center) {
DispatchQueue.main.async {
self.mapView.setCenter(pointCoordinate, animated: true)
}
}
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.8
self.transform = CGAffineTransform.identity.scaledBy(x: 1.5, y: 1.5)
}, completion: nil)
// Initialize haptic feedback generator and give the user a light thud.
if #available(iOS 10.0, *) {
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 = 1
self.transform = CGAffineTransform.identity.scaledBy(x: 1, y: 1)
}, completion: nil)
// Give the user more haptic feedback when they drop the annotation.
if #available(iOS 10.0, *) {
let hapticFeedback = UIImpactFeedbackGenerator(style: .light)
hapticFeedback.impactOccurred()
}
}
}
Everytime the self.mapView.setCenter(pointCoordinate, animated: true) gets called, the annotations goes back and forth to its original position.
Here is the code explaining the Adonis's solution. Essentially add a pan gesture to a custom annotation's view and update the coords as and when the annotation is panned.
class CustomDraggableAnnotaionView: MGLAnnotationView {
required init(
reuseIdentifier: String?,
image: UIImage?,
annotation: CustomMapGLAnnotaion
) {
super.init(reuseIdentifier: reuseIdentifier)
setupDraggableAnnotations()
self.layer.zPosition = 10
}
required init?(coder _: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Draggable annotation handlers
private func setupDraggableAnnotations() {
addDraggableAnnotationGestureRecognizers()
}
private func addDraggableAnnotationGestureRecognizers() {
let panGesture = UIPanGestureRecognizer(
target: self,
action: #selector(self.draggedView(_:))
)
let tapGesture = UITapGestureRecognizer(
target: self,
action: #selector(self.tappedAnnotation(_:))
)
self.isUserInteractionEnabled = true
self.addGestureRecognizer(panGesture)
self.addGestureRecognizer(tapGesture)
for recognizer in self.gestureRecognizers! where recognizer is UITapGestureRecognizer {
tapGesture.require(toFail: recognizer)
}
for recognizer in self.gestureRecognizers! where recognizer is UIPanGestureRecognizer {
panGesture.require(toFail: recognizer)
}
}
#objc func draggedView(_ sender: UIPanGestureRecognizer) {
annotationObject?.draggable!.isCurrentlyDragging = true
let point = sender.location(in: MapManager.shared.mapView)
let coordinates = MapManager.shared.mapView.convert(
point,
toCoordinateFrom: MapManager.shared.mapView
)
annotationObject?.coordinate = coordinates
if sender.state == .ended {
// endDragging()
} else if sender.state == .began {
// startDragging()
annotationObject?.draggable!.handler.didStartDragging()
} else {
//
}
}
}

GMSMarker infoWindow not updating

I'm using Google map and it's marker. For displaying information on the marker I'm using custom View to display it. But values are not updating once I init it.Below is my code for that.
func mapView(_ mapView: GMSMapView!, markerInfoWindow marker: GMSMarker) -> UIView? {
let location = CLLocation(latitude: marker.position.latitude, longitude: marker.position.longitude
var markerView : MarkerInfoView = Bundle.main.loadNibNamed("MarkerInfoView", owner: self, options: nil)![0] as! MarkerInfoView
let geoCoder = CLGeocoder()
geoCoder.reverseGeocodeLocation(location) { (placemarkers, error) in
if let placemarker = placemarkers?.last {
var strAddress = ""
if let str = placemarker.name {
strAddress += str
}
if let str = placemarker.subAdministrativeArea {
strAddress += ", " + str
}
print(strAddress)
markerView.deviceInfo.text = "HELLO TESTING"
print(markerView.deviceInfo.text!) // This is printing "HELLO TESTING", but not updating on marker
markerView.addressInfo.text = strAddress
}
}
if let str = marker.snippet {
markerView.deviceInfo.text = str.components(separatedBy: "|")[0]
//TODO: add time
markerView.dateInfo.text = str.components(separatedBy: "|")[1]
// markerView.addressInfo.text = ""
}
else {
markerView.deviceInfo.text = ""
markerView.dateInfo.text = ""
// markerView.addressInfo.text = ""
}
return markerView
}
Please guide me how to update values in infoWindow.
Try this code
// MARKER - GoogleMaps delegate
func mapView(_ mapView: GMSMapView, markerInfoContents marker: GMSMarker) -> UIView? {
print("title \(markerTitle)")
var infoView:UIView!
infoView = UIView()
infoView.frame = CGRect(x: 0, y: 0, width: 300, height: 75)
infoView.backgroundColor = .black
let orderIDLbl = UILabel(frame: CGRect(x: 0, y: 0, width: 300, height: 75))
orderIDLbl.text = "markerTitle" //Your title here
orderIDLbl.font = UIFont.systemFont(ofSize: 15)
orderIDLbl.textAlignment = NSTextAlignment.center
orderIDLbl.numberOfLines = 0
orderIDLbl.textColor = .white
infoView.addSubview(orderIDLbl)
return infoView
}

didTapAtCoordinate not showing selected Marker - Google Maps IOS

I want to change the icon of my selected marker to a deselected one if the user taps on the map. I am using the following delegate method :
internal func mapView(_ mapView: GMSMapView, didTapAt coordinate: CLLocationCoordinate2D) {
print("\(map.selectedMarker) ===> Selected Marker")
}
however map.selectedMarker always comes nil. Even when the documentation says This is called before deselecting any currently selected marker (the implicit action for tapping on the map)
so I was expecting to be able to see my selected marker.
This is the code I am running when a marker gets tapped
internal func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
if map.selectedMarker == marker {
return true
}
if let selectedMarker = map.selectedMarker {
deselectActiveMarker(currentSelectedMarker: selectedMarker)
}
selectMarker(marker: marker)
return true
}
the selectMarker(marker: marker) method is the following one:
private func selectMarker(marker: GMSMarker) {
marker.iconView?.setAnchorPoint(anchorPoint: CGPoint(x: 0.5, y: 0))
if let cluster = marker.userData as? GMUCluster {
let count = "\(cluster.count)" as NSString
if let icon = UIImage(named: "clusterSelected") {
marker.iconView = UIImageView(image: createIconWithCount(drawText: count, inImage: icon, isSelected: true))
}
}
else {
if let icon = UIImage(named: "pinSelected") {
marker.iconView = UIImageView(image: icon)
}
}
marker.iconView?.transform = CGAffineTransform(scaleX: 0, y: 0)
UIView.animate(withDuration: 0.3, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 5, options: .curveEaseIn, animations: {
marker.iconView?.transform = CGAffineTransform(scaleX: 1, y: 1)
}, completion: {
[weak self = self]
_ in
guard let instance = self else { return }
instance.map.selectedMarker = marker
})
}
Hopefully you can help me. Thanks

how to animate drop of MKAnnotationView with custom image

when I try to access the animate drop property of an MKAnnotationView, it doesn't exist. I know this property exists for a MKPinAnnotationView, but I'm using a custom image and therefore have to use a MKAnnotationView. Any way to animate the drop of a MKAnnotationView? Code:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation { return nil }
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: pinIdentifier)
if annotationView == nil {
annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: pinIdentifier)
annotationView?.canShowCallout = true
let pinImage = UIImage(named:"mappin")!
annotationView?.image = pinImage
//annotationView?.animatesDrop = true
} else {
annotationView?.annotation = annotation
}
return annotationView
}
1) Create a xib file and an associated .swift file to hold your AnimatedAnnotation code.
2) in AnimatedAnnotation.xib you can put your UI creativity to task and create your annotation.
3) In your AnimatedAnnotation.swift file, create your own AnimatedAnnotation class.
class AnimatedAnnotation: MKAnnotationView {
}
4) Next, write a configuration method that will configure an instance of this "invisible" container view with an animationDuration, and annotationImage (this will be your custom image).
class AnimatedAnnotation: UIView {
var animationDuration: Double!
var annotationImage: UIImage!
func configureWith(animationDuration: Double, annotationImage: UIImage) {
self.backgroundColor = .clear
self.annotationImage = annotationImage
self.animationDuration = animationDuration
}
}
5) Then declare a property animatedView of type UIImageView and instantiate it within your configuration block.
class AnimatedAnnotation: UIView {
var animatedView: UIImageView!
var animationDuration: Double!
var annotationImage: UIImage!
func configureWith(animationDuration: Double, annotationImage: UIImage) {
self.backgroundColor = .clear
self.annotationImage = annotationImage
self.animationDuration = animationDuration
instantiateAnimatedView()
}
func instantiateAnimatedView() {
let startingPosition = CGRect(x: 0, y: 0, width: 20, height: 20) // This is whatever starting position you want
let imageView = UIImageView(frame: startingPosition)
imageView.image = UIImage(name: "YourAnnotationImage")
imageView.alpha = 0
addSubview(imageView!)
}
}
6) Write the method to make the animated view appear and animate on top of it's "invisible" superview.
class AnimatedAnnotation: UIView {
var animatedView: UIImageView!
var animationDuration: Double!
var annotationImage: UIImage!
func configureWith(animationDuration: Double, annotationImage: UIImage) {
self.backgroundColor = .clear
self.annotationImage = annotationImage
self.animationDuration = animationDuration
instantiateAnimatedView()
dropAnnotation() // Call whenever you want the drop to happen
}
func instantiateAnimatedView() {
let startingPosition = CGRect(x: 0, y: 0, width: 20, height: 20) // This is whatever starting position you want
let imageView = UIImageView(frame: startingPosition)
imageView.image = annotationImage
imageView.alpha = 0
addSubview(imageView!)
}
func dropAnnotation() {
let dropHeight:CGFloat = self.bounds.size.height - animatedView.frame.size.height
let endPosition = CGPoint(x: animatedView.center.x, y: animatedView.center.y + dropHeight)
UIView.animate(withDuration: animationDuration, animations: {
self.animatedView.alpha = 1
self.animatedView.center = endPosition
})
}
}
7) USAGE in your viewFor annotation: MKAnnotation) -> MKAnnotationView? method, instantiate an AnimatedAnnotation and return that in the method.
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let bundle = Bundle.main // or your framework bundle
let xib = UINib(nibName: "AnimatedAnnotation", bundle: bundle)
let view = xib.instantiate(withOwner: self, options: nil)[0] as! AnimatedAnnotation
view.configureWith(animationDuration: 0.25, annotationImage: UIImage(named: "Your-Annotation-Image")
return view
}
I haven't actually tried this in Xcode, and I just wrote this in the SO editor, but this should work. Let me know your progress.

Resources