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 代码中解决了这个问题。
我在 '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 代码中解决了这个问题。