onReceive (self.timer) non funziona all'interno di NavigationView
Ho questa pagina di un Scrollviewcon un'intestazione personalizzata mostrata solo quando scorre oltre una certa altezza. Uso GeometryReadercon onReceiveper controllare costantemente l'altezza di scorrimento corrente:
@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)
}
}
}
Lo scorrimento e l'occultamento / visualizzazione dell'intestazione funzionano perfettamente finché non ho inserito il file ZStackin un file NavigationView. onReceivesemplicemente non viene più attivato. Se cambio NavigationViewcon un ZStacktutto funziona di nuovo come previsto.
Ho visto questo Timer onReceive non funzionare all'interno della domanda NavigationView ma non ho un componente condizionale. È un bug di SwiftUI o sto facendo qualcosa di sbagliato?
Risposte
Ecco una demo della possibile soluzione per il tuo caso. Testato con Xcode 11.4 / iOS 13.4 (ed è compatibile con le versioni successive)
L'idea è di reagire non tramite il timer ma tramite il cambio di posizione della vista che è stato letto / monitorato dalle preferenze di visualizzazione.
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
}
}
}