swift - SwiftUI 将位置设置为不同视图的中心
问题描述
我有两个不同的视图,一个红色矩形和一个黑色矩形,始终位于屏幕底部。当我单击红色矩形时,它应该将自己定位在另一个矩形内。
目前红色矩形是静态定位的:.position(x: self.tap ? 210 : 50, y: self.tap ? 777 : 50)
. 有没有办法将 210 和 777 动态替换到黑色矩形中心位置的位置?
我知道我可以使用 GeometryReader 来获取视图大小,但是如何使用该大小来定位不同的视图?这甚至是正确的方法吗?
struct ContentView: View {
@State private var tap = false
var body: some View {
ZStack {
VStack {
Spacer()
RoundedRectangle(cornerRadius: 10)
.frame(maxWidth: .infinity, maxHeight: 50, alignment: .center)
}
.padding()
VStack {
ZStack {
Rectangle()
.foregroundColor(.red)
Text("Click me")
.fontWeight(.light)
.foregroundColor(.white)
}
.frame(width: 50, height: 50)
.position(x: self.tap ? 210 : 50, y: self.tap ? 777 : 50)
.onTapGesture {
withAnimation {
self.tap.toggle()
}
}
}
}
}
}
解决方案
首先定义一些结构来存储一些视图的 .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 定义为一个视图,在这里我们可以简单地将其中心位置保存在我们的首选项中以供进一步使用。无需将中心转换为不同的坐标系。要识别视图,还必须保存其标签值
struct PositionReader: View {
let tag: Int
var body: some View {
// we don't need geometry reader at all
//GeometryReader { proxy in
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:))
用来保存 View 的中心位置(这可以通过直接在 View 上应用 .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))
}
}
用于创建将自身定位在其他视图中心的黑色小矩形。只需点击绿色或红色矩形中的任意位置,黑色的会立即移动 :-)
这是此示例应用程序运行的视图。
推荐阅读
- swift - 无法在视图控制器上创建 UIBarButtonItem
- java - Home screen shortcut wont add
- java - 如何在 oracle 查询中将所有相似的行数据视为一个?
- google-maps - 谷歌地图地址地理编码 API 不起作用
- machine-learning - 如何获得随机森林算法在自变量上使用的最终方程来预测因变量?
- r - 如何在 MATLAB(当前为 R)中编写这行代码?
- wordpress - htaccess 用下划线合并 2 个段
- javascript - 等待 curl_exec 得到响应时显示 Gif
- python - java.lang.RuntimeExceptionPyRaisable:java.lang.RuntimeException:找不到类 com.teradata.jdbc.TeraDriver
- oracle - 使用 Oracle trunc() 的奇怪结果?