Help us understand the problem. What is going on with this article?

SwiftUIでChildViewのFrameを取得する

SwiftUIでChildView(子View)のFrameを取得

親ViewのFrameGeometryReaderを使うことで取得できますが、ChilViewでの取得方法がわからなかったので調べました。

GeometoryReanderbackgroundを利用する

backgroundファンクションはViewColorを与えるために利用してると思います。
ここでbackgroundファンクションを良くみてみましょう。

実は引数はView protocolに準拠しているものであればなんでもOKです。

@inlinable public func background<Background>(_ background: Background, alignment: Alignment = .center) -> some View where Background : View

ColorだってViewに準拠してます。

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
extension Color : View {
}

GeometoryReander

GeometoryReanderViewに準拠したstructです。
GeometoryReanderを使うとこんな感じでGeometoryReanderを囲むViewframeを取得できます。

GeometryReader { proxy -> AnyView in
                    let rect = proxy.frame(in: .global)
                    print(rect
                    return AnyView(EmptyView())
                }

ChildViewではどうするのか

今回はViewfuncの両方を使う方法を紹介します。

Viewを使う方法

まずはViewを使う方法

ChildReaderを作りました。

struct ChildReader<Content>: View where Content: View {

    var viewFrame: Binding<CGRect>

    var content: Content

    init (viewFrame: Binding<CGRect>, @ViewBuilder content: @escaping () -> Content) {
        self.viewFrame = viewFrame
        self.content = content()
    }

    var body: some View {
        self.content
            .background(
                GeometryReader { proxy -> AnyView in
                    let rect = proxy.frame(in: .global)
                    if rect.integral != self.viewFrame.wrappedValue.integral {
                        DispatchQueue.main.async {
                            self.viewFrame.wrappedValue = rect
                            print(rect)
                        }
                    }
                    return AnyView(EmptyView())

                }
        )
    }
}

取得したいViewChildReaderで囲むとchildRectframeが格納されます。

struct ContentView: View {
    @State var childRect: CGRect = .zero
    var body: some View {
        ChildReader(viewFrame: $childRect) {
            Text("Hello World!")
        }
    }
}

funcを使う方法

次にfuncを使う方法を紹介します。ここではViewをextensionしています。

extension View {
    func frame(frame: Binding<CGRect>) -> some View {
        self.background(
                GeometryReader { proxy -> AnyView in
                    let rect = proxy.frame(in: .global)
                    if rect.integral != frame.wrappedValue.integral {
                        DispatchQueue.main.async {
                            frame.wrappedValue = rect
                            print(rect)
                        }
                    }
                    return AnyView(EmptyView())
                }
        )
    }
}

使い方はシンプルです。

struct ContentView: View {
    @State var childRect: CGRect = .zero
    var body: some View {
        Text("Hello World!")
        .frame(frame: $childRect)
    }
}

それではみなさん快適なSwiftUIライフをお送りください!


SwiftUIとCloud Firestoreを簡単に連携できるサンプルプログラムを公開しています。
是非参考にどうぞ!

https://github.com/1amageek/Ballcap-iOS/tree/master/examples/WithSwiftUI


次の記事もオススメです。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away