SwiftUI - 如何使 NavigationView 不影响几何 reader 的位置
SwiftUI - How to make NavigationView not affect geometry reader's location
我正在尝试使用几何 reader 获取滚动视图的位置。但是,我注意到它给了我一个不同的值,具体取决于我是否将它嵌入到 NavigationView 中。这是一个例子:
struct ContentView: View {
@State var headerLocation: CGFloat = 0
var body: some View {
ZStack(alignment: .top) {
Color.white
.ignoresSafeArea()
VStack(spacing: 0) {
ZStack {
Text("test.username")
.font(.custom("Helvetica Bold", size: 18))
}
.frame(height: 40)
.overlay(alignment: .bottom) {
GeometryReader { geo -> Color in
let minY = geo.frame(in: .global).minY
DispatchQueue.main.async {
if headerLocation == 0 {
headerLocation = minY
}
}
return Color.blue
}
.frame(width: 100, height: 1)
}
ScrollView {
VStack(spacing: 8) {
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut eu sem integer vitae. Pretium aenean pharetra magna ac. Vel pretium lectus quam id leo in vitae. Molestie nunc non blandit massa enim nec dui nunc mattis. Ornare quam viverra orci sagittis eu volutpat odio facilisis. Ultrices neque ornare aenean euismod elementum nisi quis. Non diam phasellus vestibulum lorem sed. Platea dictumst quisque sagittis purus sit amet volutpat consequat mauris. Dapibus ultrices in iaculis nunc sed augue lacus viverra.")
GeometryReader { proxy -> Color in
let offset = proxy.frame(in: .global).minY
print("\(offset), \(headerLocation)")
return Color.green
}
.frame(width: 40, height: 1)
.padding(.top, 10)
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut eu sem integer vitae. Pretium aenean pharetra magna ac. Vel pretium lectus quam id leo in vitae. Molestie nunc non blandit massa enim nec dui nunc mattis. Ornare quam viverra orci sagittis eu volutpat odio facilisis. Ultrices neque ornare aenean euismod elementum nisi quis. Non diam phasellus vestibulum lorem sed. Platea dictumst quisque sagittis purus sit amet volutpat consequat mauris. Dapibus ultrices in iaculis nunc sed augue lacus viverra.")
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut eu sem integer vitae. Pretium aenean pharetra magna ac. Vel pretium lectus quam id leo in vitae. Molestie nunc non blandit massa enim nec dui nunc mattis. Ornare quam viverra orci sagittis eu volutpat odio facilisis. Ultrices neque ornare aenean euismod elementum nisi quis. Non diam phasellus vestibulum lorem sed. Platea dictumst quisque sagittis purus sit amet volutpat consequat mauris. Dapibus ultrices in iaculis nunc sed augue lacus viverra.")
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut eu sem integer vitae. Pretium aenean pharetra magna ac. Vel pretium lectus quam id leo in vitae. Molestie nunc non blandit massa enim nec dui nunc mattis. Ornare quam viverra orci sagittis eu volutpat odio facilisis. Ultrices neque ornare aenean euismod elementum nisi quis. Non diam phasellus vestibulum lorem sed. Platea dictumst quisque sagittis purus sit amet volutpat consequat mauris. Dapibus ultrices in iaculis nunc sed augue lacus viverra.")
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut eu sem integer vitae. Pretium aenean pharetra magna ac. Vel pretium lectus quam id leo in vitae. Molestie nunc non blandit massa enim nec dui nunc mattis. Ornare quam viverra orci sagittis eu volutpat odio facilisis. Ultrices neque ornare aenean euismod elementum nisi quis. Non diam phasellus vestibulum lorem sed. Platea dictumst quisque sagittis purus sit amet volutpat consequat mauris. Dapibus ultrices in iaculis nunc sed augue lacus viverra.")
}
}
}
}
}
}
上面的代码功能如预期。当应用程序启动时,它会设置屏幕顶部蓝色矩形的位置。如果我们滚动滚动视图,我们可以看到当我们将绿色矩形滚动到与蓝色矩形相同的位置时,数字是相等的。
现在接受下面的代码:
struct ContentView: View {
@State var headerLocation: CGFloat = 0
var body: some View {
NavigationView {
ZStack(alignment: .top) {
Color.white
.ignoresSafeArea()
VStack(spacing: 0) {
ZStack {
Text("test.username")
.font(.custom("Helvetica Bold", size: 18))
}
.frame(height: 40)
.overlay(alignment: .bottom) {
GeometryReader { geo -> Color in
let minY = geo.frame(in: .global).minY
DispatchQueue.main.async {
if headerLocation == 0 {
headerLocation = minY
}
}
return Color.blue
}
.frame(width: 100, height: 1)
}
ScrollView {
VStack(spacing: 8) {
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut eu sem integer vitae. Pretium aenean pharetra magna ac. Vel pretium lectus quam id leo in vitae. Molestie nunc non blandit massa enim nec dui nunc mattis. Ornare quam viverra orci sagittis eu volutpat odio facilisis. Ultrices neque ornare aenean euismod elementum nisi quis. Non diam phasellus vestibulum lorem sed. Platea dictumst quisque sagittis purus sit amet volutpat consequat mauris. Dapibus ultrices in iaculis nunc sed augue lacus viverra.")
GeometryReader { proxy -> Color in
let offset = proxy.frame(in: .global).minY
print("\(offset), \(headerLocation)")
return Color.green
}
.frame(width: 40, height: 1)
.padding(.top, 10)
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut eu sem integer vitae. Pretium aenean pharetra magna ac. Vel pretium lectus quam id leo in vitae. Molestie nunc non blandit massa enim nec dui nunc mattis. Ornare quam viverra orci sagittis eu volutpat odio facilisis. Ultrices neque ornare aenean euismod elementum nisi quis. Non diam phasellus vestibulum lorem sed. Platea dictumst quisque sagittis purus sit amet volutpat consequat mauris. Dapibus ultrices in iaculis nunc sed augue lacus viverra.")
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut eu sem integer vitae. Pretium aenean pharetra magna ac. Vel pretium lectus quam id leo in vitae. Molestie nunc non blandit massa enim nec dui nunc mattis. Ornare quam viverra orci sagittis eu volutpat odio facilisis. Ultrices neque ornare aenean euismod elementum nisi quis. Non diam phasellus vestibulum lorem sed. Platea dictumst quisque sagittis purus sit amet volutpat consequat mauris. Dapibus ultrices in iaculis nunc sed augue lacus viverra.")
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut eu sem integer vitae. Pretium aenean pharetra magna ac. Vel pretium lectus quam id leo in vitae. Molestie nunc non blandit massa enim nec dui nunc mattis. Ornare quam viverra orci sagittis eu volutpat odio facilisis. Ultrices neque ornare aenean euismod elementum nisi quis. Non diam phasellus vestibulum lorem sed. Platea dictumst quisque sagittis purus sit amet volutpat consequat mauris. Dapibus ultrices in iaculis nunc sed augue lacus viverra.")
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut eu sem integer vitae. Pretium aenean pharetra magna ac. Vel pretium lectus quam id leo in vitae. Molestie nunc non blandit massa enim nec dui nunc mattis. Ornare quam viverra orci sagittis eu volutpat odio facilisis. Ultrices neque ornare aenean euismod elementum nisi quis. Non diam phasellus vestibulum lorem sed. Platea dictumst quisque sagittis purus sit amet volutpat consequat mauris. Dapibus ultrices in iaculis nunc sed augue lacus viverra.")
}
}
}
}
.navigationTitle("")
.navigationBarTitleDisplayMode(.inline)
.navigationBarHidden(true)
}
}
}
当我们尝试做同样的事情时,我们可以看到当绿色矩形和蓝色矩形位于同一位置时,它们具有不同的值。如何在保留 NavigationView 的同时实现第一个代码块的功能?我已经尝试过不同的 NavigationBarDisplayModes 和 NavigationTitles
让我向您介绍 .coordinateSpace()
,您可以在其中选择要用于比较的视图 GeometryReaders
。简而言之,如您所见,NavigationView
与 GeometryReaders
中的值混淆,因此由于它们都存在于 ZStack
中,我们可以轻松地在那里比较它们。诀窍是告诉 GeometryReaders
使用什么视图。我们将 .coordinateSpace()
放在 ZStack
上,然后不使用 .global()
,而是使用 .named()
。我已将您的 Text()
缩写为更短更清晰。这是工作中的代码:
struct ContentView: View {
@State var headerLocation: CGFloat = 0
var body: some View {
NavigationView {
ZStack(alignment: .top) {
Color.white
.ignoresSafeArea()
VStack(spacing: 0) {
ZStack {
Text("test.username")
.font(.custom("Helvetica Bold", size: 18))
}
.frame(height: 40)
.overlay(alignment: .bottom) {
GeometryReader { geo -> Color in
//Use .named() instead of .global()
let minY = geo.frame(in: .named("ZStack")).minY
DispatchQueue.main.async {
if headerLocation == 0 {
headerLocation = minY
}
}
return Color.blue
}
.frame(width: 100, height: 1)
}
ScrollView {
VStack(spacing: 8) {
Text("Lorem.")
GeometryReader { proxy -> Color in
//Use .named() instead of .global()
let offset = proxy.frame(in: .named("ZStack")).minY
print("\(offset), \(headerLocation)")
return Color.green
}
.frame(width: 40, height: 1)
.padding(.top, 10)
Text("Lorem.")
Text("Lorem.")
Text("Lorem.")
Text("Lorem.")
}
}
}
}
// Put the .coordinateSpace here. The name can be any hashable value.
.coordinateSpace(name: "ZStack")
.navigationTitle("")
.navigationBarTitleDisplayMode(.inline)
.navigationBarHidden(true)
}
}
}
我正在尝试使用几何 reader 获取滚动视图的位置。但是,我注意到它给了我一个不同的值,具体取决于我是否将它嵌入到 NavigationView 中。这是一个例子:
struct ContentView: View {
@State var headerLocation: CGFloat = 0
var body: some View {
ZStack(alignment: .top) {
Color.white
.ignoresSafeArea()
VStack(spacing: 0) {
ZStack {
Text("test.username")
.font(.custom("Helvetica Bold", size: 18))
}
.frame(height: 40)
.overlay(alignment: .bottom) {
GeometryReader { geo -> Color in
let minY = geo.frame(in: .global).minY
DispatchQueue.main.async {
if headerLocation == 0 {
headerLocation = minY
}
}
return Color.blue
}
.frame(width: 100, height: 1)
}
ScrollView {
VStack(spacing: 8) {
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut eu sem integer vitae. Pretium aenean pharetra magna ac. Vel pretium lectus quam id leo in vitae. Molestie nunc non blandit massa enim nec dui nunc mattis. Ornare quam viverra orci sagittis eu volutpat odio facilisis. Ultrices neque ornare aenean euismod elementum nisi quis. Non diam phasellus vestibulum lorem sed. Platea dictumst quisque sagittis purus sit amet volutpat consequat mauris. Dapibus ultrices in iaculis nunc sed augue lacus viverra.")
GeometryReader { proxy -> Color in
let offset = proxy.frame(in: .global).minY
print("\(offset), \(headerLocation)")
return Color.green
}
.frame(width: 40, height: 1)
.padding(.top, 10)
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut eu sem integer vitae. Pretium aenean pharetra magna ac. Vel pretium lectus quam id leo in vitae. Molestie nunc non blandit massa enim nec dui nunc mattis. Ornare quam viverra orci sagittis eu volutpat odio facilisis. Ultrices neque ornare aenean euismod elementum nisi quis. Non diam phasellus vestibulum lorem sed. Platea dictumst quisque sagittis purus sit amet volutpat consequat mauris. Dapibus ultrices in iaculis nunc sed augue lacus viverra.")
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut eu sem integer vitae. Pretium aenean pharetra magna ac. Vel pretium lectus quam id leo in vitae. Molestie nunc non blandit massa enim nec dui nunc mattis. Ornare quam viverra orci sagittis eu volutpat odio facilisis. Ultrices neque ornare aenean euismod elementum nisi quis. Non diam phasellus vestibulum lorem sed. Platea dictumst quisque sagittis purus sit amet volutpat consequat mauris. Dapibus ultrices in iaculis nunc sed augue lacus viverra.")
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut eu sem integer vitae. Pretium aenean pharetra magna ac. Vel pretium lectus quam id leo in vitae. Molestie nunc non blandit massa enim nec dui nunc mattis. Ornare quam viverra orci sagittis eu volutpat odio facilisis. Ultrices neque ornare aenean euismod elementum nisi quis. Non diam phasellus vestibulum lorem sed. Platea dictumst quisque sagittis purus sit amet volutpat consequat mauris. Dapibus ultrices in iaculis nunc sed augue lacus viverra.")
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut eu sem integer vitae. Pretium aenean pharetra magna ac. Vel pretium lectus quam id leo in vitae. Molestie nunc non blandit massa enim nec dui nunc mattis. Ornare quam viverra orci sagittis eu volutpat odio facilisis. Ultrices neque ornare aenean euismod elementum nisi quis. Non diam phasellus vestibulum lorem sed. Platea dictumst quisque sagittis purus sit amet volutpat consequat mauris. Dapibus ultrices in iaculis nunc sed augue lacus viverra.")
}
}
}
}
}
}
上面的代码功能如预期。当应用程序启动时,它会设置屏幕顶部蓝色矩形的位置。如果我们滚动滚动视图,我们可以看到当我们将绿色矩形滚动到与蓝色矩形相同的位置时,数字是相等的。
现在接受下面的代码:
struct ContentView: View {
@State var headerLocation: CGFloat = 0
var body: some View {
NavigationView {
ZStack(alignment: .top) {
Color.white
.ignoresSafeArea()
VStack(spacing: 0) {
ZStack {
Text("test.username")
.font(.custom("Helvetica Bold", size: 18))
}
.frame(height: 40)
.overlay(alignment: .bottom) {
GeometryReader { geo -> Color in
let minY = geo.frame(in: .global).minY
DispatchQueue.main.async {
if headerLocation == 0 {
headerLocation = minY
}
}
return Color.blue
}
.frame(width: 100, height: 1)
}
ScrollView {
VStack(spacing: 8) {
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut eu sem integer vitae. Pretium aenean pharetra magna ac. Vel pretium lectus quam id leo in vitae. Molestie nunc non blandit massa enim nec dui nunc mattis. Ornare quam viverra orci sagittis eu volutpat odio facilisis. Ultrices neque ornare aenean euismod elementum nisi quis. Non diam phasellus vestibulum lorem sed. Platea dictumst quisque sagittis purus sit amet volutpat consequat mauris. Dapibus ultrices in iaculis nunc sed augue lacus viverra.")
GeometryReader { proxy -> Color in
let offset = proxy.frame(in: .global).minY
print("\(offset), \(headerLocation)")
return Color.green
}
.frame(width: 40, height: 1)
.padding(.top, 10)
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut eu sem integer vitae. Pretium aenean pharetra magna ac. Vel pretium lectus quam id leo in vitae. Molestie nunc non blandit massa enim nec dui nunc mattis. Ornare quam viverra orci sagittis eu volutpat odio facilisis. Ultrices neque ornare aenean euismod elementum nisi quis. Non diam phasellus vestibulum lorem sed. Platea dictumst quisque sagittis purus sit amet volutpat consequat mauris. Dapibus ultrices in iaculis nunc sed augue lacus viverra.")
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut eu sem integer vitae. Pretium aenean pharetra magna ac. Vel pretium lectus quam id leo in vitae. Molestie nunc non blandit massa enim nec dui nunc mattis. Ornare quam viverra orci sagittis eu volutpat odio facilisis. Ultrices neque ornare aenean euismod elementum nisi quis. Non diam phasellus vestibulum lorem sed. Platea dictumst quisque sagittis purus sit amet volutpat consequat mauris. Dapibus ultrices in iaculis nunc sed augue lacus viverra.")
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut eu sem integer vitae. Pretium aenean pharetra magna ac. Vel pretium lectus quam id leo in vitae. Molestie nunc non blandit massa enim nec dui nunc mattis. Ornare quam viverra orci sagittis eu volutpat odio facilisis. Ultrices neque ornare aenean euismod elementum nisi quis. Non diam phasellus vestibulum lorem sed. Platea dictumst quisque sagittis purus sit amet volutpat consequat mauris. Dapibus ultrices in iaculis nunc sed augue lacus viverra.")
Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut eu sem integer vitae. Pretium aenean pharetra magna ac. Vel pretium lectus quam id leo in vitae. Molestie nunc non blandit massa enim nec dui nunc mattis. Ornare quam viverra orci sagittis eu volutpat odio facilisis. Ultrices neque ornare aenean euismod elementum nisi quis. Non diam phasellus vestibulum lorem sed. Platea dictumst quisque sagittis purus sit amet volutpat consequat mauris. Dapibus ultrices in iaculis nunc sed augue lacus viverra.")
}
}
}
}
.navigationTitle("")
.navigationBarTitleDisplayMode(.inline)
.navigationBarHidden(true)
}
}
}
当我们尝试做同样的事情时,我们可以看到当绿色矩形和蓝色矩形位于同一位置时,它们具有不同的值。如何在保留 NavigationView 的同时实现第一个代码块的功能?我已经尝试过不同的 NavigationBarDisplayModes 和 NavigationTitles
让我向您介绍 .coordinateSpace()
,您可以在其中选择要用于比较的视图 GeometryReaders
。简而言之,如您所见,NavigationView
与 GeometryReaders
中的值混淆,因此由于它们都存在于 ZStack
中,我们可以轻松地在那里比较它们。诀窍是告诉 GeometryReaders
使用什么视图。我们将 .coordinateSpace()
放在 ZStack
上,然后不使用 .global()
,而是使用 .named()
。我已将您的 Text()
缩写为更短更清晰。这是工作中的代码:
struct ContentView: View {
@State var headerLocation: CGFloat = 0
var body: some View {
NavigationView {
ZStack(alignment: .top) {
Color.white
.ignoresSafeArea()
VStack(spacing: 0) {
ZStack {
Text("test.username")
.font(.custom("Helvetica Bold", size: 18))
}
.frame(height: 40)
.overlay(alignment: .bottom) {
GeometryReader { geo -> Color in
//Use .named() instead of .global()
let minY = geo.frame(in: .named("ZStack")).minY
DispatchQueue.main.async {
if headerLocation == 0 {
headerLocation = minY
}
}
return Color.blue
}
.frame(width: 100, height: 1)
}
ScrollView {
VStack(spacing: 8) {
Text("Lorem.")
GeometryReader { proxy -> Color in
//Use .named() instead of .global()
let offset = proxy.frame(in: .named("ZStack")).minY
print("\(offset), \(headerLocation)")
return Color.green
}
.frame(width: 40, height: 1)
.padding(.top, 10)
Text("Lorem.")
Text("Lorem.")
Text("Lorem.")
Text("Lorem.")
}
}
}
}
// Put the .coordinateSpace here. The name can be any hashable value.
.coordinateSpace(name: "ZStack")
.navigationTitle("")
.navigationBarTitleDisplayMode(.inline)
.navigationBarHidden(true)
}
}
}