BetterSheet
提供具有以下功能的强大 SwiftUI 抽屉扩展:
- 内置
sheet
修饰符的所有功能,但更健壮(针对 Xcode 11.0 公测版 5 进行了测试)。 - 支持模态(防止用户滑动关闭),类似于 UIKit 的
modalInPresentation
) - 当用户尝试模态时关闭抽屉时,支持调用操作。
希望苹果会在 iOS 13.0 最终版本发布之前,增加默认 sheet
修饰符的健壮性并将模态展示支持加入,这样该库就变得过时了。
基本使用
首先确保您在 SceneDelegate.swift
中导入 BetterSheet
包并将 UIHostingController
初始化为具有高能抽屉支持
window.rootViewController = UIHostingController.withBetterSheetSupport(rootView: ContentView())
显示抽屉的基本 API 类似于 SwiftUI 的 sheet(isPresented:onDismiss:content:)
视图修饰符。但您不是使用 sheet
而是用 betterSheet
。
例如
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
可用,您可以使用它从自己的代码中关闭抽屉。BetterSheet 的这个环境值的版本称为 betterSheetPresentationMode
.
一个示例
struct DetailView: View {
@Environment(\.betterSheetPresentationMode) var presentationMode
var body: some View {
Button(action: { self.presentationMode.value.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视图控制器类似功能的API。但是,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.value.dismiss()
}
func cancel() {
presentationMode.value.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文件。