{"id":18909862,"url":"https://github.com/map-ir/ios-sdk-v2-map-demo","last_synced_at":"2025-10-26T07:20:37.741Z","repository":{"id":98481746,"uuid":"193868788","full_name":"map-ir/ios-sdk-v2-map-demo","owner":"map-ir","description":"Demo of Map SDK for iOS - Map.ir","archived":false,"fork":false,"pushed_at":"2019-09-02T12:15:45.000Z","size":41495,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-12-31T12:38:16.533Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/map-ir.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-06-26T09:01:53.000Z","updated_at":"2019-06-26T10:53:09.000Z","dependencies_parsed_at":"2023-03-29T19:04:41.387Z","dependency_job_id":null,"html_url":"https://github.com/map-ir/ios-sdk-v2-map-demo","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/map-ir%2Fios-sdk-v2-map-demo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/map-ir%2Fios-sdk-v2-map-demo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/map-ir%2Fios-sdk-v2-map-demo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/map-ir%2Fios-sdk-v2-map-demo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/map-ir","download_url":"https://codeload.github.com/map-ir/ios-sdk-v2-map-demo/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239897376,"owners_count":19715202,"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","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":[],"created_at":"2024-11-08T09:35:26.981Z","updated_at":"2025-10-26T07:20:32.690Z","avatar_url":"https://github.com/map-ir.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Getting Started with MAP.IR iOS SDK\n\n## Table of Contents\n- [Getting Started with MAP.IR iOS SDK](#Getting-Started-with-MAPIR-iOS-SDK)\n  - [Table of Contents](#Table-of-Contents)\n  - [Add Map.ir SDK to Xcode project.](#Add-Mapir-SDK-to-Xcode-project)\n  - [Generating map view using code](#Generating-map-view-using-code)\n  - [Using Interface Builder to add a map view](#Using-Interface-Builder-to-add-a-map-view)\n  - [Getting user current location](#Getting-user-current-location)\n  - [Placing markers on the map](#Placing-markers-on-the-map)\n  - [Showing callouts for annotations.](#Showing-callouts-for-annotations)\n  - [Polylines](#Polylines)\n  - [Polygons](#Polygons)\n  - [Custom annotation views and images](#Custom-annotation-views-and-images)\n  - [Animated annotation views](#Animated-annotation-views)\n  - [Custom views as callouts](#Custom-views-as-callouts)\n  - [Point Conversion](#Point-Conversion)\n  - [Restrict map panning](#Restrict-map-panning)\n\n\n## Add Map.ir SDK to Xcode project.\n\n[Map.ir]() SDK is build using Mapbox iOS SDK. in order to comunicate with map.ir servers, you must only use map.ir SDK which is available on the website.\n\nFor every project you may follow these steps to add map.ir SDK to your project.\n\n1. Download the SDK from the [iOS SDK Page](corp.map.ir).\n2. from the navigation bar select your project, then select general tab.\n3. drag and drop the **.framework** file into the **Embedded Binaries** section.\n4. Check the copy items if needed. then click finish.\n5. Go to the **Build Phases** tab and click the + button. Select **New Run Script Phase**.\n6. Add the following code to the shell:\n   \n    ```bash\n    bash \"${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Mapbox.framework/strip-frameworks.sh\"\n    ```\n7. Build (⌘ + B) your code and see if there is no errors.\n8. no you can import Mapbox in your code:\n\n    ```swift\n    import Mapbox\n    ```\n\n\n## Generating map view using code\nAdd SDK to your project (See [here](#add-mapir-sdk-to-xcode-project)). replace the following code with your `ViewController` class in **ViewController.swift** file.\n\n```swift\nclass ViewController: UIViewController {\n    \n    var mapView: MGLMapView! {\n        didSet {\n            mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]\n            // Using Map.ir vector map.\n            mapView.styleURL = MGLStyle.mapirVectorStyleURL()\n            mapView.minimumZoomLevel = 1\n        }\n    }\n\n    override func viewDidLoad() {\n        super.viewDidLoad()\n        \n        mapView = MGLMapView(frame: view.bounds)\n        \n        // Set maps center coordinate and zoom level.\n        mapView.setCenter(CLLocationCoordinate2D(latitude: 30.2, longitude: 50.8), zoomLevel: 9, animated: false)\n        view.addSubview(mapView)\n    }\n}\n```\nNow run you app.\n\n## Using Interface Builder to add a map view\n1. Open **Main.storyboard**.\n2. Open Library and find UIView, then drag and drop it into the view controller.\n3. Open the identity inspector for the newly added UIView, and set the value of class to be `MGLMapView`.\n4. Now, replace the `ViewController` class with the following code.\n5. Open up the assistant editor. (make sure its showing ViewController.swift) first select the mapView then ⌃ + Drag the view and drop it above the `viewDidLoad()` function in the assistant editor.\n6. in the opened box, name your view. something like  `mapView`.\n7. Import Mapbox in **ViewController.swift** file.\n\n    ```swift\n    import Mapbox\n    ```\n8. Now, you can set up your mapView like so:\n\n    ```swift\n    @IBOutlet weak var mapView: MGLMapView! {\n        didSet {\n            mapView.styleURL = MGLStyle.mapirVectorStyleURL()\n            mapView.minimumZoomLevel = 1\n        }\n    }\n    ```\n\n## Getting user current location\n\nAccessing GPS data and location services in iOS requires user permission. in order to do so you must add a key-valuew pair to your info.plist file.\n\n1. Open the info.plist file\n2. Press the + button and add the following key to it.\n    ```\n    Privacy - Location Always and When In Use Usage Description\n    ```\n3. Now set the value as you wish. it is going to be shown in prompt that asks for user permission to access location services. for examle: We need your location to show you on the Map\n\n```swift\nimport Mapbox\n\nclass ViewController: UIViewController {\n    \n    var mapView: MGLMapView! {\n        didSet {\n            mapView.styleURL = MGLStyle.mapirVectorStyleURL()\n            mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]\n            mapView.minimumZoomLevel = 1\n        }\n    }\n    \n    override func viewDidLoad() {\n        super.viewDidLoad()\n\n        mapView = MGLMapView(frame: view.bounds)\n        view.addSubview(mapView)\n        \n        // Adding user's location annotation.\n        // Use this latitude: 35.732530 and longitude: 51.422444 for simulator if needed.\n        mapView.showsUserLocation = true\n        mapView.userTrackingMode = .followWithHeading\n        mapView.showsUserHeadingIndicator = true\n    }\n}\n```\n\u003e Note: If you are using simulator, you must simulate location data. To do it, in your simulator, from top manu choose: Debug \u003e Location \u003e Custom Location. Then enter some random latitude and longitude in iran.\n\n## Placing markers on the map\n\n```swift\nimport Mapbox\n\nclass ViewController: UIViewController {\n    \n    var mapView: MGLMapView! {\n        didSet {\n            mapView.styleURL = MGLStyle.mapirVectorStyleURL()\n            mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]\n        }\n    }\n\n    override func viewDidLoad() {\n        super.viewDidLoad()\n        \n        mapView = MGLMapView(frame: view.bounds)\n        view.addSubview(mapView)\n        \n        mapView.delegate = self\n        \n        let tehranCoordinates = CLLocationCoordinate2D(latitude: 35.6, longitude: 51.3)\n        \n        let annotaion = MGLPointAnnotation()\n        annotaion.coordinate = tehranCoordinates\n        mapView.addAnnotation(annotaion)\n        \n        mapView.zoomLevel = 2\n        mapView.centerCoordinate = tehranCoordinates\n    }\n}\n```\n\n## Showing callouts for annotations.\n\n```swift\nimport UIKit\nimport Mapbox\n\nclass ViewController: UIViewController, MGLMapViewDelegate {\n    \n    var mapView: MGLMapView! {\n        didSet {\n            mapView.styleURL = MGLStyle.mapirVectorStyleURL()\n            mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]\n        }\n    }\n\n    override func viewDidLoad() {\n        super.viewDidLoad()\n        \n        // Creating object of Map View.\n        mapView = MGLMapView(frame: view.bounds)\n        view.addSubview(mapView)\n        \n        mapView.delegate = self\n        \n        // Making point annotations.\n        let annotationA = MGLPointAnnotation()\n        annotationA.coordinate = CLLocationCoordinate2D(latitude: 35.732547, longitude: 51.422682)\n        annotationA.title = \"Map.ir\"\n        annotationA.subtitle = \"Savojinia, Tehran\"\n        \n        let annotationB = MGLPointAnnotation()\n        annotationB.coordinate = CLLocationCoordinate2D(latitude: 35.744870, longitude: 51.375341)\n        annotationB.title = \"Milad Tower\"\n        annotationB.subtitle = \"Tehran\"\n        \n        // Adding annotaions to the mapView\n        mapView.addAnnotations([annotationA, annotationB])\n        \n        // Setting center of the map for better preview.\n        mapView.zoomLevel = 11\n        mapView.centerCoordinate = CLLocationCoordinate2D(latitude: 35.739383, longitude: 51.398894)\n    }\n    \n    // Delegate method to let the annotation show callouts.\n    func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -\u003e Bool {\n        return true\n    }\n}\n```\n\n## Polylines\n\n```swift\nimport UIKit\nimport Mapbox\n\nclass ViewController: UIViewController, MGLMapViewDelegate {\n    \n    var mapView: MGLMapView! {\n        didSet {\n            mapView.styleURL = MGLStyle.mapirVectorStyleURL()\n            mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]\n        }\n    }\n    override func viewDidLoad() {\n        super.viewDidLoad()\n\n        mapView = MGLMapView(frame: view.bounds)\n        view.addSubview(mapView)\n        \n        mapView.delegate = self\n        \n        let coordinates = [CLLocationCoordinate2D(latitude: 35.719820, longitude: 51.406258),\n                           CLLocationCoordinate2D(latitude: 34.634002, longitude: 50.875116),\n                           CLLocationCoordinate2D(latitude: 33.979195, longitude: 51.412375),\n                           CLLocationCoordinate2D(latitude: 32.650998, longitude: 51.666047),\n                           CLLocationCoordinate2D(latitude: 29.578307, longitude: 52.581962)]\n        \n        let polyline = MGLPolyline(coordinates: coordinates, count: UInt(coordinates.count))\n        mapView.addAnnotation(polyline)\n        \n        var annotations = [MGLPointAnnotation]()\n        for coordinate in coordinates {\n            let annotation = MGLPointAnnotation()\n            annotation.coordinate = coordinate\n            \n            annotations.append(annotation)\n        }\n        \n        mapView.addAnnotations(annotations)\n        \n        mapView.zoomLevel = 5.5\n        mapView.centerCoordinate = CLLocationCoordinate2D(latitude: 32.282091, longitude: 53.151601)\n    }\n    \n    func mapView(_ mapView: MGLMapView, strokeColorForShapeAnnotation annotation: MGLShape) -\u003e UIColor {\n        return UIColor(red: 1, green: 0.4190880954, blue: 0.3932890296, alpha: 1)\n    }\n    \n}\n```\n\n## Polygons\n\n```swift\nimport UIKit\nimport Mapbox\n\nclass ViewController: UIViewController, MGLMapViewDelegate {\n\n    var mapView: MGLMapView! {\n        didSet {\n            mapView.styleURL = MGLStyle.mapirVectorStyleURL()\n            mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]\n            mapView.minimumZoomLevel = 1\n        }\n    }\n    override func viewDidLoad() {\n        super.viewDidLoad()\n        \n        mapView = MGLMapView(frame: view.bounds)\n        view.addSubview(mapView)\n        \n        mapView.delegate = self\n        \n        let coordinates = [CLLocationCoordinate2D(latitude: 35.749245, longitude: 51.080899),\n                           CLLocationCoordinate2D(latitude: 35.825123, longitude: 51.473877),\n                           CLLocationCoordinate2D(latitude: 35.729642, longitude: 51.619514),\n                           CLLocationCoordinate2D(latitude: 35.576021, longitude: 51.434265),\n                           CLLocationCoordinate2D(latitude: 35.576021, longitude: 51.434265)]\n        \n        // Making the polygon with coordinates\n        let polygon = MGLPolygon(coordinates: coordinates, count: UInt(coordinates.count))\n        polygon.title = \"Tehran\"\n        polygon.subtitle = \"Iran\"\n        \n        // Adding polygon to the map\n        mapView.addAnnotation(polygon)\n        \n        mapView.zoomLevel = 8\n        mapView.centerCoordinate = CLLocationCoordinate2D(latitude: 35.716877, longitude: 51.382115)\n    }\n    \n    func mapView(_ mapView: MGLMapView, strokeColorForShapeAnnotation annotation: MGLShape) -\u003e UIColor {\n        return UIColor(red: 1, green: 0.4190880954, blue: 0.3932890296, alpha: 1)\n    }\n    \n    func mapView(_ mapView: MGLMapView, fillColorForPolygonAnnotation annotation: MGLPolygon) -\u003e UIColor {\n        return UIColor(red: 1, green: 0.4190880954, blue: 0.3932890296, alpha: 1)\n    }\n    \n    func mapView(_ mapView: MGLMapView, alphaForShapeAnnotation annotation: MGLShape) -\u003e CGFloat {\n        return CGFloat(0.2)\n    }\n    \n    func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -\u003e Bool {\n        return true\n    }\n}\n```\n\n## Custom annotation views and images\n\nBefore start, [download]() and add `camera.xcassets` catalog to your project.\n\n```swift\n// MGLPointAnnotation subclass\nclass CustomPointAnnotation: MGLPointAnnotation {\n    var usesImage: Bool = false\n}\n```\n\n```swift\nimport UIKit\nimport Mapbox\n\nclass ViewController: UIViewController, MGLMapViewDelegate {\n    \n    var mapView: MGLMapView! {\n        didSet {\n            mapView.styleURL = MGLStyle.mapirVectorStyleURL()\n            mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]\n            mapView.minimumZoomLevel = 1\n        }\n    }\n    \n    override func viewDidLoad() {\n        super.viewDidLoad()\n\n        mapView = MGLMapView(frame: view.bounds)\n        view.addSubview(mapView)\n        \n        mapView.delegate = self\n        \n        // Create four new point annotations with specified coordinates and titles.\n        let pointA = CustomPointAnnotation()\n        pointA.coordinate = CLLocationCoordinate2D(latitude: 35.699738, longitude: 51.338116)\n        pointA.title = \"Azadi Tower\"\n        pointA.usesImage = true\n        \n        let pointB = CustomPointAnnotation()\n        pointB.coordinate = CLLocationCoordinate2D(latitude: 35.744870, longitude: 51.375302)\n        pointB.title = \"Milad Tower\"\n        pointB.usesImage = true\n        \n        let pointC = CustomPointAnnotation()\n        pointC.coordinate = CLLocationCoordinate2D(latitude: 35.746102, longitude: 51.342211)\n        pointC.title = \"Nahjolbalagheh Park\"\n        \n        let pointD = CustomPointAnnotation()\n        pointD.coordinate = CLLocationCoordinate2D(latitude: 35.703206, longitude: 51.351323)\n        pointD.title = \"Sharif University\"\n        \n        // adding an array of 4 points to the map instead of individually\n        mapView.addAnnotations([pointA, pointB, pointC, pointD])\n        \n        mapView.centerCoordinate = CLLocationCoordinate2D(latitude: 35.726472, longitude: 51.353191)\n        mapView.zoomLevel = 12\n    }\n    \n    // This delegate method is where you tell the map to load a view for a specific annotation based on the willUseImage property of the custom subclass.\n    func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -\u003e MGLAnnotationView? {\n        if let castAnnotation = annotation as? CustomPointAnnotation {\n            if castAnnotation.usesImage {\n                return nil\n            }\n        }\n        \n        // Assign a reuse identifier to be used by both of the annotation views, taking advantage of their similarities.\n        let reuseIdentifier = \"Point\"\n\n        // For better performance, always try to reuse existing annotations.\n        var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier)\n\n        // If there’s no reusable annotation view available, initialize a new one.\n        if annotationView == nil {\n            annotationView = MGLAnnotationView(reuseIdentifier: reuseIdentifier)\n            \n            annotationView?.frame = CGRect(x: 0.0, y: 0.0, width: 30.0, height: 30.0)\n            annotationView?.layer.cornerRadius = (annotationView?.frame.width)! / 2.0\n            annotationView?.layer.borderWidth = 4.0\n            annotationView?.layer.borderColor = UIColor.white.cgColor\n            annotationView?.backgroundColor = UIColor(red: 1, green: 0.4190880954, blue: 0.3932890296, alpha: 1)\n        }\n        \n        return annotationView\n    }\n    \n    // This delegate method is where you tell the map to load a image for a specific annotation based on the willUseImage property of the custom subclass.\n    func mapView(_ mapView: MGLMapView, imageFor annotation: MGLAnnotation) -\u003e MGLAnnotationImage? {\n        if let castAnnotation = annotation as? CustomPointAnnotation {\n            if !castAnnotation.usesImage {\n                return nil\n            }\n        }\n        // Assign a reuse identifier to be used by both of the annotation images, taking advantage of their similarities.\n        let reuseIdentifier = \"Camera\"\n\n        // For better performance, always try to reuse existing annotations.\n        var annotationImage = mapView.dequeueReusableAnnotationImage(withIdentifier: reuseIdentifier)\n\n        // If there’s no reusable annotation view available, initialize a new one.\n        if annotationImage == nil {\n            annotationImage = MGLAnnotationImage(image: UIImage(named: \"camera\")!, reuseIdentifier: reuseIdentifier)\n        }\n        \n        return annotationImage\n    }\n    \n    func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -\u003e Bool {\n        return true\n    }\n}\n```\n\n## Animated annotation views\n\nIn this example you need a custom annotation class (`AnimatedAnnotationClass`) which inherits from `MGLAnnotationView`.\n\n```swift\n// MGLAnnotatoinView subclass\nclass AnimatedAnnotationView: MGLAnnotationView {\n    override func layoutSubviews() {\n        super.layoutSubviews()\n        \n        layer.cornerRadius = bounds.width / 2\n        layer.borderWidth = 2\n        layer.borderColor = UIColor.white.cgColor\n    }\n    \n    override func setSelected(_ selected: Bool, animated: Bool) {\n        super.setSelected(selected, animated: animated)\n        \n        let animation = CABasicAnimation(keyPath: \"borderWidth\")\n        animation.duration = 0.1\n        layer.borderWidth = selected ? bounds.width / 4 : 2\n        layer.add(animation, forKey: \"borderWidth\")\n    }\n}\n```\n\n```swift\nimport UIKit\nimport Mapbox\n\nclass ViewController: UIViewController, MGLMapViewDelegate {\n    \n    var mapView: MGLMapView! {\n        didSet {\n            mapView.styleURL = MGLStyle.mapirVectorStyleURL()\n            mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]\n            mapView.minimumZoomLevel = 1\n        }\n    }\n    \n    override func viewDidLoad() {\n        super.viewDidLoad()\n        \n        mapView = MGLMapView(frame: view.bounds)\n        view.addSubview(mapView)\n        \n        mapView.delegate = self\n        \n        let coordinates = [CLLocationCoordinate2D(latitude: 35.0, longitude: 51.0),\n                           CLLocationCoordinate2D(latitude: 36.0, longitude: 52.0),\n                           CLLocationCoordinate2D(latitude: 34.0, longitude: 50.0)]\n        \n        var annotations = [MGLPointAnnotation]()\n        \n        for coordinate in coordinates {\n            let annotation = MGLPointAnnotation()\n            annotation.coordinate = coordinate\n            annotation.title = \"\\(coordinate.latitude), \\(coordinate.longitude)\"\n            \n            annotations.append(annotation)\n        }\n        \n        mapView.addAnnotations(annotations)\n        \n        mapView.centerCoordinate = CLLocationCoordinate2D(latitude: 35.0, longitude: 51.0)\n        mapView.zoomLevel = 6\n    }\n    \n    // - MARK: MGLMapViewDelegate methods\n    \n    func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -\u003e Bool {\n        return true\n    }\n    \n    func mapView(_ mapView: MGLMapView, viewFor annotation: MGLAnnotation) -\u003e MGLAnnotationView? {\n        guard annotation is MGLPointAnnotation else {\n            return nil\n        }\n            \n        let reuseIdentifier = \"\\(annotation.coordinate.latitude), \\(annotation.coordinate.longitude)\"\n        \n        var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseIdentifier) as? AnimatedAnnotationView\n        \n        if annotationView == nil {\n            annotationView = AnimatedAnnotationView(reuseIdentifier: reuseIdentifier)\n            \n            annotationView?.bounds = CGRect(x: 0, y: 0, width: 40, height: 40)\n            \n            let hue = CGFloat(annotation.coordinate.longitude.remainder(dividingBy: 50)) / 10\n            annotationView?.backgroundColor = UIColor(hue: hue, saturation: 0.5, brightness: 1, alpha: 1)\n        }\n        \n        return annotationView\n    }\n    \n}\n```\n\n## Custom views as callouts\n\nIn this example you need a `CustomCalloutView` class which conforms to `MGLCalloutView` protocol.\n\n```swift\n// MGLCalloutView subclass\nclass CustomCalloutView: UIView, MGLCalloutView {\n    var representedObject: MGLAnnotation\n    \n    var button: UIButton\n    \n    override var center: CGPoint {\n        set {\n            var newCenter = newValue\n            newCenter.y -= bounds.midY\n            super.center = newCenter\n        }\n        get {\n            return super.center\n        }\n    }\n    \n    var leftAccessoryView = UIView() // Unused\n    var rightAccessoryView = UIView() // Unused\n    \n    // Allow the callout to remain open during panning.\n    var dismissesAutomatically: Bool = false\n    var isAnchoredToAnnotation: Bool = true\n    \n    weak var delegate: MGLCalloutViewDelegate?\n    \n    init(representedObject: MGLAnnotation) {\n        self.representedObject = representedObject\n        button = UIButton(type: .system)\n        \n        super.init(frame: .zero)\n        self.backgroundColor = .clear\n        \n        button.tintColor = .white\n        button.backgroundColor = .darkGray\n        button.contentEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)\n        button.layer.cornerRadius = 10.0\n        self.addSubview(button)\n    }\n    \n    required init?(coder aDecoder: NSCoder) {\n        fatalError(\"init(coder:) has not been implemented\")\n    }\n    \n    let tipHeight: CGFloat = 10\n    let tipWidth: CGFloat = 20\n    \n    func presentCallout(from rect: CGRect, in view: UIView, constrainedTo constrainedRect: CGRect, animated: Bool) {\n        view.addSubview(self)\n        \n        button.setTitle(representedObject.title!, for: .normal)\n        button.sizeToFit()\n        \n        if isCalloutTappable() {\n            // Handle taps and eventually try to send them to the delegate (usually the map view).\n            button.addTarget(self, action: #selector(CustomCalloutView.tapCallout), for: .touchUpInside)\n        } else {\n            // Disable tapping and highlighting.\n            button.isUserInteractionEnabled = false\n        }\n        \n        // Prepare our frame, adding extra space at the bottom for the tip.\n        let frameHeight = button.bounds.size.height + tipHeight\n        let frameWidth = button.bounds.size.width\n        let frameOriginX = rect.origin.x + rect.size.width / 2.0 - frameWidth / 2.0\n        let frameOriginY = rect.origin.y - tipHeight\n        \n        self.frame = CGRect(x: frameOriginX, y: frameOriginY, width: frameWidth, height: frameHeight)\n        \n        if animated {\n            self.alpha = 0\n            \n            UIView.animate(withDuration: 0.2) { [weak self] in\n                self?.alpha = 1\n            }\n        }\n    }\n    \n    func dismissCallout(animated: Bool) {\n        if superview != nil {\n            if animated {\n                UIView.animate(withDuration: 0.2, animations: { [weak self] in\n                    self?.alpha = 0\n                    }, completion: { [weak self] _ in\n                        self?.removeFromSuperview()\n                })\n            } else {\n                self.removeFromSuperview()\n            }\n        }\n    }\n\n    // MARK: - Callout interaction handlers\n    \n    private func isCalloutTappable() -\u003e Bool {\n        if let delegate = delegate, delegate.responds(to: #selector(MGLCalloutViewDelegate.calloutViewShouldHighlight(_:))) {\n            return delegate.calloutViewShouldHighlight!(self)\n        }\n        return false\n    }\n    \n    @objc func tapCallout() {\n        if isCalloutTappable() \u0026\u0026 delegate!.responds(to: #selector(MGLCalloutViewDelegate.calloutViewTapped(_:))) {\n            delegate!.calloutViewTapped?(self)\n        }\n    }\n    \n    override func draw(_ rect: CGRect) {\n        // Draw the pointed tip at the bottom.\n        let fillColor: UIColor = .darkGray\n        \n        let tipLeft = rect.origin.x + (rect.size.width / 2.0) - (tipWidth / 2.0)\n        let tipBottom = CGPoint(x: rect.origin.x + (rect.size.width / 2.0), y: rect.origin.y + rect.size.height)\n        let heightWithoutTip = rect.size.height - tipHeight - 1\n        \n        let currentContext = UIGraphicsGetCurrentContext()!\n        \n        let tipPath = CGMutablePath()\n        tipPath.move(to: CGPoint(x: tipLeft, y: heightWithoutTip))\n        tipPath.addLine(to: CGPoint(x: tipBottom.x, y: tipBottom.y))\n        tipPath.addLine(to: CGPoint(x: tipLeft + tipWidth, y: heightWithoutTip))\n        tipPath.closeSubpath()\n        \n        fillColor.setFill()\n        currentContext.addPath(tipPath)\n        currentContext.fillPath()\n    }\n}\n```\n\n```swift\nimport UIKit\nimport Mapbox\n\nclass ViewController: UIViewController, MGLMapViewDelegate {\n    \n    var mapView: MGLMapView! {\n        didSet {\n            mapView.styleURL = MGLStyle.mapirVectorStyleURL()\n            mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]\n            mapView.minimumZoomLevel = 1\n        }\n    }\n\n    override func viewDidLoad() {\n        super.viewDidLoad()\n        \n        mapView = MGLMapView(frame: view.bounds)\n        mapView.delegate = self\n        view.addSubview(mapView)\n        \n        let annotation = MGLPointAnnotation()\n        annotation.coordinate = CLLocationCoordinate2D(latitude: 35.732547, longitude: 51.422682)\n        annotation.title = \"Milad Tower\"\n        annotation.subtitle = \"Tehran, Iran\"\n        \n        mapView.addAnnotation(annotation)\n    }\n    \n    func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -\u003e Bool {\n        return true\n    }\n    \n    func mapView(_ mapView: MGLMapView, calloutViewFor annotation: MGLAnnotation) -\u003e MGLCalloutView? {\n        return CustomCalloutView(representedObject: annotation)\n    }\n    \n    func mapView(_ mapView: MGLMapView, tapOnCalloutFor annotation: MGLAnnotation) {\n        mapView.deselectAnnotation(annotation, animated: true)\n        \n        let alert = UIAlertController(title: annotation.title!, message: annotation.subtitle!, preferredStyle: .alert)\n        alert.addAction(UIAlertAction(title: \"OK\", style: .default, handler: nil))\n        self.present(alert, animated: true, completion: nil)\n    }\n}\n```\n\n## Point Conversion\n\n1. Go to the `main.storyboard` file.\n2. Open object library and choose UITapGestureTapRecognizer, drag and drop it on the view controller.\n3. select tap gesture recognizer from the top bar then ⌃ + Drag the view and drop it below the `viewDidLoad()` function in the assistant editor.\n4. from the connection type choose action and set the sender to be `UITabGestureRecognizer` then name the funcion. this block will be added to your code:\n   ```swift\n   @IBAction func handleMapTap(_ sender: UITapGestureRecognizer) {\n\n   }\n   ```\n\nNow you can use this example to convert tap points to coordinates.\n\n```swift\nimport UIKit\nimport Mapbox\n\nclass ViewController: UIViewController, MGLMapViewDelegate {\n\n    var mapView: MGLMapView! {\n        didSet {\n            mapView.styleURL = MGLStyle.mapirVectorStyleURL()\n            mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]\n            mapView.minimumZoomLevel = 1\n        }\n    }\n    override func viewDidLoad() {\n        super.viewDidLoad()\n\n        mapView = MGLMapView(frame: view.bounds)\n        view.addSubview(mapView)\n        \n        mapView.delegate = self        \n    }\n\n    @IBAction func handleMapTap(_ sender: UITapGestureRecognizer) {\n        switch sender.state {\n        case .ended:\n            // Convert tap location (CGPoint) to geographic coordinate (CLLocationCoordinate2D).\n\n            let tapPoint = sender.location(in: mapView)\n            let tapCoordinate: CLLocationCoordinate2D = mapView.convert(tapPoint, toCoordinateFrom: nil)\n            \n            print(\"Tap Coordinate: \\(tapCoordinate.latitude), \\(tapCoordinate.longitude)\")\n            \n            // Remove last annotations from the map\n            if mapView.annotations != nil, let existingAnnotations = mapView.annotations {\n                mapView.removeAnnotations(existingAnnotations)\n            }\n            \n            // Add a new point annotation at the tap coordinate\n            let annotation = MGLPointAnnotation()\n            annotation.coordinate = tapCoordinate\n            annotation.title = \"\\(tapCoordinate.latitude), \\(tapCoordinate.longitude)\"\n            \n            mapView.addAnnotation(annotation)\n            \n        default:\n            break\n        }\n    }\n    \n    func mapView(_ mapView: MGLMapView, annotationCanShowCallout annotation: MGLAnnotation) -\u003e Bool {\n        return true\n    }\n}\n\n```\n\n## Restrict map panning\n\n```swift\nimport UIKit\nimport Mapbox\n\nclass ViewController: UIViewController, MGLMapViewDelegate {\n\n    var mapView: MGLMapView! {\n        didSet {\n            mapView.styleURL = MGLStyle.mapirVectorStyleURL()\n            mapView.autoresizingMask = [.flexibleWidth, .flexibleHeight]\n            mapView.minimumZoomLevel = 1\n        }\n    }\n    \n    var iran: MGLCoordinateBounds!\n    \n    override func viewDidLoad() {\n        super.viewDidLoad()\n        \n        mapView = MGLMapView(frame: view.bounds)\n        view.addSubview(mapView)\n        \n        mapView.delegate = self\n        \n        // Iran's bounds\n        let northwest = CLLocationCoordinate2D(latitude: 42.0, longitude: 66.0)\n        let southeast = CLLocationCoordinate2D(latitude: 22.0, longitude: 42.0)\n        \n        iran = MGLCoordinateBounds(sw: southeast, ne: northwest)\n        \n        mapView.zoomLevel = 7\n        mapView.centerCoordinate = CLLocationCoordinate2D(latitude: 35.631642, longitude: 51.349183)\n        \n    }\n    \n    // This example uses Iran's boundaries to restrict the camera movement.\n    func mapView(_ mapView: MGLMapView, shouldChangeFrom oldCamera: MGLMapCamera, to newCamera: MGLMapCamera) -\u003e Bool {\n        \n        // Get the current camera to restore it after.\n        let currentCamera = mapView.camera\n        \n        // From the new camera obtain the center to test if it’s inside the boundaries.\n        let newCameraCenter = newCamera.centerCoordinate\n        \n        // Set the map’s visible bounds to newCamera.\n        mapView.camera = newCamera\n        let newVisibleCoordinates = mapView.visibleCoordinateBounds\n        \n        // Revert the camera.\n        mapView.camera = currentCamera\n        \n        // Test if the newCameraCenter and newVisibleCoordinates are inside self.iran.\n        let inside = MGLCoordinateInCoordinateBounds(newCameraCenter, self.iran)\n        let intersects = MGLCoordinateInCoordinateBounds(newVisibleCoordinates.ne, iran) \u0026\u0026 MGLCoordinateInCoordinateBounds(newVisibleCoordinates.sw, iran)\n        \n        return inside \u0026\u0026 intersects\n    }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmap-ir%2Fios-sdk-v2-map-demo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmap-ir%2Fios-sdk-v2-map-demo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmap-ir%2Fios-sdk-v2-map-demo/lists"}