iosuiviewuikituipickerview

UIPickerView won't scroll on a custom UIView


I've got a custom class that conforms to UIView. It contains a UIPickerView and UIButton. I'm using the following code:

class ColorPickerView: UIView {

    // MARK: - Subviews

    private lazy var colorPicker: UIPickerView = {
        let picker = UIPickerView()
        picker.translatesAutoresizingMaskIntoConstraints = false
        picker.dataSource = self
        picker.delegate = self
        picker.isHidden = false
        return picker
    }()
    
    private lazy var colorSave: UIButton = {
        let button = UIButton(type: .system)
        button.setTitle("Save", for: .normal)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.isHidden = false
        button.backgroundColor = .black
        button.addTarget(self, action: #selector(didTapColorSave), for: .touchUpInside)
        return button
    }()
    
    // MARK: - Lifecycle

    init() {
        super.init(frame: .zero)
        addSubviews()
        setupConstraints()
    }
    
    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // MARK: - Actions
    
    @IBAction func didTapColorSave(sender: AnyObject) {
        hideUI()
    }
    
    // MARK: - Private
    
    private func addSubviews() {
        self.isUserInteractionEnabled = false
        addSubview(colorSave)
        addSubview(colorPicker)
    }
    
    private func setupConstraints() {
        NSLayoutConstraint.activate([
            colorSave.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
            colorSave.topAnchor.constraint(equalTo: bottomAnchor, constant: -64),
            colorSave.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
            colorSave.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -16),
            
            colorPicker.leadingAnchor.constraint(equalTo: leadingAnchor),
            colorPicker.topAnchor.constraint(equalTo: bottomAnchor, constant: -256),
            colorPicker.trailingAnchor.constraint(equalTo: trailingAnchor),
            colorPicker.bottomAnchor.constraint(equalTo: colorSave.topAnchor, constant: -16)
        ])
    }
    
    private func hideUI() {
        colorPicker.isHidden = true
        colorSave.isHidden = true
    }
}

extension ColorPickerView: UIPickerViewDataSource, UIPickerViewDelegate {
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }
    
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int{
        return customColor.allValues.count
    }
    
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        print(customColor.allValues[row].rawValue)
        return customColor.allValues[row].rawValue
    }
}

UIPickerView is used to chose a color from an enum customColor. Then all elements can be hidden using colorSave button.

Yet once I'm putting an instance of ColorPickerView class, it's shown correctly but does not scroll. What am I doing wrong? Does it have smth to do with isUserInteractionEnabled?


Solution

  • Your custom view has no height. I suggest you completely redo your constraints to make them easier to follow. Assuming you want the button below the picker view, do something like the following:

    NSLayoutConstraint.activate([
        colorPicker.leadingAnchor.constraint(equalTo: leadingAnchor),
        colorPicker.trailingAnchor.constraint(equalTo: trailingAnchor),
        colorPicker.topAnchor.constraint(equalTo: topAnchor, constant: 16),
    
        colorSave.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
        colorSave.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
        colorSave.heightAnchor.constraint(equalToConstant: 50),
        colorSave.topAnchor.constraint(equalTo: colorPicker.bottomAnchor, constant: 20),
        colorSave.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -16),    
    ])
    

    Those constraints assume the UIPickerView has an intrinsic height. I think it does but I could be mistaken. If it doesn't then add a height constraint to colorPicker as needed.

    At the moment it puts the top of the picker 16 points from the top of the view and the button 16 points from the bottom of the view. It puts a gap of 20 points between the picker and the button. It sets the button height to 50 points. Adjust those as needed.

    When setting up constraints it's much simpler to think in a top-down layout and/or a left-right layout. Your code makes everything setup to be based on the bottom of the view. You never set the view's top anchor to anything so your main view has no height. That is why your picker and button do not work. They are being clipped by the zero-height parent view.