How to remove vertical safeAreaInsets while close gesture is active?

Viewed 47

Here's a main ContentView and DetailedCardView and when open DetailedCardView there's a ScrollView with more content. It also have closeGesture which allow to close the card when swiping from left to right and vice versa. So, at that point problems start to appear, like this ones inside DetailedCardView:

  • When scrolling in ScrollView then safeAreaInsets .top and .bottom start to be visible and as a result it's shrink the whole view vertically.

  • The closeGesture and standard ScrollView gesture while combined is not smooth and I'd like to add closing card not only when swiping horizontally but vertically too.

As a result I'd like to have the same gesture behaviour like opening and closing detailed card in App Store App Today section. Would love to get your help

    ContentView             DetailedCardView        closeGesture in Action          safeAreaInsets 

This is how it looks like now

struct ContentView: View {
    @State private var showCardView = false
    let colors: [Color] = [.red, .purple, .yellow, .green, .blue, .mint, .orange]
    @State private var selectedCardNumber = 0
    
    var body: some View {
        ZStack {
            ScrollView {
                LazyVStack(spacing: 0) {
                    ForEach(1..<101) { number in
                        colors[number % colors.count]
                            .overlay(Text("\(number)").font(.largeTitle.bold()).foregroundColor(.white))
                            .frame(height: 300)
                            .cornerRadius(30)
                            .padding(10)
                            .onTapGesture {
                                showCardView.toggle()
                                selectedCardNumber = number
                            }
                    }
                }
            }
            .opacity(showCardView ? 0 : 1)
            .animation(.spring(), value: showCardView)
            
            if showCardView {
                CardDetailView(showCardView: $showCardView, number: selectedCardNumber)
            }
        }
    }
}

struct CardDetailView: View {
    @Binding var showCardView: Bool
    @State private var cardPosition: CGSize = .zero
    let number: Int
    
    var body: some View {
        ScrollView {
            RoundedRectangle(cornerRadius: 30, style: .continuous)
                .fill(.green)
                .overlay(Text("\(number)").font(.system(size: 100).bold()).foregroundColor(.white))
                .frame(height: 500)
            Color.red.opacity(0.8)
                .frame(height: 200)
            Color.gray.opacity(0.8)
                .frame(height: 200)
            Color.blue
                .frame(height: 200)
            Color.yellow
                .frame(height: 200)
        }
        .scaleEffect(gestureScale())
        .gesture(closeGesture)
    }
    
    private var closeGesture: some Gesture {
        DragGesture()
            .onChanged { progress in
                withAnimation(.spring()) {
                    cardPosition = progress.translation
                }
            }
            .onEnded { _ in
                withAnimation(.spring()) {
                    let positionInTwoDirections = abs(cardPosition.width)
                    if positionInTwoDirections > 100 {
                        showCardView = false
                    }
                    
                    cardPosition = .zero
                }
            }
    }
    
    private func gestureScale() -> CGFloat {
        let max = UIScreen.main.bounds.width / 2
        let currentAmount = abs(cardPosition.width)
        let percentage = currentAmount / max
        
        return 1 - min(percentage, 0.5)
    }
}


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
0 Answers
Related