SwiftUI - Get size of child?

前端 未结 4 1143
死守一世寂寞
死守一世寂寞 2020-12-15 08:32

Is there any way to get the size of a child view in SwiftUI?

I\'m basically looking to do the UIKit equivalent of:



        
相关标签:
4条回答
  • 2020-12-15 09:04

    Basically, the answer at this point is to use a GeometryReader inside of the child's background(...) modifier.

    // This won't be valid until the first layout pass is complete
    @State var childSize: CGSize = .zero
    
    var body: some View {
        ZStack {
            Text("Hello World!")
                .background(
                    GeometryReader { proxy in
                        Color.clear.
                        .preference(key: SizePreferenceKey.self, value: proxy.size)
                    }
                )
          }
          .onPreferenceChange(SizePreferenceKey.self) { preferences in
              self.childSize = preferences
          }
    }
    
    struct SizePreferenceKey: PreferenceKey {
        typealias Value = CGSize
        static var defaultValue: Value = .zero
    
        static func reduce(value: inout Value, nextValue: () -> Value) {
            value = nextValue()
        }
    }
    
    0 讨论(0)
  • 2020-12-15 09:16

    You can use AnchorPreferences to bubble information about a child view's geometry up to a parent view. See: https://swiftui-lab.com/communicating-with-the-view-tree-part-2/

    0 讨论(0)
  • 2020-12-15 09:24

    Updated and generalized @arsenius code. Now you can easily bind a parent view's state variable.

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

    Usage:

    struct ChildSizeReaderExample: View {
        @State var textSize: CGSize = .zero
        var body: some View {
            VStack {
                ChildSizeReader(size: $textSize) {
                    Text("Hello I am some arbitrary text.")
                }
                Text("My size is \(textSize.debugDescription)!")
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-15 09:28

    Here's a reusable variant of the accepted answer:

    protocol SizeReaderKey: PreferenceKey where Value == CGSize {}
    
    extension SizeReaderKey {
        static func reduce(value _: inout CGSize, nextValue: () -> CGSize) {
            _ = nextValue()
        }
    }
    
    struct SizeReader<Key: SizeReaderKey>: ViewModifier {
        func body(content: Content) -> some View {
            content
                .background(
                    GeometryReader { geo in
                        Color.clear
                            .preference(key: Key.self, value: geo.size)
                    }
                )
        }
    }
    
    extension View {
        func onSizeChanged<Key: SizeReaderKey>(
            _ key: Key.Type,
            perform action: @escaping (CGSize) -> Void) -> some View
        {
            self
                .modifier(SizeReader<Key>())
                .onPreferenceChange(key) { value in
                    action(value)
                }
        }
    }
    

    Usage:

    struct Example: View {
        var body: some View {
            Text("Hello, World!")
                .onSizeChanged(CustomViewSizeKey.self) { size in
                    print("size: \(size)")
                }
        }
    
        struct CustomViewSize: SizePreferenceKey {
            static var defaultValue: CGSize = .zero
        }
    }
    
    0 讨论(0)
提交回复
热议问题