LoginSignup
12

More than 3 years have passed since last update.

SwiftUIでChildViewのFrameを取得する

Last updated at Posted at 2019-09-20

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を簡単に連携できるサンプルプログラムを公開しています。
是非参考にどうぞ!


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

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12