Как передать представление в структуру, одновременно получив ее высоту? - PullRequest
0 голосов
/ 12 июля 2020

Я пытаюсь создать View, который поместит Blur () в нижнюю часть макета iPhone, соблюдая безопасные области и который я могу легко использовать повторно.

Что-то вроде этого:

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()
    }
}

Вот, кстати, расширение ~~ blue ~~ blur:

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 ():

import SwiftUI

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

Ответы [ 2 ]

1 голос
/ 12 июля 2020

Здесь возможен подход, который дает возможность использовать контейнер типа

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"))
        }
    }
}

и общий c контейнер размытия

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)
        }
    }
}
0 голосов
/ 12 июля 2020

Взяв на себя инициативу от @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)
        }
    }
}

Оказывается, не так уж и сложно получить высоту ContendedView (). Я нашел это решение отсюда: SwiftUI - Получить размер дочернего элемента?

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()
    }
}

Остающаяся проблема, которую я вижу, заключается в том, что ContainView () не центрируется в альбомной ориентации. Но, надеюсь, я скоро смогу вернуться и отредактировать решение для этого.

РЕДАКТИРОВАТЬ: Это может быть то решение: .frame(width: geometry.size.width) на HStack.

...