iosxcodestoryboardunwind-segueuipickerviewcontroller

Swift 4 Odd unexpected jump back to earlier storyboard


I’m very new to Swift and Xcode and I’ve built a simple two storyboard app which has a welcome screen and a button that triggers a seque to second storyboard (for which I have created its own class based on UIVIewController. Second storyboard has a couple of UIPickerView objects a and a couple of UIDatePickerView objects and a button. I have some basic actions under the didSelectRow function. However when I build and run the app I get an odd and seemingly random bevahiour where selecting something in the first picker view takes me back to the welcome screen (an unwind seque?). I haven’t put in any code to do that! Any ideas?

import UIKit
class SecondViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
    @IBOutlet weak var cityPicker: UIPickerView!
    @IBOutlet weak var fromDatePicker: UIDatePicker!
    @IBOutlet weak var toDatePicker: UIDatePicker!
    @IBOutlet weak var nextThirdViewButton: UIButton!
    // temporay array for tsting - Populate with sqlite db extract
    var cityData = ["Please select city", "Beijing", "Calcutta", "Detroit", "Edinburgh", "Glasgow", "London", "Manchester", "New  York", "Rio de Janerio", "Washington"]
    // key variables and flags
    var cityPicked = false
    var fromTimePicked = false
    var toTimePicked = false
    var citySelected: String?
    var fromDate: Date?
    var toDate: Date?
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        fromDatePicker.minimumDate = Date()
        toDatePicker.minimumDate = Date()
        fromDate = Date()
        nextThirdViewButton.layer.cornerRadius = 15
        nextThirdViewButton.layer.masksToBounds = true
        cityPicker.delegate = self
        cityPicker.dataSource = self
    }
    //PickerView functions
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return cityData.count
    }
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        return cityData[row]
    }
    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        //check user has selcted a city from pickerview and set flags accordingly
        if self.cityData[row] != "Please select city" {
            self.citySelected = self.cityData[row]
            self.cityPicked = true
        } else {
            self.citySelected = "Please select city"
            self.cityPicked = false
        }
    }
    @IBAction func fromDatePicked(_ sender: Any) {
        fromDate = fromDatePicker.date
        if toTimePicked == true {
            checkDates(fromDate: fromDate!, toDate: toDate!)
        }
        fromTimePicked = true
    }
    @IBAction func toDatePicked(_ sender: Any) {
        toDate = toDatePicker.date
        checkDates(fromDate: fromDate!, toDate: toDate!)
    }
    //Function to check if 'to date' is earlier than 'from date' and issue alert
    func checkDates (fromDate: Date, toDate: Date) {
        switch toDate.compare(fromDate) {
        case .orderedDescending:
            toTimePicked = true
        case .orderedAscending:
            presentAlert(title: "Date Alert", message: "You can't travel back in time!")
            toTimePicked = false
        default: return
        }
    }
    // Function to move to third storyboard
    @IBAction func nextThirdViewButton(_ sender: Any) {
        if !(cityPicked && fromTimePicked && toTimePicked) {
            presentAlert(title: "You're not ready yet", message: "Please complete choices")
        } else {
            performSegue(withIdentifier: "thirdViewSeque", sender: nil)
        }
    }
    //reusable alert function
    func presentAlert(title: String, message: String) {
        let presentAlert = UIAlertController(title: title, message: message, preferredStyle: .alert)
        let dismissButton = UIAlertAction (title: "Dismiss", style: .cancel, handler: {
            (dateAlert: UIAlertAction) -> Void in
        })
        presentAlert.addAction(dismissButton)
        self.present(presentAlert, animated: true, completion: nil)
    }
}

Solution

  • The problem is that you are using a .partialCurl presented view controller. Don't. You've stumbled on a terrible bug, namely that there is an invisible area of the presented view controller which, if tapped, dismisses the view controller.

    I have an elaborate and extremely hacky workaround, though the simplest solution is to avoid .partialCurl entirely. Here it is; put this code into your presented view controller:

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        // workaround for curl bug
        if let grs = self.view.gestureRecognizers {
            for g in grs {
                if NSStringFromClass(type(of:g)).hasSuffix("CurlUpTapGestureRecognizer") {
                    g.isEnabled = false
                }
            }
        }
    }