diff --git a/dimensionhub/Views/Detail/DetailModel.swift b/dimensionhub/Views/Detail/DetailModel.swift index 41f1b48..bb7d4d3 100644 --- a/dimensionhub/Views/Detail/DetailModel.swift +++ b/dimensionhub/Views/Detail/DetailModel.swift @@ -95,19 +95,21 @@ final class DetailModel { print(code) print(message) case .result(let detail): + preloadImages(channels: detail.channels) + await MainActor.run { self.name = detail.name self.summary = Utils.converHtmlToString(html: detail.summary) ?? "" self.thumb = detail.thumb - self.statuses = detail.status.flatMap({ s in + self.statuses = detail.status.flatMap { s in if let status = DramaStatus(s) { return [status] } else { return [] } - }) - self.channels = detail.channels + } + self.channels = detail.channels self.selectedChannelIdx = 0 self.selectedEpisodes = detail.channels[0].episodes } @@ -136,4 +138,15 @@ final class DetailModel { } } + // 预加载图片信息 + private func preloadImages(channels: [Channel]) { + let cacheManager = CacheManager.shared + channels.forEach { channel in + let urls = channel.episodes.map { $0.thumb } + Task.detached { + try? await cacheManager.preloadImages(urls: urls) + } + } + } + } diff --git a/dimensionhub/Views/Detail/DetailView.swift b/dimensionhub/Views/Detail/DetailView.swift index f29a80a..c9484fd 100644 --- a/dimensionhub/Views/Detail/DetailView.swift +++ b/dimensionhub/Views/Detail/DetailView.swift @@ -168,39 +168,21 @@ extension DetailView { var body: some View { VStack(alignment: .center) { - - AsyncImage(url: URL(string: episode.thumb)) { phase in - switch phase { - case .empty: - ProgressView() - case .success(let image): - image - .resizable() - .aspectRatio(contentMode: .fill) - .frame(width: 90, height: 70) - .clipped() - default: - Image("ph_img_small") - .resizable() - .aspectRatio(contentMode: .fill) - .clipped() + FlexImage(urlString: episode.thumb, width: 90, height: 70, placeholder: "ph_img_small") + .frame(width: 90, height: 70) + .overlay(alignment: .topLeading) { + if !episode.num_name.isEmpty { + Text(episode.num_name) + .font(.system(size: 12)) + .foregroundColor(.white) + .padding(3) + .background(Color.black.opacity(0.5)) + .cornerRadius(3) + .padding(3) + } else { + EmptyView() + } } - - } - .frame(width: 90, height: 70) - .overlay(alignment: .topLeading) { - if !episode.num_name.isEmpty { - Text(episode.num_name) - .font(.system(size: 12)) - .foregroundColor(.white) - .padding(3) - .background(Color.black.opacity(0.5)) - .cornerRadius(3) - .padding(3) - } else { - EmptyView() - } - } Text(episode.name) .font(.system(size: 12)) diff --git a/dimensionhub/Views/FollowList/FollowListModel.swift b/dimensionhub/Views/FollowList/FollowListModel.swift index 6bc9da7..4349215 100644 --- a/dimensionhub/Views/FollowList/FollowListModel.swift +++ b/dimensionhub/Views/FollowList/FollowListModel.swift @@ -45,9 +45,23 @@ final class FollowListModel { case .error(let code, let message): print("index load data get error_code: \(code), message: \(message)") case .result(let result): + self.preloadImages(dramas: result.dramas) await MainActor.run { self.dramas = result.dramas } } } + + private func preloadImages(dramas: [DramaItem]) { + let cacheManager = CacheManager.shared + dramas.forEach { dramaItem in + let urls = dramaItem.episodes.map { $0.thumb } + if urls.count > 0 { + Task.detached(priority: .medium) { + try? await cacheManager.preloadImages(urls: urls) + } + } + } + } + } diff --git a/dimensionhub/Views/FollowList/FollowListView.swift b/dimensionhub/Views/FollowList/FollowListView.swift index ce054d8..8a7ea6c 100644 --- a/dimensionhub/Views/FollowList/FollowListView.swift +++ b/dimensionhub/Views/FollowList/FollowListView.swift @@ -83,44 +83,28 @@ extension FollowListView { var body: some View { VStack(alignment: .center) { GeometryReader { geometry in + let width = geometry.frame(in: .local).width - AsyncImage(url: URL(string: item.thumb)) { phase in - switch phase { - case .empty: - ProgressView() - case .success(let image): - image - .resizable() - .aspectRatio(contentMode: .fill) - .frame(width: geometry.frame(in: .local).width, height: 80) - .clipped() - default: - Image("ph_img_medium") - .resizable() - .aspectRatio(contentMode: .fill) - .frame(width: geometry.frame(in: .local).width, height: 80) - .clipped() - } - } - .frame(width: geometry.frame(in: .local).width, height: 80) - .overlay(alignment: .topLeading) { - if !item.num_name.isEmpty { - HStack(alignment: .center) { - Text(item.num_name) - .font(.system(size: 12)) - .foregroundColor(.white) - .lineLimit(1) + FlexImage(urlString: item.thumb, width: width, height: 80, placeholder: "ph_img_medium") + .frame(width: width, height: 80) + .overlay(alignment: .topLeading) { + if !item.num_name.isEmpty { + HStack(alignment: .center) { + Text(item.num_name) + .font(.system(size: 12)) + .foregroundColor(.white) + .lineLimit(1) + } + .padding(3) + .background( + Color.black.opacity(0.6) + ) + .cornerRadius(3) + .padding(3) + } else { + EmptyView() } - .padding(3) - .background( - Color.black.opacity(0.6) - ) - .cornerRadius(3) - .padding(3) - } else { - EmptyView() } - } } Text(item.name) diff --git a/dimensionhub/Views/List/ListModel.swift b/dimensionhub/Views/List/ListModel.swift index 857ed4c..5b7a0f3 100644 --- a/dimensionhub/Views/List/ListModel.swift +++ b/dimensionhub/Views/List/ListModel.swift @@ -52,6 +52,8 @@ final class ListModel { print(code) print(message) case .result(let detail): + self.preloadImages(channels: detail.channels) + await MainActor.run { self.name = detail.name self.summary = Utils.converHtmlToString(html: detail.summary) ?? "" @@ -69,4 +71,14 @@ final class ListModel { self.selectedEpisodes = self.channels[channelIdx].episodes } + private func preloadImages(channels: [Channel]) { + let cacheManager = CacheManager.shared + channels.forEach { channel in + let urls = channel.episodes.map { $0.thumb } + Task.detached { + try? await cacheManager.preloadImages(urls: urls) + } + } + } + } diff --git a/dimensionhub/Views/Search/SearchDramaGroupView.swift b/dimensionhub/Views/Search/SearchDramaGroupView.swift index 7fe29b1..1ac0ad2 100644 --- a/dimensionhub/Views/Search/SearchDramaGroupView.swift +++ b/dimensionhub/Views/Search/SearchDramaGroupView.swift @@ -12,47 +12,30 @@ struct SearchDramaGroupView: View { let group: SearchModel.DramaGroup var body: some View { - VStack(alignment: .center, spacing: 10) { + LazyVStack(alignment: .center, spacing: 10) { ForEach(group.items, id: \.id) { item in NavigationLink(destination: DetailView(id: item.id)) { - AsyncImage(url: URL(string: item.thumb)) { phase in - switch phase { - case .empty: - ProgressView() - case .success(let image): - image - .resizable() - .aspectRatio(contentMode: .fill) - .frame(height: 180) - .clipped() - default: - Image("ph_img_big") - .resizable() - .aspectRatio(contentMode: .fill) - .clipped() + FlexImage(urlString: item.thumb, width: 370, height: 180, placeholder: "ph_img_big") + .frame(width: 370, height: 180) + .overlay(alignment: .topLeading) { + VStack(alignment: .leading, spacing: 8) { + Text(item.name) + .font(.system(size: 16)) + .foregroundColor(.white) + .lineLimit(1) + + Text(item.status) + .font(.system(size: 12)) + .foregroundColor(.white) + .lineLimit(1) + } + .padding(5) + .background( + Color.black.opacity(0.6) + ) + .cornerRadius(5) + .padding(8) } - - } - .frame(width: 370, height: 180) - .overlay(alignment: .topLeading) { - VStack(alignment: .leading, spacing: 8) { - Text(item.name) - .font(.system(size: 16)) - .foregroundColor(.white) - .lineLimit(1) - - Text(item.status) - .font(.system(size: 12)) - .foregroundColor(.white) - .lineLimit(1) - } - .padding(5) - .background( - Color.black.opacity(0.6) - ) - .cornerRadius(5) - .padding(8) - } } } }