From 5e49672b2466226ad974260e260593c4f6be3160 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=88=B4=E9=93=AD?= Date: Wed, 3 Apr 2024 23:26:00 +0800 Subject: [PATCH] use nuke for image --- SwiftPamphletApp.xcodeproj/project.pbxproj | 41 +++++++++++++++++++ .../GitHubAPI/DetailView/IssueView.swift | 4 +- .../GitHubAPI/DetailView/RepoView.swift | 10 ++--- .../GitHubAPI/DetailView/UserView.swift | 5 +-- .../Developer/DeveloperListView.swift | 2 +- .../GitHubAPI/Developer/EditDeveloper.swift | 4 +- .../InfoOrganizer/Info/EditInfoView.swift | 20 +-------- .../InfoOrganizer/Info/InfoRowView.swift | 12 +----- .../ViewComponet/ViewComponet.swift | 32 +++++++++++++++ 9 files changed, 88 insertions(+), 42 deletions(-) diff --git a/SwiftPamphletApp.xcodeproj/project.pbxproj b/SwiftPamphletApp.xcodeproj/project.pbxproj index 057effbf4..d926a277f 100644 --- a/SwiftPamphletApp.xcodeproj/project.pbxproj +++ b/SwiftPamphletApp.xcodeproj/project.pbxproj @@ -264,6 +264,10 @@ 08D107BD278826BB007B7009 /* HTMLEntities in Frameworks */ = {isa = PBXBuildFile; productRef = 08D107BC278826BB007B7009 /* HTMLEntities */; }; 08ED80162B9C54DE0069B7EC /* SMNetwork in Frameworks */ = {isa = PBXBuildFile; productRef = 08ED80152B9C54DE0069B7EC /* SMNetwork */; }; 08ED801C2B9D1EEC0069B7EC /* SettingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08ED801B2B9D1EEC0069B7EC /* SettingView.swift */; }; + 08F14B3C2BBDA3EA005B46CC /* Nuke in Frameworks */ = {isa = PBXBuildFile; productRef = 08F14B3B2BBDA3EA005B46CC /* Nuke */; }; + 08F14B3E2BBDA3EA005B46CC /* NukeExtensions in Frameworks */ = {isa = PBXBuildFile; productRef = 08F14B3D2BBDA3EA005B46CC /* NukeExtensions */; }; + 08F14B402BBDA3EA005B46CC /* NukeUI in Frameworks */ = {isa = PBXBuildFile; productRef = 08F14B3F2BBDA3EA005B46CC /* NukeUI */; }; + 08F14B422BBDA3EA005B46CC /* NukeVideo in Frameworks */ = {isa = PBXBuildFile; productRef = 08F14B412BBDA3EA005B46CC /* NukeVideo */; }; 08F1AB612BA0821900AEA0CA /* CodeEditor in Frameworks */ = {isa = PBXBuildFile; productRef = 08F1AB602BA0821900AEA0CA /* CodeEditor */; }; 08F4BE6028609D8700733F12 /* PlayCharts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08F4BE5F28609D8600733F12 /* PlayCharts.swift */; }; 08F4BE6228616DC100733F12 /* PlayWeatherKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08F4BE6128616DC100733F12 /* PlayWeatherKit.swift */; }; @@ -544,12 +548,16 @@ 08448F092796A83D00B61353 /* SwiftDate in Frameworks */, 08BF26D02768A59A0064DDAC /* SQLite in Frameworks */, 08448F4E279E8CA400B61353 /* Ink in Frameworks */, + 08F14B402BBDA3EA005B46CC /* NukeUI in Frameworks */, + 08F14B422BBDA3EA005B46CC /* NukeVideo in Frameworks */, + 08F14B3E2BBDA3EA005B46CC /* NukeExtensions in Frameworks */, 08B9D5312BA3E08300CACCBE /* SwiftHTMLtoMarkdown in Frameworks */, 08BF26D32768A5B40064DDAC /* MarkdownUI in Frameworks */, 08F1AB612BA0821900AEA0CA /* CodeEditor in Frameworks */, 08397E322B9F39C100DFDD02 /* SwiftSoup in Frameworks */, 08ED80162B9C54DE0069B7EC /* SMNetwork in Frameworks */, 08D107BD278826BB007B7009 /* HTMLEntities in Frameworks */, + 08F14B3C2BBDA3EA005B46CC /* Nuke in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1432,6 +1440,10 @@ 08397E312B9F39C100DFDD02 /* SwiftSoup */, 08F1AB602BA0821900AEA0CA /* CodeEditor */, 08B9D5302BA3E08300CACCBE /* SwiftHTMLtoMarkdown */, + 08F14B3B2BBDA3EA005B46CC /* Nuke */, + 08F14B3D2BBDA3EA005B46CC /* NukeExtensions */, + 08F14B3F2BBDA3EA005B46CC /* NukeUI */, + 08F14B412BBDA3EA005B46CC /* NukeVideo */, ); productName = SwiftPamphletApp; productReference = 086A5F032744E88E00FECE02 /* 戴铭的开发小册子.app */; @@ -1472,6 +1484,7 @@ 08397E302B9F39C100DFDD02 /* XCRemoteSwiftPackageReference "SwiftSoup" */, 08F1AB5F2BA0821900AEA0CA /* XCRemoteSwiftPackageReference "CodeEditor" */, 08B9D52F2BA3E08300CACCBE /* XCRemoteSwiftPackageReference "SwiftHTMLToMarkdown" */, + 08F14B3A2BBDA3EA005B46CC /* XCRemoteSwiftPackageReference "Nuke" */, ); productRefGroup = 086A5F042744E88E00FECE02 /* Products */; projectDirPath = ""; @@ -2042,6 +2055,14 @@ minimumVersion = 4.0.0; }; }; + 08F14B3A2BBDA3EA005B46CC /* XCRemoteSwiftPackageReference "Nuke" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/kean/Nuke.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 12.5.0; + }; + }; 08F1AB5F2BA0821900AEA0CA /* XCRemoteSwiftPackageReference "CodeEditor" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/ZeeZide/CodeEditor.git"; @@ -2092,6 +2113,26 @@ isa = XCSwiftPackageProductDependency; productName = SMNetwork; }; + 08F14B3B2BBDA3EA005B46CC /* Nuke */ = { + isa = XCSwiftPackageProductDependency; + package = 08F14B3A2BBDA3EA005B46CC /* XCRemoteSwiftPackageReference "Nuke" */; + productName = Nuke; + }; + 08F14B3D2BBDA3EA005B46CC /* NukeExtensions */ = { + isa = XCSwiftPackageProductDependency; + package = 08F14B3A2BBDA3EA005B46CC /* XCRemoteSwiftPackageReference "Nuke" */; + productName = NukeExtensions; + }; + 08F14B3F2BBDA3EA005B46CC /* NukeUI */ = { + isa = XCSwiftPackageProductDependency; + package = 08F14B3A2BBDA3EA005B46CC /* XCRemoteSwiftPackageReference "Nuke" */; + productName = NukeUI; + }; + 08F14B412BBDA3EA005B46CC /* NukeVideo */ = { + isa = XCSwiftPackageProductDependency; + package = 08F14B3A2BBDA3EA005B46CC /* XCRemoteSwiftPackageReference "Nuke" */; + productName = NukeVideo; + }; 08F1AB602BA0821900AEA0CA /* CodeEditor */ = { isa = XCSwiftPackageProductDependency; package = 08F1AB5F2BA0821900AEA0CA /* XCRemoteSwiftPackageReference "CodeEditor" */; diff --git a/SwiftPamphletApp/GitHubAPI/DetailView/IssueView.swift b/SwiftPamphletApp/GitHubAPI/DetailView/IssueView.swift index d6f40039a..17f8b0d08 100644 --- a/SwiftPamphletApp/GitHubAPI/DetailView/IssueView.swift +++ b/SwiftPamphletApp/GitHubAPI/DetailView/IssueView.swift @@ -33,7 +33,7 @@ struct IssueView: View { Text(" \(howLongFromNow(timeStr:vm.issue.updatedAt))更新过").font(.footnote) } else { HStack { - AsyncImageWithPlaceholder(size: .smallSize, url: vm.issue.user.avatarUrl) + NukeImage(width: 40, height:40, url: vm.issue.user.avatarUrl) VStack(alignment:.leading) { NavigationLink(destination: UserView(vm: UserVM(userName: vm.issue.user.login)), label: { Text(vm.issue.user.login) @@ -63,7 +63,7 @@ struct IssueView: View { VStack(alignment: .leading) { GitHubApiTimeView(timeStr: comment.updatedAt) HStack { - AsyncImageWithPlaceholder(size: .smallSize, url: comment.user.avatarUrl) + NukeImage(width: 40, height: 40, url: comment.user.avatarUrl) ButtonGoGitHubWeb(url: comment.user.login, text: comment.user.login, ignoreHost: true) Text(comment.authorAssociation) diff --git a/SwiftPamphletApp/GitHubAPI/DetailView/RepoView.swift b/SwiftPamphletApp/GitHubAPI/DetailView/RepoView.swift index a596e033d..c71c8fb76 100644 --- a/SwiftPamphletApp/GitHubAPI/DetailView/RepoView.swift +++ b/SwiftPamphletApp/GitHubAPI/DetailView/RepoView.swift @@ -54,7 +54,7 @@ struct RepoView: View { HStack { Text("作者:") - AsyncImageWithPlaceholder(size: .smallSize, url: vm.repo.owner.avatarUrl) + NukeImage(width: 40, height: 40, url: vm.repo.owner.avatarUrl) ButtonGoGitHubWeb(url: vm.repo.owner.login, text: vm.repo.owner.login, ignoreHost: true) } } // end VStack @@ -218,7 +218,7 @@ struct RepoCommitLabelView: View { Image(systemName: "envelope.badge.fill") } if commit.author != nil { - AsyncImageWithPlaceholder(size: .tinySize, url: commit.author?.avatarUrl ?? "") + NukeImage(width: 20, height: 20, url: commit.author?.avatarUrl ?? "") ButtonGoGitHubWeb(url: commit.author?.login ?? "", text: commit.author?.login ?? "", ignoreHost: true, bold: true) } else { @@ -244,7 +244,7 @@ struct IssueLabelView: View { .font(.footnote) } HStack { - AsyncImageWithPlaceholder(size: .tinySize, url: issue.user.avatarUrl) + NukeImage(width: 20, height: 20, url: issue.user.avatarUrl) ButtonGoGitHubWeb(url: issue.user.login, text: issue.user.login, ignoreHost: true) } MarkdownView(s: issue.body ?? "") @@ -258,7 +258,7 @@ struct IssueEventLabelView: View { VStack(alignment: .leading, spacing: 5) { GitHubApiTimeView(timeStr: issueEvent.createdAt) HStack { - AsyncImageWithPlaceholder(size: .tinySize, url: issueEvent.actor.avatarUrl) + NukeImage(width: 20, height: 20, url: issueEvent.actor.avatarUrl) ButtonGoGitHubWeb(url: issueEvent.actor.login, text: issueEvent.actor.login, ignoreHost: true) Text(issueEvent.event) .foregroundColor(.secondary) @@ -271,7 +271,7 @@ struct IssueEventLabelView: View { .font(.footnote) } HStack { - AsyncImageWithPlaceholder(size: .tinySize, url: issueEvent.issue.user.avatarUrl) + NukeImage(width: 20, height: 20, url: issueEvent.issue.user.avatarUrl) ButtonGoGitHubWeb(url: issueEvent.issue.user.login, text: issueEvent.issue.user.login, ignoreHost: true) } MarkdownView(s: issueEvent.issue.body ?? "") diff --git a/SwiftPamphletApp/GitHubAPI/DetailView/UserView.swift b/SwiftPamphletApp/GitHubAPI/DetailView/UserView.swift index 451e1c788..46f289ea8 100644 --- a/SwiftPamphletApp/GitHubAPI/DetailView/UserView.swift +++ b/SwiftPamphletApp/GitHubAPI/DetailView/UserView.swift @@ -18,7 +18,7 @@ struct UserView: View { HStack { VStack(alignment: .leading, spacing: 10) { HStack { - AsyncImageWithPlaceholder(size: .normalSize, url: vm.user.avatarUrl) + NukeImage(width: 60, height: 60, url: vm.user.avatarUrl) VStack(alignment: .leading, spacing: 5) { HStack { Text(vm.user.name ?? vm.user.login).font(.system(.title)) @@ -177,8 +177,7 @@ struct AUserEventLabel: View { } if isShowActor == true { - AsyncImageWithPlaceholder(size: .tinySize, url: event.actor.avatarUrl) - + NukeImage(width: 20, height: 20, url: event.actor.avatarUrl) Text(event.actor.login).bold() } // end if diff --git a/SwiftPamphletApp/GitHubAPI/Developer/DeveloperListView.swift b/SwiftPamphletApp/GitHubAPI/Developer/DeveloperListView.swift index 99b491dbf..36cdad915 100644 --- a/SwiftPamphletApp/GitHubAPI/Developer/DeveloperListView.swift +++ b/SwiftPamphletApp/GitHubAPI/Developer/DeveloperListView.swift @@ -17,7 +17,7 @@ struct DeveloperListView: View { List(selection: $selectDev) { ForEach(devs) { dev in HStack { - AsyncImageWithPlaceholder(size: .smallSize, url: dev.avatar) + NukeImage(width: 40, height: 40, url: dev.avatar) VStack { HStack { if dev.repoName.isEmpty { diff --git a/SwiftPamphletApp/GitHubAPI/Developer/EditDeveloper.swift b/SwiftPamphletApp/GitHubAPI/Developer/EditDeveloper.swift index 95729707a..1bd0800c5 100644 --- a/SwiftPamphletApp/GitHubAPI/Developer/EditDeveloper.swift +++ b/SwiftPamphletApp/GitHubAPI/Developer/EditDeveloper.swift @@ -71,7 +71,7 @@ struct EditDeveloper: View { HStack { Text("作者:") - AsyncImageWithPlaceholder(size: .smallSize, url: vmRepo.repo.owner.avatarUrl) + NukeImage(width:40, height: 40, url: vmRepo.repo.owner.avatarUrl) ButtonGoGitHubWeb(url: vmRepo.repo.owner.login, text: vmRepo.repo.owner.login, ignoreHost: true) } } // end VStack @@ -142,7 +142,7 @@ struct EditDeveloper: View { HStack { VStack(alignment: .leading, spacing: 10) { HStack { - AsyncImageWithPlaceholder(size: .normalSize, url: vm.user.avatarUrl) + NukeImage(width:60, height: 60, url: vm.user.avatarUrl) VStack(alignment: .leading, spacing: 5) { HStack { Text(vm.user.name ?? vm.user.login).font(.system(.title)) diff --git a/SwiftPamphletApp/InfoOrganizer/Info/EditInfoView.swift b/SwiftPamphletApp/InfoOrganizer/Info/EditInfoView.swift index 8f8e16b50..1eda28a6e 100644 --- a/SwiftPamphletApp/InfoOrganizer/Info/EditInfoView.swift +++ b/SwiftPamphletApp/InfoOrganizer/Info/EditInfoView.swift @@ -188,15 +188,7 @@ struct EditInfoView: View { } } } else if img.url.isEmpty == false { - AsyncImage(url: URL(string: img.url), content: { image in - image - .resizable() - .scaledToFit() - .cornerRadius(5) - }, - placeholder: { - Image(systemName: "photo.fill") - }) + NukeImage(url: img.url) .contextMenu { Button { IOInfo.updateCoverImage(info: info, img: IOImg(url: img.url)) @@ -219,15 +211,7 @@ struct EditInfoView: View { if info.imageUrls.isEmpty == false { LazyVGrid(columns: [GridItem(.adaptive(minimum: 150), spacing: 10)]) { ForEach(info.imageUrls, id:\.self) { img in - AsyncImage(url: URL(string: img), content: { image in - image - .resizable() - .aspectRatio(contentMode: .fit) - .cornerRadius(5) - }, - placeholder: { - Image(systemName: "photo.fill") - }) + NukeImage(url: img) .contextMenu { Button { IOInfo.updateCoverImage(info: info, img: IOImg(url: img)) diff --git a/SwiftPamphletApp/InfoOrganizer/Info/InfoRowView.swift b/SwiftPamphletApp/InfoOrganizer/Info/InfoRowView.swift index ed3266467..5f7989e44 100644 --- a/SwiftPamphletApp/InfoOrganizer/Info/InfoRowView.swift +++ b/SwiftPamphletApp/InfoOrganizer/Info/InfoRowView.swift @@ -23,17 +23,7 @@ struct InfoRowView: View { HStack(alignment:.top) { if let coverImg = info.coverImage { if coverImg.url.isEmpty == false { - AsyncImage(url: URL(string: coverImg.url), content: { image in - image - .resizable() - .scaledToFill() - .frame(width: 60, height: 60) - .cornerRadius(5) - }, - placeholder: { - Image(systemName: "photo.fill") - .frame(width: 60, height: 60) - }) + NukeImage(width: 60, height: 60, url: coverImg.url, contentModel: .fill) } else if let imgData = coverImg.imgData { if let nsImage = NSImage(data: imgData) { Image(nsImage: nsImage) diff --git a/SwiftPamphletApp/ViewComponet/ViewComponet.swift b/SwiftPamphletApp/ViewComponet/ViewComponet.swift index 0e9a196d2..1d801ffb8 100644 --- a/SwiftPamphletApp/ViewComponet/ViewComponet.swift +++ b/SwiftPamphletApp/ViewComponet/ViewComponet.swift @@ -8,6 +8,8 @@ import SwiftUI import WebKit import MarkdownUI +import Nuke +import NukeUI // MARK: - 大纲 struct SPOutlineListView: View where D: RandomAccessCollection, D.Element: Identifiable, Content: View { @@ -294,6 +296,36 @@ struct FixAwfulPerformanceStyle: ButtonStyle { } // MARK: - 图片 +struct NukeImage: View { + private let pipeline = ImagePipeline { + $0.dataLoader = { + let config = DataLoader.defaultConfiguration + config.urlCache = DataLoader.sharedUrlCache + return DataLoader(configuration: config) + }() + } + var width: CGFloat? = nil + var height: CGFloat? = nil + var url: String + var contentModel: ContentMode = .fit + var body: some View { + LazyImage(url: URL(string: url)) { state in + if let image = state.image { + image + .resizable() + .aspectRatio(contentMode: contentModel) + .frame(width: width, height: height) + .cornerRadius(5) + } else { + Color.gray.opacity(0.2) // Placeholder + .frame(width: width, height: height) + } + } + .pipeline(pipeline) + + } +} + struct AsyncImageWithPlaceholder: View { enum Size { case tinySize, smallSize,normalSize, bigSize