From b95420ef74ff4b8a5e7b06a9bca0c9c5575c373d Mon Sep 17 00:00:00 2001 From: anlicheng <244108715@qq.com> Date: Thu, 19 Jun 2025 17:31:45 +0800 Subject: [PATCH] fix index --- dimensionhub/Views/Index/IndexMainView.swift | 20 ++- dimensionhub/Views/Index/IndexModel.swift | 171 ++++++++++++------- 2 files changed, 122 insertions(+), 69 deletions(-) diff --git a/dimensionhub/Views/Index/IndexMainView.swift b/dimensionhub/Views/Index/IndexMainView.swift index f070aac..6ab8ccb 100644 --- a/dimensionhub/Views/Index/IndexMainView.swift +++ b/dimensionhub/Views/Index/IndexMainView.swift @@ -14,7 +14,7 @@ struct IndexMainView: View { @AppStorage("userId") private var userId: String = Utils.defaultUserId() @State var indexModel = IndexModel() - @State private var scrollID: Int? = 0 + @State private var scrollID: IndexModel.ScrollTarget? = nil // 提示信息 @State var showPrompt: Bool = false @@ -32,6 +32,8 @@ struct IndexMainView: View { @State private var scrollOffset: CGFloat = 0 @State private var showFloatGroupLabel: Bool = false + @State private var scrollHeight: Double = 0 + var body: some View { VStack(alignment: .center) { @@ -77,22 +79,23 @@ struct IndexMainView: View { // 基于日期的更新列表 LazyVStack(alignment: .center, spacing: 10) { - ForEach(Array(indexModel.dramaGroupElements.enumerated()), id: \.offset) { idx, item in - switch item { + ForEach(indexModel.dramaGroupElements, id: \.id) { item in + switch item.data { case .label(let groupId, let groupName, _): DramaGroupLabelView(group_name: groupName) { selectGroupId = groupId indexModel.selectedDate = groupId showDateNavPopover = true } + .id(item.id) case .item(let groupId, let item, _): DramaGroupItemView(groupId: groupId, item: item) - .id(idx) + .id(item.id) } } } .scrollTargetLayout() - + ProgressView() } .frame(width: 370) @@ -110,9 +113,10 @@ struct IndexMainView: View { // 上拉刷新功能 self.headerRefreshing = true Task { - await self.indexModel.loadMoreUpdateDramasTask(userId: self.userId, mode: .prev) - DispatchQueue.main.async { - self.headerRefreshing = false + await self.indexModel.loadPrevUpdateDramasTask(userId: self.userId) { anchorGroupElement in + DispatchQueue.main.async { + self.headerRefreshing = false + } } } } diff --git a/dimensionhub/Views/Index/IndexModel.swift b/dimensionhub/Views/Index/IndexModel.swift index b9be770..ba2fedd 100644 --- a/dimensionhub/Views/Index/IndexModel.swift +++ b/dimensionhub/Views/Index/IndexModel.swift @@ -50,11 +50,22 @@ final class IndexModel { let follow_num: Int } - enum GroupElement { - // groupId, groupName - case label(String, String, Double) - // groupId, UpdateDramaGroup.Item - case item(String, UpdateDramaGroup.Item, Double) + // target的目标对象 + enum ScrollTarget: Hashable { + case group(id: String) + case item(id: Int) + } + + struct GroupElement { + enum Data { + // groupId, groupName + case label(String, String, Double) + // groupId, UpdateDramaGroup.Item + case item(String, UpdateDramaGroup.Item, Double) + } + + let id: ScrollTarget + let data: Data } var selectedDate: String @@ -65,6 +76,10 @@ final class IndexModel { // 转换后的数据, 方便在一次循环中展示数据 var dramaGroupElements: [GroupElement] = [] + // 保存映射建立元素到group的关系 + @ObservationIgnored + var dramaGroupMap: [Int:String] = [:] + var follow_num: String = "0" // 用来显示固定栏目的group_name @@ -87,7 +102,7 @@ final class IndexModel { // 用来追踪scrollID的变化 @ObservationIgnored - var scrollIDPublisher = PassthroughSubject<(String, Int), Never>() + var scrollIDPublisher = PassthroughSubject<(String, ScrollTarget), Never>() @ObservationIgnored private var cancel: AnyCancellable? @@ -100,23 +115,42 @@ final class IndexModel { self.selectedDate = "" self.cancel = self.scrollIDPublisher - .sink { userId, scrollID in - // 更新group的label的部分 - switch self.dramaGroupElements[scrollID] { - case .label(_, _, _): - () - case .item(let groupId, _, _): + .sink { userId, scrollTarget in + switch scrollTarget { + case .group(id: let groupId): DispatchQueue.main.async { self.setFixedDrameGroup(groupId: groupId) } + case .item(id: let id): + // 通过id查找对应的group_id + if let groupId = self.dramaGroupMap[id] { + DispatchQueue.main.async { + self.setFixedDrameGroup(groupId: groupId) + } + } + } + + // 判断最后6个元素里面是否包含 + let isCloseBottom = self.dramaGroupElements.suffix(6).contains { elem in + switch elem.data { + case .label(let groupId, _, _): + if case .group(id: let groupId0) = scrollTarget { + return groupId == groupId0 + } + case .item(_, let item, _): + if case .item(id: let id) = scrollTarget { + return item.id == id + } + } + return false } // 判断更新周期 let timeDistance = self.updateInterval.distance(to: Date()) // 滑动停止的时候,检测是否到达了底部 - if timeDistance > 1.0 && self.dramaGroupElements.count - scrollID < 6 { + if timeDistance > 1.0 && isCloseBottom { Task { - await self.loadMoreUpdateDramasTask(userId: userId, mode: .next) + await self.loadMoreUpdateDramasTask(userId: userId) } self.updateInterval = Date() } @@ -130,7 +164,7 @@ final class IndexModel { let (_, labelHeight, elementHeight) = self.elementHeight self.dramaGroupElements.forEach { element in - switch element { + switch element.data { case .label(let groupId, _, let y): if offset + y >= -labelHeight && offset + y <= 0 { rangeGroupId = groupId @@ -186,13 +220,16 @@ final class IndexModel { } func setFixedDrameGroup(groupId: String) { - if let newFixedDramaGroup = self.updateDramaGroups.first(where: {$0.group_id == groupId}), - newFixedDramaGroup.group_id != self.fixedDramaGroup?.group_id { - self.fixedDramaGroup = newFixedDramaGroup + if let newFixedDramaGroup = self.updateDramaGroups.first(where: {$0.group_id == groupId}) { + if let oldGroupId = self.fixedDramaGroup?.group_id, oldGroupId != newFixedDramaGroup.group_id { + self.fixedDramaGroup = newFixedDramaGroup + } else { + self.fixedDramaGroup = newFixedDramaGroup + } } } - func loadMoreUpdateDramasTask(userId: String, mode: API.LoadMode) async { + func loadMoreUpdateDramasTask(userId: String) async { guard !self.isMoreLoading else { return } @@ -200,51 +237,59 @@ final class IndexModel { // 按照id来判断不一定正确,需要借助其他值 let dramaIds = self.getDramaIds(self.updateDramaGroups) //print("current ids: \(dramaIds)") + if let lastId = dramaIds.last { + self.isMoreLoading = true + let response = await API.loadMoreUpdateDramas(userId: userId, mode: .next, id: lastId, as: [UpdateDramaGroup].self) + if case let .result(groups) = response { + if groups.count > 0 { + preloadGroupImages(groups: groups) + + displayDramaGroups(self.updateDramaGroups, label: "before") + await MainActor.run { + self.updateDramaGroups = appendMergeDramaGroups(groups: self.updateDramaGroups, mergeGroups: groups) + self.fixedDramaGroup = self.updateDramaGroups.first + + self.dramaGroupElements = transformUpdateDramaGroups(groups: self.updateDramaGroups) + } + + displayDramaGroups(self.updateDramaGroups, label: "after") + } + } + + self.isMoreLoading = false + } + } + + func loadPrevUpdateDramasTask(userId: String, callback: (GroupElement?) -> Void) async { + guard !self.isMoreLoading else { + return + } - switch mode { - 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 { - preloadGroupImages(groups: groups) + let anchorGroupElement = dramaGroupElements.first + + // 按照id来判断不一定正确,需要借助其他值 + let dramaIds = self.getDramaIds(self.updateDramaGroups) + // 查找最小的id + if let firstId = dramaIds.first { + self.isMoreLoading = true + let response = await API.loadMoreUpdateDramas(userId: userId, mode: .prev, id: firstId, as: [UpdateDramaGroup].self) + if case let .result(groups) = response { + if groups.count > 0 { + preloadGroupImages(groups: groups) + + displayDramaGroups(self.updateDramaGroups, label: "before") + await MainActor.run { + self.updateDramaGroups = preappendMergeDramaGroups(groups: self.updateDramaGroups, mergeGroups: groups) + self.fixedDramaGroup = self.updateDramaGroups.first - displayDramaGroups(self.updateDramaGroups, label: "before") - await MainActor.run { - self.updateDramaGroups = preappendMergeDramaGroups(groups: self.updateDramaGroups, mergeGroups: groups) - self.fixedDramaGroup = self.updateDramaGroups.first - - self.dramaGroupElements = transformUpdateDramaGroups(groups: self.updateDramaGroups) - } - displayDramaGroups(self.updateDramaGroups, label: "after") + self.dramaGroupElements = transformUpdateDramaGroups(groups: self.updateDramaGroups) + // 处理回调 + callback(anchorGroupElement) } + 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 { - preloadGroupImages(groups: groups) - - displayDramaGroups(self.updateDramaGroups, label: "before") - await MainActor.run { - self.updateDramaGroups = appendMergeDramaGroups(groups: self.updateDramaGroups, mergeGroups: groups) - self.fixedDramaGroup = self.updateDramaGroups.first - - self.dramaGroupElements = transformUpdateDramaGroups(groups: self.updateDramaGroups) - } - - displayDramaGroups(self.updateDramaGroups, label: "after") - } - } - - self.isMoreLoading = false } + self.isMoreLoading = false } } @@ -253,13 +298,17 @@ final class IndexModel { var offset: Double = 0 let (spacing, labelHeight, elementHeight) = self.elementHeight + self.dramaGroupMap.removeAll() + var groupElements: [GroupElement] = [] for group in groups { - groupElements.append(.label(group.group_id, group.group_name, offset)) + groupElements.append(GroupElement(id: .group(id: group.group_id), data: .label(group.group_id, group.group_name, offset))) offset += labelHeight + spacing for item in group.items { - groupElements.append(.item(group.group_id, item, offset)) + groupElements.append(GroupElement(id: .item(id: item.id), data: .item(group.group_id, item, offset))) offset += elementHeight + spacing + + self.dramaGroupMap[item.id] = group.group_id } }