SwiftUI - 获取孩子的大小?

ars*_*ius 17 swiftui

有没有办法在 SwiftUI 中获取子视图的大小?

我基本上希望做 UIKit 相当于:

self.child.frame.origin.x -= self.child.intrinsicContentSize.width/2.0
Run Code Online (Sandbox Code Playgroud)

我认为 GeometryReader 不会工作,因为它返回父级中的可用大小。

[编辑] 我发现可以使用.alignmentGuide(_, computeValue:)虽然这绝对是一个黑客来获取和保存尺寸。

LessonSliderText(text: self.textForProgress(self.progress), color: self.completedColor)
    .alignmentGuide(HorizontalAlignment.leading) { (dimensions) -> Length in
        self.textSize = CGSize(width: dimensions.width, height: dimensions.height)
        return 0
    }
    .offset(x: self.width*self.currentPercentage - self.textSize.width / 2.0)
    .offset(y: -self.textSize.height/2.0)
    .animation(nil)
    .opacity(self.isDragging ? 1.0 : 0.0)
    .animation(.basic())
Run Code Online (Sandbox Code Playgroud)

我想要完成的事情

Wil*_*ler 24

更新和通用的@arsenius 代码。现在您可以轻松绑定父视图的状态变量。

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()
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

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)!")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 仅供参考,当代理返回的宽度/高度如 41.3333333329 时,此代码遇到了无限循环问题,那么首选项会将其设置为 41.3333333333。通过舍入宽度和高度来修复它,以确保其保持一致。`content().frame(宽度: size != .zero ? Floor(size.width) : nil, height: size != .zero ? Floor(size.height) : nil)` (3认同)
  • @robhasacamera 这帮了我很多忙!您是如何发现这会导致无限循环的? (2认同)

ars*_*ius 23

基本上,此时的答案是使用子级修饰符的GeometryReader 内部background(...)

// 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()
    }
}     
Run Code Online (Sandbox Code Playgroud)

  • 不这么认为,获取子视图的框架非常困难:(应该是一些简单的方法 (3认同)