I want to change background color of selected row in UIPickerView with animation. I'm reloading all components when a new row selected and in viewForRow function If current row is selected one, I make Its' background color to red. However, It looks like in bug. First screen shot is what I want to achieve and second one is in my app. Btw, It will be excellent If I can set that red color with animation
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
pickerView.reloadAllComponents()
}
func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
let view = UIView()
view.frame = CGRect(x: 0, y: 0, width: width, height: height)
let label = UILabel()
label.frame = CGRect(x: 0, y: 0, width: height, height: height)
label.textAlignment = .center
label.font = UIFont.systemFont(ofSize: 30)
label.text = numbers[row]
view.addSubview(label)
view.transform = CGAffineTransform(rotationAngle: 90 * (.pi/180))
if pickerView.selectedRow(inComponent: component) == row {
label.attributedText = NSAttributedString(string: numbers[row], attributes: [NSAttributedStringKey.font:UIFont.systemFont(ofSize: 30),NSAttributedStringKey.foregroundColor:UIColor.white])
view.backgroundColor = UIColor.red
}
return view
}
A slightly different approach since I couldn't find a smooth way to animate background on selected row. It can be done but not much of an improvement, so give this a try:
override func viewDidLoad() {
//...
/*
Create a colored `view` that stays bang in the center on top
of the `pickerView`.
The `pickerView` will scroll behind it normally and no need
for animating background color or even reloading
*/
createHighlightView()
}
func createHighlightView() {
let highlightView = UIView(frame: CGRect.zero)
highlightView.backgroundColor = UIColor.red.withAlphaComponent(0.2)
/*
Now lets programmatically add constraints
*/
highlightView.translatesAutoresizingMaskIntoConstraints = false
pickerView.addSubview(highlightView)
//HightLight View's width
highlightView.addConstraint(NSLayoutConstraint(item: highlightView,
attribute: .width,
relatedBy: .equal,
toItem: nil,
attribute: .notAnAttribute,
multiplier: 1,
constant: width))
//HightLight View's height
highlightView.addConstraint(NSLayoutConstraint(item: highlightView,
attribute: .height,
relatedBy: .equal,
toItem: nil,
attribute: .notAnAttribute,
multiplier: 1,
constant: height))
//HightLight View should be bang center-aligned with pickerView
pickerView.addConstraint(NSLayoutConstraint(item: highlightView,
attribute: .centerX,
relatedBy: .equal,
toItem: pickerView,
attribute: .centerX,
multiplier: 1,
constant: 0))
pickerView.addConstraint(NSLayoutConstraint(item: highlightView,
attribute: .centerY,
relatedBy: .equal,
toItem: pickerView,
attribute: .centerY,
multiplier: 1,
constant: 0))
}
And now your delegates can be simply:
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
//no need to reload
//do whatever else you want
}
func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
/*
Just return a `UILabel`. No need to put it in a `UIView`.
Nothing special either, just slap text into it
*/
var label = view as? UILabel
if label == nil {
label = UILabel()
//All the rest are safe force unwraps so chill tf out
label!.frame = CGRect(x: 0, y: 0, width: height, height: height)
label!.textAlignment = .center
label!.font = UIFont.systemFont(ofSize: 30)
label!.transform = CGAffineTransform(rotationAngle: 90 * (.pi/180))
}
label!.text = arrDatasource[row]
return label!
}