diff --git a/PlantUML/AsyncImage+Cache.swift b/PlantUML/AsyncImage+Cache.swift new file mode 100644 index 0000000..e336447 --- /dev/null +++ b/PlantUML/AsyncImage+Cache.swift @@ -0,0 +1,66 @@ +// +// AsyncImage+Cache.swift +// PlantUML4iPad +// +// Created by Bartolomeo Sorrentino on 04/12/22. +// + +import SwiftUI + +struct CachedAsyncImage: View where Content: View { + + private let url: URL + private let scale: CGFloat + private let transaction: Transaction + private let content: (AsyncImagePhase) -> Content + + init( + url: URL?, + scale: CGFloat = 1.0, + @ViewBuilder content: @escaping (AsyncImagePhase) -> Content + ){ + self.url = url! + self.scale = scale + self.transaction = Transaction() + self.content = content + } + + var body: some View { + + if let cached = ImageCache[url] { +// let _ = print("cached: \(url.absoluteString)") + content(.success(cached)) + } else { +// let _ = print("request: \(url.absoluteString)") + AsyncImage( + url: url, + scale: scale, + transaction: transaction, + content: cacheAndRender + ) + } + } + + func cacheAndRender(phase: AsyncImagePhase) -> some View{ + if case .success (let image) = phase { + ImageCache[url] = image + } + return content(phase) + } + +} + +fileprivate class ImageCache{ + + static private var cache: [URL: Image] = [:] + + static subscript(url: URL) -> Image?{ + get{ + ImageCache.cache[url] + } + set{ + ImageCache.cache[url] = newValue + } + } +} + diff --git a/PlantUML/PlantUMLApp.swift b/PlantUML/PlantUMLApp.swift index b585724..c7e63c4 100644 --- a/PlantUML/PlantUMLApp.swift +++ b/PlantUML/PlantUMLApp.swift @@ -9,7 +9,12 @@ import SwiftUI @main struct PlantUMLApp: App { - + + init() { + URLCache.shared.memoryCapacity = 10_000_000 // ~10 MB memory space + URLCache.shared.diskCapacity = 100_000_000 // ~1GB disk cache space + } + var body: some Scene { DocumentGroup(newDocument: PlantUMLDocument()) { file in diff --git a/PlantUML/PlantUMLContentView.swift b/PlantUML/PlantUMLContentView.swift index 4d74d8f..9f034da 100644 --- a/PlantUML/PlantUMLContentView.swift +++ b/PlantUML/PlantUMLContentView.swift @@ -45,13 +45,15 @@ struct PlantUMLContentView: View { showLine: $showLine) } - Divider().background(Color.blue).padding() +// Divider().background(Color.blue).padding() if isDiagramVisible { if isScaleToFit { PlantUMLDiagramView( url: diagram.buildURL() ) + .frame( width: geometry.size.width, height: geometry.size.height ) } else { - PlantUMLScrollableDiagramView( url: diagram.buildURL(), size: geometry.size ) + PlantUMLScrollableDiagramView( url: diagram.buildURL() ) + .frame( width: geometry.size.width, height: geometry.size.height ) } } } diff --git a/PlantUML/PlantUMLDiagramView.swift b/PlantUML/PlantUMLDiagramView.swift index b5933cd..620751e 100644 --- a/PlantUML/PlantUMLDiagramView.swift +++ b/PlantUML/PlantUMLDiagramView.swift @@ -12,6 +12,58 @@ import SwiftUI import WebKit import Combine + +struct PlantUMLScrollableDiagramView : View { + + var url: URL? + + var body: some View { + ScrollView(.horizontal, showsIndicators: true) { + PlantUMLDiagramView( url: url, contentMode: .fill ) + } + } + +} + +struct PlantUMLDiagramView : View { + var url: URL? + var contentMode = ContentMode.fit + + var body: some View { + CachedAsyncImage(url: url, scale: 1 ) { phase in + + if let image = phase.image { + // if the image is valid + image + .resizable() + .aspectRatio(contentMode: contentMode) + } + else if let _ = phase.error { + EmptyView() + } + else { + // showing progress view as placeholder + Image("uml") + .resizable() + .frame( width: 200, height: 150) + ProgressView() + .font(.largeTitle) + } + } + } +} + + +struct PlantUMLDiagramView_Previews: PreviewProvider { + static var previews: some View { + PlantUMLDiagramView( url: URL( string: "https://picsum.photos/id/870/100/150" ) ) + } +} + + +/// +/// OLD IMPLEMENTATION +/// private class PlantUMLDiagramState: ObservableObject { private var updateSubject = PassthroughSubject() @@ -38,22 +90,10 @@ private class PlantUMLDiagramState: ObservableObject { } } -struct PlantUMLScrollableDiagramView : View { - - var url: URL? - var size: CGSize - - var body: some View { - ScrollView(.horizontal, showsIndicators: true) { - PlantUMLDiagramView( url: url ) - .frame( width: size.width, height: size.height ) - } - } - -} - - -struct PlantUMLDiagramView: UIViewRepresentable { +/// +/// OLD IMPLEMENTATION +/// +private struct PlantUMLDiagramView_old: UIViewRepresentable { @StateObject private var state = PlantUMLDiagramState() @@ -79,9 +119,3 @@ struct PlantUMLDiagramView: UIViewRepresentable { } } - -struct PlantUMLPreviw_Previews: PreviewProvider { - static var previews: some View { - PlantUMLDiagramView( url: URL( string: "http://www.soulsoftware.it" )! ) - } -} diff --git a/PlantUML4iPad.xcodeproj/project.pbxproj b/PlantUML4iPad.xcodeproj/project.pbxproj index 9f5f919..4496c1e 100644 --- a/PlantUML4iPad.xcodeproj/project.pbxproj +++ b/PlantUML4iPad.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + A0277C0B293D11D7005435AE /* AsyncImage+Cache.swift in Sources */ = {isa = PBXBuildFile; fileRef = A09A6DDA293D0E5E000856ED /* AsyncImage+Cache.swift */; }; A02F69B228D1E19600090D97 /* PlantUMLFramework in Frameworks */ = {isa = PBXBuildFile; productRef = A02F69B128D1E19600090D97 /* PlantUMLFramework */; }; A043CE7C28E08C4C005A3AF7 /* PlantUMLKeyboard in Frameworks */ = {isa = PBXBuildFile; productRef = A043CE7B28E08C4C005A3AF7 /* PlantUMLKeyboard */; }; A0A42A76289ABC2D00E929EB /* PlantUMLDiagramView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A0A42A75289ABC2D00E929EB /* PlantUMLDiagramView.swift */; }; @@ -46,6 +47,7 @@ /* Begin PBXFileReference section */ A01552A228CF47DF00F2B8A1 /* PlantUMLFramework */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = PlantUMLFramework; sourceTree = ""; }; + A09A6DDA293D0E5E000856ED /* AsyncImage+Cache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AsyncImage+Cache.swift"; sourceTree = ""; }; A0A42A75289ABC2D00E929EB /* PlantUMLDiagramView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlantUMLDiagramView.swift; sourceTree = ""; }; A0A42A78289AC37C00E929EB /* PlantUMLDiagramObject.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlantUMLDiagramObject.swift; sourceTree = ""; }; A0A42A7B289AD9FA00E929EB /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; @@ -128,6 +130,7 @@ isa = PBXGroup; children = ( A0F2B14129353C2D00A44481 /* SwiftUI+Rotate.swift */, + A09A6DDA293D0E5E000856ED /* AsyncImage+Cache.swift */, A0D3C64928984A0E000838D7 /* PlantUMLApp.swift */, A0D3C64B28984A0E000838D7 /* PlantUMLDocument.swift */, A0D3C64D28984A0E000838D7 /* PlantUMLContentView.swift */, @@ -306,6 +309,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + A0277C0B293D11D7005435AE /* AsyncImage+Cache.swift in Sources */, A0A42A79289AC37C00E929EB /* PlantUMLDiagramObject.swift in Sources */, A0D3C64A28984A0E000838D7 /* PlantUMLApp.swift in Sources */, A0D3C64C28984A0E000838D7 /* PlantUMLDocument.swift in Sources */,