diff --git a/dimensionhub/Views/Index/IndexMainView.swift b/dimensionhub/Views/Index/IndexMainView.swift index fc8f554..5b20f7e 100644 --- a/dimensionhub/Views/Index/IndexMainView.swift +++ b/dimensionhub/Views/Index/IndexMainView.swift @@ -28,9 +28,6 @@ struct IndexMainView: View { @State private var footerRefreshing: Bool = false @State private var noMore: Bool = false - // 保存底部元素在global坐标系里面的minY值 - @State private var footerOffset: Int = 0 - var body: some View { VStack(alignment: .center) { @@ -74,8 +71,9 @@ struct IndexMainView: View { .contentShape(Rectangle()) } } - + ProgressView() + .id(UUID()) .background(GeometryReader { geometry in let minY = geometry.frame(in: .global).minY Color.clear.preference(key: FooterOffsetPreferenceKey.self, value: Int(minY)) @@ -83,25 +81,12 @@ struct IndexMainView: View { } .frame(width: 370) .coordinateSpace(name: "indexScrollView") - .onScrollPhaseChange { oldPhase, newPhase in - guard !footerRefreshing && !showDateNavPopover else { - return + + .onPreferenceChange(FooterOffsetPreferenceKey.self) { offset in + // 延迟到当前帧结束更新state的值,避免导致循环更新的警告 + DispatchQueue.main.async { + indexModel.loadMorePublisher.send((userId, offset)) } - - print("scroll old: \(oldPhase), new: \(newPhase), footer-offset: \(footerOffset)") - // 滑动停止的时候,检测是否到达了底部 - let height = Int(UIScreen.main.bounds.height) - let distance = height - footerOffset - if newPhase == .idle && (distance >= 0 && distance < 50) { - self.footerRefreshing = true - Task { - await self.indexModel.loadMoreUpdateDramas(userId: self.userId, mode: .next) - DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { - self.footerRefreshing = false - } - } - } - } .refreshable { guard !self.showDateNavPopover && !self.headerRefreshing else { @@ -110,8 +95,12 @@ struct IndexMainView: View { // 上拉刷新功能 self.headerRefreshing = true - await self.indexModel.loadMoreUpdateDramas(userId: self.userId, mode: .prev) - self.headerRefreshing = false + Task { + await self.indexModel.loadMoreUpdateDramasTask(userId: self.userId, mode: .prev) + DispatchQueue.main.async { + self.headerRefreshing = false + } + } } .overlay(alignment: .topTrailing) { HStack(alignment: .center) { @@ -155,13 +144,9 @@ struct IndexMainView: View { .onPreferenceChange(DramaGroupElementPreferenceKey.self) { frames in let visibleFrames = frames.filter { $0.value >= 0} if let minFrame = visibleFrames.min(by: { $0.value <= $1.value}) { - indexModel.setFixedDrameGroup(groupId: minFrame.key) - } - } - .onPreferenceChange(FooterOffsetPreferenceKey.self) { offset in - // 延迟到当前帧结束更新state的值,避免导致循环更新的警告 - DispatchQueue.main.async { - self.footerOffset = offset + DispatchQueue.main.async { + indexModel.setFixedDrameGroup(groupId: minFrame.key) + } } } @@ -268,10 +253,7 @@ extension IndexMainView { static var defaultValue: Int = 0 static func reduce(value: inout Int, nextValue: () -> Int) { - let newValue = nextValue() - if newValue > 0 && abs(newValue - value) >= 50 { - value = newValue - } + value += nextValue() } } diff --git a/dimensionhub/Views/Index/IndexModel.swift b/dimensionhub/Views/Index/IndexModel.swift index 5d423d0..28d6ba8 100644 --- a/dimensionhub/Views/Index/IndexModel.swift +++ b/dimensionhub/Views/Index/IndexModel.swift @@ -7,6 +7,8 @@ import Foundation import Observation +import Combine +import SwiftUI @Observable final class IndexModel { @@ -60,12 +62,45 @@ final class IndexModel { @ObservationIgnored private var isLoaded = false + // 是否正在刷新 + @ObservationIgnored + private var isMoreLoading = false + // 是否显示debug信息 @ObservationIgnored private var debug: Bool = false + // 用来追踪最近的更新时间 + @ObservationIgnored + private var updateInterval = Date() + + // 用来记录追踪当前footer的offset的变化 + @ObservationIgnored + var loadMorePublisher = PassthroughSubject<(String, Int), Never>() + + @ObservationIgnored + private var bag = Set() + init() { self.selectedDate = "" + + self.loadMorePublisher + .debounce(for: 0.2, scheduler: RunLoop.main) + .dropFirst() + .sink { (userId, offset) in + // 判断更新周期 + let timeDistance = self.updateInterval.distance(to: Date()) + // 滑动停止的时候,检测是否到达了底部 + let height = Int(UIScreen.main.bounds.height) + let distance = height - offset + if timeDistance > 1.0 && (distance >= 0 && distance < 50) { + Task { + await self.loadMoreUpdateDramasTask(userId: userId, mode: .next) + } + self.updateInterval = Date() + } + } + .store(in: &bag) } func loadData(userId: String) async { @@ -94,7 +129,11 @@ final class IndexModel { } } - func loadMoreUpdateDramas(userId: String, mode: API.LoadMode) async { + func loadMoreUpdateDramasTask(userId: String, mode: API.LoadMode) async { + guard !self.isMoreLoading else { + return + } + // 按照id来判断不一定正确,需要借助其他值 let dramaIds = self.getDramaIds(self.updateDramaGroups) print("current ids: \(dramaIds)") @@ -103,6 +142,7 @@ final class IndexModel { case .prev: // 查找最小的id if let firstId = dramaIds.first { + self.isMoreLoading = true let response = await API.loadMoreUpdateDramas(userId: userId, mode: mode, id: firstId, as: [UpdateDramaGroup].self) if case let .result(groups) = response { if groups.count > 0 { @@ -114,9 +154,11 @@ final class IndexModel { displayDramaGroups(self.updateDramaGroups, label: "after") } } + self.isMoreLoading = false } case .next: if let lastId = dramaIds.last { + self.isMoreLoading = true let response = await API.loadMoreUpdateDramas(userId: userId, mode: mode, id: lastId, as: [UpdateDramaGroup].self) if case let .result(groups) = response { if groups.count > 0 { @@ -128,6 +170,8 @@ final class IndexModel { displayDramaGroups(self.updateDramaGroups, label: "after") } } + + self.isMoreLoading = false } } }