SwiftUI中用GeometryReader定位Rectangle至另一矩形左上角出错排查
问题
在ZStack中堆叠三个Rectangle并各自设置coordinateSpace,想要将粉色Rectangle定位到黄色Rectangle的左上角,使用代码.position(x: proxy.frame(in: .named("YellowRectangle")).minX, y: proxy.frame(in: .named("YellowRectangle")).minY)后,粉色Rectangle却出现在屏幕左上角,请问错误原因是什么?
完整示例代码:
struct ContentView: View { private let uiScreenWidth = UIScreen.main.bounds.width private let uiScreenHeight = UIScreen.main.bounds.height var body: some View { GeometryReader { proxy in ZStack { Rectangle() .fill(.cyan) .frame(width: uiScreenWidth * 0.9, height: uiScreenHeight * 0.9) .coordinateSpace(name: "CyanRectangle") Rectangle() .fill(.yellow) .frame(width: uiScreenWidth * 0.6, height: uiScreenHeight * 0.6) .coordinateSpace(name: "YellowRectangle") Rectangle() .fill(.pink) .frame(width: 100, height: 100) .position(x: proxy.frame(in: .named("YellowRectangle")).minX, y: proxy.frame(in: .named("YellowRectangle")).minY) .coordinateSpace(name: "PinkRectangle") } .background(.gray) } .border(.black, width: 1) } }
错误原因分析
position的作用逻辑误解:position是用来设置视图中心相对于父容器(这里是ZStack)的坐标,而不是视图的左上角。你用黄色Rectangle的左上角坐标来定义粉色Rectangle的中心位置,结果自然会偏离目标。- 坐标系获取错误:
proxy.frame(in: .named("YellowRectangle"))拿到的是黄色Rectangle在全局坐标系(屏幕坐标系)中的位置,而非相对于ZStack的本地坐标。直接将这个值传给position,相当于把粉色Rectangle的中心定位到了全局坐标系里黄色Rectangle左上角的位置,也就是屏幕左上角区域。
修正方案
要实现粉色Rectangle左上角对齐黄色Rectangle左上角,有两种常见的调整方式:
方法一:利用ZStack对齐+偏好键(推荐)
通过偏好键获取黄色Rectangle的锚点,再用偏移量定位粉色视图:
struct ContentView: View { private let uiScreenWidth = UIScreen.main.bounds.width private let uiScreenHeight = UIScreen.main.bounds.height var body: some View { GeometryReader { proxy in ZStack(alignment: .topLeading) { Rectangle() .fill(.cyan) .frame(width: uiScreenWidth * 0.9, height: uiScreenHeight * 0.9) Rectangle() .fill(.yellow) .frame(width: uiScreenWidth * 0.6, height: uiScreenHeight * 0.6) .anchorPreference(key: YellowAnchorKey.self, value: .topLeading) { $0 } Rectangle() .fill(.pink) .frame(width: 100, height: 100) .offsetPreference(YellowAnchorKey.self) { anchor in guard let anchor = anchor else { return .zero } return CGSize(width: anchor.x, height: anchor.y) } } .background(.gray) .transformAnchorPreference(key: YellowAnchorKey.self, value: .topLeading) { $0 = $0.map { proxy[$0] } } } .border(.black, width: 1) } } struct YellowAnchorKey: PreferenceKey { static var defaultValue: Anchor<CGPoint>? = nil static func reduce(value: inout Anchor<CGPoint>?, nextValue: () -> Anchor<CGPoint>?) { value = value ?? nextValue() } }
方法二:手动计算正确的position坐标
如果坚持用position,需要把全局坐标转换为ZStack本地坐标系下的粉色视图中心位置:
struct ContentView: View { private let uiScreenWidth = UIScreen.main.bounds.width private let uiScreenHeight = UIScreen.main.bounds.height var body: some View { GeometryReader { proxy in ZStack { // 获取ZStack在全局坐标系的帧 let zstackGlobalFrame = proxy.frame(in: .global) // 获取黄色Rectangle在全局坐标系的帧 let yellowGlobalFrame = proxy.frame(in: .named("YellowRectangle")) // 计算粉色视图中心在ZStack本地的坐标:黄色左上角偏移到ZStack本地 + 粉色自身半宽高 let pinkCenterX = yellowGlobalFrame.minX - zstackGlobalFrame.minX + 50 let pinkCenterY = yellowGlobalFrame.minY - zstackGlobalFrame.minY + 50 Rectangle() .fill(.cyan) .frame(width: uiScreenWidth * 0.9, height: uiScreenHeight * 0.9) Rectangle() .fill(.yellow) .frame(width: uiScreenWidth * 0.6, height: uiScreenHeight * 0.6) .coordinateSpace(name: "YellowRectangle") Rectangle() .fill(.pink) .frame(width: 100, height: 100) .position(x: pinkCenterX, y: pinkCenterY) } .background(.gray) } .border(.black, width: 1) } }
内容的提问来源于stack exchange,提问作者Janyee
相关产品推荐
相关产品推荐

