iosswiftuikitbottom-sheet

iOS bottom sheet with fraction detents UIKit


I would like to open bottom sheet with custom option and not medium or large detents.I checked in SwiftUI there is API called .fraction but couldn't find similar API in UIKit.

SwiftUI

Text("Detail")
    .presentationDetents([.fraction(0.1)])

UIKit

if let sheet = viewControllerToPresent.sheetPresentationController {
               sheet.detents = [.medium()]


Solution

  • I inspected the underlying UISheetPresentationController of a SwiftUI sheet on iOS 17.4, and saw that the detents have a type of "custom".

    import SwiftUIIntrospect
    
    struct ContentView: View {
    
        var body: some View {
            Text("Foo")
                .sheet(isPresented: .constant(true)) {
                    Text("Sheet")
                        .presentationDetents([.height(100), .fraction(0.5)])
                        .introspect(.sheet, on: .iOS(.v17)) { x in
                            print((x as! UISheetPresentationController).detents)
                        }
                }
        }
    }
    
    /*
    Output:
    [<UISheetPresentationControllerDetent: 0x600000c62610: _type=custom, _identifier=Fraction:0.5>, <UISheetPresentationControllerDetent: 0x600000c626a0: _type=custom, _identifier=Height:100.0>]
    */
    

    This suggests that .fraction(...) and .height(...) in SwiftUI simply creates UIKit detents using the .custom factory method. On the other hand, .medium and .large in SwiftUI does correspond to .medium() and .large() in UIKit.

    Here is an extension on UISheetPresentationController.Detent that adds a fraction factory method:

    extension UISheetPresentationController.Detent {
        static func fraction(_ value: CGFloat) -> UISheetPresentationController.Detent {
            .custom(identifier: Identifier("Fraction:\(value)")) { context in
                context.maximumDetentValue * value
            }
        }
    }