Custom smaller Detents in UISheetPresentationController?
Asked Answered
L

8

43

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?

Landau answered 9/6, 2021 at 16:34 Comment(3)
> FB9147171 - Add small() collapsed Detent to UISheetPresentationControllerLandau
My thoughts exactly. The way the API is designed and even how they described usage in the WWDC videos makes me think they’ll add support for .collapsed() or .small() in a future release. I’d also love to figure out how to support the Find My style UI where the sheet sits on top of the tab bar.Roxy
Also if you look at the stocks app there’s actually a variant of the large style detent that allows for a little extra space where they display scrolling stock tickers. Some customization options to support creative interfaces like this would be highly welcomed.Roxy
A
22

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)
Arhat answered 19/2, 2022 at 15:23 Comment(5)
For iOS 15 you have to subclass and use an undocumented interface... risky. And I think that's frowned upon by apple guidelines, although I'm not sure if this case is enough to get the app rejected from the app store. I'm on the fence between releasing my app as iOS 15 or to the newly released iOS 16 (e.g. I don't want to shut out too many potential clients). So maybe it's better to implement it both ways - for iOS 15 and iOS 16 and guard it with a version check. At least in this case we know how iOS implementation will change and it won't leave developers scrambling to do a rewrite.Brace
As convenient as UISheetPresentationController is, I think I'm going to go the extra 9 yards to use UIPresentationController and just customize it myself using public interfaces, and plus I want to add side effects like adjusting the size of one of the views of the presenter during the animation, which I am not sure how to do if I steal the sheetPresentationController object from the vc property that vends the pre-canned sheet controller (because I can't subclass that).Brace
how do you init with the custom(identifier:resolver:) ? can you give an example ?Fee
anyone tried to put an app in appstore using this private api ? is it allowed ?Deragon
same question, anyone tried on Apple Store?Spermatogonium
E
15

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.

Elect answered 8/7, 2021 at 3:58 Comment(1)
This should be posted as a comment, not as answer.Contrariwise
D
7

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]
Darleen answered 19/4, 2023 at 12:17 Comment(0)
M
2

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.
}
Monarchist answered 19/12, 2022 at 15:36 Comment(0)
K
1

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
    })]
}
Katlynkatmai answered 5/11, 2023 at 19:49 Comment(1)
in iOS 15, not in iOS 16...Hilarius
S
0

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

bottomSheet
    .presentationDetents([.height(300)])
Seriocomic answered 17/5, 2023 at 9:8 Comment(0)
F
-1
id detent = [UISheetPresentationControllerDetent customDetentWithIdentifier:UISheetPresentationControllerDetentIdentifierLarge
                                                                   resolver:^CGFloat(id<UISheetPresentationControllerDetentResolutionContext>  _Nonnull context) {
    return 150;
}];
Facing answered 10/5, 2023 at 10:54 Comment(0)
E
-1

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, () => { });

        }
Ebb answered 19/10, 2023 at 6:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.