Skip to content

Instantly share code, notes, and snippets.

@jboullianne
Created September 17, 2020 02:58
Show Gist options
  • Save jboullianne/07ae2df80c27c75394d3fd1c99d4eeb3 to your computer and use it in GitHub Desktop.
Save jboullianne/07ae2df80c27c75394d3fd1c99d4eeb3 to your computer and use it in GitHub Desktop.
SwiftUI Custom Sliding Sheet
//
// SheetBase.swift
// PopoverSheet_Tests
//
// Created by Jean-Marc Boullianne on 9/13/20.
// Copyright © 2020 TrailingClosure. All rights reserved.
//
import SwiftUI
struct SlidingSheetModifier<SheetContent: View>: ViewModifier {
@State private var yOffset: CGFloat // Sheet offset before drag
@State private var dragOffset: CGFloat = 0 // Offset set by user input
private let sheetContent: SheetContent // Content of the sheet
var minHeight: CGFloat // Minimum height of sliding sheet
@Binding var expanded: Bool // Expanded or Closed Position
init(minHeight: CGFloat = 200, expanded: Binding<Bool>, @ViewBuilder sheetContent: () -> SheetContent) {
self.sheetContent = sheetContent()
self.minHeight = minHeight
self._yOffset = State(initialValue: minHeight * -1)
self._expanded = expanded
}
func body(content: Content) -> some View {
ZStack {
content
VStack {
Color.gray.brightness(0.2)
.frame(width: 35, height: 5)
.cornerRadius(3)
VStack(spacing: 0) {
sheetContent
}
.frame(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
.background(Color.black.brightness(0.11))
.cornerRadius(10)
}
.edgesIgnoringSafeArea(.bottom)
.offset(x: 0, y: UIScreen.main.bounds.height + yOffset + dragOffset)
.gesture(DragGesture()
.onChanged({ event in
if UIScreen.main.bounds.height + self.yOffset + self.dragOffset <= 40 {
self.dragOffset = ((UIScreen.main.bounds.height + self.yOffset - 40) * -1)
self.expanded = true
} else {
self.dragOffset = event.translation.height
self.expanded = false
}
})
.onEnded({ event in
self.yOffset = self.yOffset + self.dragOffset
self.dragOffset = 0
withAnimation {
let height = UIScreen.main.bounds.height
let topDiff = height + self.yOffset
let bottomDiff = self.minHeight + self.yOffset
if abs(topDiff) < abs(bottomDiff) {
self.yOffset = (height - 50) * -1
self.expanded = true
} else {
self.yOffset = self.minHeight * -1
}
}
})
)
}
}
}
struct SheetBase_Previews: PreviewProvider {
@State static var expanded: Bool = false
static var previews: some View {
Color.white
.slidingSheet(minHeight: 125, expanded: $expanded) {
VStack(alignment: .leading, spacing: 0) {
Text("SlidingSheetModifier")
.font(.title)
.bold()
.foregroundColor(.white)
.padding(.all, 40)
Spacer()
}
}
}
}
extension View {
func slidingSheet<Content: View>(minHeight: CGFloat, expanded: Binding<Bool>, @ViewBuilder content: () -> Content) -> some View {
self.modifier(SlidingSheetModifier(minHeight: minHeight, expanded: expanded, sheetContent: content))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment