{"id":1383,"url":"https://github.com/okhanokbay/MapViewPlus","last_synced_at":"2025-08-02T04:31:10.210Z","repository":{"id":62447580,"uuid":"124649350","full_name":"okhanokbay/MapViewPlus","owner":"okhanokbay","description":"Use any custom view as custom callout view for MKMapView with cool animations. Use any image as annotation view.","archived":true,"fork":false,"pushed_at":"2019-12-07T20:33:13.000Z","size":29514,"stargazers_count":165,"open_issues_count":8,"forks_count":21,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-07-19T04:11:20.564Z","etag":null,"topics":["animations","callout","calloutview","custom-callout","custom-callout-view","customcalloutview","mapkit","mapview","mkmapview","swift"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/okhanokbay.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-03-10T11:28:25.000Z","updated_at":"2025-05-18T02:04:51.000Z","dependencies_parsed_at":"2022-11-01T23:17:27.718Z","dependency_job_id":null,"html_url":"https://github.com/okhanokbay/MapViewPlus","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/okhanokbay/MapViewPlus","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/okhanokbay%2FMapViewPlus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/okhanokbay%2FMapViewPlus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/okhanokbay%2FMapViewPlus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/okhanokbay%2FMapViewPlus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/okhanokbay","download_url":"https://codeload.github.com/okhanokbay/MapViewPlus/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/okhanokbay%2FMapViewPlus/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268334610,"owners_count":24233793,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-02T02:00:12.353Z","response_time":74,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["animations","callout","calloutview","custom-callout","custom-callout-view","customcalloutview","mapkit","mapview","mkmapview","swift"],"created_at":"2024-01-05T20:15:45.192Z","updated_at":"2025-08-02T04:31:09.502Z","avatar_url":"https://github.com/okhanokbay.png","language":"Swift","readme":"# MapViewPlus\n\n[![Swift 5.0](https://img.shields.io/badge/Swift-5.0-orange.svg?style=flat)](https://developer.apple.com/swift/)\n![iOS 10.0+](https://img.shields.io/badge/iOS-10.0%2B-blue.svg)\n[![Version](https://img.shields.io/cocoapods/v/MapViewPlus.svg?style=flat)](http://cocoapods.org/pods/MapViewPlus)\n[![License](https://img.shields.io/cocoapods/l/MapViewPlus.svg?style=flat)](http://cocoapods.org/pods/MapViewPlus)\n[![Platform](https://img.shields.io/cocoapods/p/MapViewPlus.svg?style=flat)](http://cocoapods.org/pods/MapViewPlus)\n\n\u003cimg src=\"https://github.com/okhanokbay/MapViewPlus/blob/master/Example/basic_example_ld.gif\" width=320\u003e \u003cimg src=\"https://github.com/okhanokbay/MapViewPlus/blob/master/Example/default_callout_ld.gif\" width=320\u003e\n\n## About\n\nMapViewPlus gives you the missing methods of MapKit which are: ```imageForAnnotation``` and ```calloutViewForAnnotationView``` delegate methods.\n\n- Return any UIImage from ```imageForAnnotation``` (image shouldn't have paddings)\n- Create any UIView to use as your custom callout view and return it from ```calloutViewForAnnotationView```\n- MapViewPlus will:\n  1) Add an anchor view to bottom of your callout view\n  2) Combine callout view with the anchor view and add shadow to both of them\n  3) Add a cool animation to ```CalloutAndAnchorView```\n  4) Make it user interaction enabled (this may be easy but tricky sometimes)\n  5) Scrolls map view to show the callout view completely after tapping the annotation view\n  5) Even give a ready-to-use template for callout view\n  6) [Forward all of the delegate methods](#forwarding-delegate-methods) of ```MKMapView``` to your subclass of ```MapViewPlus``` (except ```mapView:viewForAnnotation:```)\n\n## Requirements\n\n- Swift 5.0\n- iOS 10.0+\n\n## Installation\n\nMapViewPlus is available through [CocoaPods](http://cocoapods.org). To install\nit, simply add the following line to your Podfile:\n\n```ruby\npod 'MapViewPlus'\n```\n\nIf you don't use CocoaPods, you can drag and drop all the classes and use it in your project.\n\n## How to use\n\n**1) If you are using Interface Builder, set your map view's class and module as MapViewPlus:**\n\n\u003cimg src=\"https://github.com/okhanokbay/MapViewPlus/blob/master/Example/settings_and_module.png\"\u003e\n\n\n**2) Setup your Callout View Model:**\n\n```swift\nimport UIKit\nimport MapViewPlus\n\n// CalloutViewModel is a protocol of MapVieWPlus. Currently, it has no requirements to its conformant classes.\nclass YourCalloutViewModel: CalloutViewModel {\n\n  var title: String\n  var image: UIImage\n\n  init(title: String, image: UIImage) {\n    self.title = title\n    self.image = image\n  }\n}\n```\n\n**3) Setup your CalloutView:**\n\n- Create any view in an xib file (or programmatically):\n\n\u003cimg src=\"https://github.com/okhanokbay/MapViewPlus/blob/master/Example/calloutview_example.png\"\u003e\n\n- Wire your view to your callout view class:\n\n```swift\nimport UIKit\nimport MapViewPlus\n\n// CalloutViewPlus is a protocol of MapViewPlus. \n// Currently, it has just one requirement -\u003e func configureCallout(_ viewModel: CalloutViewModel)\nclass YourCalloutView: UIView, CalloutViewPlus {\n\n  @IBOutlet weak var label: UILabel!\n  @IBOutlet weak var imageView: UIImageView!\n\n  func configureCallout(_ viewModel: CalloutViewModel) {\n    let viewModel = viewModel as! YourCalloutViewModel\n\n    label.text = viewModel.title\n    imageView.image = viewModel.image\n  }\n}\n```\n\n**4) Setup your Annotations in your View Controller:**\n\n```swift\nimport UIKit\nimport CoreLocation\nimport MapViewPlus\n\nclass YourViewController: UIViewController {\n\n  @IBOutlet weak var mapView: MapViewPlus!\n\n  override func viewDidLoad() {\n    super.viewDidLoad()\n\n    //Required\n    mapView.delegate = self\n\n    let viewModel = YourCalloutViewModel(title: \"Cafe\", image: UIImage(named: \"cafe.png\")!)\n\n    let annotation = AnnotationPlus(viewModel: viewModel,\n                                    coordinate: CLLocationCoordinate2DMake(50.11, 8.68))\n\n    var annotations: [AnnotationPlus] = []\n    annotations.append(annotation)\n\n    mapView.setup(withAnnotations: annotations)\n  }\n}\n```\n\n**5) Return the image for Annotation and the View for Callout**\n\n```swift\nextension YourViewController: MapViewPlusDelegate {\n\n  func mapView(_ mapView: MapViewPlus, imageFor annotation: AnnotationPlus) -\u003e UIImage {\n    return UIImage(named: \"your_annotation_image.png\")!\n  }\n\n  func mapView(_ mapView: MapViewPlus, calloutViewFor annotationView: AnnotationViewPlus) -\u003e CalloutViewPlus{\n    let calloutView = Bundle.main.loadNibNamed(\"YourCalloutView\", owner: nil, options: nil)!.first as! YourCalloutView\n    return calloutView\n  }\n}\n\n```\n\nThis is it. You are all set and ready to go now.\n\n## Customization (Optional)\n\nMapViewPlus is highly customizable:\n\n### CalloutViewCustomizerDelegate\n\n- Change the center of the callout view related to anchor view and callout view:\n\n```swift\nfunc mapView(_ mapView: MapViewPlus, centerForCalloutViewOf annotationView: AnnotationViewPlus) -\u003e CalloutViewPlusCenter\n```\n\n- Change the bounds for callout view without changing the frames in Interface Builder. If you don't use this delegate method, then your callout view's frame will stay the same as the Interface Builder file. You can also use this to change frame dynamically according to the data in callout view!\n\n```swift\nfunc mapView(_ mapView: MapViewPlus, boundsForCalloutViewOf annotationView: AnnotationViewPlus) -\u003e CalloutViewPlusBound\n```\n\n- Change the inset of anchor view related to callout view. Anchor view will go under the callout view as amount the value you returned from this method. Defaults to 0.\n\n```swift\nfunc mapView(_ mapView: MapViewPlus, insetFor calloutView: CalloutViewPlus) -\u003e CGFloat\n```\n\n- Change the animation type for **showing** the callout view. Defaults to ```.fromBottom``` and available types are, ```.fromTop```, ```.fromBottom```, ```.fromLeft```, ```.fromRight```\n\n```swift\nfunc mapView(_ mapView: MapViewPlus, animationTypeForShowingCalloutViewOf annotationView: AnnotationViewPlus) -\u003e CalloutViewShowingAnimationType\n```\n\n- Change the animation type for **hiding** the callout view. Defaults to ```.toBottom``` and available types are, ```.toTop```, ```.toBottom```, ```.toLeft```, ```.toRight```\n\n```swift\nfunc mapView(_ mapView: MapViewPlus, animationTypeForHidingCalloutViewOf annoationView: AnnotationViewPlus) -\u003e CalloutViewHidingAnimationType\n```\n\n### AnchorViewCustomizerDelegate\n\n- Change the height for anchor. Anchor view **will always draw a equilateral triangle** by taking the value you supplied as the triangle's height. You can change the height and the anchor view will calculate the necessary size for you:\n\n```swift\nfunc mapView(_ mapView: MapViewPlus, heightForAnchorOf calloutView: CalloutViewPlus) -\u003e CGFloat\n```\n\n- Change the background color of anchor view. This is very important. You can supply any color you want for the anchor view. Generally you would like to make this color as same as the background color of your callout view. Just change it like:\n\n```swift\nfunc mapView(_ mapView: MapViewPlus, fillColorForAnchorOf calloutView: CalloutViewPlus) -\u003e UIColor\n```\n\n## Notes\n\n### DefaultCalloutView\n\n- MapViewPlus supplies you a ready-to-go template for callout view. You can see the usage example of it in the example project.\n- It allows you to specify the source of the image. It has three options:\n  1) ```.downloadable(imageURL: URL, placeholder: UIImage?)```\n      - Downloads the image with the help of Kingfisher framework\n  2) ```.fromBundle(image: UIImage)```\n  3) ```.none```\n\n### Forwarding Delegate Methods\n\n```MapViewPlus``` uses methods from ```MKMapViewDelegate```, but not all of them. It forwards all of the delegate methods except ```mapView:viewForAnnotation:```. This method is used internally and won't be redirected to your subclass.\n\nNormally, ```MapViewPlus``` will abstract you from ```MapKit``` when you don't want to use the other methods of ```MKMapViewDelegate```. But when you want to use the other methods from ```MKMapViewDelegate```, you can easily do that without any extra efforts. Just write them down and they will get called by ```MapViewPlusDelegate```. Please see how ```mapView(_:regionDidChangeAnimated:)``` method is being called in ```DefaultCalloutViewController.swift``` (in the example project) even if you don't conform to ```MKMapViewDelegate```. If in the future, some new methods are added to ```MKMapViewDelegate```, they will be automagically forwarded to you by ```MapViewPlusDelegate``` without a new version of the framework. There is no wrapping occuring in the background.\n\n## Example\n\nTo run the example project, clone the repo, and run `pod install` from the Example directory first.\n\n## Screenshots\n\n\u003cimg src=\"https://github.com/okhanokbay/MapViewPlus/blob/master/Example/basic_example_new.png\" width=320\u003e \u003cimg src=\"https://github.com/okhanokbay/MapViewPlus/blob/master/Example/default_callout_example_new.png\" width=320\u003e\n\n## License\n\nMapViewPlus is available under the MIT license. See the LICENSE file for more info.\n\n","funding_links":[],"categories":["Maps","Libs","Maps [🔝](#readme)"],"sub_categories":["Other Hardware","Maps","Other free courses"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fokhanokbay%2FMapViewPlus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fokhanokbay%2FMapViewPlus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fokhanokbay%2FMapViewPlus/lists"}