https://github.com/fabernovel/dynamicoverlay
A SwiftUI library that makes easier to develop overlay based interfaces, such as the one presented in the Apple Maps app.
https://github.com/fabernovel/dynamicoverlay
overlay sheet swiftui
Last synced: about 1 month ago
JSON representation
A SwiftUI library that makes easier to develop overlay based interfaces, such as the one presented in the Apple Maps app.
- Host: GitHub
- URL: https://github.com/fabernovel/dynamicoverlay
- Owner: faberNovel
- License: mit
- Created: 2020-12-03T14:19:25.000Z (almost 5 years ago)
- Default Branch: main
- Last Pushed: 2023-07-05T17:06:30.000Z (over 2 years ago)
- Last Synced: 2025-09-04T05:04:41.099Z (about 1 month ago)
- Topics: overlay, sheet, swiftui
- Language: Swift
- Homepage:
- Size: 32.3 MB
- Stars: 233
- Watchers: 8
- Forks: 23
- Open Issues: 11
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# DynamicOverlay
DynamicOverlay 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.---
- [Requirements](#requirements)
- [Getting started](#getting-started)
- [Examples](#examples)
- [Magnetic notch overlay](#magnetic-notch-overlay)
- [Specifying the notches](#specifying-the-notches)
- [Drag gesture support](#drag-gesture-support)
- [Scroll view support](#scroll-view-support)
- [Responding to overlay update](#responding-to-overlay-update)
- [Moving the overlay](#moving-the-overlay)
- [Disabling notches](#disabling-notches)
- [Installation](#installation)
- [CocoaPods](#cocoapods)
- [Carthage](#carthage)
- [Swift Package Manager](#swift-package-manager)
- [Under the hood](#under-the-hood)
- [Release](#release)
- [Author](#author)
- [License](#license)## Requirements
`DynamicOverlay` is written in Swift 5. Compatible with iOS 13.0+.
## Getting started
A dynamic overlay is an overlay that dynamically reveals or hides the content underneath it.
You add a dynamic overlay as a [regular one](https://developer.apple.com/documentation/swiftui/view/overlay(_:alignment:)) using a view modifier:
```swift
Color.blue.dynamicOverlay(Color.red)
```
Its behavior is defined by the `DynamicOverlayBehavior` associated to it if any.```swift
Color.blue
.dynamicOverlay(Color.red)
.dynamicOverlayBehavior(myOverlayBehavior)var myOverlayBehavior: some DynamicOverlayBehavior {
...
}
```
If you do not specify a behavior in the overlay view hierarchy, it uses a default one.## Examples
- [Map App](https://github.com/faberNovel/DynamicOverlay/blob/main/DynamicOverlay_Example/DynamicOverlay_Example/View/MapRootView.swift)
| Min | Max |
| ------------------- | ------------------ |
||
|
## Magnetic notch overlay
`MagneticNotchOverlayBehavior` is a `DynamicOverlayBehavior` instance. It is the only behavior available for now.
It 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.
### Specifying the notches
The preferred way to define the notches is to declare an `CaseIterable` enum:
```swift
enum Notch: CaseIterable, Equatable {
case min, max
}
```
You specify the dimensions of each notch when you create a `MagneticNotchOverlayBehavior` instance:```swift
@State var isCompact = falsevar myOverlayBehavior: some DynamicOverlayBehavior {
MagneticNotchOverlayBehavior { notch in
switch notch {
case .max:
return isCompact ? .fractional(0.5) : .fractional(0.8)
case .min:
return .fractional(0.3)
}
}
}
```
There are two kinds of dimension:
```swift
extension NotchDimension {/// Creates a dimension with an absolute point value.
static func absolute(_ value: Double) -> NotchDimension/// Creates a dimension that is computed as a fraction of the height of the overlay parent view.
static func fractional(_ value: Double) -> NotchDimension
}
```
### Drag gesture supportBy default, all the content of the overlay is draggable but you can limit this behavior using the `draggable` view modifier.
Here only the list header is draggable:
```swift
var body: some View {
Color.green
.dynamicOverlay(myOverlayContent)
.dynamicOverlayBehavior(myOverlayBehavior)
}var myOverlayContent: some View {
VStack {
Text("Header").draggable()
List {
Text("Row 1")
Text("Row 2")
Text("Row 3")
}
}
}var myOverlayBehavior: some DynamicOverlayBehavior {
MagneticNotchOverlayBehavior { ... }
}
```
Here we disable the drag gesture entirely:
```swift
var myOverlayContent: some View {
VStack {
Text("Header")
List {
Text("Row 1")
Text("Row 2")
Text("Row 3")
}
}
.draggable(false)
}
```### Scroll view support
A magnetic notch overlay can coordinate its motion with the scrolling of a scroll view.
Mark the ScrollView or List that should dictate the overlays movement with `divingScrollView()`.
```swift
var myOverlayContent: some View {
VStack {
Text("Header").draggable()
List {
Text("Row 1")
Text("Row 2")
Text("Row 3")
}
.drivingScrollView()
}
}
```### Responding to overlay updates
You 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.
Here we define a control that should be right above the overlay:
```swift
struct ControlView: View {let height: CGFloat
let action: () -> Voidvar body: some View {
VStack {
Button("Action", action: action)
Spacer().frame(height: height)
}
}
}
```
We make sure the control is always visible thanks to the translation parameter:```swift
@State var height: CGFloat = 0.0var body: some View {
ZStack {
Color.blue
ControlView(height: height, action: {})
}
.dynamicOverlay(Color.red)
.dynamicOverlayBehavior(myOverlayBehavior)
}var myOverlayBehavior: some DynamicOverlayBehavior {
MagneticNotchOverlayBehavior { ... }
.onTranslation { translation in
height = translation.height
}
}
```
You can also be notified when a notch is reached using a binding:
```swift
@State var notch: Notch = .minvar body: some View {
Color.blue
.dynamicOverlay(Text("\(notch)"))
.dynamicOverlayBehavior(myOverlayBehavior)
}var myOverlayBehavior: some DynamicOverlayBehavior {
MagneticNotchOverlayBehavior { ... }
.notchChange($notch)
}
```### Moving the overlay
You can move explicitly the overlay using a notch binding.
```swift
@State var notch: Notch = .minvar body: some View {
ZStack {
Color.green
Button("Move to top") {
notch = .max
}
}
.dynamicOverlay(Color.red)
.dynamicOverlayBehavior(myOverlayBehavior)
}var myOverlayBehavior: some DynamicOverlayBehavior {
MagneticNotchOverlayBehavior { ... }
.notchChange($notch)
}
```
Wrap the change in an animation block to animate the change.```swift
Button("Move to top") {
withAnimation {
notch = .max
}
}
```### Disabling notches
When a notch is disabled, the overlay will ignore it. Here we block the overlay in its `min` position:
```swift
@State var notch: Notch = .maxvar myOverlayBehavior: some DynamicOverlayBehavior {
MagneticNotchOverlayBehavior { ... }
.notchChange($notch)
.disable(.max, notch == .min)
}
```## Under the hood
`DynamicOverlay` is built on top of [OverlayContainer](https://github.com/applidium/OverlayContainer). If you need more control, consider using it or open an issue.
## Installation
`DynamicOverlay` is available through [CocoaPods](https://cocoapods.org). To install it, simply add the following line to your Podfile:
### Cocoapods
```ruby
pod 'DynamicOverlay'
```### Carthage
Add the following to your Cartfile:
```ruby
github "https://github.com/fabernovel/DynamicOverlay"
```### Swift Package Manager
`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:
```swift
.package(url: "https://github.com/fabernovel/DynamicOverlay.git")
```## Release
- Create a release branch for the new version (release/#version#)
- Update the [CHANGELOG.md](https://github.com/faberNovel/DynamicOverlay/blob/main/CHANGELOG.md) (Be sure to spell your release version correctly)
- Push your release branch
- Run the [release workflow](https://github.com/faberNovel/DynamicOverlay/actions/workflows/release.yml) from your release branch## Author
[@gaetanzanella](https://twitter.com/gaetanzanella), gaetan.zanella@fabernovel.com
## License
`DynamicOverlay` is available under the MIT license. See the LICENSE file for more info.