skip to Main Content

I would like a certain view to start as a half-screen sheet, but when it’s done with some loading, it should transform into a full-screen sheet.

So in UIKit my solution would be that:

let vc = UIViewController()
vc.view.backgroundColor = .red
vc.sheetPresentationController?.detents = [.medium(), .large()]
self.present(vc, animated: true)

DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) {
    vc.sheetPresentationController?.animateChanges {
        vc.sheetPresentationController?.selectedDetentIdentifier = .large
    }
}

That works fine in UIKit… but is this also somehow possible in SwiftUI?

Something similar in SwiftUI could look like the following:

    struct ContentView: View {
    @State var showSheet = false
    
    var body: some View {
        Button {
            showSheet = true
        } label: {
            Text("Show-Sheet")
        }
        .sheet(isPresented: $showSheet, content: {
            SheetView()
        })
    }
}

struct SheetView: View {
    @State var showFullSheet = false
    
    var body: some View {
        Button {
            withAnimation {
                showFullSheet.toggle()
            }
        } label: {
            Text("Toggle")
        }
        .presentationDetents(showFullSheet ? [.large] : [.medium, .large])
    }
}

This basically works, but the problem is that the transition between half and full is NOT animated. I’d need it animated though. Is this somehow solvable in SwiftUI?
Cause I guess wrapping my presented view in a UIViewController that then hosts my existing SwiftUI View would not be an option, as the presentation part would still be done by swift-ui, right?

2

Answers


  1. First of all use selection binding of presentationDetents(_:selection:)
    to keep track of your selectedDetent state. The reason you are facing this animation issue is when you set sheet to large you are setting allow detents set only with [.large] so it’s not animating and directly jumping to large. To overcome this issue I have added @State property to update allow detents with presentationDetents modifier. Now if you update detents set as soon as you update your selectedDetent state then also it won’t show animation, so to fix this issue I have added a one-second delay to update detents and this will fix your animation issue.

    struct SheetView: View {
        @State var selectedDetent: PresentationDetent = .medium
        
        @State var detents: Set<PresentationDetent> = [.large, .medium]
        
        var body: some View {
            Button {
                selectedDetent = selectedDetent == .large ? .medium : .large
            } label: {
                Text("Toggle")
            }
            .presentationDetents(detents, selection: $selectedDetent)
            .onChange(of: selectedDetent) { newValue in
                if newValue == .large {
                    updateDetentsWithDelay()
                } else {
                    detents = [.large, .medium]
                }
            }
        }
        
        func updateDetentsWithDelay() {
            Task {
                //(1 second = 1_000_000_000 nanoseconds)
                try? await Task.sleep(nanoseconds: 100_000_000)
                guard selectedDetent == .large else { return }
                detents = [.large]
            }
        }
    }
    
    Login or Signup to reply.
  2. Could it just be that you’re missing withAnimation from your showSheet = true

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search