SwiftUI GeometryReader 教學

在 SwiftUI 中,View 通常會自動適應內容大小。但有時候,我們需要知道父容器的具體尺寸,或是某個 View 在螢幕上的座標位置,這時候就需要 GeometryReader

獲取尺寸 (Size)

GeometryReader 是一個特殊的 View,它會回傳一個 GeometryProxy 物件,透過這個物件我們可以讀取剩餘空間的大小。

GeometryReader 會盡可能佔滿所有可用空間,這點跟大多數 View 只佔用最小空間的行為不同。請小心使用,以免意外撐大佈局。
GeometryReader { geometry in
    VStack {
        Text("寬度: \(geometry.size.width)")
        Text("高度: \(geometry.size.height)")
        
        Rectangle()
            .fill(.blue)
            .frame(width: geometry.size.width / 2) // 使用一半的寬度
    }
}
.frame(height: 200) // 強制指定 GeometryReader 的高度

獲取座標 (Frame & Coordinates)

除了尺寸,GeometryProxy 也可以告訴我們 View 在不同座標系中的位置。

  • .local: 相對於 GeometryReader 本身的座標 (通常原點 (0,0) 在左上角)。
  • .global: 相對於整個螢幕 (Window) 的座標。

這在製作複雜的滾動特效(例如 Parallax 視差效果)時非常有用。

簡單的視差滾動範例

ScrollView {
    VStack {
        ForEach(0..<10) { _ in
            GeometryReader { geo in
                Image("nature")
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .frame(width: geo.size.width, height: 200)
                    // 根據 Y 軸位置製造視差位移
                    .offset(y: -geo.frame(in: .global).minY / 5) 
            }
            .frame(height: 200)
            .clipped()
        }
    }
}

何時該使用?

雖然 GeometryReader 很強大,但它是比較重的操作。在絕大多數的版面佈局需求中(例如兩欄均分),應該優先使用 HStack + Spacer 或是 .frame(maxWidth: .infinity) 來達成,只有在真的需要精確數值計算時才使用 GeometryReader