Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,11 @@ The ViewModifiers are used to customise the look and feel of the BottomSheet.

`.enableFloatingIPadSheet(Bool)`: Makes it possible to make the sheet appear like on iPhone.

`.iPadSheetAlignment(Alignment)`: Allows for different alignments of the sheet on iPad.

`.sheetSidePadding(CGFloat)`: Gives equal padding to all edges of the bottom sheet.
`.sheetSidePadding(Edge.Set, CGFloat)`: Gives padding on iPad when enableFloatingIPadSheet is disabled.

`.onDismiss(() -> Void)`: A action that will be performed when the BottomSheet is dismissed.
- Please note that when you dismiss the BottomSheet yourself, by setting the bottomSheetPosition to .hidden, the action will not be called.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// BottomSheet+SheetSidePadding.swift
//
// Created by Lucas Zischka.
// Copyright © 2022 Lucas Zischka. All rights reserved.
//

import Foundation
import SwiftUI

public extension BottomSheet {

/// Gives padding to specific edges of the sheet
///
/// - Parameters:
/// - edges: The edges to apply padding to. Defaults to `.all`.
/// - length: The amount of padding to apply to the specified edges. Defaults to `0.0`.
///
/// - Returns: A BottomSheet with the configured edge padding.
func sheetSidePadding(_ edges: Edge.Set = .all, _ length: CGFloat = 0.0) -> BottomSheet {
let edgeInsets = EdgeInsets(
top: edges.contains(.top) ? length : 0,
leading: edges.contains(.leading) ? length : 0,
bottom: edges.contains(.bottom) ? length : 0,
trailing: edges.contains(.trailing) ? length : 0
)
self.configuration.sheetSidePadding = edgeInsets
return self
}

/// Gives equal padding to all edges of the sheet
///
/// - Parameters:
/// - padding: The amount of padding to apply to all edges. Defaults to `0.0`.
///
/// - Returns: A BottomSheet with the configured padding.
func sheetSidePadding(_ padding: CGFloat = 0.0) -> BottomSheet {
self.configuration.sheetSidePadding = EdgeInsets(top: padding, leading: padding, bottom: padding, trailing: padding)
return self
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// BottomSheet+iPadSheetAlignment.swift
//
// Created by Lucas Zischka.
// Copyright © 2022 Lucas Zischka. All rights reserved.
//

import Foundation
import SwiftUI

public extension BottomSheet {

/// Makes it possible to align the sheet at different places on iPad if floating sheet is disabled
///
/// - Parameters:
/// - alignment: An alignment for the bottom sheet
///
/// - Returns: A BottomSheet that will align to what's specified
func iPadSheetAlignment(_ alignment: Alignment = .bottomLeading) -> BottomSheet {
self.configuration.iPadSheetAlignment = alignment
return self
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ internal extension BottomSheetView {
return self.isIPadFloating || self.isMac
}

/// Determines if the iPad sheet alignment is top-based (top, topLeading, topTrailing)
var isIPadSheetAlignmentTop: Bool {
switch self.configuration.iPadSheetAlignment {
case .top, .topLeading, .topTrailing:
return true
default:
return false
}
}

var isIPadBottom: Bool {
return self.isIPad && !self.configuration.iPadFloatingSheet
}
Expand Down Expand Up @@ -190,8 +200,8 @@ internal extension BottomSheetView {
// On iPhone landscape use 40% of the width
return geometry.size.width * 0.4
} else {
// On iPhone portrait or iPad split screen use 100% of the width
return geometry.size.width
// On iPhone portrait or iPad split screen use 100% of the width minus side padding
return geometry.size.width - self.configuration.sheetSidePadding.leading - self.configuration.sheetSidePadding.trailing
}
#endif
}
Expand Down Expand Up @@ -265,4 +275,16 @@ internal extension BottomSheetView {
$0.height < $1.height
})
}

func getGestureTranslation(for value: DragGesture.Value) -> CGFloat {
// For iPad floating and Mac:
// - For top alignments: Reverse translation direction
// - For other alignments: Use normal translation direction
// For other devices: Use normal translation direction
if self.isIPadFloatingOrMac {
return self.isIPadSheetAlignmentTop ? -value.translation.height : value.translation.height
} else {
return value.translation.height
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ internal extension BottomSheetView {
// Perform custom onChanged action
self.configuration.onDragChanged(value)

// Update translation; on iPad floating and Mac the drag direction is reversed
self.translation = self.isIPadFloatingOrMac ? -value.translation.height : value.translation.height
// Update translation based on drag gesture
self.translation = self.getGestureTranslation(for: value)

// Dismiss the keyboard on drag
self.endEditing()
}
Expand All @@ -43,8 +44,8 @@ internal extension BottomSheetView {

// Notify the ScrollView that the user is dragging
self.dragState = .none
// Update translation; on iPad floating and Mac the drag direction is reversed
self.translation = self.isIPadFloatingOrMac ? -value.translation.height : value.translation.height
// Update translation based on drag gesture
self.translation = self.getGestureTranslation(for: value)
}

// Dismiss the keyboard on dragging/scrolling
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,15 @@ internal extension BottomSheetView {
spacing: 0
) {
// Drag indicator on the top (iPhone and iPad not floating)
if self.configuration.isResizable && self.configuration.isDragIndicatorShown && !self.isIPadFloatingOrMac {
if self.configuration.isResizable && self.configuration.isDragIndicatorShown && (!self.isIPadFloatingOrMac || !self.isIPadSheetAlignmentTop) {
self.dragIndicator( with: geometry)
}

// The header an main content
self.bottomSheetContent(with: geometry)

// Drag indicator on the bottom (iPad floating and Mac)
if self.configuration.isResizable && self.configuration.isDragIndicatorShown && self.isIPadFloatingOrMac {
if self.configuration.isResizable && self.configuration.isDragIndicatorShown && self.isIPadFloatingOrMac && self.isIPadSheetAlignmentTop {
self.dragIndicator(with: geometry)
}
}
Expand All @@ -57,7 +57,7 @@ internal extension BottomSheetView {
.frame(
width: self.width(with: geometry),
height: self.bottomSheetPosition.isDynamic && self.translation == 0 ? nil : self.height(with: geometry),
alignment: self.isIPadFloatingOrMac ? .bottom : .top
alignment: self.isIPadFloatingOrMac ? (self.isIPadSheetAlignmentTop ? .bottom : .top) : .top
)
// Clip BottomSheet for transition to work correctly for iPad and Mac
.clipped()
Expand All @@ -74,9 +74,13 @@ internal extension BottomSheetView {
.top,
self.topPadding
)
// Add side padding
.padding(
self.configuration.sheetSidePadding
)
// Make the BottomSheet transition via move
.transition(.move(
edge: self.isIPadFloatingOrMac ? .top : .bottom
edge: self.isIPadFloatingOrMac ? (self.isIPadSheetAlignmentTop ? .top : .bottom) : .bottom
))
}

Expand Down Expand Up @@ -193,7 +197,7 @@ internal extension BottomSheetView {
// Align content correctly and make it use all available space to fix transition
.frame(
maxHeight: self.maxMainContentHeight(with: geometry),
alignment: self.isIPadFloatingOrMac ? .bottom : .top
alignment: self.isIPadFloatingOrMac ? (self.isIPadSheetAlignmentTop ? .bottom : .top) : .top
)
// Clip main content so that it doesn't go beneath the header content
.clipped()
Expand All @@ -209,7 +213,7 @@ internal extension BottomSheetView {
)
// Make the main content transition via move
.transition(.move(
edge: self.isIPadFloatingOrMac ? .top : .bottom
edge: self.isIPadFloatingOrMac ? (self.isIPadSheetAlignmentTop ? .top : .bottom) : .bottom
))
}

Expand Down Expand Up @@ -314,7 +318,7 @@ internal extension BottomSheetView {
// Only add top padding if no drag indicator
.padding(
(!self.configuration.isDragIndicatorShown || !self.configuration.isResizable) ||
self.isIPadFloatingOrMac ? .top : []
(self.isIPadFloatingOrMac && self.isIPadSheetAlignmentTop) ? .top : []
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ internal extension BottomSheetView {
if let dragPositionSwitchAction = self.configuration.dragPositionSwitchAction {
dragPositionSwitchAction(geometry, value)
} else {
// On iPad floating and Mac the drag direction is reversed
let translationHeight: CGFloat = self.isIPadFloatingOrMac ? -value.translation.height : value.translation.height
let translationHeight: CGFloat = self.getGestureTranslation(for: value)
// The height in percent relative to the screen height the user has dragged
let height: CGFloat = translationHeight / geometry.size.height

Expand Down
4 changes: 2 additions & 2 deletions Sources/BottomSheet/BottomSheetView/BottomSheetView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ internal struct BottomSheetView<HContent: View, MContent: View>: View {
// On iPad floating and Mac the BottomSheet is aligned to the top left
// On iPhone and iPad not floating it is aligned to the bottom center,
// in horizontal mode to the bottom left
alignment: self.isIPadFloatingOrMac ? .topLeading : .bottomLeading
alignment: self.isIPadFloatingOrMac ? self.configuration.iPadSheetAlignment : .bottomLeading
) {
// Hide everything when the BottomSheet is hidden
if !self.bottomSheetPosition.isHidden {
Expand Down Expand Up @@ -129,7 +129,7 @@ internal struct BottomSheetView<HContent: View, MContent: View>: View {
// On iPad floating and Mac ignore top safe area, because the BottomSheet moves to the top edge
.ignoresSafeAreaCompatible(
.container,
edges: self.isIPadFloatingOrMac ? .top : .bottom
edges: self.isIPadFloatingOrMac ? (self.isIPadSheetAlignmentTop ? .top : .bottom) : .bottom
)
}
}
6 changes: 5 additions & 1 deletion Sources/BottomSheet/Models/BottomSheetConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ internal class BottomSheetConfiguration: Equatable {
lhs.isTapToDismissEnabled == rhs.isTapToDismissEnabled &&
lhs.iPadFloatingSheet == rhs.iPadFloatingSheet &&
lhs.sheetWidth == rhs.sheetWidth &&
lhs.accountForKeyboardHeight == rhs.accountForKeyboardHeight
lhs.accountForKeyboardHeight == rhs.accountForKeyboardHeight &&
lhs.iPadSheetAlignment == rhs.iPadSheetAlignment &&
lhs.sheetSidePadding == rhs.sheetSidePadding
}

var animation: Animation? = .spring(
Expand Down Expand Up @@ -61,4 +63,6 @@ internal class BottomSheetConfiguration: Equatable {
var iPadFloatingSheet: Bool = true
var sheetWidth: BottomSheetWidth = .platformDefault
var accountForKeyboardHeight: Bool = false
var iPadSheetAlignment: Alignment = .bottomLeading
var sheetSidePadding: EdgeInsets = EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)
}