Row of Picker wheels in SwiftUI, why are drag-to-scroll areas offset from Picker controls on screen?
Asked Answered
L

2

3

I'm working on a SwiftUI view whose purpose is to allow the user to enter an amount of dollars. I want a row of seven wheel-style Pickers, each of whose values are the digits 0 to 9.

The following code works, except that when I try to touch & drag a digit column to scroll through it, I find that my drag gesture causes not the Picker I'm touching to scroll, but rather a Picker several columns over to the right. To put it another way, the drag-to-scroll area for each Picker is offset quite a bit to the left of where the Picker column itself appears on the screen.

I've tried putting borders on the views, but everything seems to be in the right place. Any ideas what's causing this or how to fix it?

import SwiftUI

struct CurrencyPickerView: View {

    @State private var centsDigit: Int = 0
    @State private var tenCentsDigit: Int = 0
    @State private var onesDigit: Int = 0
    @State private var tensDigit: Int = 0
    @State private var hundredsDigit: Int = 0
    @State private var thousandsDigit: Int = 0
    @State private var tenThousandsDigit: Int = 0

    var body: some View {
        let pickerWidth = CGFloat(20)
        return HStack {
            Picker(selection: $tenThousandsDigit, label: EmptyView()) {
                ForEach((0...9), id: \.self) { ix in
                    Text("\(ix)").tag(ix)
                }
            }.frame(width: pickerWidth)

            Picker(selection: $thousandsDigit, label: EmptyView()) {
                ForEach((0...9), id: \.self) { ix in
                    Text("\(ix)").tag(ix)
                }
            }.frame(width: pickerWidth)

            Picker(selection: $hundredsDigit, label: EmptyView()) {
                ForEach((0...9), id: \.self) { ix in
                    Text("\(ix)").tag(ix)
                }
            }.frame(width: pickerWidth)

            Picker(selection: $tensDigit, label: EmptyView()) {
                ForEach((0...9), id: \.self) { ix in
                    Text("\(ix)").tag(ix)
                }
            }.frame(width: pickerWidth)

            Picker(selection: $onesDigit, label: EmptyView()) {
                ForEach((0...9), id: \.self) { ix in
                    Text("\(ix)").tag(ix)
                }
            }.frame(width: pickerWidth)

            Text(".")

            Picker(selection: $tenCentsDigit, label: EmptyView()) {
                ForEach((0...9), id: \.self) { ix in
                    Text("\(ix)").tag(ix)
                }
            }.frame(width: pickerWidth)

            Picker(selection: $centsDigit, label: EmptyView()) {
                ForEach((0...9), id: \.self) { ix in
                    Text("\(ix)").tag(ix)
                }
            }.frame(width: pickerWidth)

        }
    }
}
Levania answered 17/10, 2019 at 17:40 Comment(0)
L
4

Applying .clipped() to each .frame(...) solved the problem. Presumably the touch areas for these Picker wheels were being presented outside the frame area (which is used for layout but does not inherently constrain content to the frame bounds) and were overlapping in ways that caused the weird offset effect. Using .clipped() keeps SwiftUI from displaying/activating content outside the frame, and now the Picker wheels work correctly.

Levania answered 18/10, 2019 at 18:20 Comment(1)
Wasted 2 hours on this - Thanks. You should mark the correct answerChadbourne
M
2

The accepted answer was working until 15.0 but with 15.1 I started having the same issue.

adding this extension is working for me in 15.4

extension UIPickerView {   
   open override var intrinsicContentSize: CGSize {     
      return CGSize(width: UIView.noIntrinsicMetric, height: super.intrinsicContentSize.height)} 
}

found at https://developer.apple.com/forums/thread/687986?answerId=706782022#706782022

Morocco answered 27/3, 2022 at 11:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.