iOS15 - SwiftUI WheelPicker 可在框架外滚动和剪切区域破坏其他界面

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

我在 'hour' 和 'min' 的 HStack 中包含两个 WheelPicker。 每个 Picker 都设置在一个 frame(width: 50, height: 30) 内并被额外裁剪。

在 iOS14 中,它的行为符合预期,我可以滚动 'hour' 选择器来更改小时,滚动 'minute' 选择器来更改分钟。

但是在 iOS15 中,'minute' wheelpicker 超出了框架宽度 50 并重叠到 'hour' picker 中;如果我在 'hour' 选择器上滚动,'mins' 值会更改(而不是 'hour' 值),如果我在 'minute' 上滚动选择器,它会按预期更改 'mins'。如果我触摸 'hour' 选择器外的最左边,然后 'hour' 值会改变。

有人遇到同样的问题以及解决此问题的方法吗?

我遇到了添加 'mask(rectangle()' 的解决方法并尝试了它,但它在 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)
    }
}

NumberPicker 中尝试在 clipped(...) 之前添加 compositingGroup 作为:

.compositingGroup()
.clipped(antialiased: true)

在 iOS 15.1 中似乎没有任何解决方法,所以我使用 UIViewRepresentable 自行解决。该示例是为我与 Double 一起使用而制作的,但您可以轻松地对其进行调整。

除了能够在单个轮式拾取器中放置多个组件外,您还可以将它们水平堆叠在 HStack 中,没有问题:

用法

import SwiftUI

struct ContentView: View {
    
    @State private var selections1: [Double] = [5, 10]
    
    private let data1: [[Double]] = [
        Array(stride(from: 0, through: 60, by: 1)),
        Array(stride(from: 0, through: 60, by: 1))
    ]
    
    var body: some View {
        VStack {
            HStack{
                MultiWheelPicker(selections: self.$selections1, data: data1)
                    .frame(width: 150)
                MultiWheelPicker(selections: self.$selections1, data: data1)
                    .frame(width: 150)
            }
        }
        
    }
}

查看

struct MultiWheelPicker: UIViewRepresentable {
    var selections: Binding<[Double]>
    let data: [[Double]]
    
    func makeCoordinator() -> MultiWheelPicker.Coordinator {
        Coordinator(self)
    }
    
    func makeUIView(context: UIViewRepresentableContext<MultiWheelPicker>) -> UIPickerView {
        let picker = UIPickerView()
        picker.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
        
        picker.dataSource = context.coordinator
        picker.delegate = context.coordinator
        
        return picker
    }
    
    func updateUIView(_ view: UIPickerView, context: UIViewRepresentableContext<MultiWheelPicker>) {
        for comp in selections.indices {
            if let row = data[comp].firstIndex(of: selections.wrappedValue[comp]) {
                view.selectRow(row, inComponent: comp, animated: false)
            }
        }
    }
    
    class Coordinator: NSObject, UIPickerViewDataSource, UIPickerViewDelegate {
        var parent: MultiWheelPicker
      
        init(_ pickerView: MultiWheelPicker) {
            parent = pickerView
        }
        
        func numberOfComponents(in pickerView: UIPickerView) -> Int {
            return parent.data.count
        }
        
        func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
            return parent.data[component].count
        }
        
        func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
            return 48
        }
        
        func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
            return String(format: "%02.0f", parent.data[component][row])
        }
        
        func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
            parent.selections.wrappedValue[component] = parent.data[component][row]
        }
    }
}

我将 .pickerStyle(WheelPickerStyle()) 添加到选择器视图并在我的 iOS 15 代码中解决了这个问题。