onReceive (self.timer) no funciona dentro de un NavigationView
Tengo esta página Scrollviewcon un encabezado personalizado que solo se muestra cuando se desplaza más allá de una cierta altura. Utilizo GeometryReadercon onReceivepara comprobar constantemente la altura de desplazamiento actual:
@State var userInfoUpateInterval = Timer.publish(every: 0.1, on: .current, in: .tracking).autoconnect()
@State var showHeader: Bool = false
var body: some View {
NavigationView {
ZStack(alignment: .top) {
ScrollView(.vertical) {
GeometryReader { geometry in
Text("User info component").onReceive(self.userInfoUpateInterval) { (_) in
self.onUserInfoLayoutChange(geometry)
}
}
VStack {
Text("content")
}.frame(width: UIScreen.screenWidth, height: 1500)
}
ProfileHeader(title: "user.userName", showHeader: $showHeader)
}
}
}
El desplazamiento y la ocultación / visualización del encabezado funcionan perfectamente hasta que envuelvo el ZStacken un NavigationView. onReceivesimplemente ya no se activa. Si cambio NavigationViewcon un, ZStacktodo vuelve a funcionar como se esperaba.
He visto que este Timer onReceive no funciona dentro de la pregunta de NavigationView pero no tengo un componente condicional. ¿Es esto un error de SwiftUI o estoy haciendo algo mal?
Respuestas
Aquí hay una demostración de una posible solución para su caso. Probado con Xcode 11.4 / iOS 13.4 (y es compatible con versiones posteriores)
La idea es reaccionar no por el temporizador sino por el cambio de posición de la vista que ha sido leído / seguido por las preferencias de vista.
struct ViewOffsetKey: PreferenceKey {
typealias Value = CGFloat
static var defaultValue: CGFloat { 0 }
static func reduce(value: inout Value, nextValue: () -> Value) {
value = value + nextValue()
}
}
struct DemoView: View {
@State var showHeader: Bool = false
var body: some View {
NavigationView {
ZStack(alignment: .top) {
ScrollView(.vertical) {
Text("User info component")
.background(GeometryReader {
Color.clear.preference(key: ViewOffsetKey.self,
value: -$0.frame(in: .named("scroll_area")).origin.y) }) VStack { Text("content") }.frame(maxWidth: .infinity, minHeight: 1500) }.coordinateSpace(name: "scroll_area") if showHeader { Text("ProfileHeader") } } } .onPreferenceChange(ViewOffsetKey.self) { self.showHeader = $0 > 200 // << your condition
}
}
}