首先定义一些结构来存储某个视图的.center位置。
struct PositionData: Identifiable {
let id: Int
let center: Anchor<CGPoint>
}
内置的机制用于保存数据并将其暴露给父级视图的方法是设置/读取(或反应)符合PreferenceKey协议的值。
struct Positions: PreferenceKey {
static var defaultValue: [PositionData] = []
static func reduce(value: inout [PositionData], nextValue: () -> [PositionData]) {
value.append(contentsOf: nextValue())
}
}
为了能读取View的中心位置,我们可以使用广泛讨论过的GeometryReader。我将我的PositionReader定义为一个View,在这里,我们可以简单地将其中心位置保存在我们的preferences中以供进一步使用。无需将中心位置转换为不同的坐标系。为了识别View,必须也保存其标记值。
struct PositionReader: View {
let tag: Int
var body: some View {
Color.clear.anchorPreference(key: Positions.self, value: .center) { (anchor) in
[PositionData(id: self.tag, center: anchor)]
}
}
}
为了演示如何将所有这些内容结合起来使用,请参见下一个简单的应用程序(复制-粘贴-运行)。
import SwiftUI
struct PositionData: Identifiable {
let id: Int
let center: Anchor<CGPoint>
}
struct Positions: PreferenceKey {
static var defaultValue: [PositionData] = []
static func reduce(value: inout [PositionData], nextValue: () -> [PositionData]) {
value.append(contentsOf: nextValue())
}
}
struct PositionReader: View {
let tag: Int
var body: some View {
Color.clear.anchorPreference(key: Positions.self, value: .center) { (anchor) in
[PositionData(id: self.tag, center: anchor)]
}
}
}
struct ContentView: View {
@State var tag = 0
var body: some View {
ZStack {
VStack {
Color.green.background(PositionReader(tag: 0))
.onTapGesture {
self.tag = 0
}
HStack {
Rectangle()
.foregroundColor(Color.red)
.aspectRatio(1, contentMode: .fit)
.background(PositionReader(tag: 1))
.onTapGesture {
self.tag = 1
}
Rectangle()
.foregroundColor(Color.red)
.aspectRatio(1, contentMode: .fit)
.background(PositionReader(tag: 2))
.onTapGesture {
self.tag = 2
}
Rectangle()
.foregroundColor(Color.red)
.aspectRatio(1, contentMode: .fit)
.background(PositionReader(tag: 3))
.onTapGesture {
self.tag = 3
}
}
}
}.overlayPreferenceValue(Positions.self) { preferences in
GeometryReader { proxy in
Rectangle().frame(width: 50, height: 50).position( self.getPosition(proxy: proxy, tag: self.tag, preferences: preferences))
}
}
}
func getPosition(proxy: GeometryProxy, tag: Int, preferences: [PositionData])->CGPoint {
let p = preferences.filter({ (p) -> Bool in
p.id == tag
})[0]
return proxy[p.center]
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
代码几乎是自解释的,我们使用
.background(PositionReader(tag:))
来保存视图的中心位置(这可以通过直接在视图上应用
.anchorPreference
来避免),并且...
.overlayPreferenceValue(Positions.self) { preferences in
GeometryReader { proxy in
Rectangle().frame(width: 50, height: 50).position( self.getPosition(proxy: proxy, tag: self.tag, preferences: preferences))
}
}
此功能用于创建一个小黑色矩形,它将定位在其他视图的中心。只需轻触绿色或红色矩形的任何位置,黑色矩形即会立即移动 :-)
这是运行此示例应用程序的视图。
![输入图像描述](https://istack.dev59.com/Gsu8e.gif)