Slide a Scrollview over another view

Viewed 177

Here is what i am trying to achieve :



Here is what i currently have :


I cannot seem to get the my ScrollView to slide over the green header view (schedulerHeaderView) like in the first example.
Here is my code

struct CreateShiftView: View {
    @EnvironmentObject private var createShiftViewModel: CreateShiftViewModel
    
    var body: some View {
        GeometryReader { geo in
            VStack {
                schedulerHeaderView
                Spacer()
            }
            .background(
                Color(createShiftViewModel.isNavbarTitleHidden ? ThemeManager.current().greenery : .white)
                    .ignoresSafeArea(.all, edges: .top)
            )
            .overlay(
                    ScrollView {
                        VStack(spacing: 42) {
                            addEmployeeSection
                            shiftInfoSection
                            addBreakSection
                            addResourceSection
                            addJustificationSection
                            savePublishSection
                        }
                        .background(Color.white)
                        .cornerRadius(20, corners: [.topLeft, .topRight])
                        .padding(.horizontal, 24)
                    }
                    .frame(minWidth: UIScreen.main.bounds.width)
                    .offset(y: geo.size.height * 0.155)
            )
        }
    }
}


I'm guessing i need to dynamically change the offset or position of the scrollView with a DragGesture but i haven't been able to get a working example so far. Any help would be appreciated. Thanks in advance.

1 Answers

I have found the solution to my question. In summary, i need to keep track of the last offset (using state) and use DragGesture's .onChanged and .onEnded to set a new offset depending on the value of the drag translation. Here is my updated code which now works

struct CreateShiftView: View {
    @EnvironmentObject private var createShiftViewModel: CreateShiftViewModel
    @State private var offsets = (top: CGFloat.zero, bottom: CGFloat.zero)
    @State private var offset: CGFloat = .zero
    @State private var lastOffset: CGFloat = .zero
    @State private var isAtTopOffset = false
    @State private var dragging = false
    
    var body: some View {
        GeometryReader { geo in
            VStack {
                schedulerHeaderView
                Spacer()
            }
            .background(
                Color(createShiftViewModel.isNavbarTitleHidden ? ThemeManager.current().greenery : .white)
                    .ignoresSafeArea(.all, edges: .top)
            )
            .overlay(
                SlideOverView(cardviewInitialPosition: geo.size.height * 0.30, viewModel: createShiftViewModel) {
                    VStack(spacing: 42) {
                        informationSection
                        shiftInfoSection
                        addEmployeeSection
                        addBreakSection
                        addResourceSection
                        addJustificationSection
                        savePublishSection
                    }
                    .background(Color.white)
                    .cornerRadius(20, corners: [.topLeft, .topRight])
                    .padding(.horizontal, 24)
                }
                .onAppear {
                    self.offsets = (
                        top: .zero,
                        bottom: geo.size.height * 0.155
                    )
                    self.offset = self.offsets.bottom
                    self.lastOffset = self.offset
                }
                .offset(y: self.offset)
                .animation(dragging ? nil : {
                    Animation.interpolatingSpring(stiffness: 250.0, damping: 40.0, initialVelocity: 5.0)
                }())
                .simultaneousGesture(
                    DragGesture(minimumDistance: createShiftViewModel.isNavbarTitleHidden ? 5 : 100, coordinateSpace: .local)
                        .onChanged { v in
                            dragging = true
                            let newOffset = self.lastOffset + v.translation.height
                            if (newOffset > self.offsets.top && newOffset < self.offsets.bottom) {
                                self.offset = newOffset
                            }
                        }
                        .onEnded{ v in
                            dragging = false
                            if (self.lastOffset == self.offsets.top && v.translation.height > 0) {
                                self.offset = self.offsets.bottom
                                createShiftViewModel.cardAtBottom()
                            } else if (self.lastOffset == self.offsets.bottom && v.translation.height < 0) {
                                self.offset = self.offsets.top
                                createShiftViewModel.cardAtTop()
                            }
                            self.lastOffset = self.offset
                        }
                )
            )
        }
    }
}

Related