looping functions in swift

Hi,

my idea is to have a map which contains annotations and these annotations display a specific image depending on the time.

Now i have got everything but the timedependence. I use the for-loop to repeatedly create new annotations and then (outside of the for loop) i have functions which are responsible for different annotationactions (e.g. callouts), including the image shown by the annotations.


My solution: put everything, responsible for creating every single annotation inside the loop. Now the algorithm should run through every calculation inside the for-loop/ specifically for every annotation.

Result: t shows all the annotations but with the normal apple-logo/ without my custom image

i hope this helps! Thank you


i thought there must be a way to do this:

for a in b {
      func c () {  }
      func d () {  }
]

Explanation: for a in b, call function c and also call function d and do this until the for loop ends


but i couldn't get it to work

Any ideas?


Thanks for you help,

Gus

Accepted Reply

Your code shown is inconsistent and cannot be compiled.

Maybe you have mixed up some different versions of your code.


I assume `Location` as:

//Locationvariables
struct Location {
    let title: String
    let latitude: Double
    let longitude: Double
    let timeA: Float //<-
    let timeB: Float //<-
}

And I needed to fill some whitespaces.


With those fixes above, I can see two annotations with custom image.

So, I do not understand why shows all the annotations but with the normal apple-logo/ without my custom image.


You may have another version of your code and might be watching the result of a different code.

Anyway, since you have shown a non-buildable code, I cannot rely on the behavior you have described.


Though, the image assigned is not as expected, so I just show you how to fix that part.


With your code (I mean the code shown in your post with my fixes), you use the global variable `icon`.

So, in your `mapView(_:viewFor:)` uses the last value assigned to `icon`, which may be all the same.

You use many global variables, but as far as I can see, any of them have no reason to be global.


So, first of all, your custom annotation should have its own `icon` as it may change in each instance.

//create custom Annotation
class CustomAnnotation: MKPointAnnotation {
    var isCollapsed = true
    var setNeedsToggle = false
    var icon: String = ""
}

(By the way, don't you feel creepy using non-Capitalized name for types?)


And then remove all global variables and update your for-in loop.

    override func viewDidLoad() {
        super.viewDidLoad()
        
        //userLocation
        manager.delegate = self
        manager.desiredAccuracy = kCLLocationAccuracyBest
        manager.requestWhenInUseAuthorization()
        manager.startUpdatingLocation()
        
        //Timevariables
        let date = Date()
        let calendar = Calendar.current
        let minutes = calendar.component(.minute, from: date)
        let hour = calendar.component(.hour, from: date)
        let Hour = hour*100 + minutes
        
        var icon = ""

        //Loop creating different Locations
        for location in locations {
            let annotation = CustomAnnotation()
            annotation.title = location.title
            annotation.coordinate = CLLocationCoordinate2D (latitude: location.latitude, longitude: location.longitude)
            let timeAInt = Int(location.timeA)
            let timeBInt = Int(location.timeB)
            if timeAInt ... timeBInt ~= Hour { icon = "testPin1" }
            else { icon = "testPin2" }
            annotation.icon = icon //<-
            map.addAnnotation(annotation)
        }
        self.map.delegate = self
    }

You also need to update your `mapView(_:viewFor:)` in order to use the `icon` property of the CustomAnnotation.

    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        guard let annot = annotation as? CustomAnnotation else { return nil }
        
        let annotationView = MKAnnotationView(annotation: annot, reuseIdentifier: "customAnnotation")
        annotationView.image = UIImage(named: annot.icon)
        annotationView.canShowCallout = true
        
        let rightButton = UIButton(type: .detailDisclosure)
        rightButton.setImage(UIImage(named: annot.isCollapsed ? "ic_showmore" : "ic_showless"), for: .normal)
        annotationView.rightCalloutAccessoryView = rightButton
        
        if annot.isCollapsed {
            annotationView.detailCalloutAccessoryView = nil
        } else {
            let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 21))
            label.text = "Description Shown When Button is Tapped"
            label.font = UIFont.italicSystemFont(ofSize: 14.0)
            label.numberOfLines = 0
            annotationView.detailCalloutAccessoryView = label
            
            label.widthAnchor.constraint(lessThanOrEqualToConstant: label.frame.width).isActive = true
            label.heightAnchor.constraint(lessThanOrEqualToConstant: 90.0).isActive = true
        }
        return annotationView
    }

You may need to fix some other issues, but when you ask something more, please show your actual latest code which compiles.

Replies

Please show enough code to see if there is something wrong in it. Your explanation is so conceptual and your code example is too simplified.

sure

its a lot but i thought it might be easier

if you have all the code


import UIKit
import MapKit
import CoreLocation
 
//Locationvariables
struct Location {
    let title: String
    let latitude: Double
    let longitude: Double
    let oHour: Float
    let cHour: Float
}
 
/////////////////////////

//set Locations
let locations = [
    Location(title: "TestPin1", latitude: 37.330117, longitude: -122.019916, timeA: 1200, timeB: 1300),
    Location(title: "TestPin2", latitude: 37.337761, longitude: -122.033006, timeA: 0, timeB: 1800)
]
 
/////////////////////////

//create custom Annotation
class customAnnotation: MKPointAnnotation {
    var isCollapsed = true
    var setNeedsToggle = false
}
 
//Timevariables
let date = Date()
let calendar = Calendar.current
let minutes = calendar.component(.minute, from: date)
let hour = calendar.component(.hour, from: date)
let Hour = hour*100 + minutes
 
var icon = ""
 
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate {
 
    @IBOutlet var map: MKMapView!
    
    //userLocation [variables]
    let manager = CLLocationManager()
    
    func locationManager(_ manager: CLLocationManager, didUpdateLocations position: [CLLocation]) {
        let position = position[0]
        let span: MKCoordinateSpan = MKCoordinateSpanMake(0.1, 0.1)
        let myLocation: CLLocationCoordinate2D = CLLocationCoordinate2DMake(position.coordinate.latitude, position.coordinate.longitude)
        let region: MKCoordinateRegion = MKCoordinateRegionMake(myLocation, span)
        map.setRegion(region, animated: true)
        
        self.map.showsUserLocation = true
        
    }
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        //userLocation [algorithm]
        manager.delegate = self
        manager.desiredAccuracy = kCLLocationAccuracyBest
        manager.requestWhenInUseAuthorization()
        manager.startUpdatingLocation()
        
        //Loop creating different Locations
        for location in locations {
            let annotation = customAnnotation()
            annotation.title = location.title
            annotation.coordinate = CLLocationCoordinate2D (latitude: location.latitude, longitude: location.longitude)
            let timeAInt= Int(location.timeA)
            let timeBInt= Int(location.timeB)
            if timeAInt... timeBInt~= Hour { icon = "testPin1" }
            else { icon = "testPin2" }
            map.addAnnotation(annotation)
        }
        self.map.delegate = self
    }
    
    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        guard let annot = annotation as? customAnnotation else { return nil }
        
        if annotation is MKUserLocation {
            return nil
        }
        let annotationView = MKAnnotationView(annotation: annotation, reuseIdentifier: "customAnnotation")
        annotationView.image = UIImage(named: icon)
        annotationView.canShowCallout = true
        
        let rightButton = UIButton(type: .detailDisclosure)
        rightButton.setImage(UIImage(named: annot.isCollapsed ? "ic_showmore" : "ic_showless"), for: .normal)
        annotationView.rightCalloutAccessoryView = rightButton
        
        if (annot.isCollapsed) {
            annotationView.detailCalloutAccessoryView = nil
        }
        else {
            let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 21))
            label.text = "Description Shown When Button is Tapped"
            label.font = UIFont.italicSystemFont(ofSize: 14.0)
            label.numberOfLines = 0
            annotationView.detailCalloutAccessoryView = label
            
            label.widthAnchor.constraint(lessThanOrEqualToConstant: label.frame.width).isActive = true
            label.heightAnchor.constraint(lessThanOrEqualToConstant: 90.0).isActive = true
        }
        return annotationView
    }
    
    func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
        guard let oldAnnot = view.annotation as? customAnnotation else { return }
        
        let annotation = customAnnotation()
        annotation.title = oldAnnot.title
        annotation.coordinate = oldAnnot.coordinate
        annotation.setNeedsToggle = true
        
        if (oldAnnot.isCollapsed) {
            annotation.isCollapsed = false
        }
        
        mapView.removeAnnotation(oldAnnot)
        mapView.addAnnotation(annotation)
    }
    
    internal func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) {
        for view in views {
            if let annotation = view.annotation as? customAnnotation, annotation.setNeedsToggle {
                mapView.selectAnnotation(annotation, animated: true)
                return
            }
        }
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}


i hope this helps!

Your code shown is inconsistent and cannot be compiled.

Maybe you have mixed up some different versions of your code.


I assume `Location` as:

//Locationvariables
struct Location {
    let title: String
    let latitude: Double
    let longitude: Double
    let timeA: Float //<-
    let timeB: Float //<-
}

And I needed to fill some whitespaces.


With those fixes above, I can see two annotations with custom image.

So, I do not understand why shows all the annotations but with the normal apple-logo/ without my custom image.


You may have another version of your code and might be watching the result of a different code.

Anyway, since you have shown a non-buildable code, I cannot rely on the behavior you have described.


Though, the image assigned is not as expected, so I just show you how to fix that part.


With your code (I mean the code shown in your post with my fixes), you use the global variable `icon`.

So, in your `mapView(_:viewFor:)` uses the last value assigned to `icon`, which may be all the same.

You use many global variables, but as far as I can see, any of them have no reason to be global.


So, first of all, your custom annotation should have its own `icon` as it may change in each instance.

//create custom Annotation
class CustomAnnotation: MKPointAnnotation {
    var isCollapsed = true
    var setNeedsToggle = false
    var icon: String = ""
}

(By the way, don't you feel creepy using non-Capitalized name for types?)


And then remove all global variables and update your for-in loop.

    override func viewDidLoad() {
        super.viewDidLoad()
        
        //userLocation
        manager.delegate = self
        manager.desiredAccuracy = kCLLocationAccuracyBest
        manager.requestWhenInUseAuthorization()
        manager.startUpdatingLocation()
        
        //Timevariables
        let date = Date()
        let calendar = Calendar.current
        let minutes = calendar.component(.minute, from: date)
        let hour = calendar.component(.hour, from: date)
        let Hour = hour*100 + minutes
        
        var icon = ""

        //Loop creating different Locations
        for location in locations {
            let annotation = CustomAnnotation()
            annotation.title = location.title
            annotation.coordinate = CLLocationCoordinate2D (latitude: location.latitude, longitude: location.longitude)
            let timeAInt = Int(location.timeA)
            let timeBInt = Int(location.timeB)
            if timeAInt ... timeBInt ~= Hour { icon = "testPin1" }
            else { icon = "testPin2" }
            annotation.icon = icon //<-
            map.addAnnotation(annotation)
        }
        self.map.delegate = self
    }

You also need to update your `mapView(_:viewFor:)` in order to use the `icon` property of the CustomAnnotation.

    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        guard let annot = annotation as? CustomAnnotation else { return nil }
        
        let annotationView = MKAnnotationView(annotation: annot, reuseIdentifier: "customAnnotation")
        annotationView.image = UIImage(named: annot.icon)
        annotationView.canShowCallout = true
        
        let rightButton = UIButton(type: .detailDisclosure)
        rightButton.setImage(UIImage(named: annot.isCollapsed ? "ic_showmore" : "ic_showless"), for: .normal)
        annotationView.rightCalloutAccessoryView = rightButton
        
        if annot.isCollapsed {
            annotationView.detailCalloutAccessoryView = nil
        } else {
            let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 21))
            label.text = "Description Shown When Button is Tapped"
            label.font = UIFont.italicSystemFont(ofSize: 14.0)
            label.numberOfLines = 0
            annotationView.detailCalloutAccessoryView = label
            
            label.widthAnchor.constraint(lessThanOrEqualToConstant: label.frame.width).isActive = true
            label.heightAnchor.constraint(lessThanOrEqualToConstant: 90.0).isActive = true
        }
        return annotationView
    }

You may need to fix some other issues, but when you ask something more, please show your actual latest code which compiles.

Perfect thank you very much!

i will try your changes and see if it works

The code i have shown is my default code which shows the same image for every annotation


the problem i have described in my question ("apple-logo") appeared when i put all the functions inside the for-loop

i just wanted to describe a problem i had when trying to solve this

--> this is my latest code

Yep worked

thanks a lot