iOS/Sources/Shared/DesignSystem/Components/AppleLikeBottomSheet.swift
Bruno Pantaleão Gonçalves bbe33a76b6
Remove custom toast and notify Improv via notification (#2860)
<!-- Thank you for submitting a Pull Request and helping to improve Home
Assistant. Please complete the following sections to help the processing
and review of your changes. Please do not delete anything from this
template. -->

## Summary
<!-- Provide a brief summary of the changes you have made and most
importantly what they aim to achieve -->

## Screenshots
<!-- If this is a user-facing change not in the frontend, please include
screenshots in light and dark mode. -->

## Link to pull request in Documentation repository
<!-- Pull requests that add, change or remove functionality must have a
corresponding pull request in the Companion App Documentation repository
(https://github.com/home-assistant/companion.home-assistant). Please add
the number of this pull request after the "#" -->
Documentation: home-assistant/companion.home-assistant#

## Any other notes
<!-- If there is any other information of note, like if this Pull
Request is part of a bigger change, please include it here. -->
2024-07-22 13:43:01 +02:00

136 lines
4.2 KiB
Swift

import SwiftUI
public enum AppleLikeBottomSheetViewState {
case initial
case dismiss
}
public struct AppleLikeBottomSheet<Content: View>: View {
@Environment(\.dismiss) private var dismiss
/// Used for appear and disappear bottom sheet animation
@State private var displayBottomSheet = false
private let content: Content
@State private var showCloseButton: Bool
@Binding private var state: AppleLikeBottomSheetViewState?
private let willDismiss: (() -> Void)?
private let bottomSheetMinHeight: CGFloat = 400
public init(
@ViewBuilder content: () -> Content,
showCloseButton: Bool = true,
state: Binding<AppleLikeBottomSheetViewState?>,
willDismiss: (() -> Void)? = nil
) {
self.content = content()
self.showCloseButton = showCloseButton
self._state = state
self.willDismiss = willDismiss
}
public var body: some View {
VStack {
Spacer()
ZStack(alignment: .top) {
content
.frame(maxWidth: .infinity)
.padding(.horizontal, Spaces.two)
.padding(.vertical, Spaces.six)
if showCloseButton {
closeButton
}
}
.padding(.horizontal)
.frame(minHeight: bottomSheetMinHeight)
.frame(maxWidth: maxWidth, alignment: .center)
.background(.regularMaterial)
.clipShape(RoundedRectangle(cornerRadius: perfectCornerRadius))
.shadow(color: .black.opacity(0.2), radius: 20)
.padding(Spaces.one)
.fixedSize(horizontal: false, vertical: true)
.offset(y: displayBottomSheet ? 0 : bottomSheetMinHeight)
.onAppear {
state = .initial
withAnimation(.bouncy) {
displayBottomSheet = true
}
}
}
.ignoresSafeArea()
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(.black.opacity(0.3))
.modify {
if #available(iOS 16, *) {
$0.persistentSystemOverlays(.hidden)
} else {
$0
}
}
.onChange(of: state) { newValue in
if newValue == .dismiss {
if #available(iOS 17.0, *) {
withAnimation(.bouncy) {
displayBottomSheet = false
} completion: {
willDismiss?()
dismiss()
}
} else {
withAnimation(.bouncy) {
displayBottomSheet = false
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
willDismiss?()
dismiss()
}
}
}
}
}
}
private var maxWidth: CGFloat {
if UIDevice.current.userInterfaceIdiom == .phone {
.infinity
} else {
400
}
}
private var perfectCornerRadius: CGFloat {
if UIDevice.current.userInterfaceIdiom == .phone {
UIScreen.main.displayCornerRadius - Spaces.one
} else {
50
}
}
@ViewBuilder
private var closeButton: some View {
VStack {
HStack {
Spacer()
Button(action: {
state = .dismiss
}, label: {
Image(systemName: "xmark.circle.fill")
.font(.system(size: 20))
.foregroundStyle(.gray, Color(uiColor: .tertiarySystemBackground))
})
}
Spacer()
}
.padding(.top, Spaces.three)
.padding([.trailing, .bottom], Spaces.one)
}
}
#Preview {
ZStack {
VStack {}
.background(.blue)
.frame(maxWidth: .infinity, maxHeight: .infinity)
AppleLikeBottomSheet(content: { Text("Hello World") }, state: .constant(.initial)) {}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}