GeometryReader role
In the SwiftUI layout, the parent view and Child View are loosely linked: A Child View cannot obtain the parent View’s size. If the size of the child View needs to be determined by the size of the parent View, this will not work. Let’s say I want a Rectangle that is half the size of the Parent View
struct ContentView: View {
var body: some View {
VStack{
Rectangle()
.frame(width: vstack.width / 2, height: vstack.height / 2)
}
.frame(width: 300, height: 300)
.background(Color.yellow)
}
}
Copy the code
To implement the above requirements, GeometryReader is needed.
What is GeometryReader
GeometryReader itself is a view, but it differs from the other views in that it exposes an object that can be used to obtain the size of the Parent View, and to Rectangle it. (For Rectangle, the parent View)
var body: some View {
VStack{
Rectangle()
.frame(width: 150, height: 150)
}
.frame(width: 300, height: 300)
.background(Color.yellow)
}
Copy the code
In the above code, we replace VStack with GeomertyReader
struct ContentView: View {
var body: some View {
GeometryReader{ proxy in
Rectangle()
.frame(width: 150, height: 150)
}
.frame(width: 300, height: 300)
.background(Color.yellow)
}
}
Copy the code
Comparing the two codes, the only difference is that GeometryReader exposes the proxy object (the proxy type is GeometryProxy, which doesn’t have much impact on the understanding of this article). Here, FOR those who are not proficient in SWIFT, I will elaborate:
VStack
和GeometryReader
Of two structuresinit
Each method takes a closure function as an argument.GeometryReader
theinit
The closure function itself takes a proxy object as an argument.
Then, from the exposed proxy object, we can get the size of the GeometryReader view.
struct ContentView: View {
var body: some View {
GeometryReader{ proxy in
Rectangle()
.frame(width: proxy.size.width / 2, height: proxy.size.height / 2) // This is the point
}
.frame(width: 300, height: 300)
.background(Color.yellow)
}
}
Copy the code
In the code above, proxy.size is used to obtain the size of GeometryReader.
As an extension, we can put GeometryReader into the VStack
struct ContentView: View {
var body: some View {
VStack {
GeometryReader{ proxy in
Rectangle()
.frame(width: proxy.size.width / 2, height: proxy.size.height / 2)
}
}.frame(width: 300, height: 300)
.background(Color.yellow)
}
}
Copy the code
Compare this code to the original code, just introduce GeometryReader between VStack and Rectangle, and then use the size provided by proxy object to get the size of VStack. Rectangle can be set to half the size of VStack.
There’s one detail that’s missing: If you look at the code carefully, the Rectangle size obtained from the proxy should be the size of the GeometryReader, but since the size of the GeometryReader is the same as the size of the VStack, it is the same as the size of the VStack.
The size of the parent View can be obtained through the proxy of GeometryReader. In addition to getting the size of the parent view, the child View can also get its own coordinates within the parent View area, i.e., “where am I in dad’s place?” Look at the following code:
struct ContentView: View {
var body: some View {
VStack {
GeometryReader{ proxy in
Text("I'm in (x: \(proxy.frame(in: .local).minX), y: \(proxy.frame(in: .local).minY))")
.font(.title2)
}
}
.frame(height: 200)
.background(Color.yellow)
}
}
Copy the code
The running results are as follows:
The proxy provides the frame(in:) method. From the value returned by this method, the Rectangle can know its coordinates in the VStack. (Again, the actual parent view is GeometryReader, but the GeometryReader has the same size and position as the VStack.)
The VStack region is in yellow, and the coordinate system is set at the top left corner. You can see that the Text is in the top left corner of the yellow region, and the starting coordinate is (0, 0). This validates the return value of frame(in:).
Note that frame(in:) has one argument, and we passed.local above. Frame (in:) can actually pass three different arguments:
frame(in: .local)
As shown above, the child View can get the position of the parent View area.frame(in: .global)
Using this parameter returns the position of the Child View throughout the screen.frame(in: .named())
Returns the position in the custom coordinate system.
One and two make sense, but what does three mean? Think about it this way: using.local and.global you can get the location in the parent view and the entire screen, but what if you want the child view to get the location in the “grandfather view”? At this point, use frame(in:.named()). The specific functions are as follows:
- Any in the Child ViewThe immediateUsed in ancestors
.coordinateSpace(name: "custom")
Custom coordinates. - Child View
proxy.frame(in :.named("custom"))
I can get my position in that frame.
The specific code is as follows
struct ContentView: View {
var body: some View {
HStack{
VStack {
VStack{
GeometryReader{proxy in
Text("I'm in (x: \(Int(proxy.frame(in: .named("custom")).minX)), y: \(Int(proxy.frame(in: .named("custom")).minY)))")
}
}.frame(width: 200, height: 200)
.background(Color.yellow)
}
.frame(width: 300, height: 300)
.background(Color.blue)
.coordinateSpace(name: "custom") // Customize a coordinate system}}}Copy the code
When running, you can see that the Text coordinate is (50,50), which is the coordinate system relative to the blue area. The blue area is HStack and is the grandfather view of the Text.
Frame (in:.named()) must be a direct ancestor of the child View. The following code
struct ContentView: View {
var body: some View {
HStack{
VStack{
}.frame(width: 150, height: 150)
.background(Color.blue)
.coordinateSpace(name: "custom")
VStack{
GeometryReader{proxy in
Text("I'm in (x: \(Int(proxy.frame(in: .global).minX)), y: \(Int(proxy.frame(in: .named("custom")).minY)))")
}
}.frame(width: 200, height: 200)
.background(Color.yellow)
}
}
}
Copy the code
To summarize:
- use
GeometryReader
exposedproxy.width && proxy.height
Get the size of the parent View. - use
GeometryReader
exposedproxy.frame(in:)
Method to get the location of the Child View.