Created
September 17, 2020 02:58
-
-
Save jboullianne/07ae2df80c27c75394d3fd1c99d4eeb3 to your computer and use it in GitHub Desktop.
SwiftUI Custom Sliding Sheet
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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