{"id":28413728,"url":"https://github.com/fabernovel/dynamicoverlay","last_synced_at":"2025-09-04T05:07:16.258Z","repository":{"id":37465323,"uuid":"318216903","full_name":"faberNovel/DynamicOverlay","owner":"faberNovel","description":"A SwiftUI library that makes easier to develop overlay based interfaces, such as the one presented in the Apple Maps app.","archived":false,"fork":false,"pushed_at":"2023-07-05T17:06:30.000Z","size":33853,"stargazers_count":233,"open_issues_count":11,"forks_count":23,"subscribers_count":8,"default_branch":"main","last_synced_at":"2025-09-04T05:04:41.099Z","etag":null,"topics":["overlay","sheet","swiftui"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/faberNovel.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-12-03T14:19:25.000Z","updated_at":"2025-09-03T09:55:48.000Z","dependencies_parsed_at":"2022-07-12T15:50:38.954Z","dependency_job_id":null,"html_url":"https://github.com/faberNovel/DynamicOverlay","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/faberNovel/DynamicOverlay","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faberNovel%2FDynamicOverlay","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faberNovel%2FDynamicOverlay/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faberNovel%2FDynamicOverlay/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faberNovel%2FDynamicOverlay/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/faberNovel","download_url":"https://codeload.github.com/faberNovel/DynamicOverlay/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faberNovel%2FDynamicOverlay/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273555462,"owners_count":25126316,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-04T02:00:08.968Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["overlay","sheet","swiftui"],"created_at":"2025-06-03T05:19:47.910Z","updated_at":"2025-09-04T05:07:16.199Z","avatar_url":"https://github.com/faberNovel.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DynamicOverlay\n\n\u003cH4 align=\"center\"\u003e\nDynamicOverlay is a SwiftUI library. It makes easier to develop overlay based interfaces, such as the one presented in the Apple Maps, Stocks or Shortcuts apps.\n\u003c/H4\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://developer.apple.com/\"\u003e\u003cimg alt=\"Platform\" src=\"https://img.shields.io/badge/platform-iOS-green.svg\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://developer.apple.com/swift\"\u003e\u003cimg alt=\"Swift5\" src=\"https://img.shields.io/badge/language-Swift%205.0-orange.svg\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://cocoapods.org/pods/DynamicOverlay\"\u003e\u003cimg alt=\"CocoaPods\" src=\"https://img.shields.io/cocoapods/v/DynamicOverlay.svg?style=flat\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/Carthage/Carthage\"\u003e\u003cimg alt=\"Carthage\" src=\"https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/fabernovel/DynamicOverlay/actions\"\u003e\u003cimg alt=\"Build Status\" src=\"https://github.com/fabernovel/DynamicOverlay/workflows/CI/badge.svg?branch=main\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/fabernovel/DynamicOverlay/blob/main/LICENSE\"\u003e\u003cimg alt=\"License\" src=\"https://img.shields.io/cocoapods/l/DynamicOverlay.svg?style=flat\"/\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n- [Requirements](#requirements)\n- [Getting started](#getting-started)\n- [Examples](#examples)\n- [Magnetic notch overlay](#magnetic-notch-overlay)\n  - [Specifying the notches](#specifying-the-notches)\n  - [Drag gesture support](#drag-gesture-support)\n  - [Scroll view support](#scroll-view-support)\n  - [Responding to overlay update](#responding-to-overlay-update)\n  - [Moving the overlay](#moving-the-overlay)\n  - [Disabling notches](#disabling-notches)\n  - [Installation](#installation)\n    - [CocoaPods](#cocoapods)\n    - [Carthage](#carthage)\n    - [Swift Package Manager](#swift-package-manager)\n- [Under the hood](#under-the-hood)\n- [Release](#release)\n- [Author](#author)\n- [License](#license)\n\n## Requirements\n\n`DynamicOverlay` is written in Swift 5. Compatible with iOS 13.0+.\n\n## Getting started\n\nA dynamic overlay is an overlay that dynamically reveals or hides the content underneath it.\n\nYou add a dynamic overlay as a [regular one](https://developer.apple.com/documentation/swiftui/view/overlay(_:alignment:)) using a view modifier:\n\n```swift\nColor.blue.dynamicOverlay(Color.red)\n```\nIts behavior is defined by the `DynamicOverlayBehavior` associated to it if any.\n\n```swift\n\nColor.blue\n    .dynamicOverlay(Color.red)\n    .dynamicOverlayBehavior(myOverlayBehavior)\n\nvar myOverlayBehavior: some DynamicOverlayBehavior {\n    ...\n}\n```\nIf you do not specify a behavior in the overlay view hierarchy, it uses a default one.\n\n## Examples\n\n- [Map App](https://github.com/faberNovel/DynamicOverlay/blob/main/DynamicOverlay_Example/DynamicOverlay_Example/View/MapRootView.swift)\n\n| Min | Max |\n| ------------------- | ------------------ |\n| \u003cimg src=\"https://github.com/faberNovel/DynamicOverlay/blob/main/Screenshots/min.png\" width=200 /\u003e | \u003cimg src=\"https://github.com/faberNovel/DynamicOverlay/blob/main/Screenshots/max.png\" width=200 /\u003e |\n\n## Magnetic notch overlay\n\n`MagneticNotchOverlayBehavior` is a `DynamicOverlayBehavior` instance. It is the only behavior available for now.\n\nIt describes an overlay that can be dragged up and down alongside predefined notches. Whenever a drag gesture ends, the overlay motion will continue until it reaches one of its notches.\n\n### Specifying the notches\n\nThe preferred way to define the notches is to declare an `CaseIterable` enum:\n\n```swift\nenum Notch: CaseIterable, Equatable {\n    case min, max\n}\n```\nYou specify the dimensions of each notch when you create a  `MagneticNotchOverlayBehavior`  instance:\n\n```swift\n@State var isCompact = false\n\nvar myOverlayBehavior: some DynamicOverlayBehavior {\n    MagneticNotchOverlayBehavior\u003cNotch\u003e { notch in\n        switch notch {\n        case .max:\n            return isCompact ? .fractional(0.5) : .fractional(0.8)\n        case .min:\n            return .fractional(0.3)\n        }\n    }\n}\n```\nThere are two kinds of dimension:\n```swift\nextension NotchDimension {\n\n    /// Creates a dimension with an absolute point value.\n    static func absolute(_ value: Double) -\u003e NotchDimension\n\n    /// Creates a dimension that is computed as a fraction of the height of the overlay parent view.\n    static func fractional(_ value: Double) -\u003e NotchDimension\n}\n```\n### Drag gesture support\n\nBy default, all the content of the overlay is draggable but you can limit this behavior using the `draggable`  view modifier.\n\nHere only the list header is draggable:\n\n```swift\nvar body: some View {\n    Color.green\n        .dynamicOverlay(myOverlayContent)\n        .dynamicOverlayBehavior(myOverlayBehavior)\n}\n\nvar myOverlayContent: some View {\n    VStack {\n        Text(\"Header\").draggable()\n        List {\n            Text(\"Row 1\")\n            Text(\"Row 2\")\n            Text(\"Row 3\")\n        }\n    }\n}\n\nvar myOverlayBehavior: some DynamicOverlayBehavior {\n    MagneticNotchOverlayBehavior\u003cNotch\u003e { ... }\n}\n```\nHere we disable the drag gesture entirely:\n```swift\nvar myOverlayContent: some View {\n    VStack {\n        Text(\"Header\")\n        List {\n            Text(\"Row 1\")\n            Text(\"Row 2\")\n            Text(\"Row 3\")\n        }\n    }\n    .draggable(false)\n}\n```\n\n### Scroll view support\n\nA magnetic notch overlay can coordinate its motion with the scrolling of a scroll view.\n\nMark the ScrollView or List that should dictate the overlays movement with `divingScrollView()`.\n\n```swift\nvar myOverlayContent: some View {\n    VStack {\n        Text(\"Header\").draggable()\n        List {\n            Text(\"Row 1\")\n            Text(\"Row 2\")\n            Text(\"Row 3\")\n        }\n        .drivingScrollView()\n    }\n}\n```\n\n### Responding to overlay updates\n\nYou can track the overlay motions using the `onTranslation(_:)` view modifier. It is a great occasion to update your UI based on the current overlay state.\n\nHere we define a control that should be right above the overlay:\n\n```swift\nstruct ControlView: View {\n\n    let height: CGFloat\n    let action: () -\u003e Void\n\n    var body: some View {\n        VStack {\n            Button(\"Action\", action: action)\n            Spacer().frame(height: height)\n        }\n    }\n}\n```\nWe make sure the control is always visible thanks to the translation parameter:\n\n```swift\n@State var height: CGFloat = 0.0\n\nvar body: some View {\n    ZStack {\n        Color.blue\n        ControlView(height: height, action: {})\n    }\n    .dynamicOverlay(Color.red)\n    .dynamicOverlayBehavior(myOverlayBehavior)\n}\n\nvar myOverlayBehavior: some DynamicOverlayBehavior {\n    MagneticNotchOverlayBehavior\u003cNotch\u003e { ... }\n    .onTranslation { translation in\n        height = translation.height\n    }\n}\n```\nYou can also be notified when a notch is reached using a binding:\n```swift\n@State var notch: Notch = .min\n\nvar body: some View {\n    Color.blue\n        .dynamicOverlay(Text(\"\\(notch)\"))\n        .dynamicOverlayBehavior(myOverlayBehavior)\n}\n\nvar myOverlayBehavior: some DynamicOverlayBehavior {\n    MagneticNotchOverlayBehavior\u003cNotch\u003e { ... }\n    .notchChange($notch)\n}\n```\n\n### Moving the overlay\n\nYou can move explicitly the overlay using a notch binding.\n\n```swift\n@State var notch: Notch = .min\n\nvar body: some View {\n    ZStack {\n        Color.green\n        Button(\"Move to top\") {\n            notch = .max\n        }\n    }\n    .dynamicOverlay(Color.red)\n    .dynamicOverlayBehavior(myOverlayBehavior)\n}\n\nvar myOverlayBehavior: some DynamicOverlayBehavior {\n    MagneticNotchOverlayBehavior\u003cNotch\u003e { ... }\n    .notchChange($notch)\n}\n```\nWrap the change in an animation block to animate the change.\n\n```swift\nButton(\"Move to top\") {\n    withAnimation {\n        notch = .max\n    }\n}\n```\n\n### Disabling notches\n\nWhen a notch is disabled, the overlay will ignore it. Here we block the overlay in its `min` position:\n\n```swift\n@State var notch: Notch = .max\n\nvar myOverlayBehavior: some DynamicOverlayBehavior {\n    MagneticNotchOverlayBehavior\u003cNotch\u003e { ... }\n    .notchChange($notch)\n    .disable(.max, notch == .min)\n}\n```\n\n## Under the hood\n\n`DynamicOverlay` is built on top of [OverlayContainer](https://github.com/applidium/OverlayContainer). If you need more control, consider using it or open an issue.\n\n## Installation\n\n`DynamicOverlay` is available through [CocoaPods](https://cocoapods.org). To install it, simply add the following line to your Podfile:\n\n### Cocoapods\n\n```ruby\npod 'DynamicOverlay'\n```\n\n### Carthage\n\nAdd the following to your Cartfile:\n\n```ruby\ngithub \"https://github.com/fabernovel/DynamicOverlay\"\n```\n\n### Swift Package Manager\n\n`DynamicOverlay` can be installed as a Swift Package with Xcode 11 or higher. To install it, add a package using Xcode or a dependency to your Package.swift file:\n\n```swift\n.package(url: \"https://github.com/fabernovel/DynamicOverlay.git\")\n```\n\n## Release\n\n- Create a release branch for the new version (release/#version#)\n- Update the [CHANGELOG.md](https://github.com/faberNovel/DynamicOverlay/blob/main/CHANGELOG.md) (Be sure to spell your release version correctly)\n- Push your release branch\n- Run the [release workflow](https://github.com/faberNovel/DynamicOverlay/actions/workflows/release.yml) from your release branch\n\n## Author\n\n[@gaetanzanella](https://twitter.com/gaetanzanella), gaetan.zanella@fabernovel.com\n\n## License\n\n`DynamicOverlay` is available under the MIT license. See the LICENSE file for more info.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffabernovel%2Fdynamicoverlay","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffabernovel%2Fdynamicoverlay","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffabernovel%2Fdynamicoverlay/lists"}