从 SwiftUI 中的 iOS 16.4 开始,我们可以使用
.sheet
修饰符和 .presentationDetents
来指定底部工作表的高度。我们可以进一步使用 .presentationBackgroundInteraction
来允许与工作表后面的视图进行交互。
我遇到了一个在我看来像是 SwiftUI bug 的问题。
您可以将此代码粘贴到游乐场中以查看实际的错误行为(这是我存在问题的实际应用程序的简化版本)。
错误在于,有时从一个选择更改为另一个选择时,工作表完全忽略
.presentationDetents
和 .presentationBackgroundInteraction
修饰符。它只是显示为全高模态,没有任何背景交互。
import SwiftUI
import PlaygroundSupport
struct ContentView: View {
let choices = [nil, "One", "Two", "Three"]
@State var selection: String?
var body: some View {
VStack {
Picker("Choose", selection: $selection) {
ForEach(choices, id: \.self) { choice in
Text(choice ?? "None")
.tag(choice)
}
}
.padding()
Spacer()
}
.pickerStyle(.segmented)
.sheet(item: $selection) { choice in
Text("Item selected: \(choice)")
.presentationDetents([.medium])
.presentationBackgroundInteraction(
.enabled(upThrough: .medium)
)
}
}
}
extension String: Identifiable {
public var id: String { self }
}
PlaygroundPage.current.setLiveView(ContentView().frame(width: 320, height: 568))
以下是一些截图:
观察
PickerView
,所以与此无关。presentationBackgroundInteraction
,因为它已经超过了.medium
并达到了最高高度。因此,在更改为另一个选择之前,工作表的选择没有通过零状态似乎是一个问题,这对我来说是一件完全有效的事情(我不希望工作表消失然后重新出现) .
假设这是一个实际的错误,有什么想法可以解决它吗?我真的不想为应该在本机工作的东西编写或导入第三方库。
预先感谢您的任何见解!
如果您不希望工作表消失并重新出现,我认为您不能使用
sheet(item:)
。即使修复了 presentationDetents
错误,我认为 sheet(item:)
的预期行为仍然是在项目更改时“关闭该工作表,然后显示另一个工作表”。
请使用
sheet(isPresented:)
代替。只要选择不为零,就显示工作表。
.sheet(isPresented: isNotNil($selection)) {
if let selection {
Text("Item selected: \(selection)")
.presentationDetents([.medium])
.presentationBackgroundInteraction(.enabled(upThrough: .medium))
}
}
func isNotNil<T>(_ binding: Binding<T?>) -> Binding<Bool> {
Binding {
binding.wrappedValue != nil
} set: { notNil in
if !notNil {
binding.wrappedValue = nil
}
}
}
如果您不介意工作表消失并再次出现,您可以添加单独的
@State
用于预设工作表。当 nil
更改时,将该状态设置为中间 selection
值。
@State var sheetItem: String?
.onChange(of: selection) { x in
sheetItem = nil
DispatchQueue.main.async {
sheetItem = x
}
}
.sheet(item: $selection) { choice in ... }