Skip to content

Instantly share code, notes, and snippets.

@michzio
Created March 17, 2021 09:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save michzio/9b6de42f9d7b37f9d76e4a312d3b3496 to your computer and use it in GitHub Desktop.
Save michzio/9b6de42f9d7b37f9d76e4a312d3b3496 to your computer and use it in GitHub Desktop.
import SwiftUI
struct AdaptiveBottomSheet<SheetContent>: ViewModifier where SheetContent: View {
// MARK: - Binding
private var isPresented: Binding<Bool>
// MARK: - State
@State private var showBottomSheet : Bool = false
@State private var showBackground: Bool = false
// MARK: - Actions
private let onDone: (() -> Void)?
private let sheetContent: () -> SheetContent
// MARK: - Init
init(isPresented: Binding<Bool>, onDone: (() -> Void)? = nil, @ViewBuilder content: @escaping () -> SheetContent) {
self.isPresented = isPresented
self.onDone = onDone
self.sheetContent = content
}
func body(content: Content) -> some View {
let isShowingBinding = Binding<Bool>(get: {
if self.isPresented.wrappedValue {
DispatchQueue.main.async {
withAnimation {
self.showBackground = self.isPresented.wrappedValue
}
}
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) {
withAnimation {
self.showBottomSheet = self.isPresented.wrappedValue
}
}
} else {
DispatchQueue.main.async {
withAnimation {
self.showBottomSheet = false
}
}
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(350)) {
withAnimation {
self.showBackground = false
}
}
}
print("Adaptive Bottom Sheet show: \(self.showBackground)")
return self.showBackground
}, set: {
self.isPresented.wrappedValue = $0
})
return Group {
if DeviceType.IS_ANY_IPAD {
content.popover(isPresented: isPresented) {
self.sheetContent()
}
} else {
content.present(isShowing: isShowingBinding, animated: false) {
ZStack {
if self.showBackground {
Color("Dim")
.opacity(0.25)
.transition(.opacity)
.onTapGesture(perform: self.dismissBottomSheetAction)
}
VStack {
Spacer()
if self.showBottomSheet {
self.bottomSheet
.background(Color.white)
.transition(.move(edge: .bottom))
.animation(.easeInOut(duration: 0.35))
}
}
}
.edgesIgnoringSafeArea(.all)
}
}
}
}
var bottomSheet: some View {
VStack {
if self.onDone != nil {
Divider().background(Color.white)
.shadow(color: Color("Dim"), radius: 4)
HStack {
Spacer()
Button("DONE") {
print("Tapped picker done button!")
self.dismissBottomSheetAction()
}
.foregroundColor(Color("Accent"))
.padding(.trailing, 16)
}
}
self.sheetContent()
}
}
func dismissBottomSheetAction() {
self.isPresented.wrappedValue = false
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500)) {
self.onDone?()
}
}
}
extension View {
func adaptiveBottomSheet<Content: View>(isPresented: Binding<Bool>, onDone: (() -> Void)? = nil, @ViewBuilder content: @escaping () -> Content) -> some View {
self.modifier(AdaptiveBottomSheet(isPresented: isPresented, onDone: onDone, content: content))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment