Search code examples
iosswiftsegue

Segue Loads Next Screen Twice


I've searched around and can't seem to find an answer to why my segue is loading the next view controller twice. For some reason it only happens occasionally, I'd say around 1/5 times the getMyLocation button is pressed. What I would like to happen is the user press getMylocation and from there grab the user's coordinates, store the data, and present the next view with the stored data. Because of this, I have performSegue in didUpdatedLocations so that I can store the data before calling the segue. I should note that the segue was created in the storyboard by ctrl click and dragging my main view to the next one, not by dragging a button.

My code first the first view:

// Declare all IB Outlets
@IBOutlet weak var cityLabel: SpringTextField!

@IBOutlet weak var stateLabel: SpringTextField!

@IBOutlet weak var searchButton: SpringButton!

@IBOutlet weak var getLocationButton: SpringButton!

@IBOutlet weak var locationButton: UIButton!

@IBOutlet weak var citySearchButton: UIButton!

// Declare all variables

var latAndLong = ""
var city = ""
var state = ""
let locationManager = CLLocationManager()
let limitLength = 2

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {

    guard let locValue: CLLocationCoordinate2D = manager.location?.coordinate else { return }
    let lat = locValue.latitude
    let long = locValue.longitude
    latAndLong = "\(lat),\(long)"
    locationManager.stopUpdatingLocation()
    self.performSegue(withIdentifier: "goToSecond", sender: self)

}


override func viewDidLoad() {
    super.viewDidLoad()


}
override func viewDidAppear(_ animated: Bool) {


    // Declare text label delegates to allow control
    cityLabel.delegate = self as UITextFieldDelegate
    stateLabel.delegate = self as UITextFieldDelegate

    // Define what tap to look for
    let tap : UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(UIViewController.dismissKeyboard))
    tap.cancelsTouchesInView = false
    view.addGestureRecognizer(tap)

    // Delgate Location Manager
    locationManager.delegate = self
    locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters


}

// Allow keyboard to switch text fields from city to state by pressing return
internal func textFieldShouldReturn(_ textField : UITextField) -> Bool {
    if textField == cityLabel {
        city = cityLabel.text!
        textField.resignFirstResponder()
        stateLabel.becomeFirstResponder()
    }
    if textField == stateLabel {
        state = stateLabel.text!
        performSegue(withIdentifier: "goToSecond", sender: self)
    }
    return true
}
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        var newLength = 0
        if textField == stateLabel {
            guard let text = textField.text else { return true }
            newLength = text.count + string.count - range.length
            return newLength <= 2
        }
        else if textField == cityLabel {
            guard let text = textField.text else { return true }
            newLength = text.count + string.count - range.length
            return newLength <= 40
        }
       return true
    }

// Location manager required function + store location variables + perform transition to next view


// Defines what happens when either "Get My Location" or "Search by city is pressed"

@IBAction func getMyLocationPressed(_ sender: Any) {
    city = ""
    state = ""
    let status = CLLocationManager.authorizationStatus()
    if status == .authorizedWhenInUse {
        locationManager.startUpdatingLocation()
    }
        else if status == .notDetermined {
            locationManager.requestWhenInUseAuthorization()
            locationManager.startUpdatingLocation()
           return
            }
            else if status == .denied || status == .restricted {
                let alert = UIAlertController(title : "Location Services Disabled", message: "Please enable location, or search by City and State", preferredStyle: .alert)
                let okAction = UIAlertAction(title: "OK",style: .default, handler: nil)
                alert.addAction(okAction)
                present(alert, animated: true, completion: nil)
                return

    }


}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
    print("didnt work")
}



@IBAction func getLocationPressed(_ sender: SpringButton) {
    let status = CLLocationManager.authorizationStatus()
    if status == .notDetermined {
        locationManager.requestWhenInUseAuthorization()

        return
    }
    else if status == .denied || status == .restricted {
        let alert = UIAlertController(title : "Location Services Disabled", message: "Please enable location, or search by City and State", preferredStyle: .alert)
        let okAction = UIAlertAction(title: "OK",style: .default, handler: nil)
        alert.addAction(okAction)
        present(alert, animated: true, completion: nil)
        return
    }
    locationManager.startUpdatingLocation()

}



    // Defines what happens during transition to next screen including what variables to send ober
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "goToSecond" {
        let nextVC = segue.destination as! SecondViewController
        nextVC.cityAndState = (city) + "," + (state)
        nextVC.latAndLong = latAndLong

    }
}

}

And my code for my second view:

var latAndLong = ""
var cityAndState = ""

@IBOutlet weak var tableView: UITableView!
var selectedRow = ""
var namesOfPictures = ["Activities-1", "Food", "Drinks", "Random"]

let display = ["Activities", "Food", "Drink", "Random"]
let cellColors = ["#4D606E","#3FBAC2","#D3D4D8","#F5F5F5"]

override func viewDidLoad() {
    super.viewDidLoad()

    configureTableView()
    tableView.separatorStyle = .singleLine

    tableView.register(UINib(nibName: "TableViewCell", bundle: nil), forCellReuseIdentifier: "realCell")

    tableView.delegate = self
    tableView.dataSource = self

    // Do any additional setup after loading the view.
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "realCell", for: indexPath) as! TableViewCell
    cell.backgroundView = UIImageView(image: UIImage(named: namesOfPictures[indexPath.row]))
    cell.label.text = display[indexPath.row]
    return cell
}

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return display.count
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    print(latAndLong)
    selectedRow = display[indexPath.row]
    print(selectedRow)
    tableView.deselectRow(at: indexPath, animated: true)
    performSegue(withIdentifier: "goToActivities", sender: self)


}



func configureTableView() {
    tableView.rowHeight = 152.0

}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}


override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "goToActivities" {
        let nextVC = segue.destination as! ActivitiesViewController
        nextVC.selectedItem = selectedRow
        nextVC.cityAndState = cityAndState
        nextVC.latAndLong = latAndLong
    }
}

Solution

  • CLLoction manager delegate - "didUpdateLocations:" will fire more than once even after stoping location update

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])

    You have to set delegate to nil after getting a valid user location

     func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        //After validating user location
        locationManager.delegate = nil