Guy
Guy

Reputation: 6522

UIPickerView delegate never called when created programatically

I am creating UIPickerView programatically and then adding it to UIAlertController. I've also created a class called StringPickerDelegate, which implements the picker's delegate and data source protocols.

Here's my code when creating the UIPickerView

let pickerView = UIPickerView(frame: CGRect(x: 5, y: 20, width: 250, height: 140))
alertController.view.addSubview(pickerView)
let delegate = StringPickerDelegate(items: items) { index, item in
    onItemSelected?(item)
}
pickerView.dataSource = delegate
pickerView.delegate = delegate

And here's the StringPickerDelegate class

class StringPickerDelegate: NSObject, UIPickerViewDelegate, UIPickerViewDataSource {

    private let items: [String]
    private let didSelectItem: ((Int, String) -> Void)

    init(items: [String], didSelectItem: @escaping ((Int, String) -> Void)) {
        self.items = items
        self.didSelectItem = didSelectItem
    }

    // MARK: UIPickerViewDataSource

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

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

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

    // MARK: UIPickerViewDelegate

    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        didSelectItem(row, items[row])
    }

}

I really can't comprehend why the methods are not getting called. The class properly implements both delegates and I'm also making sure that UIPickerView is getting allocated and added as a subview before settings the delegate and data source.

Edit: I've also tried settings delegates AFTER the UIAlertController is presented to make sure all of the views are fully instantiated, but the result is the same.

Upvotes: 0

Views: 99

Answers (1)

Sweeper
Sweeper

Reputation: 271390

UIPickerView.delegate is declared as weak (as is most other delegates):

weak var delegate: UIPickerViewDelegate? { get set }

This means that the picker only holds a weak reference it. After your code executes, nothing else would hold a strong reference to the StringPickerDelegate, so it will be deinitialised!

You need to store an instance of StringPickerDelegate in your VC:

// outside of any method
var pickerDelegate: StringPickerDelegate?

// ...
pickerDelegate = StringPickerDelegate(items: items) { index, item in
    onItemSelected?(item)
}
pickerView.dataSource = pickerDelegate!
pickerView.delegate = pickerDelegate!

Upvotes: 2

Related Questions