SheeKit

使用 SheeKitSwiftUI 中自定义和调整 sheet 的大小。利用 UISheetPresentationController 和其他 UIKit 功能的强大功能。

概述

SheeKit 是 SwiftUI 和 UIKit 之间的桥梁,它使用 UIKit 中可用的功能来丰富 SwiftUI 中的模态演示。

SheeKit 提供了两个用于呈现 sheet 的修饰符,类似于 SwiftUI.sheet(...)

此外,SheeKit 允许

自定义 sheet detents 以呈现半屏 sheet

在 iOS 15 中,sheets 可以在 large()medium() detents 之间调整大小,并动画显示大小转换。 为了自定义 detents,请将 SheetProperties 提供给 ModalPresentationStyle/pageSheet(properties:)ModalPresentationStyle/formSheet(properties:)

struct ShowLicenseAgreement: View {
    @State private var isShowingSheet = false
    @State private var selectedDetentIdentifier = UISheetPresentationController.Detent.Identifier.medium 
    var body: some View {
        Button(action: {
            isShowingSheet.toggle()
        }) {
            Text("Show License Agreement")
        }
        .shee(isPresented: $isShowingSheet,
              presentationStyle: .formSheet(properties: .init(detents: [ .medium(), .large() ], selectedDetentIdentifier: $selectedDetentIdentifier, animatesSelectedDetentIdentifierChange: true)),
              onDismiss: didDismiss) {
            VStack {
                Text("License Agreement")
                    .font(.title)
                    .padding(50)
                Text("""
                        Terms and conditions go here.
                    """)
                    .padding(50)
                Button("Dismiss",
                       action: { isShowingSheet.toggle() })
            }
        }
    }

    func didDismiss() {
        // Handle the dismissing action.
    }
}

为不同的 Identifiable 项目定义不同的模态演示样式

在 SwiftUI 中,有三个不同的修饰符用于 popover、fullScreenCover 和 sheet,它们不允许开发者根据相同的真值来源(由 item 提供)显示不同样式的对话框。

使用 SheeKit,这是可能的 - 只需提供与您的 item 对应的 presentationStyle

struct ShowPartDetail: View {
    @State var sheetDetail: InventoryItem?
    var body: some View {
        Button("Show Part Details") {
            sheetDetail = InventoryItem(
                id: "0123456789",
                partNumber: "Z-1234A",
                quantity: 100,
                name: "Widget")
        }
        .shee(item: $sheetDetail,
              presentationStyle: presentationStyle,
              onDismiss: didDismiss) { detail in
            VStack(alignment: .leading, spacing: 20) {
                Text("Part Number: \(detail.partNumber)")
                Text("Name: \(detail.name)")
                Text("Quantity On-Hand: \(detail.quantity)")
            }
            .onTapGesture {
                sheetDetail = nil
            }
        }
    }

    func didDismiss() {
        // Handle the dismissing action.
    }

    var presentationStyle: ModalPresentationStyle {
        var sheetProperties = SheetProperties()
        sheetProperties.detents = sheetDetail?.quantity ?? 0 > 100500 ? [ .large() ] : [ .medium() ]
        return .formSheet(properties: sheetProperties)
    }
}

struct InventoryItem: Identifiable {
    var id: String
    let partNumber: String
    let quantity: Int
    let name: String
}

通过 UIViewControllerProxy 自定义首选的呈现视图控制器属性

在 UIKit 中,UIViewController 类有许多属性,允许根据用例更改用户体验,例如禁止交互式关闭 sheet(通过 isModalInPresentation),自定义状态栏外观、首选内容大小或模态转换样式。 不幸的是,此功能未在 SwiftUI 中公开。 SheeKit 通过允许使用者提供 UIViewControllerProxy 来解决此问题,该代理定义了呈现的视图控制器的首选参数。

struct ShowLicenseAgreement: View {
    @State private var isShowingSheet = false
    @State private var selectedDetentIdentifier = UISheetPresentationController.Detent.Identifier.medium 
    var body: some View {
        Button(action: {
            isShowingSheet.toggle()
        }) {
            Text("Show License Agreement")
        }
        .shee(isPresented: $isShowingSheet,
              presentationStyle: .formSheet(properties: .init(detents: [ .medium(), .large() ], selectedDetentIdentifier: $selectedDetentIdentifier, animatesSelectedDetentIdentifierChange: true)),
              presentedViewControllerParameters: presentedViewControllerParameters,
              onDismiss: didDismiss) {
            VStack {
                Text("License Agreement")
                    .font(.title)
                    .padding(50)
                Text("""
                        Terms and conditions go here.
                    """)
                    .padding(50)
                Button("Dismiss",
                       action: { isShowingSheet.toggle() })
            }
        }
    }

    func didDismiss() {
        // Handle the dismissing action.
    }

    var presentedViewControllerParameters: UIViewControllerProxy {
        var parameters = UIViewControllerProxy()
        parameters.preferredStatusBarStyle = .darkContent
        parameters.preferredStatusBarUpdateAnimation = .fade
        parameters.isModalInPresentation = true
        parameters.modalTransitionStyle = .flipHorizontal
        return parameters
    }
}

利用 UIPopoverPresentationController 的 adaptiveSheetPresentationController 并自定义 popover 的自适应 sheet

在 SwiftUI 中,当用户在 iPad 上将应用程序最小化到另一个应用程序之上的最小尺寸时,或当 popover 在 iPhone 上显示为 sheet 时,开发者无法在场景的紧凑尺寸类中获得 medium-detent sheet 而不是 popover。 popover 适应的 sheet 始终具有 .large() detent。

SheeKit 允许开发者自定义此行为,并指定 popover 适应的 sheet 的 detents,以及首选的 popover 箭头方向和源矩形。

struct ShowLicenseAgreement: View {
    @State private var isShowingSheet = false
    @State private var selectedDetentIdentifier = UISheetPresentationController.Detent.Identifier.medium 
    var body: some View {
        Button(action: {
            isShowingSheet.toggle()
        }) {
            Text("Show License Agreement")
        }
        .shee(isPresented: $isShowingSheet,
              presentationStyle: .popover(permittedArrowDirections: .top, 
                                          sourceRectTransform: { $0.offsetBy(dx: 16, dy: 16) }, 
                                          adaptiveSheetProperties: .init(detents: [ .medium(), .large() ], 
                                                                         selectedDetentIdentifier: $selectedDetentIdentifier, 
                                                                         animatesSelectedDetentIdentifierChange: true)),
              onDismiss: didDismiss) {
            VStack {
                Text("License Agreement")
                    .font(.title)
                    .padding(50)
                Text("""
                        Terms and conditions go here.
                    """)
                    .padding(50)
                Button("Dismiss",
                       action: { isShowingSheet.toggle() })
            }
        }
    }

    func didDismiss() {
        // Handle the dismissing action.
    }
}

库的演示

SheeKit Demo on YouTube

安装

Xcode 13

  1. 在文件导航器中选择您的项目,然后在目标列表顶部再次选择该项目。 您将看到包的列表。
  2. 按 + 按钮。
  3. 在出现的窗口中,按左下角的 + 按钮。
  4. 在出现的菜单中,选择“添加 Swift 包集合”
  5. 在出现的对话框中,输入包集合 URL:https://swiftpackageindex.cn/edudnyk/collection.json
  6. 按“添加集合”
  7. 从集合中选择 SheeKit 包。

如果您想在任何其他使用 SwiftPM 的项目中使用 SheeKit,请在 Package.swift 中将该包添加为依赖项

dependencies: [
  .package(name: "SheeKit", url: "https://github.com/edudnyk/SheeKit.git", from: "2.0.0"),
]

接下来,将 SheeKit 添加为您的测试目标的依赖项

targets: [
  .target(name: "MyApp", dependencies: ["SheeKit"], path: "Sources"),
]

Carthage

如果您使用 Carthage,您可以将以下依赖项添加到您的 Cartfile

github "edudnyk/SheeKit" ~> 2.0.0

CocoaPods

如果您的项目使用 CocoaPods,请将 pod 添加到 Podfile 中的任何适用目标

target 'MyApp' do
  pod 'SheeKit', '~> 2.0.0'
end

主题