Search code examples
swiftxcodeuipickerview

UIPickerView not returning the correct row that the user has selected


So I just used this SO Post in order to add a second pickerView to my ViewController. I used the answer from the user 'LAOMUSIC Arts', which implemented the idea of using tags from the accepted solution.

Here is my implementation:

import UIKit

class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource, UITextFieldDelegate{
    var molyRow = ""
    var tungRow = ""

    var molyPickerViewOptions = ["mils", "mm", "in."]
    var tungPickerViewOptions = ["mils", "mm", "in."]



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

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        if (pickerView.tag == 1){
            return molyPickerViewOptions.count
        }else{
            return tungPickerViewOptions.count
        }
    }

    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        if (pickerView.tag == 1){
            molyRow = "\(molyPickerViewOptions[row])"
            print("molyRow = ", "\(molyPickerViewOptions[row])")
            return "\(molyPickerViewOptions[row])"

        }else{
            tungRow = "\(tungPickerViewOptions[row])"
            print("tungRow = ", "\(tungPickerViewOptions[row])")
            return "\(tungPickerViewOptions[row])"
        }
    }

As you can see, I have it print the tungRow and molyRow along with their corresponding values. This way, as I'm running the app in the simulator, I can see what value it is getting.

When I tried this, I stumbled on something really weird. It will properly return "mils" and "mm" when those are selected in the simulator, but if I 'flick' from either the "mils" or "mm" row down to the "in." row, it will for some reason return "mils". I'll attach a video to show you.

Video

As you can see, the picker seems to correctly return "mils" and "mm" most of the time, however based on how I 'flick' the picker, "in." will not always be returned when it should.

Please let me know if there is anything I can do to make this post better, more specific, etc.

Looking forward to a response. Thanks in advance!

EDIT: After getting a suggestion to try pickerView(_:didSelectRow:inComponent:), I tried implementing the function to both return, and print the row number. I edited the pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) function so that it simply sets the title:

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

        }else{
            return "\(tungPickerViewOptions[row])"
        }
    }

    private func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) -> Int{
        if (pickerView.tag == 1){
            molyRow = row
            print("molyRow = ", "\(row)")
            return row

        }else{
            tungRow = row
            print("tungRow = ", "\(row)")
            return row
        }
    }

Note that when I tried implementing the pickerView(_:didSelectRow:inComponent:) function, Xcode warned me: Instance method 'pickerView(_:didSelectRow:inComponent:)' nearly matches optional requirement 'pickerView(_:didSelectRow:inComponent:)' of protocol 'UIPickerViewDelegate' Make 'pickerView(_:didSelectRow:inComponent:)' private to silence this warning

I have tried running with and without making the function private. Either way, it still doesn't print the row number.


Solution

  • It looks like you're maybe trying to determine the row selection in the pickerView(_:titleForRow:forComponent:) method. That method is being called by UIKit whenever it needs to determine the text to display in a picker row.

    To find out which row was selected whenever the selection changes, you should check out the pickerView(_:didSelectRow:inComponent:) method of UIPickerViewDelegate.

    EDIT

    Swift isn't calling the method you've written because it doesn't match the signature of the delegate protocol's method. pickerView(_:didSelectRow:inComponent:) is not the same as pickerView(_:didSelectRow:inComponent:) -> Int. This is why you got that compiler warning about the method name almost—but not quite—matching.

    To get UIKit to see the method as the one it should call, you need to remove the Int return. If the method signature matches, and your class is properly assigned as the picker's delegate (which it seems to be since titleForRow: is getting called), then UIKit will see it and call it.