134 lines
5.5 KiB
Swift
134 lines
5.5 KiB
Swift
//
|
|
// Copyright 2022-2024 New Vector Ltd.
|
|
//
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
// Please see LICENSE in the repository root for full details.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
/// `VectorHostingBottomSheetPreferences` defines the bottom sheet behaviour using the `UISheetPresentationController` of the `UIViewController`
|
|
class VectorHostingBottomSheetPreferences {
|
|
|
|
// MARK: - Detent
|
|
|
|
enum Detent {
|
|
case medium
|
|
case large
|
|
|
|
/// only available on iOS16, medium behaviour will be used instead
|
|
/// - Parameters:
|
|
/// - height: The height of the custom detent, if the height is bigger than the maximum possible height for a detent the latter will be returned
|
|
/// - identifier: The identifier used to identify the custom detent during detent transitions, by default the value is set to "custom", however if you are supporting multiple custom detents in a bottom sheet, you should specify a different identifier for each
|
|
case custom(height: CGFloat, identifier: String = "custom")
|
|
|
|
@available(iOS 15, *)
|
|
fileprivate func uiSheetDetent() -> UISheetPresentationController.Detent {
|
|
switch self {
|
|
case .medium: return .medium()
|
|
case .large: return .large()
|
|
case let .custom(height, identifier):
|
|
if #available(iOS 16, *) {
|
|
let identifier = UISheetPresentationController.Detent.Identifier(identifier)
|
|
return .custom(identifier: identifier) { context in
|
|
return min(height, context.maximumDetentValue)
|
|
}
|
|
} else {
|
|
return .medium()
|
|
}
|
|
}
|
|
}
|
|
|
|
@available(iOS 15, *)
|
|
fileprivate func uiSheetDetentId() -> UISheetPresentationController.Detent.Identifier {
|
|
switch self {
|
|
case .medium: return .medium
|
|
case .large: return .large
|
|
case let .custom(_, identifier):
|
|
if #available(iOS 16, *) {
|
|
return UISheetPresentationController.Detent.Identifier(identifier)
|
|
} else {
|
|
return .medium
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Public
|
|
|
|
// The array of detents that the sheet may rest at.
|
|
// This array must have at least one element.
|
|
// Detents must be specified in order from smallest to largest height.
|
|
// Default: [.medium, .large]
|
|
let detents: [Detent]
|
|
|
|
// The default detent. When nil or the identifier is not found in detents, the sheet is displayed at the smallest detent.
|
|
// Default: nil
|
|
let defaultDetent: Detent?
|
|
|
|
// The largest detent that is not dimmed. When nil or the identifier is not found in detents, all detents are dimmed.
|
|
// Default: nil
|
|
let largestUndimmedDetent: Detent?
|
|
let cornerRadius: CGFloat?
|
|
|
|
// If there is a larger detent to expand to than the selected detent, and a descendent scroll view is scrolled to top, this controls whether scrolling down will expand to a larger detent.
|
|
// Useful to set to NO for non-modal sheets, where scrolling in the sheet should not expand the sheet and obscure the content above.
|
|
// Default: YES
|
|
let prefersScrollingExpandsWhenScrolledToEdge: Bool
|
|
|
|
// Set to YES to show a grabber at the top of the sheet.
|
|
// Default: `nil` -> the grabber is shown if more than one detent is configured
|
|
let prefersGrabberVisible: Bool?
|
|
|
|
// MARK: - Setup
|
|
|
|
init(detents: [Detent] = [.medium, .large],
|
|
defaultDetent: Detent? = nil,
|
|
largestUndimmedDetent: Detent? = nil,
|
|
prefersGrabberVisible: Bool? = nil,
|
|
cornerRadius: CGFloat? = nil,
|
|
prefersScrollingExpandsWhenScrolledToEdge: Bool = true) {
|
|
self.detents = detents
|
|
self.defaultDetent = defaultDetent
|
|
self.largestUndimmedDetent = largestUndimmedDetent
|
|
self.prefersGrabberVisible = prefersGrabberVisible
|
|
self.cornerRadius = cornerRadius
|
|
self.prefersScrollingExpandsWhenScrolledToEdge = prefersScrollingExpandsWhenScrolledToEdge
|
|
}
|
|
|
|
// MARK: - Public
|
|
|
|
func setup(viewController: UIViewController) {
|
|
guard #available(iOS 15.0, *) else { return }
|
|
|
|
guard let sheetController = viewController.sheetPresentationController else {
|
|
MXLog.debug("[VectorHostingBottomSheetPreferences] setup: no sheetPresentationController found")
|
|
return
|
|
}
|
|
|
|
sheetController.detents = self.uiSheetDetents()
|
|
if let prefersGrabberVisible = self.prefersGrabberVisible {
|
|
sheetController.prefersGrabberVisible = prefersGrabberVisible
|
|
} else {
|
|
sheetController.prefersGrabberVisible = self.detents.count > 1
|
|
}
|
|
sheetController.selectedDetentIdentifier = self.defaultDetent?.uiSheetDetentId()
|
|
sheetController.largestUndimmedDetentIdentifier = self.largestUndimmedDetent?.uiSheetDetentId()
|
|
sheetController.prefersScrollingExpandsWhenScrolledToEdge = self.prefersScrollingExpandsWhenScrolledToEdge
|
|
if let cornerRadius = self.cornerRadius {
|
|
sheetController.preferredCornerRadius = cornerRadius
|
|
}
|
|
}
|
|
|
|
// MARK: - Private
|
|
|
|
@available(iOS 15, *)
|
|
fileprivate func uiSheetDetents() -> [UISheetPresentationController.Detent] {
|
|
var uiSheetDetents: [UISheetPresentationController.Detent] = []
|
|
for detent in detents {
|
|
uiSheetDetents.append(detent.uiSheetDetent())
|
|
}
|
|
return uiSheetDetents
|
|
}
|
|
}
|