I am using the UISheetPresentationController in UIKit to present a UIViewController with the available default detents .medium and .large and with a custom smaller detent (with a height of around 100-120). This sheet will be presented on top of a UIViewController which shows a camera view/video live feed. The camera view is used to scan barcodes and only the visible area of the camera view should recognize the barcode (e.g. the camera feed below the bottom sheet should ignore any barcodes). Therefor I would need to get the currently selected height of the presented sheet to inset the recognition area of the camera view by that height. The selectedDetentIdentifier property of the UISheetPresentationController can be used in combination with the UISheetPresentationControllerDelegate to get the current detent and to respond to any changes of the sheet but the main issue is now that the Detent does not have a corresponding CGFloat value representing the real height of the sheet.
The only working solution that I was able to come up with was to use the resolvedValue (Documentation) to get the height of the detents. The issue with this approach is that for this function you need to have a UISheetPresentationControllerDetentResolutionContext which you can only access in the creation of a custom detent. So what I came up with is to resolve the values for each of the detents and store it in a property that can be used to inset the underlying camera view:
if let sheet = cameraViewController.sheetPresentationController {
let mediumDetent = UISheetPresentationController.Detent.medium()
let largeDetent = UISheetPresentationController.Detent.large()
sheet.detents = [
.custom(identifier: .small) { context in
let small: CGFloat = 100
self.sizes[UISheetPresentationController.Detent.Identifier.small] = small
self.sizes[UISheetPresentationController.Detent.Identifier.medium] = mediumDetent.resolvedValue(in: context)
self.sizes[UISheetPresentationController.Detent.Identifier.large] = largeDetent.resolvedValue(in: context)
return small
},
mediumDetent,
largeDetent
]
sheet.largestUndimmedDetentIdentifier = .medium
sheet.prefersGrabberVisible = true
sheet.delegate = self
}
taskController.isModalInPresentation = true
present(taskController, animated: true, completion: completion)
I was then able to determine the height using the following code:
if let selectedDetentIdentifier = taskController.sheetPresentationController?.selectedDetentIdentifier,
let height = sizes[selectedDetentIdentifier] {
sheetHeight = height + view.safeAreaInsets.bottom // Sheet automatically adds the safeAreaInsets so we need to do that here as well.
}
But this seems more like a dirty workaround and nothing too reliable that should be used and therefor I would like to know if you have any other solutions or best practices?
The approach you've taken to resolve the height of the custom detent using the
resolvedValuefunction is a valid approach. However, as you've pointed out, it does have the limitation of requiring access to theUISheetPresentationControllerDetentResolutionContextwhich is only available during the creation of the detent.Another approach to consider is to use Auto Layout to dynamically adjust the size of your camera view based on the size of the presented sheet. You can achieve this by creating constraints between the bottom of your camera view and the top of the presented sheet, and then updating the constant value of these constraints based on the height of the sheet.
To do this, you can create an outlet to the constraint that defines the distance between the bottom of your camera view and the top of the presented sheet. Then, in the delegate method
didSelectof theUISheetPresentationControllerDelegate, you can update the constant value of this constraint based on the selected detent.Here's an example of what the code might look like:
This approach avoids the need to use the
resolvedValuefunction and should provide a more reliable solution.