Search code examples
swiftuiios15

iOS15 - SwiftUI WheelPicker scrollable outside frame and clipped area destructing other interfaces


I have two WheelPickers contained inside a HStack for 'hour' and 'min'. Each Picker is set within a frame(width: 50, height: 30) and additionally clipped.

In iOS14, it behaved as expected and I could scrolled the 'hour' picker to change the hour and 'minute' picker to change the mins.

HOWEVER in iOS15, the 'minute' wheelpicker is extended beyond the frame width of 50 and overlapped into the 'hour' picker; if I scroll on the 'hour' picker, the 'mins' value changes (instead of 'hour' value), if I scroll on 'minute' picker, it changes the 'mins' as expected. If I touch on the far left outside the 'hour' picker, then the 'hour' value changes.

Anyone has the same issue and any workaround for this issue?

I came across a workaround to add 'mask(rectangle()' and tried it, but it did not work on iOS15.


    @State private var hour: Int = 0
    @State private var minute: Int = 0
    
    
    var body: some View {
        
        VStack {
            
            HStack (alignment: .center, spacing: 3) {
                
                NumberPicker("", selection: $hour
                                 , startValue: 0
                                 , endValue: 23
                                 , pickerSize: CGSize(width: 50, height: 30)
                )

                Text("hr")
                
                NumberPicker("", selection: $minute
                                 , startValue: 0
                                 , endValue: 59
                                 , pickerSize: CGSize(width: 50, height: 30)
                )

                Text("min")

            } // HStack
            
        } // VStack

    }

}
struct NumberPicker: View {
    
    let startValue: Int
    let endValue: Int
    let pickerSize: CGSize
    let title: String
    
    @Binding var selection: Int
    @State var value: Int = 0
    
    init(_ title: String = ""
         , selection: Binding<Int>
         , startValue: Int = 0
         , endValue: Int
         , pickerSize: CGSize = CGSize(width: 50, height: 30)
    ) {
        self._selection = selection
        self.title = title
        self.startValue = startValue
        self.endValue = (endValue + 1)
        self.pickerSize = pickerSize
        
        self._value = State(initialValue: selection.wrappedValue)
    }
    
    
    var body: some View {
        
        Picker(title, selection: $value) {
            
            ForEach(startValue..<endValue, id: \.self) { currentValue in
                
                Text("\(currentValue)")
                    .tag(currentValue)
            }
        }
        .pickerStyle(WheelPickerStyle())
        .fixedSize(horizontal: true, vertical: true)
        .frame(width: pickerSize.width, height: pickerSize.height)
        .clipped(antialiased: true)
    }
}

Solution

  • In NumberPicker try adding compositingGroup just before clipped(...) as:

    .compositingGroup()
    .clipped(antialiased: true)