Custom map annotations and clustersFraction (rational number) structure with custom operatorsCreate random...

Do authors have to be politically correct in article-writing?

How to avoid being sexist when trying to employ someone to function in a very sexist environment?

How do I say "Brexit" in Latin?

What's the most convenient time of year to end the world?

Is a debit card dangerous for an account with low balance and no overdraft protection?

How can animals be objects of ethics without being subjects as well?

Placing an adverb between a verb and an object?

Parsing a string of key-value pairs as a dictionary

What is the most triangles you can make from a capital "H" and 3 straight lines?

What makes the Forgotten Realms "forgotten"?

Dilemma of explaining to interviewer that he is the reason for declining second interview

Difference between two quite-similar Terminal commands

Does fast page mode apply to ROM?

How to prevent users from executing commands through browser URL

Process to change collation on a database

Can a person refuse a presidential pardon?

Avoiding morning and evening handshakes

How would an AI self awareness kill switch work?

Grade 10 Analytic Geometry Question 23- Incredibly hard

If I sold a PS4 game I owned the disc for, can I reinstall it digitally?

How to tag distinct options/entities without giving any an implicit priority or suggested order?

What to do if authors don't respond to my serious concerns about their paper?

Every character has a name - does this lead to too many named characters?

insert EOF statement before the last line of file



Custom map annotations and clusters


Fraction (rational number) structure with custom operatorsCreate random way from one point to another in mapCustom view for data input (in-app (non-system) keyboard)A custom UIView with gradient fill, border, and variable corner radiiTransformations an array, combination of between map and reduceSingleton class with custom initializerHackerRank Caesar Cipher, not using map, filter, reduce, regexUITableViewDataSource with different sections and SearchControllerCustom caching class in SwiftSwift Throttler and Debouncer













2












$begingroup$


I registered a CustomMapAnnotationView and a ClusterView: the first one simply extends MKMarkerAnnotationView and overrides annotation this way:



override var annotation: MKAnnotation? {
willSet {
// CustomMapAnnotation is based on MKPointAnnotation
if let c = value as? CustomMapAnnotation {
clusteringIdentifier = "c"
markerTintColor = UIColor.orange
displayPriority = .defaultLow
}
}
}


ClusterView is a MKAnnotationView that is used when annotations can be grouped.



 mapView.register(CustomMapAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
mapView.register(ClusterView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultClusterAnnotationViewReuseIdentifier)


Now the code review part.



I use CustomMapAnnotationView and ClusterView this way:



func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {

guard !(annotation is MKUserLocation) else {
return nil
}

if(annotation is CustomMapAnnotation){

let annotationID = "c"

var annotationView: CustomMapAnnotationView?

if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationID) as? CustomMapAnnotationView {
annotationView = dequeuedAnnotationView
annotationView!.annotation = annotation
}
else
{
annotationView = CustomMapAnnotationView(annotation: annotation, reuseIdentifier: annotationID)
annotationView?.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
}

if let annotationView = annotationView {
annotationView.canShowCallout = true
}

return annotationView

}else{

let clusterID = "Cluster"
var clusterView = mapView.dequeueReusableAnnotationView(withIdentifier: clusterID)
if clusterView == nil {
clusterView = ClusterView(annotation: annotation, reuseIdentifier: clusterID)
} else {
clusterView?.annotation = annotation
}

return clusterView
}
}


This code perfectly works or at least it does what I wanted it to do.
But I'm not sure this is correct way to perform this action inside the body of this function. Any suggestion is appreciated.










share|improve this question









$endgroup$

















    2












    $begingroup$


    I registered a CustomMapAnnotationView and a ClusterView: the first one simply extends MKMarkerAnnotationView and overrides annotation this way:



    override var annotation: MKAnnotation? {
    willSet {
    // CustomMapAnnotation is based on MKPointAnnotation
    if let c = value as? CustomMapAnnotation {
    clusteringIdentifier = "c"
    markerTintColor = UIColor.orange
    displayPriority = .defaultLow
    }
    }
    }


    ClusterView is a MKAnnotationView that is used when annotations can be grouped.



     mapView.register(CustomMapAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
    mapView.register(ClusterView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultClusterAnnotationViewReuseIdentifier)


    Now the code review part.



    I use CustomMapAnnotationView and ClusterView this way:



    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {

    guard !(annotation is MKUserLocation) else {
    return nil
    }

    if(annotation is CustomMapAnnotation){

    let annotationID = "c"

    var annotationView: CustomMapAnnotationView?

    if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationID) as? CustomMapAnnotationView {
    annotationView = dequeuedAnnotationView
    annotationView!.annotation = annotation
    }
    else
    {
    annotationView = CustomMapAnnotationView(annotation: annotation, reuseIdentifier: annotationID)
    annotationView?.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
    }

    if let annotationView = annotationView {
    annotationView.canShowCallout = true
    }

    return annotationView

    }else{

    let clusterID = "Cluster"
    var clusterView = mapView.dequeueReusableAnnotationView(withIdentifier: clusterID)
    if clusterView == nil {
    clusterView = ClusterView(annotation: annotation, reuseIdentifier: clusterID)
    } else {
    clusterView?.annotation = annotation
    }

    return clusterView
    }
    }


    This code perfectly works or at least it does what I wanted it to do.
    But I'm not sure this is correct way to perform this action inside the body of this function. Any suggestion is appreciated.










    share|improve this question









    $endgroup$















      2












      2








      2





      $begingroup$


      I registered a CustomMapAnnotationView and a ClusterView: the first one simply extends MKMarkerAnnotationView and overrides annotation this way:



      override var annotation: MKAnnotation? {
      willSet {
      // CustomMapAnnotation is based on MKPointAnnotation
      if let c = value as? CustomMapAnnotation {
      clusteringIdentifier = "c"
      markerTintColor = UIColor.orange
      displayPriority = .defaultLow
      }
      }
      }


      ClusterView is a MKAnnotationView that is used when annotations can be grouped.



       mapView.register(CustomMapAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
      mapView.register(ClusterView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultClusterAnnotationViewReuseIdentifier)


      Now the code review part.



      I use CustomMapAnnotationView and ClusterView this way:



      func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {

      guard !(annotation is MKUserLocation) else {
      return nil
      }

      if(annotation is CustomMapAnnotation){

      let annotationID = "c"

      var annotationView: CustomMapAnnotationView?

      if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationID) as? CustomMapAnnotationView {
      annotationView = dequeuedAnnotationView
      annotationView!.annotation = annotation
      }
      else
      {
      annotationView = CustomMapAnnotationView(annotation: annotation, reuseIdentifier: annotationID)
      annotationView?.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
      }

      if let annotationView = annotationView {
      annotationView.canShowCallout = true
      }

      return annotationView

      }else{

      let clusterID = "Cluster"
      var clusterView = mapView.dequeueReusableAnnotationView(withIdentifier: clusterID)
      if clusterView == nil {
      clusterView = ClusterView(annotation: annotation, reuseIdentifier: clusterID)
      } else {
      clusterView?.annotation = annotation
      }

      return clusterView
      }
      }


      This code perfectly works or at least it does what I wanted it to do.
      But I'm not sure this is correct way to perform this action inside the body of this function. Any suggestion is appreciated.










      share|improve this question









      $endgroup$




      I registered a CustomMapAnnotationView and a ClusterView: the first one simply extends MKMarkerAnnotationView and overrides annotation this way:



      override var annotation: MKAnnotation? {
      willSet {
      // CustomMapAnnotation is based on MKPointAnnotation
      if let c = value as? CustomMapAnnotation {
      clusteringIdentifier = "c"
      markerTintColor = UIColor.orange
      displayPriority = .defaultLow
      }
      }
      }


      ClusterView is a MKAnnotationView that is used when annotations can be grouped.



       mapView.register(CustomMapAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
      mapView.register(ClusterView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultClusterAnnotationViewReuseIdentifier)


      Now the code review part.



      I use CustomMapAnnotationView and ClusterView this way:



      func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {

      guard !(annotation is MKUserLocation) else {
      return nil
      }

      if(annotation is CustomMapAnnotation){

      let annotationID = "c"

      var annotationView: CustomMapAnnotationView?

      if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationID) as? CustomMapAnnotationView {
      annotationView = dequeuedAnnotationView
      annotationView!.annotation = annotation
      }
      else
      {
      annotationView = CustomMapAnnotationView(annotation: annotation, reuseIdentifier: annotationID)
      annotationView?.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
      }

      if let annotationView = annotationView {
      annotationView.canShowCallout = true
      }

      return annotationView

      }else{

      let clusterID = "Cluster"
      var clusterView = mapView.dequeueReusableAnnotationView(withIdentifier: clusterID)
      if clusterView == nil {
      clusterView = ClusterView(annotation: annotation, reuseIdentifier: clusterID)
      } else {
      clusterView?.annotation = annotation
      }

      return clusterView
      }
      }


      This code perfectly works or at least it does what I wanted it to do.
      But I'm not sure this is correct way to perform this action inside the body of this function. Any suggestion is appreciated.







      swift






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Jan 31 at 14:37







      user122593





























          1 Answer
          1






          active

          oldest

          votes


















          2












          $begingroup$

          A couple of observations:





          1. You have code that says:



            var annotationView: CustomMapAnnotationView?

            if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationID) as? CustomMapAnnotationView {
            annotationView = dequeuedAnnotationView
            annotationView!.annotation = annotation
            }
            else
            {
            annotationView = CustomMapAnnotationView(annotation: annotation, reuseIdentifier: annotationID)
            ...
            }


            In iOS 11 and later, the mapView.dequeueReusableAnnotationView(withIdentifier:for:) can reduce that to a single line:



            let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationID, for: annotation)



          2. Your mapView(_:viewFor:) is configuring the annotation views. I’d move that configuration code into the MKAnnotationView implementations for better separation of responsibilities and eliminating cruft from your view controller. For example:



            class CustomMapAnnotationView: MKMarkerAnnotationView {
            static let clusteringIdentifier = Bundle.main.bundleIdentifier! + ".c"

            override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
            super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)

            clusteringIdentifier = CustomMapAnnotationView.clusteringIdentifier
            markerTintColor = .orange
            displayPriority = .defaultLow
            collisionMode = .circle
            rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
            }

            required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
            }

            override var annotation: MKAnnotation? {
            willSet {
            clusteringIdentifier = CustomMapAnnotationView.clusteringIdentifier
            }
            }
            }


            And



            class ClusterView: MKMarkerAnnotationView {
            override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
            super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
            displayPriority = .required
            collisionMode = .circle
            markerTintColor = .purple
            }

            required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
            }
            }


            This cleans up your mapView(_:viewFor:) and keeps the annotation view configuration code where it belongs.




          3. Consider your registration of these classes:



            mapView.register(CustomMapAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
            mapView.register(ClusterView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultClusterAnnotationViewReuseIdentifier)


            Note, you’re registering these identifiers, so your mapView(_:viewFor:) shouldn’t then use its own reuse identifiers of c and Cluster, respectively.




          4. But the whole point of MKMapViewDefaultAnnotationViewReuseIdentifier, etc., in iOS 11 and later is so that you don’t have to implement mapView(_:viewFor:) at all. So you can comment out that whole routine (now that you’ve moved the annotation view configuration code to the annotation views, themselves, where it belongs).



            That having been said, sometimes you need a mapView(_:viewFor:). For example, maybe you need to support iOS versions prior to 11. Or perhaps you have more than three types of annotation types (your main custom annotation, your cluster annotation, and MKUserLocation). But then you wouldn’t register MKMapViewDefaultAnnotationViewReuseIdentifier as the reuse identifier. You’d register your own reuse identifier.



            If you do use your own reused identifier, I would have a static property in my annotation view with the preferred reuse identifier, rather than sprinkling hardcoded strings throughout my codebase. E.g.



            class CustomMapAnnotationView: MKMarkerAnnotationView {
            static let preferredReuseIdentifier = Bundle.main.bundleIdentifier! + ".customMapAnnotationView”

            ...
            }


            And



            mapView.register(CustomMapAnnotationView.self, forAnnotationViewWithReuseIdentifier: CustomMapAnnotationView.preferredReuseIdentifier)


            And



            extension ViewController: MKMapViewDelegate {
            func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
            switch annotation {
            case let annotation as CustomMapAnnotation:
            return mapView.dequeueReusableAnnotationView(withIdentifier: CustomMapAnnotationView.preferredReuseIdentifier, for: annotation)

            ...
            }
            }
            }


            But that’s not needed in your case. You can remove mapView(_:viewFor:) entirely.








          share|improve this answer











          $endgroup$













            Your Answer





            StackExchange.ifUsing("editor", function () {
            return StackExchange.using("mathjaxEditing", function () {
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
            });
            });
            }, "mathjax-editing");

            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "196"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: false,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f212628%2fcustom-map-annotations-and-clusters%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown
























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            2












            $begingroup$

            A couple of observations:





            1. You have code that says:



              var annotationView: CustomMapAnnotationView?

              if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationID) as? CustomMapAnnotationView {
              annotationView = dequeuedAnnotationView
              annotationView!.annotation = annotation
              }
              else
              {
              annotationView = CustomMapAnnotationView(annotation: annotation, reuseIdentifier: annotationID)
              ...
              }


              In iOS 11 and later, the mapView.dequeueReusableAnnotationView(withIdentifier:for:) can reduce that to a single line:



              let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationID, for: annotation)



            2. Your mapView(_:viewFor:) is configuring the annotation views. I’d move that configuration code into the MKAnnotationView implementations for better separation of responsibilities and eliminating cruft from your view controller. For example:



              class CustomMapAnnotationView: MKMarkerAnnotationView {
              static let clusteringIdentifier = Bundle.main.bundleIdentifier! + ".c"

              override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
              super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)

              clusteringIdentifier = CustomMapAnnotationView.clusteringIdentifier
              markerTintColor = .orange
              displayPriority = .defaultLow
              collisionMode = .circle
              rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
              }

              required init?(coder aDecoder: NSCoder) {
              fatalError("init(coder:) has not been implemented")
              }

              override var annotation: MKAnnotation? {
              willSet {
              clusteringIdentifier = CustomMapAnnotationView.clusteringIdentifier
              }
              }
              }


              And



              class ClusterView: MKMarkerAnnotationView {
              override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
              super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
              displayPriority = .required
              collisionMode = .circle
              markerTintColor = .purple
              }

              required init?(coder aDecoder: NSCoder) {
              fatalError("init(coder:) has not been implemented")
              }
              }


              This cleans up your mapView(_:viewFor:) and keeps the annotation view configuration code where it belongs.




            3. Consider your registration of these classes:



              mapView.register(CustomMapAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
              mapView.register(ClusterView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultClusterAnnotationViewReuseIdentifier)


              Note, you’re registering these identifiers, so your mapView(_:viewFor:) shouldn’t then use its own reuse identifiers of c and Cluster, respectively.




            4. But the whole point of MKMapViewDefaultAnnotationViewReuseIdentifier, etc., in iOS 11 and later is so that you don’t have to implement mapView(_:viewFor:) at all. So you can comment out that whole routine (now that you’ve moved the annotation view configuration code to the annotation views, themselves, where it belongs).



              That having been said, sometimes you need a mapView(_:viewFor:). For example, maybe you need to support iOS versions prior to 11. Or perhaps you have more than three types of annotation types (your main custom annotation, your cluster annotation, and MKUserLocation). But then you wouldn’t register MKMapViewDefaultAnnotationViewReuseIdentifier as the reuse identifier. You’d register your own reuse identifier.



              If you do use your own reused identifier, I would have a static property in my annotation view with the preferred reuse identifier, rather than sprinkling hardcoded strings throughout my codebase. E.g.



              class CustomMapAnnotationView: MKMarkerAnnotationView {
              static let preferredReuseIdentifier = Bundle.main.bundleIdentifier! + ".customMapAnnotationView”

              ...
              }


              And



              mapView.register(CustomMapAnnotationView.self, forAnnotationViewWithReuseIdentifier: CustomMapAnnotationView.preferredReuseIdentifier)


              And



              extension ViewController: MKMapViewDelegate {
              func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
              switch annotation {
              case let annotation as CustomMapAnnotation:
              return mapView.dequeueReusableAnnotationView(withIdentifier: CustomMapAnnotationView.preferredReuseIdentifier, for: annotation)

              ...
              }
              }
              }


              But that’s not needed in your case. You can remove mapView(_:viewFor:) entirely.








            share|improve this answer











            $endgroup$


















              2












              $begingroup$

              A couple of observations:





              1. You have code that says:



                var annotationView: CustomMapAnnotationView?

                if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationID) as? CustomMapAnnotationView {
                annotationView = dequeuedAnnotationView
                annotationView!.annotation = annotation
                }
                else
                {
                annotationView = CustomMapAnnotationView(annotation: annotation, reuseIdentifier: annotationID)
                ...
                }


                In iOS 11 and later, the mapView.dequeueReusableAnnotationView(withIdentifier:for:) can reduce that to a single line:



                let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationID, for: annotation)



              2. Your mapView(_:viewFor:) is configuring the annotation views. I’d move that configuration code into the MKAnnotationView implementations for better separation of responsibilities and eliminating cruft from your view controller. For example:



                class CustomMapAnnotationView: MKMarkerAnnotationView {
                static let clusteringIdentifier = Bundle.main.bundleIdentifier! + ".c"

                override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
                super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)

                clusteringIdentifier = CustomMapAnnotationView.clusteringIdentifier
                markerTintColor = .orange
                displayPriority = .defaultLow
                collisionMode = .circle
                rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
                }

                required init?(coder aDecoder: NSCoder) {
                fatalError("init(coder:) has not been implemented")
                }

                override var annotation: MKAnnotation? {
                willSet {
                clusteringIdentifier = CustomMapAnnotationView.clusteringIdentifier
                }
                }
                }


                And



                class ClusterView: MKMarkerAnnotationView {
                override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
                super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
                displayPriority = .required
                collisionMode = .circle
                markerTintColor = .purple
                }

                required init?(coder aDecoder: NSCoder) {
                fatalError("init(coder:) has not been implemented")
                }
                }


                This cleans up your mapView(_:viewFor:) and keeps the annotation view configuration code where it belongs.




              3. Consider your registration of these classes:



                mapView.register(CustomMapAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
                mapView.register(ClusterView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultClusterAnnotationViewReuseIdentifier)


                Note, you’re registering these identifiers, so your mapView(_:viewFor:) shouldn’t then use its own reuse identifiers of c and Cluster, respectively.




              4. But the whole point of MKMapViewDefaultAnnotationViewReuseIdentifier, etc., in iOS 11 and later is so that you don’t have to implement mapView(_:viewFor:) at all. So you can comment out that whole routine (now that you’ve moved the annotation view configuration code to the annotation views, themselves, where it belongs).



                That having been said, sometimes you need a mapView(_:viewFor:). For example, maybe you need to support iOS versions prior to 11. Or perhaps you have more than three types of annotation types (your main custom annotation, your cluster annotation, and MKUserLocation). But then you wouldn’t register MKMapViewDefaultAnnotationViewReuseIdentifier as the reuse identifier. You’d register your own reuse identifier.



                If you do use your own reused identifier, I would have a static property in my annotation view with the preferred reuse identifier, rather than sprinkling hardcoded strings throughout my codebase. E.g.



                class CustomMapAnnotationView: MKMarkerAnnotationView {
                static let preferredReuseIdentifier = Bundle.main.bundleIdentifier! + ".customMapAnnotationView”

                ...
                }


                And



                mapView.register(CustomMapAnnotationView.self, forAnnotationViewWithReuseIdentifier: CustomMapAnnotationView.preferredReuseIdentifier)


                And



                extension ViewController: MKMapViewDelegate {
                func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
                switch annotation {
                case let annotation as CustomMapAnnotation:
                return mapView.dequeueReusableAnnotationView(withIdentifier: CustomMapAnnotationView.preferredReuseIdentifier, for: annotation)

                ...
                }
                }
                }


                But that’s not needed in your case. You can remove mapView(_:viewFor:) entirely.








              share|improve this answer











              $endgroup$
















                2












                2








                2





                $begingroup$

                A couple of observations:





                1. You have code that says:



                  var annotationView: CustomMapAnnotationView?

                  if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationID) as? CustomMapAnnotationView {
                  annotationView = dequeuedAnnotationView
                  annotationView!.annotation = annotation
                  }
                  else
                  {
                  annotationView = CustomMapAnnotationView(annotation: annotation, reuseIdentifier: annotationID)
                  ...
                  }


                  In iOS 11 and later, the mapView.dequeueReusableAnnotationView(withIdentifier:for:) can reduce that to a single line:



                  let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationID, for: annotation)



                2. Your mapView(_:viewFor:) is configuring the annotation views. I’d move that configuration code into the MKAnnotationView implementations for better separation of responsibilities and eliminating cruft from your view controller. For example:



                  class CustomMapAnnotationView: MKMarkerAnnotationView {
                  static let clusteringIdentifier = Bundle.main.bundleIdentifier! + ".c"

                  override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
                  super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)

                  clusteringIdentifier = CustomMapAnnotationView.clusteringIdentifier
                  markerTintColor = .orange
                  displayPriority = .defaultLow
                  collisionMode = .circle
                  rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
                  }

                  required init?(coder aDecoder: NSCoder) {
                  fatalError("init(coder:) has not been implemented")
                  }

                  override var annotation: MKAnnotation? {
                  willSet {
                  clusteringIdentifier = CustomMapAnnotationView.clusteringIdentifier
                  }
                  }
                  }


                  And



                  class ClusterView: MKMarkerAnnotationView {
                  override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
                  super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
                  displayPriority = .required
                  collisionMode = .circle
                  markerTintColor = .purple
                  }

                  required init?(coder aDecoder: NSCoder) {
                  fatalError("init(coder:) has not been implemented")
                  }
                  }


                  This cleans up your mapView(_:viewFor:) and keeps the annotation view configuration code where it belongs.




                3. Consider your registration of these classes:



                  mapView.register(CustomMapAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
                  mapView.register(ClusterView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultClusterAnnotationViewReuseIdentifier)


                  Note, you’re registering these identifiers, so your mapView(_:viewFor:) shouldn’t then use its own reuse identifiers of c and Cluster, respectively.




                4. But the whole point of MKMapViewDefaultAnnotationViewReuseIdentifier, etc., in iOS 11 and later is so that you don’t have to implement mapView(_:viewFor:) at all. So you can comment out that whole routine (now that you’ve moved the annotation view configuration code to the annotation views, themselves, where it belongs).



                  That having been said, sometimes you need a mapView(_:viewFor:). For example, maybe you need to support iOS versions prior to 11. Or perhaps you have more than three types of annotation types (your main custom annotation, your cluster annotation, and MKUserLocation). But then you wouldn’t register MKMapViewDefaultAnnotationViewReuseIdentifier as the reuse identifier. You’d register your own reuse identifier.



                  If you do use your own reused identifier, I would have a static property in my annotation view with the preferred reuse identifier, rather than sprinkling hardcoded strings throughout my codebase. E.g.



                  class CustomMapAnnotationView: MKMarkerAnnotationView {
                  static let preferredReuseIdentifier = Bundle.main.bundleIdentifier! + ".customMapAnnotationView”

                  ...
                  }


                  And



                  mapView.register(CustomMapAnnotationView.self, forAnnotationViewWithReuseIdentifier: CustomMapAnnotationView.preferredReuseIdentifier)


                  And



                  extension ViewController: MKMapViewDelegate {
                  func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
                  switch annotation {
                  case let annotation as CustomMapAnnotation:
                  return mapView.dequeueReusableAnnotationView(withIdentifier: CustomMapAnnotationView.preferredReuseIdentifier, for: annotation)

                  ...
                  }
                  }
                  }


                  But that’s not needed in your case. You can remove mapView(_:viewFor:) entirely.








                share|improve this answer











                $endgroup$



                A couple of observations:





                1. You have code that says:



                  var annotationView: CustomMapAnnotationView?

                  if let dequeuedAnnotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationID) as? CustomMapAnnotationView {
                  annotationView = dequeuedAnnotationView
                  annotationView!.annotation = annotation
                  }
                  else
                  {
                  annotationView = CustomMapAnnotationView(annotation: annotation, reuseIdentifier: annotationID)
                  ...
                  }


                  In iOS 11 and later, the mapView.dequeueReusableAnnotationView(withIdentifier:for:) can reduce that to a single line:



                  let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: annotationID, for: annotation)



                2. Your mapView(_:viewFor:) is configuring the annotation views. I’d move that configuration code into the MKAnnotationView implementations for better separation of responsibilities and eliminating cruft from your view controller. For example:



                  class CustomMapAnnotationView: MKMarkerAnnotationView {
                  static let clusteringIdentifier = Bundle.main.bundleIdentifier! + ".c"

                  override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
                  super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)

                  clusteringIdentifier = CustomMapAnnotationView.clusteringIdentifier
                  markerTintColor = .orange
                  displayPriority = .defaultLow
                  collisionMode = .circle
                  rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
                  }

                  required init?(coder aDecoder: NSCoder) {
                  fatalError("init(coder:) has not been implemented")
                  }

                  override var annotation: MKAnnotation? {
                  willSet {
                  clusteringIdentifier = CustomMapAnnotationView.clusteringIdentifier
                  }
                  }
                  }


                  And



                  class ClusterView: MKMarkerAnnotationView {
                  override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
                  super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
                  displayPriority = .required
                  collisionMode = .circle
                  markerTintColor = .purple
                  }

                  required init?(coder aDecoder: NSCoder) {
                  fatalError("init(coder:) has not been implemented")
                  }
                  }


                  This cleans up your mapView(_:viewFor:) and keeps the annotation view configuration code where it belongs.




                3. Consider your registration of these classes:



                  mapView.register(CustomMapAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
                  mapView.register(ClusterView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultClusterAnnotationViewReuseIdentifier)


                  Note, you’re registering these identifiers, so your mapView(_:viewFor:) shouldn’t then use its own reuse identifiers of c and Cluster, respectively.




                4. But the whole point of MKMapViewDefaultAnnotationViewReuseIdentifier, etc., in iOS 11 and later is so that you don’t have to implement mapView(_:viewFor:) at all. So you can comment out that whole routine (now that you’ve moved the annotation view configuration code to the annotation views, themselves, where it belongs).



                  That having been said, sometimes you need a mapView(_:viewFor:). For example, maybe you need to support iOS versions prior to 11. Or perhaps you have more than three types of annotation types (your main custom annotation, your cluster annotation, and MKUserLocation). But then you wouldn’t register MKMapViewDefaultAnnotationViewReuseIdentifier as the reuse identifier. You’d register your own reuse identifier.



                  If you do use your own reused identifier, I would have a static property in my annotation view with the preferred reuse identifier, rather than sprinkling hardcoded strings throughout my codebase. E.g.



                  class CustomMapAnnotationView: MKMarkerAnnotationView {
                  static let preferredReuseIdentifier = Bundle.main.bundleIdentifier! + ".customMapAnnotationView”

                  ...
                  }


                  And



                  mapView.register(CustomMapAnnotationView.self, forAnnotationViewWithReuseIdentifier: CustomMapAnnotationView.preferredReuseIdentifier)


                  And



                  extension ViewController: MKMapViewDelegate {
                  func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
                  switch annotation {
                  case let annotation as CustomMapAnnotation:
                  return mapView.dequeueReusableAnnotationView(withIdentifier: CustomMapAnnotationView.preferredReuseIdentifier, for: annotation)

                  ...
                  }
                  }
                  }


                  But that’s not needed in your case. You can remove mapView(_:viewFor:) entirely.









                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited 12 hours ago

























                answered 12 hours ago









                RobRob

                32639




                32639






























                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Code Review Stack Exchange!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    Use MathJax to format equations. MathJax reference.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f212628%2fcustom-map-annotations-and-clusters%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    is 'sed' thread safeWhat should someone know about using Python scripts in the shell?Nexenta bash script uses...

                    How do i solve the “ No module named 'mlxtend' ” issue on Jupyter?

                    Pilgersdorf Inhaltsverzeichnis Geografie | Geschichte | Bevölkerungsentwicklung | Politik | Kultur...