Search code examples
iosswiftxcodeswift3uipickerview

How to hide UIPIckerView when the user touches the row already selected


I have in my code more than one UIPickerView, and let's say that the user opens the PickerView unintentionally, but wants to keep the same row selected, as I do for when the user touches the already selected row, the selector hide?

I tried this: UIPicker detect tap / touch on currently selected row, but I could not make it work.

--- edit ---

I'll try to be more specific. I'm a beginner in programming and the Swift language.

This is my ViewController.swift

import UIKit

class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {

@IBOutlet weak var colorLabel: UILabel!
@IBOutlet weak var sizeLabel: UILabel!

@IBOutlet weak var colorButton: UIButton!
@IBOutlet weak var sizeButton: UIButton!

@IBOutlet weak var resultButton: UIButton!

@IBOutlet weak var colorPickerView: UIPickerView! = UIPickerView()
@IBOutlet weak var sizePickerView: UIPickerView! = UIPickerView()

var colorPickerData = ["Blue", "Red"]
var sizePickerData = ["Small", "Big"]

var descri = String()
var resultadoImagem = UIImage()

override func viewDidLoad() {
    super.viewDidLoad()


    colorPickerView.isHidden = true
    sizePickerView.isHidden = true


    self.colorPickerView.delegate = self
    self.colorPickerView.dataSource = self

    self.sizePickerView.delegate = self
    self.sizePickerView.dataSource = self


    colorLabel.text = colorPickerData[0]
    sizeLabel.text = sizePickerData[0]
}

override func didReceiveMemoryWarning() {

    super.didReceiveMemoryWarning()

}

func numberOfComponents(in pickerView: UIPickerView) -> Int {
    return 1
}

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
    if pickerView.tag == 1 {
        return colorPickerData.count
    } else if pickerView.tag == 2 {
        return sizePickerData.count
    }
    return 0
}

func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
    if pickerView.tag == 1 {
        return "\(colorPickerData[row])"
    } else if pickerView.tag == 2 {
        return "\(sizePickerData[row])"
    }
    return ""
}

func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
    if pickerView.tag == 1 {
        colorLabel.text = colorPickerData[row]
        colorPickerView.isHidden = true
    } else if pickerView.tag == 2 {
        sizeLabel.text = sizePickerData[row]
        sizePickerView.isHidden = true
    }
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    colorPickerView.isHidden = true
    sizePickerView.isHidden = true
}

@IBAction func colorButton(_ sender: Any) {
    colorPickerView.isHidden = !colorPickerView.isHidden
}

@IBAction func sizeButton(_ sender: Any) {
    sizePickerView.isHidden = !sizePickerView.isHidden
}

@IBAction func resultButton(_ sender: Any) {
    if (colorLabel.text == "Blue")
        && (sizeLabel.text == "Big") { resultadoImagem = UIImage(named: "blue-big.png")!; descri = "BLUE"}
    else if (colorLabel.text == "Blue")
        && (sizeLabel.text == "Small") { resultadoImagem = UIImage(named: "blue-small.png)!; descri = "BLUE"}
    else if (colorLabel.text == "Red")
        && (sizeLabel.text == "Big") { resultadoImagem = UIImage(named: "red-big.png")!; descri = "RED"}
    else if (colorLabel.text == "Red")
        && (sizeLabel.text == "Small") { resultadoImagem = UIImage(named: "red-small.png")!; descri = "RED"}

    self.performSegue(withIdentifier: "resultSegue", sender: nil)
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "resultSegue" {
        let DestViewController : SecondViewController = segue.destination as! SecondViewController

    DestViewController.descText = descri
    DestViewController.resultPhoto = resultadoImagem
    }
}

}

This code works great, when I hit the "Result" button it goes to the SecondViewController and shows photo and description.

My difficulty is, the colorPickerView opens with the data "Blue" selected, just as the sizerPickerView opens with the data "Small" by default. I want pickerView to close/hide when the user touches the data that is already selected.


Solution

  • Conform class to UIGestureRecognizerDelegate protocol

    class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource, UIGestureRecognizerDelegate { 
        // ... 
    }
    

    Add a UITapGestureRecognizer to your colorPickerView

    let tap = UITapGestureRecognizer(target: self, action: #selector(self.tapAction(_:)))
    tap.cancelsTouchesInView = false
    tap.delegate = self
    colorPickerView.addGestureRecognizer(tap)
    

    Implement UIGestureRecognizerDelegate method

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
    

    Finally implement tapAction

    func tapAction(_ tapRecognizer:UITapGestureRecognizer) {
        if tapRecognizer.state == .ended {
            let rowHeight : CGFloat  = colorPickerView.rowSize(forComponent: 0).height
            let selectedRowFrame: CGRect = colorPickerView.bounds.insetBy(dx: 0.0, dy: (colorPickerView.frame.height - rowHeight) / 2.0)
            let userTappedOnSelectedRow = selectedRowFrame.contains(tapRecognizer.location(in: colorPickerView))
            if (userTappedOnSelectedRow){
                let selectedRow = colorPickerView.selectedRow(inComponent: 0)
                //do whatever you want here
            }
        }
    }
    

    Replicate steps 2 and 4 using sizerPickerView to extend functionality.