Custom smaller Detents in UISheetPresentationController?

15.2k Views Asked by At

Apple has finally released an Apple Maps-style "bottom sheet" control in iOS 15 in 2021: UISheetPresentationController.

This type of sheet natively supports "detents", the heights at which a sheet naturally rests. The default large() detent represents a full-screen sheet presentation, whereas the medium() detent covers approximately half the screen.

However, there is no small() detent in the API.

Apple Maps and similar apps typically show a small fully-collapsed sheet at the bottom of the screen, which can be dragged to half-height, which can be dragged to full-screen. Apple Maps actually shows a 1/3rd height screen, which appears to be different than the medium() detent.

Apple Maps Small Collapsed Bottom Sheet

Using UISheetPresentationController, not any 3rd-party reimplementation of bottom sheets, how can a sheet be presented with an Apple Maps-style collapsed small detent at the bottom of the screen?

8

There are 8 best solutions below

0
On

How it's done in Xamarin.iOS or .Net for iOS:

        if (!UIDevice.CurrentDevice.CheckSystemVersion(15, 0))
        {
            //Normal popup
        }
        else
        {
            popViewController = new UIViewController();

            var sheet = popViewController.SheetPresentationController;
            if (sheet != null)
            {
                if (UIDevice.CurrentDevice.CheckSystemVersion(16, 0))
                {
                    var detent = UISheetPresentationControllerDetent.Create(UISheetPresentationControllerDetentIdentifier.Unknown.ToString(),
                                                               resolver: (context) =>
                                                               {
                                                                   return dropDownView.Frame.Height;
                                                               });
                    sheet.Detents = new UISheetPresentationControllerDetent[2]
                    {
                        detent,
                        UISheetPresentationControllerDetent.CreateLargeDetent()
                    };
                }
                else
                {
                    sheet.Detents = new UISheetPresentationControllerDetent[2]
                    {
                        UISheetPresentationControllerDetent.CreateMediumDetent(),
                        UISheetPresentationControllerDetent.CreateLargeDetent()
                    };
                }
                sheet.PrefersGrabberVisible = true;
            }
            popViewController.Add(dropDownView);
            popViewController.View.BackgroundColor = UIColor.White;
            popViewController.PreferredContentSize = new CGSize(dropDownView.Frame.Width, dropDownView.Frame.Height);

            PresentViewController(popViewController, animated: true, () => { });

        }
1
On

Step 1:

if let sheet = vc.sheetPresentationController {
    sheet.detents = [.custom(resolver:(UISheetPresentationControllerDetentResolutionContext) -> CGFloat?]
}

Step 2:

if let sheet = vc.sheetPresentationController {
    sheet.detents = [.custom(resolver: { context in
        code
    })]
}

Step 3:

if let sheet = vc.sheetPresentationController {
    sheet.detents = [.custom(resolver: { context in
        return 300 // your custom height
    })]
}
0
On

SwiftUI - iOS 16+

You can add your own custom detents size with something like that:

.sheet(isPresented: $showSheet) {
    YourView()
        .presentationDetents([.large, .medium, .fraction(0.25)]) //Fraction 0.25 means the sheet will be sized 25% of the whole screen.
}
1
On

I filed a radar to ask for support. I suggest anyone else who wants to see this does the same. Realistically medium and large won't cut it and we'll be relying on third party libs still if this doesn't get added before iOS 15 is released.

0
On
id detent = [UISheetPresentationControllerDetent customDetentWithIdentifier:UISheetPresentationControllerDetentIdentifierLarge
                                                                   resolver:^CGFloat(id<UISheetPresentationControllerDetentResolutionContext>  _Nonnull context) {
    return 150;
}];
0
On

in SwiftUI iOS 16, you can set the height of the sheet

bottomSheet
    .presentationDetents([.height(300)])
5
On

For iOS 16+


For iOS 15 : Use +[UISheetPresentationControllerDetent _detentWithIdentifier:constant:].

It's a private method.

Sample

 Summary: UIKitCore`+[UISheetPresentationControllerDetent _detentWithIdentifier:constant:]        Address: UIKitCore[0x00000001838d50fc] (UIKitCore.__TEXT.__text + 17876312)
0
On

In UIKit - iOS 16+ fraction detent can be implemented like this:

viewControllerToShow.modalPresentationStyle = .formSheet    
let sheet = viewControllerToShow.sheetPresentationController
let multiplier = 0.25    
let fraction = UISheetPresentationController.Detent.custom { context in 
    // height is the view.frame.height of the view controller which presents this bottom sheet
    height * multiplier
}
sheet?.detents = [fraction]