What I’m trying to do I’m wrapping a custom UIKit UIPickerView in SwiftUI because I need:
custom row height (56 pt)
SwiftUI data binding
Everything works, except I still see the system‑gray selection indicator / loupe that UIPickerView draws in the middle. I’d like to remove it entirely or change its color so only my own blue rounded rectangle is visible.
// ------------------------- SwiftUI wrapper -------------------------
struct WheelPickerDemoUIKit: View {
@State private var selected: DefaultTool = .basic // enum cases below
var body: some View {
VStack(spacing: 30) {
DefaultToolPickerRepresentable(selection: $selected,
rowHeight: 56)
.frame(height: UIScreen.main.bounds.height * 0.7)
Text("Selected → \(selected.rawValue)")
}
.padding()
}
}
// ------------------------- UIKit picker view -------------------------
final class DefaultToolPickerView: UIView, UIPickerViewDataSource, UIPickerViewDelegate {
let picker = UIPickerView()
private let rowHeight: CGFloat
var selectedTool: DefaultTool {
DefaultTool.allCases[picker.selectedRow(inComponent: 0)]
}
var onSelection: ((DefaultTool) -> Void)?
init(rowHeight: CGFloat = 56) {
self.rowHeight = rowHeight
super.init(frame: .zero)
commonInit()
}
required init?(coder: NSCoder) { fatalError() }
private func commonInit() {
picker.dataSource = self
picker.delegate = self
picker.backgroundColor = .clear
picker.translatesAutoresizingMaskIntoConstraints = false
addSubview(picker)
NSLayoutConstraint.activate([
picker.leadingAnchor.constraint(equalTo: leadingAnchor),
picker.trailingAnchor.constraint(equalTo: trailingAnchor),
picker.topAnchor.constraint(equalTo: topAnchor),
picker.bottomAnchor.constraint(equalTo: bottomAnchor)
])
}
// MARK: UIPickerViewDataSource / Delegate
func numberOfComponents(in pickerView: UIPickerView) -> Int { 1 }
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent _: Int) -> Int {
DefaultTool.allCases.count
}
func pickerView(_ pickerView: UIPickerView,
viewForRow row: Int,
forComponent _: Int,
reusing view: UIView?) -> UIView {
let label = (view as? UILabel) ?? UILabel()
label.text = DefaultTool.allCases[row].rawValue
label.font = .systemFont(ofSize: 24, weight: .medium)
label.textAlignment = .center
label.textColor = .black
return label
}
func pickerView(_: UIPickerView, rowHeightForComponent _: Int) -> CGFloat {
rowHeight
}
func pickerView(_ pickerView: UIPickerView, didSelectRow _: Int, inComponent _: Int) {
pickerView.reloadAllComponents()
onSelection?(selectedTool)
}
}
// ------------------------- SwiftUI <-> UIKit bridge -------------------------
struct DefaultToolPickerRepresentable: UIViewRepresentable {
@Binding var selection: DefaultTool
var rowHeight: CGFloat = 56
func makeUIView(context: Context) -> DefaultToolPickerView {
let view = DefaultToolPickerView(rowHeight: rowHeight)
view.onSelection = { selection = $0 }
return view
}
func updateUIView(_ uiView: DefaultToolPickerView, context: Context) {
if selection != uiView.selectedTool,
let idx = DefaultTool.allCases.firstIndex(of: selection) {
uiView.picker.selectRow(idx, inComponent: 0, animated: true)
uiView.picker.reloadAllComponents()
}
}
}
// ------------------------- Demo enum -------------------------
enum DefaultTool: String, CaseIterable, Identifiable {
var id: Self { self }
case basic = "Basic Calc", emi = "EMI Calc", sip = "SIP Calc"
case percent = "Percentage Calc", gst = "GST Calc", bmi = "BMI Calc"
}
What I already tried
picker.subviews
.filter { $0.frame.height < 2 }
.forEach { $0.isHidden = true }
Attempted KVC
picker.setValue(UIColor.clear, forKey: "selectionIndicator")
Question Is there any supported way to hide or recolor the default selection indicator in UIPickerView, when I embed it in SwiftUI? Or is the only safe approach to overlay my own view and accept that the gray band is still rendered underneath?
Image :
picker.subviews[1].backgroundColor = UIColor.clear
This is how I remove color of selected item but do it after UIPickerView
is loaded perfectly, otherwise it's going to crash