Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Manage a UIPickerView from an External Class - Using Swift

I cannot seem to find the connection to have an external class manage a view in a ViewController. I'm new to iOS and have spent considerable looking for a solution. Simple Example:

Subclass of UIPickerView

I create a file that is a subclass of UIPickerView and have it conform to the PickerView delegate and datasource.

class MyPickerView: UIPickerView, UIPickerViewDelegate, UIPickerViewDataSource {
    //In here I conform to all the required methods...no problems there
}

Main View Controller with Outlet for the PickerView

In my MainViewController, I create an outlet for my picker view. Also, in the StoryBoard I hookup the "custom class" for my Picker View to MyPickerView above.

class MainViewController: UIViewController {
    @IBOutlet weak var myPickerView: UIPickerView!

    override func viewDidLoad() {
        //how do I hookup my picker view class
    }
}

My Questions:

  1. How do I tell my MainViewController that my subclass "MyPickerView" is managing the picker view for it?

  2. How did I enable communication between the subclass and the view controller?

---------------------

UPDATE: FINAL SOLUTION Incorporating @Oscar's Answer

@Oscar's suggestion below was great. To clarify, I wanted my PickerView subclass to be the UIPickerView Delegate because the Picker will always have the same UI and there are many PickerView delegate methods for UI. (attributedTitleForRow, widthForComponent, rowHeightForComponent, etc) I don't want to call those delegate methods in every ViewController that uses this PickerView.

Now, when the PickerView "didSelectRow" is called, we need to notify our ViewController and pass the value that was selected. To get this to work, I used a Protocol. (summarized below) This topic took me a while to learn, but is crucial, so I suggest spending time with Protocols & Delegation if this doesn't make sense.

  1. Create a protocol in the PickerView with a func that will be used to talk to ViewControllers that present this PickerView:

    protocol MyPickerViewProtocol {
        func myPickerDidSelectRow(selectedRowValue:Int?)
    }
    
  2. In the ViewController presenting the PickerView, conform to your PickerView protocol. By doing so, you will have to place the func myPickerDidSelectRow somewhere in your ViewController:

    class MyViewController: MyPickerViewProtocol {
        func myPickerDidSelectRow(selectedRowValue:Int?) { 
            //do stuff to update your ViewController 
        }
    }
    
  3. @Oscar's answer below will hookup the picker view to your view controller, but there's one last thing. In order for the PickerView to talk back, you will want a property in your PickerView, that is a reference to the view controller it's contained in. Here's the PickeView and ViewController classes in perspective:

    //PickerView Subclass ------------------
    protocol MyPickerViewProtocol {
        func myPickerDidSelectRow(selectedRowValue:Int?)
    }
    
    class MyPickerView: UIPickerView {
        //Note: this var is of type your Picker protocol above. Because the ViewController will conform to the protocol, this var will be the reference (or the pointer) to the protocol func you implement in your ViewController...which is myPickerDidSelectRow
        var propertyThatReferencesThisViewController:MyPickerViewProtocol?
    }        
    
    //ViewController Class ----------------
    myPicker = MyPickerView()
    myPickerView.dataSource = myPicker //note: myPickerView is the outlet of type UIPickerView in your ViewController
    myPickerView.delegate = myPicker
    //HERE'S THE PROPERTY from our PickerView subclass that will point to this ViewController's protocol methods that we implemented. From the MyPickerViewProtocol
    myPicker.propertyThatReferencesThisViewController = self
    
  4. Now when a row is selected in our PickerView, let's tell the ViewController using our property: propertyThatReferencesThisViewController

    class MyPickerView: UIPickerView {
        //This Property points to the ViewController conforming to the protocol. This property will only be able to access the stuff you put in the protocol. It won't access everything in your ViewController
        var propertyThatReferencesThisViewController:MyPickerViewProtocol?
    
        //didSelectRow UIPickerView Delegate method that apple gives us
        func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
            //get your picker values that you need
            let theRowValue = someArray[row]
            propertyThatReferencesThisViewController?.myPickerDidSelectRow(theRowValue)
    
            //the ViewController func will be called passing the row value along
        }
    }
    
like image 584
Claudio Morsella Avatar asked Sep 04 '15 19:09

Claudio Morsella


2 Answers

Subclass Pickerview

class MyPickerView: UIPickerView, UIPickerViewDataSource, UIPickerViewDelegate {

    var oficinas = ["oficina 1", "Oficinas 2", "Oficina 3"]

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

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

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

Main View Controller with Outlet for the PickerView

class MainViewController: UIViewController {
    @IBOutlet weak var myPickerView: UIPickerView!

    var pickerOficinas: MyPickerView!

    override func viewDidLoad() {
        //how do I hookup my picker view class
        pickerOficinas = MyPickerView()
        myPickerView.delegate = pickerOficinas
        myPickerView.dataSource = pickerOficinas
    }
}
like image 171
Oscar Larriega Avatar answered Nov 04 '22 21:11

Oscar Larriega


I think you may have got hold of the wrong end of the stick!

Why do you want to make the picker its own delegate? The point of having a delegate is that it can tell its delegate what has been selected etc.

I would think that what you should be doing is making your view controller conform to UIPickerViewDelegate and make it the delegate of the picker and put the logic for whatever you want to happen when an item is picked in those delegate methods. I can't see any other way of 'telling' your view controller about the picker.

Also, if you reference to your picker is weak, then unless you are holding a strong reference to it somewhere else, at all times (eg it is part of the view hierarchy) it will be deallocated.

like image 1
Rupert Avatar answered Nov 04 '22 21:11

Rupert