Placing a rectangle in front of ScrollView affects scrolling

て烟熏妆下的殇ゞ 提交于 2021-02-05 06:52:29

问题


I need to place a translucent rectangle on front of ScrollView but when i put everything (Rectangle & ScrollView) inside of a ZStack, scroll & touch events stop working within this rectangle.

Atm I'm using .background modifier as it doesn't affect scrolling but I am still looking for way to make it work properly with rectangle placed over (in front of) my ScrollView.

Is there any way to put a View over ScrollView so it wouldn't affect it's functionality?

Here's the code i'm using now (i changed the colors and removed opacity to make the objects visible as my original rectangle is translucent & contains barely visible gradient)

struct ContentView: View {
    
    var body: some View {
        ZStack {
            
            ScrollView {
                LazyVStack(spacing: 0) {
                    ForEach(0...100, id:\.self) { val in
                        ZStack {
                            Text("test")
                                .font(.system(size: 128))
                        } // ZStack
                        .background(Color.blue)
                    } // ForEach
                }
            }
            .background(RadialGradient(gradient: Gradient(stops: [
                                                            .init(color: Color.blue, location: 0),
                                                            .init(color: Color.red, location: 1)]), center: .top, startRadius: 1, endRadius: 200)
                            .mask(
                                VStack(spacing: 0) {
                                    Rectangle()
                                        .frame(width: 347, height: 139)
                                        .padding(.top, 0)
                                    Spacer()
                                }
                            ))
            
        }
    }
}

回答1:


Here is a possible approach to start with - use UIVisualEffectView. And Blur view is taken from How do I pass a View into a struct while getting its height also? topic.

struct ScrollContentView: View {
    
    var body: some View {
        ZStack {
            
            ScrollView {
                LazyVStack(spacing: 0) {
                    ForEach(0...100, id:\.self) { val in
                        ZStack {
                            Text("test")
                                .font(.system(size: 128))
                        } // ZStack
                        .background(Color.blue)
                    } // ForEach
                }
            }
            Blur(style:  .systemThinMaterialLight)
                .mask(
                    VStack(spacing: 0) {
                        Rectangle()
                            .frame(width: 347, height: 139)
                            .padding(.top, 0)
                        Spacer()
                    }
                )
                .allowsHitTesting(false)
        }
    }
}



回答2:


I decided to post a solution here.. it's based on an approach suggested by Asperi.

2Asperi: Thank you, i appreciate your help, as always.

I played a little bit with applying .opacity & mask to Blur but it didn't work.

So i applied mask to the .layer property inside makeUIView and it worked fine

import SwiftUI

struct ContentView: View {
    
    var body: some View {
        ZStack {
            ZStack {
            ScrollView {
                LazyVStack(spacing: 0) {
                    ForEach(0...100, id:\.self) { val in
                        ZStack {
                            Text("test")
                                .font(.system(size: 128))
                        } // ZStack
                        .background(Color.white)
                    } // ForEach
                }
            }
                Blur(style: .systemThinMaterial)
                                .mask(
                                    VStack(spacing: 0) {
                                        Rectangle()
                                            .frame(width: 347, height: 139)
                                            .padding(.top, 0)
                                        Spacer()
                                    }
                                )
                    .allowsHitTesting(false)
            }
        }
    }
}

struct Blur: UIViewRepresentable {
    var style: UIBlurEffect.Style = .systemMaterial
    func makeUIView(context: Context) -> UIVisualEffectView {
        
        let blurEffectView = UIVisualEffectView(effect: UIBlurEffect(style: style))
        
        let gradientMaskLayer = CAGradientLayer()
        
        gradientMaskLayer.type = .radial
        
        gradientMaskLayer.frame = CGRect(x: 0, y: 0, width: 347, height: 256)
        
        gradientMaskLayer.colors = [UIColor.black.cgColor, UIColor.clear.cgColor]
        
        gradientMaskLayer.startPoint = CGPoint(x: 0.5, y: 0)
        gradientMaskLayer.endPoint = CGPoint(x: 1, y: 1)
        
        gradientMaskLayer.locations = [0 , 0.6]
        blurEffectView.layer.mask = gradientMaskLayer
        
        return blurEffectView
    }
    func updateUIView(_ uiView: UIVisualEffectView, context: Context) {
        uiView.effect = UIBlurEffect(style: style)
    }
}

The only thing i don't understand is why startPoint and endPoint work only when i set them to [0.5,0] & [1,1] but not [0.5,0] & [0.5,1] - i expected that it should determine the direction of radial gradient and in my case it should go from .topCenter to .topBottom (which it does but i don't understand the nature of endPoint)..



来源:https://stackoverflow.com/questions/65594854/placing-a-rectangle-in-front-of-scrollview-affects-scrolling

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!