提供强大的 SwiftUI Sheet 替代方案,具有以下特性:
sheet
修饰符的所有特性,但更加健壮(已针对 Xcode 11.0 beta 6 进行测试)。modalInPresentation
)希望 Apple 能让默认的 sheet
修饰符更加健壮,并且能在 iOS 13.0 的最终版本发布之前增加模态呈现支持,这样这个库就变得多余了。
首先,请确保您导入了 BetterSheet
包,并在 SceneDelegate.swift
中初始化 UIHostingController
,以获得强大的 Sheet 支持。
window.rootViewController = UIHostingController.withBetterSheetSupport(rootView: ContentView())
呈现 Sheet 的基本 API 类似于 SwiftUI 的 sheet(isPresented:onDismiss:content:)
视图修饰符。但您使用的是 betterSheet
而不是 sheet
。
例如
struct ContentView: View {
@State var showDetail = false
var body: some View {
VStack {
Button(action: { self.showDetail = true }) {
Text("Show Detail")
}
}
.betterSheet(isPresented: $showDetail) {
Text("Detail!")
}
}
}
对于更高级的用例,有一个类似于 SwiftUI 的 sheet(item:onDismiss:content:
视图修饰符的 API 可用。
struct Fruit {
let name: String
}
extension Fruit: Identifiable {
var id: String {
name
}
}
struct ContentView: View {
let fruits = [Fruit(name: "Apple"), Fruit(name: "Banana"), Fruit(name: "Orange")]
@State var selectedFruit: Fruit? = nil
var body: some View {
List(fruits) { fruit in
Button(action: { self.selectedFruit = fruit }) {
Text(fruit.name)
}
}
.betterSheet(item: $selectedFruit) { fruit in
Text("You selected \(fruit.name)")
}
}
}
与 SwiftUI 的 sheet
修饰符一样,有一个类似于 SwiftUI 的 presentationMode
的环境值可用,您可以使用它从自己的代码中关闭 Sheet。BetterSheet 版本的此环境值称为 betterSheetPresentationMode
。
一个例子
struct DetailView: View {
@Environment(\.betterSheetPresentationMode) var presentationMode
var body: some View {
Button(action: { self.presentationMode.wrappedValue.dismiss() }) {
Text("Dismiss")
}
}
}
struct ContentView: View {
@State var showDetail = false
var body: some View {
VStack {
Button(action: { self.showDetail = true }) {
Text("Show Detail")
}
}
.betterSheet(isPresented: $showDetail) {
DetailView()
}
}
}
到目前为止,我们只研究了提供类似于默认 SwiftUI Sheet 功能的 API。但是,如果您不希望用户简单地通过滑动来关闭您的 Sheet,BetterSheet 提供了一些更高级的可能性。
例如
struct Fruit {
let name: String
}
extension Fruit: Identifiable {
var id: String {
name
}
}
struct EditView: View {
@Binding var fruits: [Fruit]
let fruit: Fruit?
@State var name: String
@Environment(\.betterSheetPresentationMode) var presentationMode
@State var showDismissActions = false
init(fruits: Binding<[Fruit]>, fruit: Fruit? = nil) {
_fruits = fruits
self.fruit = fruit
_name = State(initialValue: fruit?.name ?? "")
}
var isNew: Bool {
fruit == nil
}
var isValid: Bool {
name.trimmingCharacters(in: .whitespaces).count > 0
}
var isModified: Bool {
if let fruit = fruit, name != fruit.name {
return true
} else if fruit == nil && isValid {
return true
} else {
return false
}
}
var body: some View {
NavigationView {
Form {
HStack {
Text("Name")
TextField("Fruit", text: $name).multilineTextAlignment(.trailing)
}
}
.navigationBarTitle(fruit == nil ? "Add Fruit" : "Edit Fruit")
.navigationBarItems(
leading: Button(action: save) { Text("Save").fontWeight(.bold).disabled(!isValid) },
trailing: Button(action: self.cancel) { Text("Cancel") }
)
.actionSheet(isPresented: $showDismissActions) {
ActionSheet(
title: Text("Select an option"),
message: nil,
buttons: [
.destructive(Text(isNew ? "Discard Fruit" : "Discard Changes"), action: self.cancel),
.default(Text(isNew ? "Add Fruit" : "Save Fruit"), action: self.save),
.cancel()
]
)
}
.betterSheetIsModalInPresentation(isModified)
.onBetterSheetDidAttemptToDismiss {
self.showDismissActions = true
}
}
}
func save() {
guard isValid else { return }
let fruit = Fruit(name: name)
if let index = fruits.firstIndex(where: { $0.id == self.fruit?.id }) {
fruits.remove(at: index)
fruits.insert(fruit, at: index)
} else {
fruits.append(fruit)
}
presentationMode.wrappedValue.dismiss()
}
func cancel() {
presentationMode.wrappedValue.dismiss()
}
}
struct ContentView: View {
@State var fruits: [Fruit] = [Fruit(name: "Apple")]
@State var addFruit = false
@State var editFruit: Fruit? = nil
var body: some View {
NavigationView {
List(fruits) { fruit in
Text(fruit.name)
Spacer()
Button(action: { self.editFruit = fruit }) {
Image(systemName: "pencil.circle")
}
}
.listStyle(GroupedListStyle())
.navigationBarTitle("Fruits")
.navigationBarItems(
leading: Button(action: { self.addFruit = true }) { Text("Add") }
)
.betterSheet(isPresented: $addFruit) {
EditView(fruits: self.$fruits)
}
.betterSheet(item: $editFruit) { fruit in
EditView(fruits: self.$fruits, fruit: fruit)
}
}
}
}
此项目根据 MIT 许可证的条款获得许可。 请参阅 LICENSE 文件。