10
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

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


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

Why not register and get more from Qiita?
  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
Sign upLogin
10
Help us understand the problem. What are the problem?