Skip to content

Instantly share code, notes, and snippets.

@wotjd
Last active September 20, 2024 07:50

Revisions

  1. wotjd revised this gist Sep 20, 2024. 1 changed file with 0 additions and 2 deletions.
    2 changes: 0 additions & 2 deletions View+UIKitSheet.swift
    Original file line number Diff line number Diff line change
    @@ -47,8 +47,6 @@ extension View {
    sheet: sheet
    ))
    }

    return SampleView()
    }

    // MARK: UIKitSheetViewModifier
  2. wotjd revised this gist Sep 20, 2024. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion View+UIKitSheet.swift
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,7 @@ import SwiftUI

    #Preview {
    struct SampleView: View {
    @State presentsSheet = false
    @State var presentsSheet = false

    var body: some View {
    Button { presentsSheet.toggle() } label: { Text("Toggle Sheet") }
    @@ -28,6 +28,8 @@ import SwiftUI
    Button { dismiss() } label: { Text("dismiss sheet") }
    }
    }

    return SampleView()
    }

    // MARK: - View+UIKitSheet
  3. wotjd revised this gist Sep 20, 2024. 2 changed files with 32 additions and 29 deletions.
    29 changes: 0 additions & 29 deletions SampleView.swift
    Original file line number Diff line number Diff line change
    @@ -1,29 +0,0 @@
    import SwiftUI

    struct SampleView: View {
    @State presentsSheet = false

    var body: some View {
    Button { presentsSheet.toggle() } label: { Text("Toggle Sheet") }
    .uiKitSheet(
    isPresented: $presentsSheet,
    onDismiss: { print("dismissed!") },
    sheetConfiguration: {
    $0.detents = [.custom(identifier: .medium) { _ in 340.0 }]
    $0.largestUndimmedDetentIdentifier = .medium
    $0.prefersGrabberVisible = false
    $0.preferredCornerRadius = 20
    }
    ) {
    SheetContentView()
    }
    }
    }

    struct SheetContentView: View {
    @Environment(\.dismiss) private var dismiss

    var body: some View {
    Button { dismiss() } label: { Text("dismiss sheet") }
    }
    }
    32 changes: 32 additions & 0 deletions View+UIKitSheet.swift
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,35 @@
    import SwiftUI

    #Preview {
    struct SampleView: View {
    @State presentsSheet = false

    var body: some View {
    Button { presentsSheet.toggle() } label: { Text("Toggle Sheet") }
    .uiKitSheet(
    isPresented: $presentsSheet,
    onDismiss: { print("dismissed!") },
    sheetConfiguration: {
    $0.detents = [.custom(identifier: .medium) { _ in 340.0 }]
    $0.largestUndimmedDetentIdentifier = .medium
    $0.prefersGrabberVisible = false
    $0.preferredCornerRadius = 20
    }
    ) {
    SheetContentView()
    }
    }
    }

    struct SheetContentView: View {
    @Environment(\.dismiss) private var dismiss

    var body: some View {
    Button { dismiss() } label: { Text("dismiss sheet") }
    }
    }
    }

    // MARK: - View+UIKitSheet
    extension View {
    func uiKitSheet(
    @@ -15,6 +45,8 @@ extension View {
    sheet: sheet
    ))
    }

    return SampleView()
    }

    // MARK: UIKitSheetViewModifier
  4. wotjd revised this gist Jan 7, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion View+UIKitSheet.swift
    Original file line number Diff line number Diff line change
    @@ -117,4 +117,4 @@ private class DismissableUIHostingViewController<Content: View>: UIHostingContro
    super.viewDidDisappear(animated)
    onDismiss?()
    }
    }
    }
  5. wotjd renamed this gist Jan 7, 2023. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion ContentView.swift → SampleView.swift
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    import SwiftUI

    struct ContentView: View {
    struct SampleView: View {
    @State presentsSheet = false

    var body: some View {
  6. wotjd revised this gist Jan 7, 2023. 1 changed file with 7 additions and 5 deletions.
    12 changes: 7 additions & 5 deletions View+UIKitSheet.swift
    Original file line number Diff line number Diff line change
    @@ -88,15 +88,17 @@ private struct UIKitSheet<Content: View, Sheet: View>: UIViewControllerRepresent
    sheet.sheetPresentationController.map(sheetConfigurationBlock ?? { _ in })
    case (true, nil):
    viewController.present(
    DismissableUIHostingViewController(rootView: sheet) => {
    $0.sheetPresentationController.map(sheetConfigurationBlock ?? { _ in })
    $0.overrideUserInterfaceStyle = UIUserInterfaceStyle(colorScheme)
    $0.onDismiss = {
    {
    let sheetController = DismissableUIHostingViewController(rootView: sheet)
    sheetController.sheetPresentationController.map(sheetConfigurationBlock ?? { _ in })
    sheetController.overrideUserInterfaceStyle = UIUserInterfaceStyle(colorScheme)
    sheetController.onDismiss = {
    isPresented = false
    // NOTE: calls onDismiss manualy when dismissed by gesture
    onDismiss?()
    }
    },
    return sheetController
    }(),
    animated: true
    )
    case let (false, .some(sheet)):
  7. wotjd created this gist Jan 7, 2023.
    29 changes: 29 additions & 0 deletions ContentView.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,29 @@
    import SwiftUI

    struct ContentView: View {
    @State presentsSheet = false

    var body: some View {
    Button { presentsSheet.toggle() } label: { Text("Toggle Sheet") }
    .uiKitSheet(
    isPresented: $presentsSheet,
    onDismiss: { print("dismissed!") },
    sheetConfiguration: {
    $0.detents = [.custom(identifier: .medium) { _ in 340.0 }]
    $0.largestUndimmedDetentIdentifier = .medium
    $0.prefersGrabberVisible = false
    $0.preferredCornerRadius = 20
    }
    ) {
    SheetContentView()
    }
    }
    }

    struct SheetContentView: View {
    @Environment(\.dismiss) private var dismiss

    var body: some View {
    Button { dismiss() } label: { Text("dismiss sheet") }
    }
    }
    118 changes: 118 additions & 0 deletions View+UIKitSheet.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,118 @@
    import SwiftUI

    // MARK: - View+UIKitSheet
    extension View {
    func uiKitSheet(
    isPresented: Binding<Bool>,
    onDismiss: (() -> Void)? = nil,
    sheetConfiguration: UIKitSheetConfiguration? = nil,
    @ViewBuilder sheet: () -> some View
    ) -> some View {
    modifier(UIKitSheetViewModifier(
    isPresented: isPresented,
    onDismiss: onDismiss,
    sheetConfiguration: sheetConfiguration,
    sheet: sheet
    ))
    }
    }

    // MARK: UIKitSheetViewModifier
    private struct UIKitSheetViewModifier<Sheet: View>: ViewModifier {
    @Binding var isPresented: Bool

    let sheet: Sheet

    let onDismiss: (() -> Void)?
    let sheetConfiguration: UIKitSheetConfiguration?

    init(
    isPresented: Binding<Bool>,
    onDismiss: (() -> Void)?,
    sheetConfiguration: UIKitSheetConfiguration?,
    @ViewBuilder sheet: () -> Sheet
    ) {
    self._isPresented = isPresented
    self.onDismiss = onDismiss
    self.sheetConfiguration = sheetConfiguration
    self.sheet = sheet()
    }

    func body(content: Content) -> some View {
    UIKitSheet(
    isPresented: $isPresented,
    onDismiss: onDismiss,
    sheetConfiguration: sheetConfiguration,
    content: { content },
    sheet: { sheet }
    )
    }
    }

    // MARK: - UIKitSheetConfiguration
    typealias UIKitSheetConfiguration = (UISheetPresentationController) -> Void

    // MARK: UIKitSheet
    private struct UIKitSheet<Content: View, Sheet: View>: UIViewControllerRepresentable {
    let content: Content
    let sheet: Sheet
    let onDismiss: (() -> Void)?

    let sheetConfigurationBlock: UIKitSheetConfiguration?

    @Binding var isPresented: Bool

    @Environment(\.colorScheme) private var colorScheme

    init(
    isPresented: Binding<Bool>,
    onDismiss: (() -> Void)?,
    sheetConfiguration: UIKitSheetConfiguration?,
    @ViewBuilder content: () -> Content,
    @ViewBuilder sheet: () -> Sheet
    ) {
    self.content = content()
    self.sheet = sheet()
    self.onDismiss = onDismiss
    self.sheetConfigurationBlock = sheetConfiguration
    self._isPresented = isPresented
    }

    func makeUIViewController(context: Context) -> UIViewController {
    UIHostingController(rootView: content)
    }

    func updateUIViewController(_ viewController: UIViewController, context: Context) {
    switch (isPresented, viewController.presentedViewController) {
    case let (true, .some(sheet)):
    sheet.sheetPresentationController.map(sheetConfigurationBlock ?? { _ in })
    case (true, nil):
    viewController.present(
    DismissableUIHostingViewController(rootView: sheet) => {
    $0.sheetPresentationController.map(sheetConfigurationBlock ?? { _ in })
    $0.overrideUserInterfaceStyle = UIUserInterfaceStyle(colorScheme)
    $0.onDismiss = {
    isPresented = false
    // NOTE: calls onDismiss manualy when dismissed by gesture
    onDismiss?()
    }
    },
    animated: true
    )
    case let (false, .some(sheet)):
    sheet.dismiss(animated: true)
    default:
    break
    }
    }
    }

    // MARK: - DismissableUIHostingViewController
    private class DismissableUIHostingViewController<Content: View>: UIHostingController<Content> {
    var onDismiss: (() -> Void)?

    override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    onDismiss?()
    }
    }