View 如何将视图传递到结构中,同时获取其高度?

View 如何将视图传递到结构中,同时获取其高度?,view,swiftui,var,View,Swiftui,Var,我正在尝试创建一个视图,在iPhone布局的底部放置一个Blur(),尊重安全区域,并且可以轻松重用 大概是这样的: import SwiftUI struct SafeBottomBlurContainer: View { @Environment(\.colorScheme) var colorScheme var body: some View { GeometryReader { geometry in VStack {

我正在尝试创建一个视图,在iPhone布局的底部放置一个Blur(),尊重安全区域,并且可以轻松重用

大概是这样的:

import SwiftUI

struct SafeBottomBlurContainer: View {
    @Environment(\.colorScheme) var colorScheme
    var body: some View {
        GeometryReader { geometry in
            VStack {
                Spacer()
                ZStack {
                    Blur(style:  self.colorScheme == .dark ? .systemThinMaterialDark : .systemThinMaterialLight)
                        .frame(
                            width: geometry.size.width,
                            height: geometry.safeAreaInsets.bottom + 50
                        )
                        Holder()
                            .padding(.bottom, geometry.safeAreaInsets.bottom)
                }
            }
            .edgesIgnoringSafeArea(.bottom)
        }
    }
}

struct Holder: View {
    var body: some View {
        Rectangle().opacity(0.5)
            .frame(height: 50)
    }
}


struct SafeBottomBlurContainer_Previews: PreviewProvider {
    
    static var previews: some View {
        SafeBottomBlurContainer()
    }
}

顺便说一下,这是~~蓝色~~模糊的扩展名:

import SwiftUI

struct Blur: UIViewRepresentable {
    var style: UIBlurEffect.Style = .systemMaterial
    func makeUIView(context: Context) -> UIVisualEffectView {
        return UIVisualEffectView(effect: UIBlurEffect(style: style))
    }
    func updateUIView(_ uiView: UIVisualEffectView, context: Context) {
        uiView.effect = UIBlurEffect(style: style)
    }
}
现在,我想做的是以某种方式传入
Holder()
,以便调整模糊的高度(这里是+50)。我觉得我应该以某种方式使用AnyView,但我无法理解它。那可能是我走错了路

下面是我想在ContentView()示例中使用它的方式:


这里有一种可能的方法,它提供了使用类似容器的可能性

struct ContentView: View {
    var body: some View {
        
        ZStack {
            Color.pink.edgesIgnoringSafeArea(.all)

            // holder is not needed, because you can pass any view directly
            SafeBottomBlurContainer(containedView: Text("Demo"))
        }
    }
}
和通用模糊容器

struct SafeBottomBlurContainer<V: View>: View {
    @Environment(\.colorScheme) var colorScheme

    var containedView: V
    var body: some View {
        GeometryReader { geometry in
            VStack {
                Spacer()
                ZStack {
                    Blur(style:  self.colorScheme == .dark ? .systemThinMaterialDark : .systemThinMaterialLight)
                        .frame(
                            width: geometry.size.width,
                            height: geometry.safeAreaInsets.bottom + 50
                        )
                        containedView
                            .padding(.bottom, geometry.safeAreaInsets.bottom)
                }
            }
            .edgesIgnoringSafeArea(.bottom)
        }
    }
}
struct安全容器:视图{
@环境(\.colorScheme)变量colorScheme
var-containedView:V
var body:一些观点{
GeometryReader{中的几何体
VStack{
垫片()
ZStack{
模糊(样式:self.colorScheme==.dark?.systemthinnmaterialdark:.systemthinnmateriallight)
.框架(
宽度:geometry.size.width,
高度:geometry.safeAreaInsets.bottom+50
)
包含视图
.padding(.bottom,geometry.safeAreaInsets.bottom)
}
}
.edgesIgnoringSafeArea(.bottom)
}
}
}

从@Asperi开始,他真正让我走上了正确的道路,我现在有了这个,我还试图了解这个
Blur()
视图应该在屏幕的顶部还是底部:

import SwiftUI

struct SafeEdgesBlurContainer<V: View>: View {
    @Environment(\.colorScheme) var colorScheme
    @State var containedViewSize: CGSize = .zero
    var containedView: V
    var isPlacedAtTop: Bool
    var body: some View {
        GeometryReader { geometry in
            VStack {
                if !isPlacedAtTop {
                Spacer()
                }
                ZStack {
                    Blur(style:  self.colorScheme == .dark ? .systemThinMaterialDark : .systemThinMaterialLight)
                        .frame(
                            width: geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing,
                            height: (isPlacedAtTop ? geometry.safeAreaInsets.top : geometry.safeAreaInsets.bottom ) + containedViewSize.height
                        )
                        .edgesIgnoringSafeArea([.leading])
                    HStack {
                        ChildSizeReader(size: $containedViewSize) {
                                containedView
                        }
                        .frame(width: geometry.size.width)  // ADDED This line to keep the ContainedView centered in Landscape
                        .padding(isPlacedAtTop ? .top : .bottom, isPlacedAtTop ? geometry.safeAreaInsets.top : geometry.safeAreaInsets.bottom)
                    }
                }
                if isPlacedAtTop {
                Spacer()
                }
            }
            .edgesIgnoringSafeArea(isPlacedAtTop ? .top : .bottom)
        }
    }
}
导入快捷界面
结构SafeEdgesBlurContainer:视图{
@环境(\.colorScheme)变量colorScheme
@状态变量containedViewSize:CGSize=.0
var-containedView:V
变量isPlacedAtTop:Bool
var body:一些观点{
GeometryReader{中的几何体
VStack{
如果!isPlacedAtTop{
垫片()
}
ZStack{
模糊(样式:self.colorScheme==.dark?.systemthinnmaterialdark:.systemthinnmateriallight)
.框架(
宽度:geometry.size.width+geometry.SafeAreaInsetts.leading+geometry.SafeAreaInsetts.Training,
高度:(isPlacedAtTop?geometry.SafeAreaInsetts.top:geometry.SafeAreaInsetts.bottom)+包含的视图大小.height
)
.edgesIgnoringSafeArea([.leading])
HStack{
ChildSizeReader(大小:$containedViewSize){
包含视图
}
.frame(宽度:geometry.size.width)//添加了这一行以使ContainedView在横向视图中居中
.padding(isPlacedAtTop?.top:.底部,isPlacedAtTop?geometry.SafeAreaInsetts.top:geometry.SafeAreaInsetts.bottom)
}
}
如果我放在上面{
垫片()
}
}
.edgesIgnoringSafeArea(isPlacedAtTop?.top:.底部)
}
}
}
事实证明,获取ContainedView()的高度并不难。我从这里找到了这个解决方案:

struct ChildSizeReader:视图{
@绑定变量大小:CGSize
让内容:()->内容
var body:一些观点{
ZStack{
内容()
.背景(
GeometryReader{中的代理
颜色。清晰
.preference(键:SizePreferenceKey.self,值:proxy.size)
}
)
}
.onPreferenceChange(SizePreferenceKey.self){中的首选项
self.size=首选项
}
}
}
结构SizePreferenceKey:PreferenceKey{
typealias值=CGSize
静态变量defaultValue:值=.0
静态函数reduce(值quot:inout值,nextValue:()->值){
_=下一个值()
}
}
我看到的其余问题是ContainedView()没有在横向视图中居中。但我希望,我能很快回来,编辑一个解决方案


编辑:这可能就是解决方案:
.frame(宽度:geometry.size.width)
在HStack上。

哇,太好了,谢谢!我在哪里可以了解更多关于该
?它叫什么?那只是泛型,对吗?
import SwiftUI

struct SafeEdgesBlurContainer<V: View>: View {
    @Environment(\.colorScheme) var colorScheme
    @State var containedViewSize: CGSize = .zero
    var containedView: V
    var isPlacedAtTop: Bool
    var body: some View {
        GeometryReader { geometry in
            VStack {
                if !isPlacedAtTop {
                Spacer()
                }
                ZStack {
                    Blur(style:  self.colorScheme == .dark ? .systemThinMaterialDark : .systemThinMaterialLight)
                        .frame(
                            width: geometry.size.width + geometry.safeAreaInsets.leading + geometry.safeAreaInsets.trailing,
                            height: (isPlacedAtTop ? geometry.safeAreaInsets.top : geometry.safeAreaInsets.bottom ) + containedViewSize.height
                        )
                        .edgesIgnoringSafeArea([.leading])
                    HStack {
                        ChildSizeReader(size: $containedViewSize) {
                                containedView
                        }
                        .frame(width: geometry.size.width)  // ADDED This line to keep the ContainedView centered in Landscape
                        .padding(isPlacedAtTop ? .top : .bottom, isPlacedAtTop ? geometry.safeAreaInsets.top : geometry.safeAreaInsets.bottom)
                    }
                }
                if isPlacedAtTop {
                Spacer()
                }
            }
            .edgesIgnoringSafeArea(isPlacedAtTop ? .top : .bottom)
        }
    }
}
struct ChildSizeReader<Content: View>: View {
    @Binding var size: CGSize
    let content: () -> Content
    var body: some View {
        ZStack {
            content()
                .background(
                    GeometryReader { proxy in
                        Color.clear
                            .preference(key: SizePreferenceKey.self, value: proxy.size)
                    }
                )
        }
        .onPreferenceChange(SizePreferenceKey.self) { preferences in
            self.size = preferences
        }
    }
}

struct SizePreferenceKey: PreferenceKey {
    typealias Value = CGSize
    static var defaultValue: Value = .zero

    static func reduce(value _: inout Value, nextValue: () -> Value) {
        _ = nextValue()
    }
}