Skip to content

Conversation

@mikhailChelbaev
Copy link
Collaborator

@mikhailChelbaev mikhailChelbaev commented Nov 29, 2024

This PR introduces the implementation of center and bottom modals for SwiftUI.

Shared Components

  1. ModalOverlay
    A reusable view that displays the overlay behind the modal, supporting styles like dimming or transparency.

  2. ModalContent
    A reusable view that structures and displays the content of the modal, including optional header, body, and footer sections.

Center Modal

  • SUCenterModal
    Implements a modal view that aligns its content in the center of the screen.

  • View Modifiers:

    • func centerModal(isPresented:model:onDismiss:)
      Public API for displaying a center modal. The presentation state is controlled using a Bool binding.
    • func centerModal(item:model:onDismiss:)
      Public API for displaying a center modal when an optional item is non-nil.

Bottom Modal

  • SUBottomModal
    Implements a modal view that aligns its content at the bottom of the screen. Unlike SUCenterModal, it includes a gesture recognizer to allow users to drag the modal vertically.

  • View Modifiers:

    • func bottomModal(isPresented:model:onDismiss:)
      Public API for displaying a bottom modal. The presentation state is controlled using a Bool binding.
    • func bottomModal(item:model:onDismiss:)
      Public API for displaying a bottom modal when an optional item is non-nil.

Presentation Modifiers

To present modals, fullScreenCover is used without animation, combined with custom animations for the modal itself. This introduces two properties that manage the modal's presentation state:

  • isVisible
    Indicates whether the modal is currently visible, controlling the modal's internal animation.
  • isPresented
    Indicates whether the fullScreenCover presenting the modal is active.

Lifecycle:

  • When the modal is fully visible, both isVisible and isPresented are true.
  • When the modal is dismissed:
    1. isVisible changes to false to trigger the dismissal animation.
    2. After the animation completes, isPresented is set to false, hiding the fullScreenCover.

To abstract these implementation details, two reusable presentation modifiers are introduced:

  • ModalPresentationModifier
  • ModalPresentationWithItemModifier

These modifiers encapsulate the complexity, exposing only the isVisible value to users.

How to Test

Testing with an Optional Item

You can test the API that uses an optional item with the following example:

struct ContentView: View {
  struct ModalData: Identifiable {
    var id: String { text }
    let text: String
  }

  @State private var selectedItem: ModalData?
  private let items: [ModalData] = [
    ModalData(text: "data 1"),
    ModalData(text: "data 2")
  ]

  var body: some View {
    List(items) { item in
      Button("Show Modal") {
        selectedItem = item
      }
    }
    .bottomModal(
      item: $selectedItem,
      model: BottomModalVM(),
      onDismiss: {
        print("Modal dismissed")
      },
      header: { item in
        Text("Header for \(item.text)")
      },
      body: { item in
        Text("Body content for \(item.text)")
      },
      footer: { _ in
        SUButton(model: .init {
          $0.title = "Close"
        }) {
          selectedItem = nil
        }
      }
    )
  }
}

You can use a similar example to test the center modal.

Testing with isPresented

The isPresented API can be tested by using the updated previews included in this PR.

Base automatically changed from modal-uikit to dev December 21, 2024 14:24
@mikhailChelbaev mikhailChelbaev merged commit d0d059e into dev Dec 21, 2024
1 check failed
@mikhailChelbaev mikhailChelbaev deleted the modal-swiftui branch December 21, 2024 14:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants