利用 GeometryReader 取得 SwiftUI 元件的座標尺寸

設計 SwiftUI App 畫面時,我們通常不太需要思考元件的座標尺寸,不過有時我們會想知道元件在整個螢幕的座標尺寸,或是它相對於某個 view 的座標。

這些願望要實現並不難,只要透過 GeometryReader 的幫忙。

取得元件在整個螢幕的座標尺寸

struct ContentView: View {

var body: some View {
Image("tony")
.resizable()
.scaledToFit()
.frame(width: 300)
.overlay(
GeometryReader(content: { geometry in
Text(geometry.frame(in: .global).debugDescription)
.background(Color.yellow)
})
)


}
}

我們以男神梁朝偉在 FB 分享的帥氣圖片為例,想知道它在整個螢幕的座標尺寸。

取得座標尺寸的關鍵在以下程式

.overlay(
GeometryReader(content: { geometry in
Text(geometry.frame(in: .global).debugDescription)
.background(Color.yellow)
})
)

Image 上呼叫 overlay modifier,然後傳入包含 TextGeometryReaderGeometryReader 是個看不到的容器,它的參數 geometry 可告訴我們它的位置大小。由於我們以 overlayGeometryReader 疊在 Image 上,所以它的位置大小即是 Image 的位置大小。

參數geometry 的型別是 GeometryProxy,我們呼叫它的 function frame(in:),傳入參數 .global 取得相對於整個螢幕的座標尺寸。若想單獨取得座標和尺寸,可透過 geometry.frame(in: .global).origin & geometry.frame(in: .global).size

將元件的座標尺寸存在變數裡

有些時候我們希望能將元件的座標尺寸存在變數裡,方便之後讀取。這要怎麼做到呢 ?

由於 GeometryReader 的參數 content 裡一定要生成 view,所以我們可以生成看不到的 Color.clear,然後在它身上加上 onAppear modifier,如此即可在 Color.clear 出現時取得 GeometryReader 的座標尺寸,然後存在變數裡。

struct ContentView: View {

@State private var orangeFrame = CGRect.zero

var body: some View {

VStack(spacing: 0) {
Color.blue
.frame(width: 50, height: 80, alignment: .center)
HStack(spacing: 0) {
Color.blue
.frame(width: 50, height: 50, alignment: .center)
Color.orange
.frame(width: 200.0, height: 200.0)
.overlay(
GeometryReader(content: { geometry in
Color.clear
.onAppear(perform: {
orangeFrame = geometry.frame(in: .global)
})
})
)

.onTapGesture {
print(orangeFrame)
}
Spacer()
}
Spacer()
}
.edgesIgnoringSafeArea(.all)
}
}

點選橘色長方形將印出以下座標尺寸。

(50.0, 80.0, 200.0, 200.0)

取得元件相對於 parent 的座標

有時我們想取得元件相對於 parent 的座標,比方以下偉仔相對於藍色 stack view 的座標。

struct ContentView: View {var body: some View {
VStack {
Image("tony")
.resizable()
.scaledToFit()
.frame(width: 300)
.overlay(
GeometryReader(content: { geometry in
Text(geometry.frame(in: .named("blue view")).debugDescription)
.background(Color.yellow)
})
)
}
.frame(width: 400, height: 600)
.background(Color.blue)
.coordinateSpace(name: "blue view")
}
}

此問題的難度高了一點,找出答案的關鍵在以下幾行程式

.coordinateSpace(name: "blue view")

從 VStack 呼叫 coordinateSpace(name:),給它名字 blue view,之後即可找出相對於 blue view 的座標。

.overlay(
GeometryReader(content: { geometry in
Text(geometry.frame(in: .named("blue view")).debugDescription)
.background(Color.yellow)
})
)

同樣呼叫 function frame(in:),不過這次我們傳入 .named("blue view").name( ) 裡傳入 blue view 表示我們想找出元件相對於 blue view 的座標。

--

--

彼得潘的 iOS App Neverland
彼得潘的 Swift iOS App 開發問題解答集

彼得潘的iOS App程式設計入門,文組生的iOS App程式設計入門講師,彼得潘的 Swift 程式設計入門,App程式設計入門作者,http://apppeterpan.strikingly.com