onReceive (self.timer) не работает внутри NavigationView
У меня есть эта страница Scrollviewс настраиваемым заголовком, который отображается только тогда, когда он прокручивается выше определенной высоты. Я использую GeometryReaderwith, onReceiveчтобы постоянно проверять текущую высоту прокрутки:
@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)
}
}
}
Прокрутка и скрытие / отображение заголовка работают отлично, пока я не завернул файл ZStackв файл NavigationView. onReceiveпросто больше не срабатывает. Если бы я поменяться NavigationViewс ZStackвсе работает как раз.
Я видел, что этот Timer onReceive не работает внутри вопроса NavigationView, но у меня нет условного компонента. Это ошибка SwiftUI или я что-то делаю не так?
Ответы
Вот демонстрация возможного решения для вашего случая. Протестировано с Xcode 11.4 / iOS 13.4 (и совместимо с предыдущими версиями)
Идея состоит в том, чтобы реагировать не по таймеру, а по изменению положения просмотра, которое было прочитано / отслежено настройками просмотра.
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
}
}
}