Created
August 14, 2025 13:13
-
-
Save Chronos2500/2bc3b385f5147170eac8698bb35ea3e0 to your computer and use it in GitHub Desktop.
FullDetentSheet
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import SwiftUI | |
| struct FullSheet: View { | |
| @State private var isPresented: Bool = false | |
| @Namespace private var namespace | |
| var body: some View { | |
| TabView { | |
| Tab("Home", systemImage: "house") { | |
| NavigationStack { | |
| list | |
| .navigationTitle("Home") | |
| } | |
| } | |
| Tab("Search", systemImage: "magnifyingglass") { | |
| } | |
| Tab("Profile", systemImage: "person.circle") { | |
| } | |
| Tab(role: .search) { | |
| } | |
| } | |
| .tabViewBottomAccessory { | |
| Button("Show Full Sheet") { | |
| isPresented.toggle() | |
| } | |
| // この効果を使うとZoom効果が適用されますが、現状のAppleMusicでは使われていないので消しました | |
| // .matchedTransitionSource(id: "sheet", in: namespace) | |
| // AppleMusicに寄せるには追加でカスタムVCのTransitionを書く必要があります | |
| } | |
| .sheet(isPresented: $isPresented) { | |
| VStack { | |
| Text("This is a full sheet") | |
| .font(.title) | |
| .padding() | |
| Button("Dismiss") { | |
| isPresented = false | |
| } | |
| .buttonStyle(.borderedProminent) | |
| } | |
| .foregroundColor(.white) | |
| // .navigationTransition(.zoom(sourceID: "sheet", in: namespace)) | |
| .presentationBackground(.pink.gradient) | |
| .fullDetentSheet() | |
| } | |
| } | |
| var list: some View { | |
| List { | |
| ForEach(0..<20) { index in | |
| Text("Item \(index)") | |
| } | |
| } | |
| } | |
| } | |
| extension View { | |
| public func fullDetentSheet() -> some View { | |
| modifier( FullDetentSheetModifier() ) | |
| } | |
| } | |
| fileprivate struct FullDetentSheetModifier: ViewModifier { | |
| func body(content: Content) -> some View { | |
| content.background{ VCWrapper() } | |
| } | |
| } | |
| fileprivate struct VCWrapper: UIViewControllerRepresentable { | |
| @MainActor final class DummyVC: UIViewController { | |
| override func viewDidLoad() { | |
| super.viewDidLoad() | |
| view.backgroundColor = .clear | |
| view.isUserInteractionEnabled = false | |
| } | |
| override func didMove(toParent parent: UIViewController?) { | |
| super.didMove(toParent: parent) | |
| parent?.sheetPresentationController?.detents = [ .full() ] | |
| } | |
| } | |
| func makeUIViewController(context: Context) -> DummyVC { | |
| .init() | |
| } | |
| func updateUIViewController(_ uiViewController: DummyVC, context: Context) { | |
| } | |
| } | |
| fileprivate extension UISheetPresentationController.Detent { | |
| static func full() -> UISheetPresentationController.Detent { | |
| value(forKey: "_\("full")Detent") as! UISheetPresentationController.Detent | |
| } | |
| } | |
| #Preview { | |
| FullSheet() | |
| } |
Author
You can fix that jump from full screen to below status bar setting:
@interface UISheetPresentationController ()
@property (assign,setter=_setWantsFullScreen:,nonatomic) bool _wantsFullScreen NS_SWIFT_NAME(wantsFullScreen);
@property (assign,setter=_setAllowsInteractiveDismissWhenFullScreen:,nonatomic) bool _allowsInteractiveDismissWhenFullScreen NS_SWIFT_NAME(allowsInteractiveDismissWhenFullScreen);
@endsheetPresentationController?.wantsFullScreen = true
sheetPresentationController?.allowsInteractiveDismissWhenFullScreen = trueSimulator.Screen.Recording.-.iPhone.16.Pro.Max.-.2025-09-07.at.08.07.15.mp4
Unfortunately, that disables other detents, so some juggling needs to be done to enable and disable wantsFullScreen if you want both detents and smooth interaction. Apple's Music doesn't any detents other than full screen, so they set both to true, as above.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
2025-08-14.22.04.05.mov