From 392f35d357429553fc36172e584d97175271734c Mon Sep 17 00:00:00 2001 From: Yusuke Hosonuma Date: Wed, 12 Aug 2020 19:49:39 +0900 Subject: [PATCH] improve: use cache for thumbnail image --- LifeGameApp.xcodeproj/project.pbxproj | 16 +++++++++++++ LifeGameWidget/LifeGameWidget.swift | 7 ++++-- Shared/Model/Cache/MemoryStorage.swift | 24 ++++++++++++++++++++ iOS/Screen/BoardSelect/BoardSelectCell.swift | 4 ++-- iOS/Shared/BoardThumbnailImage.swift | 21 +++++++++++++++-- 5 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 Shared/Model/Cache/MemoryStorage.swift diff --git a/LifeGameApp.xcodeproj/project.pbxproj b/LifeGameApp.xcodeproj/project.pbxproj index 6d5d584..40db9bc 100644 --- a/LifeGameApp.xcodeproj/project.pbxproj +++ b/LifeGameApp.xcodeproj/project.pbxproj @@ -43,6 +43,9 @@ E5438E4524E2ACD4007A3EB7 /* BoardThumbnailImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5E6EB1524DFF33E0012E911 /* BoardThumbnailImage.swift */; }; E5438E4724E2AD1D007A3EB7 /* BoardPreset.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5FA934B24C07A5500229AAA /* BoardPreset.swift */; }; E5438E4924E2ADE3007A3EB7 /* LifeGame in Frameworks */ = {isa = PBXBuildFile; productRef = E5438E4824E2ADE3007A3EB7 /* LifeGame */; }; + E547C30A24E3F4CF0022DF3A /* MemoryStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E547C30924E3F4CF0022DF3A /* MemoryStorage.swift */; }; + E547C30B24E3F4CF0022DF3A /* MemoryStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E547C30924E3F4CF0022DF3A /* MemoryStorage.swift */; }; + E547C30C24E3F7710022DF3A /* MemoryStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E547C30924E3F4CF0022DF3A /* MemoryStorage.swift */; }; E54904AD24DD5DF0003AE7C7 /* MainGameViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5BA30B724BF05BC002D0564 /* MainGameViewModel.swift */; }; E54904AF24DD5E2D003AE7C7 /* BoardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E54904AE24DD5E2D003AE7C7 /* BoardView.swift */; }; E54904B024DD5E2D003AE7C7 /* BoardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E54904AE24DD5E2D003AE7C7 /* BoardView.swift */; }; @@ -170,6 +173,7 @@ E5438E3924E2AB0B007A3EB7 /* LifeGameWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LifeGameWidget.swift; sourceTree = ""; }; E5438E3B24E2AB0D007A3EB7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; E5438E3D24E2AB0D007A3EB7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + E547C30924E3F4CF0022DF3A /* MemoryStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryStorage.swift; sourceTree = ""; }; E54904AE24DD5E2D003AE7C7 /* BoardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoardView.swift; sourceTree = ""; }; E54B7AF024DB0043005E1B55 /* HeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderView.swift; sourceTree = ""; }; E558A97224DB70D00012E443 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; @@ -322,6 +326,14 @@ path = Shared; sourceTree = ""; }; + E547C30824E3F4AA0022DF3A /* Cache */ = { + isa = PBXGroup; + children = ( + E547C30924E3F4CF0022DF3A /* MemoryStorage.swift */, + ); + path = Cache; + sourceTree = ""; + }; E54904AB24DD5D6F003AE7C7 /* BoardSelect */ = { isa = PBXGroup; children = ( @@ -588,6 +600,7 @@ E5FA934A24C07A3D00229AAA /* Model */ = { isa = PBXGroup; children = ( + E547C30824E3F4AA0022DF3A /* Cache */, E52FE5DB24DC00E100D93764 /* Firestore */, E5FA934B24C07A5500229AAA /* BoardPreset.swift */, E5CE237224C199F200BC5820 /* LifeGameContext.swift */, @@ -931,6 +944,7 @@ E5438E3A24E2AB0B007A3EB7 /* LifeGameWidget.swift in Sources */, E518C89824E3690600B02027 /* Color+.swift in Sources */, E56A422824E2BB6800F9AC24 /* BoardDocument.swift in Sources */, + E547C30C24E3F7710022DF3A /* MemoryStorage.swift in Sources */, E518C89624E368A700B02027 /* UserDefaultGroup.swift in Sources */, E5438E4724E2AD1D007A3EB7 /* BoardPreset.swift in Sources */, E5438E4524E2ACD4007A3EB7 /* BoardThumbnailImage.swift in Sources */, @@ -967,6 +981,7 @@ E507EBEE24CC1106002A6EC4 /* SettingView.swift in Sources */, E54B7AF124DB0043005E1B55 /* HeaderView.swift in Sources */, E5BA30B824BF05BC002D0564 /* MainGameViewModel.swift in Sources */, + E547C30A24E3F4CF0022DF3A /* MemoryStorage.swift in Sources */, E5D9F27324BEBDBD009A526B /* Application.swift in Sources */, E5B0572824CC3465005EE1AA /* CellView.swift in Sources */, E5FA935024C07AE300229AAA /* Collection+.swift in Sources */, @@ -1013,6 +1028,7 @@ E571536A24D51B9F003B4C9E /* UserDefault.swift in Sources */, E5CE237424C199F200BC5820 /* LifeGameContext.swift in Sources */, E55F7AA824DD0D9300DBC175 /* ContentView.swift in Sources */, + E547C30B24E3F4CF0022DF3A /* MemoryStorage.swift in Sources */, E5604AA124CC3ED600EA59C3 /* PreferenceView.swift in Sources */, E573BD8224D053F90096BF75 /* LifeGameCommands.swift in Sources */, E518C89424E3684A00B02027 /* UserDefaultSettingGroup.swift in Sources */, diff --git a/LifeGameWidget/LifeGameWidget.swift b/LifeGameWidget/LifeGameWidget.swift index 734f09c..7c4968e 100644 --- a/LifeGameWidget/LifeGameWidget.swift +++ b/LifeGameWidget/LifeGameWidget.swift @@ -41,7 +41,9 @@ struct Provider: TimelineProvider { // とりあえずランダムでチョイスするだけ let document = documents.randomElement()! - let data = LifeGameData(title: document.title, board: document.makeBoard().extended(by: .die), url: URL(string: "board:///\(document.id!)")!) + let data = LifeGameData(title: document.title, + board: document.makeBoard().extended(by: .die), + url: URL(string: "board:///\(document.id!)")!, cacheKey: document.id!) let entry = LifeGameEntry(date: entryDate, relevance: data) entries.append(entry) } @@ -61,6 +63,7 @@ struct LifeGameData { var title: String var board: Board var url: URL = URL(string: "board:///0")! + var cacheKey: String? } struct LifeGameWidgetEntryView : View { @@ -71,7 +74,7 @@ struct LifeGameWidgetEntryView : View { var body: some View { HStack { VStack(alignment: .leading) { - BoardThumbnailImage(board: entry.relevance.board, cellColor: cellColor) + BoardThumbnailImage(board: entry.relevance.board, cellColor: cellColor, cacheKey: entry.relevance.cacheKey) Text(entry.relevance.title) .font(.system(.footnote, design: .monospaced)) .foregroundColor(.gray) diff --git a/Shared/Model/Cache/MemoryStorage.swift b/Shared/Model/Cache/MemoryStorage.swift new file mode 100644 index 0000000..1ab5f93 --- /dev/null +++ b/Shared/Model/Cache/MemoryStorage.swift @@ -0,0 +1,24 @@ +// +// MemoryStorage.swift +// LifeGameApp +// +// Created by Yusuke Hosonuma on 2020/08/12. +// + +import Foundation +import UIKit + +final class ThumbnailImageCacheStorage { + static var shared = ThumbnailImageCacheStorage() + + private let cache = NSCache() + + func store(key: String, image: UIImage) { + cache.setObject(image, forKey: key as NSString) + } + + func value(forKey key: String) -> UIImage? { + guard let value = cache.object(forKey: key as NSString) else { return nil } + return value + } +} diff --git a/iOS/Screen/BoardSelect/BoardSelectCell.swift b/iOS/Screen/BoardSelect/BoardSelectCell.swift index 4e6326d..c1a9060 100644 --- a/iOS/Screen/BoardSelect/BoardSelectCell.swift +++ b/iOS/Screen/BoardSelect/BoardSelectCell.swift @@ -18,7 +18,7 @@ struct BoardSelectCell: View { switch style { case .grid: VStack { - BoardThumbnailImage(board: item.makeBoard().extended(by: .die)) + BoardThumbnailImage(board: item.makeBoard().extended(by: .die), cacheKey: item.id) .matchedGeometryEffect(id: "thumbnail-\(item.title)", in: nspace) HStack { @@ -54,7 +54,7 @@ struct BoardSelectCell: View { .foregroundColor(.yellow) } Spacer() - BoardThumbnailImage(board: item.makeBoard().extended(by: .die)) + BoardThumbnailImage(board: item.makeBoard().extended(by: .die), cacheKey: item.id) .frame(width: 60, height: 60, alignment: .center) .matchedGeometryEffect(id: "thumbnail-\(item.title)", in: nspace) }.padding() diff --git a/iOS/Shared/BoardThumbnailImage.swift b/iOS/Shared/BoardThumbnailImage.swift index 6a5a720..c58393b 100644 --- a/iOS/Shared/BoardThumbnailImage.swift +++ b/iOS/Shared/BoardThumbnailImage.swift @@ -13,6 +13,9 @@ struct BoardThumbnailImage: View { var board: Board var cellColor: Color? + var cacheKey: String? + + private let cacheStorage = ThumbnailImageCacheStorage.shared var body: some View { Image(uiImage: thumbnailImage) @@ -38,7 +41,21 @@ struct BoardThumbnailImage: View { } private var thumbnailImage: UIImage { - let scale = max(2, 200 / board.size) + guard let key = cacheKey else { + return renderImage() + } + + if let image = cacheStorage.value(forKey: key) { + return image + } else { + let image = renderImage() + cacheStorage.store(key: key, image: image) + return image + } + } + + private func renderImage() -> UIImage { + let scale = max(2, 140 / board.size) let size = CGSize(width: board.size * scale + 1, height: board.size * scale + 1) return UIGraphicsImageRenderer(size: size) @@ -70,7 +87,7 @@ struct BoardThumnailImage_Previews: PreviewProvider { view(preset: .nebura, colorScheme: .light) view(preset: .spaceShip, colorScheme: .dark) } - + static func view(preset: BoardPreset, colorScheme: ColorScheme) -> some View { BoardThumbnailImage(board: preset.board.board) .previewLayout(.fixed(width: 200.0, height: 200.0))