Search code examples
iosswiftuipickerview

How can I invoke a sub-class of UIPickerView inSwift


I have a ViewController with a UIPickerView as a single control myPickerView which is of a class MyPickerView which I created as a sub-class of UIPickerView. I invoke myPickerView in ViewController viewDidLoad by myPickerView.viewDidLoad. However, this does not execute the source functions of MyPickerView.

I need a clarification of how I can make this work. My reason for MyPickerView is that it has a lot of special code that I did not want to clutter up the main ViewController. See the example code below:

import UIKit

class ViewController: UIViewController {

@IBOutlet weak var myPickerView: MyPickerView!
override func viewDidLoad() {
    super.viewDidLoad()
    myPickerView.viewDidLoad()
}

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


}


import UIKit
var gSep = ","
class MyPickerView: UIPickerView , UIPickerViewDataSource,     UIPickerViewDelegate {
var pickerData = [[" "],[" "]]
var category = [""]
var subCategory = [""]
var dictMenuList = [String:String]()

//MARK:- category/subcategory picker


func viewDidLoad() {
    println("MyPickerView: viewDidLoad")

    dictMenuList = ["Medical":"Sub-Cat 1.1,Sub-Cat 1.2,Sub-Cat 1.3,Sub-Cat 1.4,Sub-Cat 1.5,Sub-Cat 1.6,Sub-Cat 1.7",
        "Taxes": "Sub-Cat 2.1,Sub-Cat 2.2,Sub-Cat 2.3,Sub-Cat 2.4",
        "Bills": "Sub-Cat 3.1,Sub-Cat 3.2,Sub-Cat 3.3,Sub-Cat 3.4,Sub-Cat 3.5,Sub-Cat 3.6,Sub-Cat 3.7"]

    println("MyPickerView dictMenuList: \(dictMenuList)")



    self.reloadAllComponents()

    let firstKey = self.loadPickerWithCategory(0)
    self.loadPickerWithSubCategory(firstKey)

}


func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
    println("MyPickerView: numberOfComponentsInPickerView \(pickerData.count)")
    return pickerData.count
}

func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
    return pickerData[component].count
}

func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
    if component == 0 {
        let selectedKey = category[row]
        loadPickerWithSubCategory(selectedKey)
    }
}

func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
    return pickerData[component][row]
}

func pickerView(pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusingView view: UIView!) -> UIView
{
    var pickerLabel = UILabel()
    pickerLabel.textColor = UIColor.blackColor()
    pickerLabel.text = pickerData[component][row]
    pickerLabel.font = UIFont(name: pickerLabel.font.fontName, size: 17)
    pickerLabel.textAlignment = NSTextAlignment.Center
    return pickerLabel
}

func loadPickerWithCategory (row: Int) -> String{
    println("loadPickerWithCategory")
    category = [String](dictMenuList.keys)
    println("MyPickerView: category: \(category)")
    if category.isEmpty {
        return ""
    }
    let n1 = dictMenuList.count
    pickerData[0].removeAll(keepCapacity: true)
    for i in 0 ..< n1
    {
        pickerData[0].append(category[i])
    }
    return category[row]
}

func loadPickerWithSubCategory (key: String) {
    println("MyPickerView: loadPickerWithSubCategory")
    let x = dictMenuList[key]
    subCategory = x!.componentsSeparatedByString(gSep)
    let n1 = subCategory.count
    pickerData[1].removeAll(keepCapacity: true)
    if subCategory.isEmpty {
        return
    }
    for i in 0 ..< n1
    {
        pickerData[1].append(subCategory[i])
    }

    self.reloadAllComponents()
}

}

Solution

  • The method viewDidLoad is a view controller method, not a view method. A UIPickerView is a subclass of UIView, not UIViewController, so the system will not call your viewDidLoad method.

    You need to override one or more of the init methods.

    If you're loading your picker view from a Storyboard or XIB, you probably want to override initWithCoder.

    If you're creating your picker in code, you probably want to override initWithFrame.

    I sometimes create a method setup that I call from both initWithCoder: and from initWithFrame:. That way my setup code gets called regardless of how the view object is loaded.

    I vaguely remember reading that there is a better way of handling this dueling initializers problem in Swift, but I don't remember what it is. (I'm still learning Swift.)

    EDIT:

    It just occurs to me that you can use the method awakeFromNib to do setup after your view has been loaded and all of it's outlets are set up. That's roughly equivalent to the viewDidLoad call for view controllers. I should have thought of that sooner.

    (awakeFromNib is a method of NSObject, so it's a bit hard to find if you don't know it exists.)