Search code examples
swiftappenduipickerview

Creating 3 columns in UIPickerView, also using append function


This is another question of an app I am trying to develop for a small company. Essentially, I want to make it easier for people to find account numbers. The first column in the PickerView is essentially the department. The second will be the task that he/she is going to do, and the third will be the location. I've figured out how to visually make 3 columns, but i cannot figure out how to put it into the code. By that, I mean how to actually set values/selections for the third column. Furthermore, I'm having some issues trying to connect it with the .append function so that only certain options are presented with certain selections in the column. The append function isn't a necessity, but it would be nice to use it if possible.

This is the first app I have ever coded and I "trained myself", hence my amateur mistakes if any. Any help is appreciated.

import UIKit

cllass Account {
var account: String
var jlCode: [String]
var location: [[String]]

init(account:String, jlCode:[String], location:[[String]]) {
    self.jlCode = jlCode
    self.account = account
    self.location = location
    }

}

class ViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {

@IBOutlet weak var pickerView: UIPickerView!
@IBOutlet weak var accountLbl: UILabel!
@IBOutlet weak var infoLbl: UILabel!
@IBOutlet weak var imageView: UIImageView!

var accounts = [Account]()

override func viewDidLoad() {

    pickerView.delegate = self
    pickerView.dataSource = self

    infoLbl.text = """
    test
    """

    //A. PARKS - Tasks for account number 12345678910

    accounts.append(Account(account: "12345678910", jlCode: ["task 1"], location: [["here"]]))

    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
}

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

func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
    if component == 0 {
        return accounts.count
    } else if component == 1 {
        let selectedAccount = pickerView.selectedRow(inComponent: 0)
        return accounts[selectedAccount].jlCode.count
    } else if component == 2 {
        let selectedAccount = pickerView.selectedRow(inComponent: 0)
        let selectedjlCode = pickerView.selectedRow(inComponent: 1)
        let location = pickerView.selectedRow(inComponent: 2)
        return accounts[selectedAccount].jlCode[selectedjlCode].location.count
    }
    return 3
}


func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
    if component == 0 {
        return accounts[row].account
    } else if component == 1 {
        let selectedAccount = pickerView.selectedRow(inComponent: 0)
        return accounts[selectedAccount].jlCode[row]
    } else if component == 2 {
        let selectedAccount = pickerView.selectedRow(inComponent: 0)
        let selectedjlCode = pickerView.selectedRow(inComponent: 1)
        let selectedLocation = pickerView.selectedRow(inComponent: 2)
        return accounts[selectedAccount].jlCode[selectedjlCode].location[selectedLocation]
    }
    return nil
}

func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
    pickerView.reloadComponent(1)

    let selectedAccount = pickerView.selectedRow(inComponent: 0)
    let selectedjlCode = pickerView.selectedRow(inComponent: 1)
    let account = accounts[selectedAccount].account
    let jlCode = accounts[selectedAccount].jlCode[selectedjlCode]

    accountLbl.text = "Account Number: \(account)\nJL Code: \(jlCode)"

}
}

Solution

  • It seems your titleForRow function is somewhat lacking. You are currently only serving component 0 (accounts) and... anything that is not accounts. You need to be more explicit and deliver the texts accordingly.

    To help you on your way, and make it easier for you to comprehend what is happening might I suggest that you create these constants in the top of your class?

    private let accountsComponent = 0
    private let tasksComponent = 1
    private let locationsComponent = 2
    

    Then you should alter the titleForRow function to cater for all the above tasks:

    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        if component == accountsComponent {
            return accounts[row].account
        } else if component == tasksComponent {
            let selectedAccount = pickerView.selectedRow(inComponent: accountsComponent)
            return accounts[selectedAccount].jlCode[row]
        } else if component == locationsComponent {
            // THIS IS WHAT IS MISSING
            // You need to return the location strings from this place.
        }
        return nil
    }   
    

    You will need to return the appropriate strings from the if component == locationsComponent part...

    [edit2] OK, since you're just starting out I'll go the extra mile: It's not obvious that you've grasped exactly what the numberOfRowsInComponent and titleForRow functions are for(?)

    The numberOfRows tells the component how many values each component ("column") contains. The titleForRow is where you return the actual text-strings that should be displayed for each row in each component.

    So, as I mentioned in my comments, create a Location struct:

    struct Location {
        var identifier: Int
        var label: String
    }
    

    In your controller, create an array of this struct.

    let locations: [Location] = [Location(identifier: 1, label: "Outdoors"),
                                 Location(identifier: 2, label: "Indoor"),
                                 Location(identifier: 3, label: "Narnia")]
    

    Then your numberOfRows should lean on this array to return the appropriate number of rows for the location-component:

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        // Only including the parts to return the location-stuff 
        } else if component == 2 {
            return locations.count
        }
        return 0 // This will never be reached as long as the numberOfComponents returns 3
    }
    

    and the titleForRow:

    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        // Only including the parts to return the location-stuff 
        } else if component == 2 {
            return locations[row].label
        }
        return nil
    }