Skip to content

Commit

Permalink
Merge pull request #992 from adevinta/component/bottomsheet
Browse files Browse the repository at this point in the history
Component/bottomsheet
  • Loading branch information
michael-zimmermann authored Jun 7, 2024
2 parents c3c95d2 + 9eb27e6 commit 20d03f1
Show file tree
Hide file tree
Showing 14 changed files with 751 additions and 0 deletions.
46 changes: 46 additions & 0 deletions core/Sources/Components/BottomSheet/SwiftUI/View-Height.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//
// BottomSheetDetentViewModifier.swift
// SparkCore
//
// Created by Michael Zimmermann on 17.05.24.
// Copyright © 2024 Adevinta. All rights reserved.
//

import SwiftUI

struct ViewHeightPreferenceKey: PreferenceKey {
static var defaultValue: CGFloat?

static func reduce(value: inout CGFloat?, nextValue: () -> CGFloat?) {
guard let nextValue = nextValue() else { return }
value = nextValue
}
}

struct ViewHeightModifier: ViewModifier {
let height: Binding<CGFloat>
func body(content: Content) -> some View {
content
.fixedSize(horizontal: true, vertical: true)
.background(
GeometryReader{ geometry in
Color.clear
.preference(key: ViewHeightPreferenceKey.self, value: geometry.size.height)
}
)
.onPreferenceChange(ViewHeightPreferenceKey.self) { viewHeight in
if let viewHeight {
height.wrappedValue = viewHeight
}
}
}
}

public extension View {
/// A view modifier for reading the height of a view. The height will be set in the given binding.
func readHeight(_ height: Binding<CGFloat>) -> some View {
self
.modifier(ViewHeightModifier(height: height))
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//
// UISheetPresentationController-customHeightDetent.swift
// SparkCore
//
// Created by Michael Zimmermann on 14.05.24.
// Copyright © 2024 Adevinta. All rights reserved.
//

import UIKit

public extension UISheetPresentationController.Detent {
/// Return a custom sheet detent with the view height fitting the expanded size as the custom height.
/// ```
/// let controller = ExampleBottomSheetViewController()
/// if #available(iOS 16.0, *) {
/// if let sheet = controller.sheetPresentationController {
/// sheet.detents = [.medium(), .large(), .expandedHeight(of: controller.view)]
/// }
/// }
/// present(controller, animated: true)
/// ```
@available(iOS 16.0, *)
static func expandedHeight(of view: UIView) -> UISheetPresentationController.Detent {
return .custom { context in
let height = min(
view.systemLayoutSizeFitting(UIView.layoutFittingExpandedSize).height,
context.maximumDetentValue - 1)

return height
}
}

/// Return a custom sheet detent with the view height fitting the compressed size as the custom height.
/// ```
/// let controller = ExampleBottomSheetViewController()
/// if #available(iOS 16.0, *) {
/// if let sheet = controller.sheetPresentationController {
/// sheet.detents = [.medium(), .large(), .compressedHeight(of: controller.view)]
/// }
/// }
/// present(controller, animated: true)
/// ```
@available(iOS 16.0, *)
static func compressedHeight(of view: UIView) -> UISheetPresentationController.Detent {
return .custom { context in
let height = min(
view.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height,
context.maximumDetentValue - 1)

return height
}
}

@available(iOS 16.0, *)
/// A custom detent which is almos the size of the large detent but avoids that the background is scaled.
static func maxHeight() -> UISheetPresentationController.Detent {
return .custom { context in
return context.maximumDetentValue - 1
}
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
21 changes: 21 additions & 0 deletions spark/Demo/Assets.xcassets/BottomSheet.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "BottomSheet.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
2 changes: 2 additions & 0 deletions spark/Demo/Classes/Enum/UIComponent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Foundation
struct UIComponent: RawRepresentable, CaseIterable, Equatable {
static var allCases: [UIComponent] = [
.badge,
.bottomSheet,
.button,
.iconButton,
.checkbox,
Expand All @@ -38,6 +39,7 @@ struct UIComponent: RawRepresentable, CaseIterable, Equatable {
var rawValue: String

static let badge = UIComponent(rawValue: "Badge")
static let bottomSheet = UIComponent(rawValue: "Bottom Sheet")
static let button = UIComponent(rawValue: "Button")
static let checkbox = UIComponent(rawValue: "Checkbox")
static let chip = UIComponent(rawValue: "Chip")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// BottomSheetPresentedView.swift
// SparkDemo
//
// Created by Michael Zimmermann on 17.05.24.
// Copyright © 2024 Adevinta. All rights reserved.
//

import SwiftUI

struct BottomSheetPresentedView: View {
var description: String = """
Sample of a SwiftUI bottom sheet with little text.
🧡💙
"""
var dismiss: () -> Void

var body: some View {
VStack(spacing: 40) {
Text("Bottom Sheet").font(.title)
Text(description)
.font(.body)
.multilineTextAlignment(.center)
.padding()
Button {
self.dismiss()
} label: {
Text("Dismiss")
}
.buttonStyle(.borderedProminent)
}
.frame(maxWidth: .infinity)
.background(alignment: .top) {
Image("BottomSheet")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
//
// BottomSheetExampleView.swift
// SparkDemo
//
// Created by Michael Zimmermann on 17.05.24.
// Copyright © 2024 Adevinta. All rights reserved.
//

import SwiftUI

private let longDescription: String = """
Sample of a SwiftUI bottom sheet with a scroll view.
🧡
💙
🧡
💙
🧡
💙
🧡
💙
🧡
💙
🧡
💙
🧡
💙
🧡
💙
🧡
💙
🧡
💙
🧡
💙
🧡
💙
🧡
💙
🧡
💙
🧡
💙
🧡
💙
🧡
💙
🧡
💙
"""
private let mediumDescription: String = """
Sample of a SwiftUI bottom sheet with a long text.
🧡
💙
🧡
💙
🧡
💙
🧡
💙
🧡
💙
🧡
💙
🧡
💙
🧡
💙
🧡
💙
🧡
💙
🧡
💙
🧡
"""
struct BottomSheetPresentingView: View {
var body: some View {
if #available(iOS 16.4, *) {
BottomSheetPresentingViewWithHeightDetent()
} else {
BottomSheetPresentingViewWithoutHeightDetent()
}
}
}

@available(iOS 16.4, *)
struct BottomSheetPresentingViewWithHeightDetent: View {
@State private var showingShortSheet = false
@State private var showingMediumSheet = false
@State private var showingLongSheet = false
@State private var shortHeight: CGFloat = 100
@State private var mediumHeight: CGFloat = 100

var body: some View {
VStack {
Button("Show bottom sheet with little text") {
self.showingShortSheet.toggle()
}
.sheet(isPresented: $showingShortSheet) {
BottomSheetPresentedView() {
self.showingShortSheet.toggle()
}
.readHeight(self.$shortHeight)
.presentationDetents([.height(self.shortHeight), .large])
}
.buttonStyle(.borderedProminent)

Button("Show bottom sheet with longer text") {
self.showingMediumSheet.toggle()
}
.sheet(isPresented: $showingMediumSheet) {
BottomSheetPresentedView(description: mediumDescription) {
self.showingMediumSheet.toggle()
}
.readHeight(self.$mediumHeight)
.presentationDetents([.height(self.mediumHeight), .large])
}
.buttonStyle(.borderedProminent)

Button("Show bottom sheet with scroll view") {
self.showingLongSheet.toggle()
}
.sheet(isPresented: $showingLongSheet) {
ScrollView {
BottomSheetPresentedView(description: longDescription) {
self.showingLongSheet.toggle()
}
}
.scrollIndicators(.visible)
.presentationDetents([.medium, .large])
}
.buttonStyle(.borderedProminent)
}
}
}

struct BottomSheetPresentingViewWithoutHeightDetent: View {
@State private var showingShortText = false
@State private var showingLongText = false

var body: some View {
VStack {
Button("Show bottom sheet") {
self.showingShortText.toggle()
}
.sheet(isPresented: $showingShortText) {
BottomSheetPresentedView() {
self.showingShortText.toggle()
}
}
.buttonStyle(.bordered)

Button("Show bottom sheet with Scroll view") {
self.showingLongText.toggle()
}
.sheet(isPresented: $showingLongText) {
ScrollView(showsIndicators: false) {
BottomSheetPresentedView(description: longDescription) {
self.showingLongText.toggle()
}
}
}
.buttonStyle(.bordered)
}
}
}
Loading

0 comments on commit 20d03f1

Please sign in to comment.