Select MKMarkerAnnotationView with custom callout after adding annotation - swift

I have an MKMapView on which I would like to add annotations on a long press gesture. After adding the annotation, I would like to select the annotation view. Quite a simple request I assumed.
The problem is that I am using the new MKMarkerAnnotationView with a custom rightCalloutAccessoryView and a custom detailCalloutAccessoryView. It is not very well documented yet, but the WWDC 2017 Session 237 states that the callout will be displayed when there is more than a title/subtitle.
Unfortunately, this is not the case for me. When I programmatically (and manually) select the annotation, I get a weird double selected state where I can see the callout AND the marker:
Here is the annotation view code:
import Foundation
import MapKit
#available(iOS 11.0, *)
protocol TemporaryUserAnnotationViewDelegate: class {
func temporaryUserAnnotationViewDidTapOk(title: String?, userAnnotationView: TemporaryUserAnnotationView)
}
#available(iOS 11.0, *)
class TemporaryUserAnnotationView: MKMarkerAnnotationView {
weak var delegate: TemporaryUserAnnotationViewDelegate?
var textField: UITextField!
init(annotation: TemporaryUserAnnotation) {
super.init(annotation: annotation, reuseIdentifier: TemporaryUserAnnotationMarkerAnnotationView.reuseIdentifier)
markerTintColor = .lbc_blue
canShowCallout = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func configure(annotation: TemporaryUserAnnotation) {
let detailView = UIView()
detailView.translatesAutoresizingMaskIntoConstraints = false
textField = UITextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.placeholder = "Titre"
detailView.addSubview(textField)
detailView.heightAnchor.constraint(equalToConstant: 21).isActive = true
detailView.widthAnchor.constraint(equalToConstant: 100).isActive = true
textField.centerXAnchor.constraint(equalTo: detailView.centerXAnchor).isActive = true
textField.centerYAnchor.constraint(equalTo: detailView.centerYAnchor).isActive = true
textField.heightAnchor.constraint(equalToConstant: 21).isActive = true
textField.widthAnchor.constraint(equalToConstant: 100).isActive = true
detailCalloutAccessoryView = detailView
let rightView = UIView(frame: CGRect(origin: .zero, size: CGSize(width: 50, height: 70)))
rightView.backgroundColor = .lbc_blue
let button = UIButton(frame: CGRect(origin: .zero, size: CGSize(width: 50, height: 40)))
button.setTitle("OK", for: .normal)
button.setTitleColor(.white, for: .normal)
button.addTarget(self, action: #selector(tapRightCalloutAccessoryViewButton), for: .touchUpInside)
rightView.addSubview(button)
rightCalloutAccessoryView = rightView
}
#objc func tapRightCalloutAccessoryViewButton() {
delegate?.temporaryUserAnnotationViewDidTapOk(title: textField.text, userAnnotationView: self)
}
}
and here is the controller code:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
print(#function)
if annotation is MKUserLocation { return nil }
if #available(iOS 11.0, *) {
switch annotation {
case let temporarayUserAnnotation as TemporaryUserAnnotation:
if let view = mapView.dequeue() as? TemporaryUserAnnotationView {
view.annotation = annotation
return view
} else {
let view = TemporaryUserAnnotationView(annotation: temporarayUserAnnotation)
view.delegate = self
view.configure(annotation: temporarayUserAnnotation)
return view
}
default:
return nil
}
} else {
let identifier = "marker"
if let view = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? MKPinAnnotationView {
view.annotation = annotation
return view
} else {
return MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
}
}
}
I simply add annotations this way:
func addUserAnnotation(coordinate: CLLocationCoordinate2D) {
let annotation = TemporaryUserAnnotation(coordinate: coordinate)
mapView.addAnnotation(annotation)
mapView.selectAnnotation(annotation, animated: true)
}
I also tried to implement the mapView(_:didAdd:) method but the result is even worse with the marker view in front of the callout.
Any help would be very appreciated! Thanks!

I found the following workaround: when you add your annotation, wait a second before programmatically selecting it:
func addUserAnnotation(coordinate: CLLocationCoordinate2D) {
let annotation = TemporaryUserAnnotation(coordinate: coordinate)
mapView.addAnnotation(annotation)
DispatchQueue.main.asyncAfter(wallDeadline: .now() + .seconds(1)) {
self.mapView.selectAnnotation(annotation, animated: true)
}
}
I still think this should not be necessary, so if you have a better solution, feel free to post it here!

I just encountered a similar problem because I had a UITapGestureRecognizer of my own competing with MKMapView's selection gesture recognizer. I created and selected the annotation on the tap event, but because both gesture recognizers were firing, and theirs was firing last, it was deselecting immediately.
My solution was to implement UIGestureRecognizerDelegate and become the delegate of my gesture recognizer, and implement the following:
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}

Related

Swift MapKit MKAnnotationView button not working

I have a custom MKAnnotationView that contains a button outlet which displays on the map when a user taps a pin. I'm struggling to make the button tap event work as it never gets called and instead the MKAnnotationView disappears. How can I make the button work?
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
if view.annotation is MapMarker {
return
}
if let waypointAnnotation = view.annotation as? WaypointAnnotation {
let calloutView = self.createCalloutView(annotation: waypointAnnotation)
calloutView.removeOutlet.addTarget(self, action: #selector(removeWaypoint(id:)), for: .touchUpInside)
waypointAnnotation.calloutView = calloutView
calloutView.center = CGPoint(x: view.bounds.size.width / 2, y: -calloutView.bounds.size.height*0.52)
view.addSubview(calloutView)
}
}
Create calloutView function:
private func createCalloutView(annotation: WaypointAnnotation) -> WaypointCalloutView {
let views = Bundle.main.loadNibNamed("WaypointCalloutView", owner: nil, options: nil)
let calloutView = views?[0] as! WaypointCalloutView
calloutView.id = annotation.id
calloutView.delegate = self
return calloutView
}
Thanks.

How to make a custom MKAnnotationView with XIB

I want to have a custom MKAnnotationView. I've created a xib file in IB and set its class to MyAnnotationView.
class MyAnnotationView: MKAnnotationView {
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
#IBOutlet weak var textLabel: UILabel!
#IBOutlet weak var busIcon: UIImageView!
}
Here's how the xib looks like - it has a textLabel and a busIcon linked:
I'm using the viewFor annotation delegate method to create views for all annotations:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// Don't want to show a custom image if the annotation is the user's location.
if (annotation is MKUserLocation) {
return nil
} else {
let annotationIdentifier = "AnnotationIdentifier"
var annotationView: MyAnnotationView?
if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "AnnotationIdentifier") as? MyAnnotationView {
annotationView = dequeuedAnnotationView
annotationView?.annotation = annotation
} else {
// if no views to dequeue, create an Annotation View
let av = MyAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
av.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
annotationView = av
}
if let annotationView = annotationView {
annotationView.canShowCallout = true // callout bubble
annotationView.image = UIImage(named: "Delivery")
annotationView.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
}
return annotationView
}
}
The annotationView.image = UIImage(named: "Delivery")
&
AnnotationView.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
are there just to check if the code is working and display a sample view on the map, as they use the standard properties inherited from MKAnnotationView.
I don't know how to make the viewFor annotation method use the XIB I have created. Could anyone please help me with that? I searched for the solution, but only found something relevant in Obj C.
Thank you!
1- Create a view subclass of UIView with xib say CallView
2- Inside viewforAnnotation
let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "id")
let customView = Bundle.main.loadNibNamed("CallView", owner: self, options: nil).first! as! CallView
// here configure label and imageView
annotationView.addSubview(customView)
UPDATED CODE BASED ON Sh-Khan's answer
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// Don't want to show a custom image if the annotation is the user's location.
if (annotation is MKUserLocation) {
return nil
} else {
let annotationIdentifier = "AnnotationIdentifier"
let nibName = "MyAnnotationView"
let viewFromNib = Bundle.main.loadNibNamed(nibName, owner: self, options: nil)?.first as! MyAnnotationView
var annotationView: MyAnnotationView?
// if there is a view to be dequeued, use it for the annotation
if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationIdentifier) as? MyAnnotationView {
if dequeuedAnnotationView.subviews.isEmpty {
dequeuedAnnotationView.addSubview(viewFromNib)
}
annotationView = dequeuedAnnotationView
annotationView?.annotation = annotation
} else {
// if no views to dequeue, create an Annotation View
let av = MyAnnotationView(annotation: annotation, reuseIdentifier: annotationIdentifier)
av.addSubview(viewFromNib)
annotationView = av // extend scope to be able to return at the end of the func
}
// after we manage to create or dequeue the av, configure it
if let annotationView = annotationView {
annotationView.canShowCallout = true // callout bubble
annotationView.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
annotationView.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
let customView = annotationView.subviews.first as! MyAnnotationView
customView.frame = annotationView.frame
customView.textLabel.text = (annotationView.annotation?.title)!
}
return annotationView
}
}
**Create Custom MKPointAnnotation Class**
import UIKit
import MapKit
class CustomPointAnnotation: MKPointAnnotation {
var id : Int
var url : String
init(id : Int , url : String ) {
self.id = id
self.url = url
}
}
Create Xib for MarkerView Class for Annotation View
class MarkerView: MKAnnotationView {
#IBOutlet weak var imgVwUser: UIImageView!
init(annotation: CustomPointAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let hitView = super.hitTest(point, with: event)
if (hitView != nil)
{
self.superview?.bringSubviewToFront(self)
}
return hitView
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
let rect = self.bounds
var isInside: Bool = rect.contains(point)
if(!isInside)
{
for view in self.subviews
{
isInside = view.frame.contains(point)
if isInside
{
break
}
}
}
return isInside
}
}
Add MKMapView Delegates In Your ViewController
extension YourViewController : MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// Don't want to show a custom image if the annotation is the user's location.
if (annotation is MKUserLocation) {
return nil
} else {
let annotationIdentifier = "AnnotationIdentifier"
let nibName = "MarkerView"
let viewFromNib = Bundle.main.loadNibNamed(nibName, owner: self, options: nil)?.first as! MarkerView
var annotationView: MarkerView?
// if there is a view to be dequeued, use it for the annotation
if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationIdentifier) as? MarkerView {
if dequeuedAnnotationView.subviews.isEmpty {
dequeuedAnnotationView.addSubview(viewFromNib)
}
annotationView = dequeuedAnnotationView
annotationView?.annotation = annotation
} else {
// if no views to dequeue, create an Annotation View
let av = MarkerView(annotation: annotation as? CustomPointAnnotation, reuseIdentifier: annotationIdentifier)
av.addSubview(viewFromNib)
annotationView = av // extend scope to be able to return at the end of the func
}
// after we manage to create or dequeue the av, configure it
if let annotationView = annotationView {
annotationView.canShowCallout = false // callout bubble
if let annotation = annotation as? CustomPointAnnotation {
annotationView.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
annotationView.frame = CGRect(x: 0, y: 0, width: 66, height: 75)
let customView = annotationView.subviews.first as? MarkerView
customView?.frame = annotationView.frame
let image = annotation.url
let imageUrl = URL(string: image)
customView?.imgVwUser.sd_setImage(with: imageUrl, placeholderImage: UIImage(named:"defaults"), options: [.refreshCached], completed: nil)
}
}
return annotationView
}
}
}
ADD Annotation to the mapview
extension YourViewController {
func addAnnotation(){
let annotationsToRemove = mapView.annotations.filter { $0 !== mapView.userLocation }
mapView.removeAnnotations( annotationsToRemove )
var annotations: [CustomPointAnnotation] = []
for i in 0..<self.arrayData.count {
let customPoints = CustomPointAnnotation.init(id: arrayData[i].id ?? 0, url: arrayData[i].url)
let location = CLLocationCoordinate2DMake(self.arrayData[i].lat ?? 0, self.arrayData[i].lng ?? 0)
customPoints.coordinate = location
annotations.append(customPoints)
}
mapView.addAnnotations(annotations)
}
}

Swift: Button Action on Custom CalloutView

I am implementing a custom calloutView on my mapView and I am attempting to implement action buttons from the custom calloutView. I am having trouble finding ways to detect the buttons tapped with respect to the calloutViews.
Below are my implementation:
//At MapViewController
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if !annotation.isKind(of: Post.self) {
return nil
}
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "pin") as? MKPinAnnotationView
if annotationView == nil {
annotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "pin")
annotationView?.canShowCallout = true
annotationView?.animatesDrop = true
} else {
annotationView?.annotation = annotation
}
let postAnnotation = annotation as! Post
let calloutView = DetailCalloutView()
calloutView.titleLabel.text = ...
calloutView.timestampLabel.text = ...
calloutView.postContentTextView.text = ...
calloutView.profileImageView.image = ...
calloutView.deleteButton.addTarget(self, action: #selector(deleteButtonTap), for: .touchUpInside)
calloutView.chatButton.addTarget(self, action: #selector(chatButtonTap), for: .touchUpInside)
annotationView?.detailCalloutAccessoryView = calloutView
return annotationView
}
#objc func deleteButtonTap(_ sender: MKAnnotationView) {
let post = sender.annotation as? Post
let index = postArray.index(of: post) //Is there a way to implement something like this here??
...
}
#objc func chatButtonTap() {
print("123")
}
//At Post
class Post: NSObject, MKAnnotation {
var coordinate: CLLocationCoordinate2D
var post: String?
//All other properties
init(coordinate: CLLocationCoordinate2D, post: String, ...) {
self.coordinate = coordinate
self.post = post
...
}
}
//At DetailCallOutView
class DetailCalloutView: UIView {
let containerView: UIView = {
...
return v
}()
//All other UI elements
let chatButton: UIButton = {
let b = UIButton()
...
return b
}()
let deleteButton: UIButton = {
let b = UIButton()
...
return b
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
}
func setupViews() {
addSubview(containerView)
//adding subviews for all UI elements
containerView.addSubview(cButton)
containerView.addSubview(dButton)
//setting up constraints for all UI elements
}
}
I am able to detect the chat and delete buttons, but am unable to obtain the post with respect to the postArray or annotation. Is there a way to do so?
First action is of type that target added to , which is UIButton here
calloutView.deleteButton.tag = <#setToIndexOfArr#>
calloutView.deleteButton.addTarget(self, action: #selector(deleteButtonTap), for: .touchUpInside)
//
#objc func deleteButtonTap(_ sender: UIButton) {
let item = modelArr[sender.tag]
}

Custom MKMarkerAnnotationView Like Photos App

I am attempting to do a custom pinView like how the Photos app does it like so:
So far, most resources I can find on SO or here and here show ways to customise the image of the pin or the callout views, but none on a customised view of the marker itself.
Eventually, what I wanna achieve is similar to the Photos app, where each image displayed on the marker itself is a profileImage of the user, but a rounded view. My code so far is very basic:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: "pin") as? MKMarkerAnnotationView
if annotationView == nil {
annotationView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: "pin")
annotationView?.canShowCallout = true
annotationView?.leftCalloutAccessoryView = UIImageView(image: UIImage(named: "profileImage"))
} else {
annotationView?.annotation = annotation
}
return annotationView
}
I hope this post do not get down voted as I've really exhausted my search online for resources. It would be great if someone could point me out in the right direction pls, thanks.
you can implement any custom design you want, you just need to provide a delegate method called viewFor.
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
var pinView = mapView.dequeueReusableAnnotationView(withIdentifier: "pin", for: annotation)
pinView = MapPinView(annotation: annotation, reuseIdentifier: "pin")
return pinView
}
In viewDidload method set the mapview delegate to self and of course register your custom view like bellow.
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
mapView.register(MapPinView.self, forAnnotationViewWithReuseIdentifier: "pin")
}
And a custom view can look like this.
import MapKit
class MapPinView: MKAnnotationView {
private lazy var containerView: UIView = {
let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
view.backgroundColor = .white
view.layer.cornerRadius = 16.0
return view
}()
private lazy var imageView: UIImageView = {
let imageview = UIImageView()
imageview.translatesAutoresizingMaskIntoConstraints = false
imageview.image = UIImage(named: "bg")
imageview.layer.cornerRadius = 8.0
imageview.clipsToBounds = true
return imageview
}()
private lazy var bottomCornerView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .white
view.layer.cornerRadius = 4.0
return view
}()
// MARK: Initialization
override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
setupView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupView() {
containerView.addSubview(bottomCornerView)
bottomCornerView.topAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -15.0).isActive = true
bottomCornerView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true
bottomCornerView.widthAnchor.constraint(equalToConstant: 24).isActive = true
bottomCornerView.heightAnchor.constraint(equalToConstant: 24).isActive = true
let angle = (39.0 * CGFloat.pi) / 180
let transform = CGAffineTransform(rotationAngle: angle)
bottomCornerView.transform = transform
addSubview(containerView)
containerView.addSubview(imageView)
imageView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 8.0).isActive = true
imageView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 8.0).isActive = true
imageView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -8.0).isActive = true
imageView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -8.0).isActive = true
}
}
And mine looks like this.

How to change default background color of callout bubble with detailCalloutAccessoryView

In my app I have the following sutuation.
I've implemented a custom callout bubble with custom detailCalloutAccessoryView with two labels inside.
I know how to change the color of detailCalloutAccessoryView with this line.
view.detailCalloutAccessoryView?.backgroundColor = UIColor.red
But I can't figure out how to change background color of the main bubble (it is transparent grey/white now). With view.detailCalloutAccessoryView?.backgroundColor = UIColor.red line my calloutbubble looks like this:
But I want my custom bubble to look like this:
Here is my viewFor annotation method:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
}
let identifier = "pin"
var view : MKAnnotationView
if let dequedView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) {
dequedView.annotation = annotation
view = dequedView
} else {
view = MKAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
}
let pinImage = UIImage.init(named: "customPin")
DispatchQueue.main.async(execute: {
view.detailCalloutAccessoryView?.backgroundColor = UIColor.red
})
view.image = pinImage
configureDetailView(annotationView: view)
return view
}
I'm working in Xcode 8 w/ Swift 3.
It would be also interesting to know how to change font type and default black color of the title from black to another color.
In detail view i can easily change color of my custom labels in xib file but don't know how to access default title properties.
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
//design your custom view and set it to detailCalloutAccessoryView
view.detailCalloutAccessoryView = yourDetailView
detail.superview?.superview?.backgroundColor = yourColor
// This view is type of MKSmallCalloutView
}
UIViewCallout is a private class. If you want custom callout view:
disable standart callout view.canShowCallout = false
implement MKMapViewDelegate methods with your custom UIView for callout:
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
let redCalloutView = RedCalloutView(view.annotation)
view.addSubview(redCalloutView)
}
func mapView(_ mapView: MKMapView, didDeselect view: MKAnnotationView) {
view.subviews.forEach {
if $0 is RedCalloutView {
$0.removeFromSuperview()
}
}
}
I had created the code for your requirement please find the below url for download the code and review it.
Link : https://www.dropbox.com/s/o2howwqceq8rsgu/MapInformation.zip?dl=0
Environment : Xcode 8 and Swift3
Highlight the code which I had done it.
I had taken the approach to display the Popup(UIPresentationController) instead of callout. For more information please find the below code.
A) I had used the UIButton to display as annotation on the MapView and display the popup when user click on it.
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation is MKUserLocation {
return nil
}
let identifier = "pin"
var annotationView = self.mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as! AnnotationView?
if annotationView == nil {
annotationView = AnnotationView(annotation: annotation, reuseIdentifier: identifier)
annotationView?.canShowCallout = false
}
else {
annotationView?.annotation = annotation
}
//Take the UIButton and implement the touchupinside action for showing the popup.
let pinImage = UIImage.init(named: "customPin")
annotationView?.frame = CGRect(x: 0, y: 0, width: (pinImage?.size.width)!, height: (pinImage?.size.width)!)
annotationView?.mapPin = UIButton(frame: (annotationView?.frame)!);
annotationView?.mapPin.addTarget(self, action: #selector(ViewController.showPopup(sender:)), for: .touchUpInside)
annotationView?.addSubview((annotationView?.mapPin)!)
annotationView?.mapPin.setImage(pinImage, for: .normal)
return annotationView
}
B) Display the popup when user click on the annotation.
func showPopup(sender: UIButton!) {
let popupVC = self.storyboard?.instantiateViewController(withIdentifier: "Popup") as? Popup
popupVC?.preferredContentSize = CGSize(width: 250, height: 150)
popupVC?.modalPresentationStyle = UIModalPresentationStyle.popover
let rect = sender.superview?.convert(sender.frame, to: self.view)
popupVC?.popoverPresentationController?.delegate = self;
popupVC?.popoverPresentationController?.sourceView = self.view
popupVC?.popoverPresentationController?.sourceRect = rect!
popupVC?.popoverPresentationController?.backgroundColor = UIColor.red
self.present(popupVC!, animated: true, completion: nil)
}
Note
If you want to change the popup color from red to other different
color then you can do only single line of coding by changing the color name.
popupVC?.popoverPresentationController?.backgroundColor = UIColor.red
Please look into the below screenshot.
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
for v in view.subviews{
if v.subviews.count > 0{
v.subviews[0].backgroundColor = UIColor.red
}
}
}
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
for v in view.subviews{
if v.subviews.count > 0 {
let colloutView = v.subviews[0]
colloutView.backgroundColor = UIColor(red: 0.0/255.0, green: 0.0/255.0, blue: 0.0/255.0, alpha: 0.8)
if colloutView.subviews.count > 0 {
if colloutView.subviews[0].subviews.count > 0{
colloutView.subviews[0].subviews.forEach { (view) in
if let label = view as? UILabel{
label.textColor = UIColor.white
}
}
}
}
}
}
}