diff --git a/Keychy/Keychy.xcodeproj/project.pbxproj b/Keychy/Keychy.xcodeproj/project.pbxproj index 47aa290b..76f1417f 100644 --- a/Keychy/Keychy.xcodeproj/project.pbxproj +++ b/Keychy/Keychy.xcodeproj/project.pbxproj @@ -277,6 +277,7 @@ 4C6622462EAF7D63001760B5 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 4C6622452EAF7D63001760B5 /* LICENSE */; }; 4C6622482EAF9B3A001760B5 /* Haptic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C6622472EAF9B3A001760B5 /* Haptic.swift */; }; 4C7775322EB0EEF100981C3E /* ItemDetailImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C7775312EB0EEF100981C3E /* ItemDetailImage.swift */; }; + AA0213F12F0E000000000001 /* TemplateImageSlideshow.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA0213F02F0E000000000001 /* TemplateImageSlideshow.swift */; }; 4C7775342EB0EF8800981C3E /* ItemDetailInfoSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C7775332EB0EF8800981C3E /* ItemDetailInfoSection.swift */; }; 4C7775382EB0EFD800981C3E /* ItemDetailMakingBtn.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C7775372EB0EFD800981C3E /* ItemDetailMakingBtn.swift */; }; 4C77753E2EB1343600981C3E /* IntroViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C7775392EB1343600981C3E /* IntroViewModel.swift */; }; @@ -758,6 +759,7 @@ 4C6622452EAF7D63001760B5 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 4C6622472EAF9B3A001760B5 /* Haptic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Haptic.swift; sourceTree = ""; }; 4C7775312EB0EEF100981C3E /* ItemDetailImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemDetailImage.swift; sourceTree = ""; }; + AA0213F02F0E000000000001 /* TemplateImageSlideshow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateImageSlideshow.swift; sourceTree = ""; }; 4C7775332EB0EF8800981C3E /* ItemDetailInfoSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemDetailInfoSection.swift; sourceTree = ""; }; 4C7775372EB0EFD800981C3E /* ItemDetailMakingBtn.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemDetailMakingBtn.swift; sourceTree = ""; }; 4C7775392EB1343600981C3E /* IntroViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntroViewModel.swift; sourceTree = ""; }; @@ -1806,6 +1808,7 @@ 4C7775312EB0EEF100981C3E /* ItemDetailImage.swift */, 4C7775332EB0EF8800981C3E /* ItemDetailInfoSection.swift */, 4C7775372EB0EFD800981C3E /* ItemDetailMakingBtn.swift */, + AA0213F02F0E000000000001 /* TemplateImageSlideshow.swift */, ); path = Items; sourceTree = ""; @@ -2928,6 +2931,7 @@ AAEB46AD2EC1C893002B13E5 /* BundleMenu.swift in Sources */, 4CF2A96D2F0F969300BA9FDA /* UpdateAlert.swift in Sources */, 4C7775322EB0EEF100981C3E /* ItemDetailImage.swift in Sources */, + AA0213F12F0E000000000001 /* TemplateImageSlideshow.swift in Sources */, AA2146B12F15D43C0048D40E /* BundleEditView+Alert.swift in Sources */, 4C07024C2ECF10760026D6DC /* EffectSyncManager.swift in Sources */, 4C25259C2F303745003CC5AD /* WidgetBundleModel.swift in Sources */, diff --git a/Keychy/Keychy/CommonModels/Template/KeyringTemplate.swift b/Keychy/Keychy/CommonModels/Template/KeyringTemplate.swift index d9e3e4fd..1bceaf0c 100644 --- a/Keychy/Keychy/CommonModels/Template/KeyringTemplate.swift +++ b/Keychy/Keychy/CommonModels/Template/KeyringTemplate.swift @@ -11,7 +11,7 @@ import FirebaseFirestore /// Firebase Firestore에서 가져오는 키링 템플릿 모델 /// - Collection: templates/{templateId} /// - 사용자별 구매/소유 상태는 별도로 관리 (users/{userId}/purchasedTemplates) -struct KeyringTemplate: Identifiable, Codable, Equatable, Hashable { +struct KeyringTemplate: Identifiable, Equatable, Hashable { /// Document ID @DocumentID var id: String? @@ -31,6 +31,11 @@ struct KeyringTemplate: Identifiable, Codable, Equatable, Hashable { /// 프리뷰 URL -----> 만들기 preview에 띄울 이미지 let previewURL: String + /// 프리뷰 슬라이드 이미지 URL 배열 (최대 5장) + /// - Firestore에 [String] 배열로 저장 + /// - 비어있으면 기존 thumbnailURL/previewURL로 fallback + let previewImages: [String] + /// 가이드 이미지 URL -----> 만들기 시작 전 가이드 화면에 띄울 이미지 let guidingImageURL: String @@ -60,7 +65,7 @@ struct KeyringTemplate: Identifiable, Codable, Equatable, Hashable { /// - 양수: 바디 중심에서 위로 이동 (구멍이 더 위에 있음) /// - 음수: 바디 중심에서 아래로 이동 (구멍이 더 아래에 있음) let hookOffsetY: CGFloat? - + let chainLength: Int? /// 무료 템플릿 여부 @@ -69,6 +74,40 @@ struct KeyringTemplate: Identifiable, Codable, Equatable, Hashable { } } +// MARK: - Codable (previewImages fallback 처리) +extension KeyringTemplate: Codable { + enum CodingKeys: String, CodingKey { + case id, templateName, description, interactions + case thumbnailURL, previewURL, previewImages + case guidingImageURL, guidingText, tags, price + case downloadCount, useCount, createdAt, isActive + case hookOffsetY, chainLength + } + + /// Firestore에 previewImages 필드가 없는 기존 문서도 안전하게 디코딩 + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + _id = try container.decode(DocumentID.self, forKey: .id) + templateName = try container.decode(String.self, forKey: .templateName) + description = try container.decode(String.self, forKey: .description) + interactions = try container.decode([String].self, forKey: .interactions) + thumbnailURL = try container.decode(String.self, forKey: .thumbnailURL) + previewURL = try container.decode(String.self, forKey: .previewURL) + // 기존 문서에 필드 없으면 빈 배열로 fallback + previewImages = (try? container.decode([String].self, forKey: .previewImages)) ?? [] + guidingImageURL = try container.decode(String.self, forKey: .guidingImageURL) + guidingText = try container.decode(String.self, forKey: .guidingText) + tags = try container.decode([String].self, forKey: .tags) + price = try container.decodeIfPresent(Int.self, forKey: .price) + downloadCount = try container.decode(Int.self, forKey: .downloadCount) + useCount = try container.decode(Int.self, forKey: .useCount) + createdAt = try container.decode(Date.self, forKey: .createdAt) + isActive = try container.decode(Bool.self, forKey: .isActive) + hookOffsetY = try container.decodeIfPresent(CGFloat.self, forKey: .hookOffsetY) + chainLength = try container.decodeIfPresent(Int.self, forKey: .chainLength) + } +} + // MARK: - Preview용 Mock Data extension KeyringTemplate { /// Firestore의 AcrylicPhoto 템플릿 데이터 @@ -79,6 +118,7 @@ extension KeyringTemplate { interactions: ["tap", "swing"], thumbnailURL: "", previewURL: "https://firebasestorage.googleapis.com/v0/b/keychy-f6011.firebasestorage.app/o/Templates%2FacrylicPhoto%2FacrylicPreview.png?alt=media&token=cc1e53cf-9de2-4a32-a50f-f02339999f24", + previewImages: [], guidingImageURL: "https://firebasestorage.googleapis.com/v0/b/keychy-f6011.firebasestorage.app/o/Templates%2FacrylicPhoto%2FguidingImage.png?alt=media&token=example", guidingText: "인물 사진을 선택해주세요\n배경이 제거된 키링을 만들 수 있습니다", tags: ["이미지형"], diff --git a/Keychy/Keychy/Core/Components/View/Items/TemplateImageSlideshow.swift b/Keychy/Keychy/Core/Components/View/Items/TemplateImageSlideshow.swift new file mode 100644 index 00000000..e47e857c --- /dev/null +++ b/Keychy/Keychy/Core/Components/View/Items/TemplateImageSlideshow.swift @@ -0,0 +1,107 @@ +// +// TemplateImageSlideshow.swift +// Keychy +// +// Created by 길지훈 on 2026/02/13. +// + +import SwiftUI +import NukeUI +import Nuke + +/// 템플릿 프리뷰 이미지를 1초 간격으로 자동 순환하는 슬라이드 컴포넌트 +/// +/// - `localFirstImageName`이 있으면 번들 이미지를 즉시 표시 + 나머지만 네트워크 다운로드 +/// - 번들 이미지가 없으면 전체를 네트워크에서 병렬 다운로드 +/// - `.task` 기반 async 루프로 뷰 lifecycle에 바인딩 (사라지면 자동 cancel) +struct TemplateImageSlideshow: View { + let imageURLs: [String] + /// 앱 번들에 포함된 첫 번째 프리뷰 이미지 이름 (ex. "preview_AcrylicPhoto") + var localFirstImageName: String? = nil + + @State private var currentIndex = 0 + @State private var loadedImages: [UIImage] = [] + + /// 번들에서 찾은 첫 번째 이미지 + private var bundleFirstImage: UIImage? { + guard let name = localFirstImageName else { return nil } + return UIImage(named: name) + } + + var body: some View { + Group { + if !loadedImages.isEmpty { + Image(uiImage: loadedImages[currentIndex]) + .resizable() + .aspectRatio(contentMode: .fit) + } else if let bundleImage = bundleFirstImage { + // 번들 이미지 즉시 표시 (네트워크 로딩 중) + Image(uiImage: bundleImage) + .resizable() + .aspectRatio(contentMode: .fit) + } else if let firstURL = imageURLs.first, let url = URL(string: firstURL) { + // 번들 없음 → 첫 번째 URL을 LazyImage로 표시 + LazyImage(url: url) { state in + if let image = state.image { + image + .resizable() + .aspectRatio(contentMode: .fit) + } else { + Color.gray50 + } + } + } else { + Color.gray50 + } + } + .task { + await preloadAllImages() + + guard loadedImages.count > 1 else { return } + while !Task.isCancelled { + try? await Task.sleep(for: .seconds(1)) + guard !Task.isCancelled else { return } + currentIndex = (currentIndex + 1) % loadedImages.count + } + } + } + + /// 이미지를 UIImage 배열로 프리로드 + /// - 번들 이미지가 있으면: [번들이미지] + 네트워크[1...] (0번 스킵) + /// - 번들 이미지가 없으면: 네트워크[0...] 전체 다운로드 + private func preloadAllImages() async { + guard loadedImages.isEmpty else { return } + + let pipeline = ImagePipeline.shared + let hasBundleFirst = bundleFirstImage != nil + + // 번들 이미지가 있으면 1번부터, 없으면 0번부터 다운로드 + let urlsToDownload = hasBundleFirst + ? Array(imageURLs.dropFirst()) + : imageURLs + + let downloaded = await withTaskGroup(of: (Int, UIImage?).self) { group in + for (index, urlString) in urlsToDownload.enumerated() { + guard let url = URL(string: urlString) else { continue } + group.addTask { + let image = try? await pipeline.image(for: url) + return (index, image) + } + } + + var indexed: [(Int, UIImage)] = [] + for await (index, image) in group { + if let image { indexed.append((index, image)) } + } + return indexed.sorted { $0.0 < $1.0 }.map(\.1) + } + + // 번들 이미지를 0번에 넣고 나머지 이어붙이기 + if hasBundleFirst, let first = bundleFirstImage { + loadedImages = [first] + downloaded + } else { + guard !downloaded.isEmpty else { return } + loadedImages = downloaded + } + } +} diff --git a/Keychy/Keychy/Core/Keyring/Scene/KeyringScale.swift b/Keychy/Keychy/Core/Keyring/Scene/KeyringScale.swift index 2ce47c34..8881ddee 100644 --- a/Keychy/Keychy/Core/Keyring/Scene/KeyringScale.swift +++ b/Keychy/Keychy/Core/Keyring/Scene/KeyringScale.swift @@ -45,8 +45,11 @@ enum KeyringScale { // MARK: - 카라비너별 뭉치 키링 스케일 private static let carabinerScales: [String: CGFloat] = [ - "basic": 0.65 - // TODO: 추가 카라비너 스케일 + "CarouselOrgel": 0.5, + "HeartPepero": 0.6, + "HeartPlanet": 0.6, + "MeltingWhiteChoco": 0.7, + "UfoCat": 0.5, ] // MARK: - 기본값 diff --git a/Keychy/Keychy/Presentation/KeyringMaker/Shared/Components/TemplatePreviewComponents.swift b/Keychy/Keychy/Presentation/KeyringMaker/Shared/Components/TemplatePreviewComponents.swift index 81c44f8f..a1d358f1 100644 --- a/Keychy/Keychy/Presentation/KeyringMaker/Shared/Components/TemplatePreviewComponents.swift +++ b/Keychy/Keychy/Presentation/KeyringMaker/Shared/Components/TemplatePreviewComponents.swift @@ -181,11 +181,22 @@ extension TemplatePreviewBody { private var templatePreview: some View { VStack { Spacer() - + if let template { - ItemDetailImage(itemURL: template.previewURL) - .scaledToFit() - .frame(width: 386, height: 386) + if template.previewImages.count > 1 { + // 슬라이드 이미지 + TemplateImageSlideshow( + imageURLs: template.previewImages, + localFirstImageName: "preview_\(template.id ?? "")" + ) + .scaledToFit() + .frame(width: 386, height: 386) + } else { + // fallback: 기존 단일 프리뷰 이미지 + ItemDetailImage(itemURL: template.previewURL) + .scaledToFit() + .frame(width: 386, height: 386) + } } else { LoadingAlert(type: .short40, message: nil) } diff --git a/Keychy/Keychy/Presentation/Workshop/Views/Components/WorkshopItemCard.swift b/Keychy/Keychy/Presentation/Workshop/Views/Components/WorkshopItemCard.swift index 5120659f..77103a6e 100644 --- a/Keychy/Keychy/Presentation/Workshop/Views/Components/WorkshopItemCard.swift +++ b/Keychy/Keychy/Presentation/Workshop/Views/Components/WorkshopItemCard.swift @@ -66,6 +66,15 @@ struct WorkshopItemCard: View { .padding(.horizontal, 5) .frame(width: twoGridCellWidth, height: itemHeight) .clipped() + } else if let template = item as? KeyringTemplate, template.previewImages.count > 1 { + // 슬라이드 이미지가 있는 키링 템플릿 + TemplateImageSlideshow( + imageURLs: template.previewImages, + localFirstImageName: "preview_\(template.id ?? "")" + ) + .padding(.vertical, 10) + .clipped() + .frame(width: twoGridCellWidth, height: itemHeight) } else { // Sound, Background, Carabiner, 키링 등은 기존처럼 이미지로 처리 (GIF 지원) SimpleAnimatedImage(url: item.thumbnailURL) diff --git a/Keychy/Keychy/Presentation/Workshop/Views/Keyring/WorkshopRecentTemplate.swift b/Keychy/Keychy/Presentation/Workshop/Views/Keyring/WorkshopRecentTemplate.swift index cf81bef7..8022cdb7 100644 --- a/Keychy/Keychy/Presentation/Workshop/Views/Keyring/WorkshopRecentTemplate.swift +++ b/Keychy/Keychy/Presentation/Workshop/Views/Keyring/WorkshopRecentTemplate.swift @@ -91,7 +91,7 @@ private struct RecentTemplateCard: View { var body: some View { Button(action: onTap) { ZStack { - LazyImage(url: URL(string: template.thumbnailURL)) { state in + LazyImage(url: URL(string: template.previewImages.first ?? template.thumbnailURL)) { state in if let image = state.image { image .resizable() diff --git a/Keychy/Keychy/Presentation/Workshop/Views/Keyring/WorkshopTemplateSelectSheet.swift b/Keychy/Keychy/Presentation/Workshop/Views/Keyring/WorkshopTemplateSelectSheet.swift index e7976b0b..d84e7b25 100644 --- a/Keychy/Keychy/Presentation/Workshop/Views/Keyring/WorkshopTemplateSelectSheet.swift +++ b/Keychy/Keychy/Presentation/Workshop/Views/Keyring/WorkshopTemplateSelectSheet.swift @@ -165,11 +165,23 @@ struct WorkshopTemplateSelectSheet: View { VStack(spacing: 8) { // 썸네일 + 가격 오버레이 (공방 스타일) ZStack(alignment: .top) { - SimpleAnimatedImage(url: template.thumbnailURL) - .aspectRatio(contentMode: .fit) - .padding(.vertical, 10) - .clipped() - .frame(width: 105, height: 140.61) + if template.previewImages.count > 1 { + // 슬라이드 이미지 + TemplateImageSlideshow( + imageURLs: template.previewImages, + localFirstImageName: "preview_\(template.id ?? "")" + ) + .padding(.vertical, 10) + .clipped() + .frame(width: 105, height: 140.61) + } else { + // fallback: 기존 단일 이미지 + SimpleAnimatedImage(url: template.thumbnailURL) + .aspectRatio(contentMode: .fit) + .padding(.vertical, 10) + .clipped() + .frame(width: 105, height: 140.61) + } // 유료/보유 오버레이 VStack { diff --git a/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ DuZzonKu.imageset/Contents.json b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ DuZzonKu.imageset/Contents.json new file mode 100644 index 00000000..e4545ccf --- /dev/null +++ b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ DuZzonKu.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "preview_ DuZzonKu.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ DuZzonKu.imageset/preview_ DuZzonKu.png b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ DuZzonKu.imageset/preview_ DuZzonKu.png new file mode 100644 index 00000000..bf2df7bd Binary files /dev/null and b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ DuZzonKu.imageset/preview_ DuZzonKu.png differ diff --git a/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ PixelKeyring.imageset/Contents.json b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ PixelKeyring.imageset/Contents.json new file mode 100644 index 00000000..febbfd46 --- /dev/null +++ b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ PixelKeyring.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "preview_ PixelKeyring.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ PixelKeyring.imageset/preview_ PixelKeyring.png b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ PixelKeyring.imageset/preview_ PixelKeyring.png new file mode 100644 index 00000000..a2cf0e3e Binary files /dev/null and b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ PixelKeyring.imageset/preview_ PixelKeyring.png differ diff --git a/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ Polaroid.imageset/Contents.json b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ Polaroid.imageset/Contents.json new file mode 100644 index 00000000..88ead02c --- /dev/null +++ b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ Polaroid.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "preview_ Polaroid.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ Polaroid.imageset/preview_ Polaroid.png b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ Polaroid.imageset/preview_ Polaroid.png new file mode 100644 index 00000000..4bf2d709 Binary files /dev/null and b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ Polaroid.imageset/preview_ Polaroid.png differ diff --git a/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ SpeechBubble.imageset/Contents.json b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ SpeechBubble.imageset/Contents.json new file mode 100644 index 00000000..23f9eede --- /dev/null +++ b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ SpeechBubble.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "preview_ SpeechBubble.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ SpeechBubble.imageset/preview_ SpeechBubble.png b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ SpeechBubble.imageset/preview_ SpeechBubble.png new file mode 100644 index 00000000..53cf4ded Binary files /dev/null and b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ SpeechBubble.imageset/preview_ SpeechBubble.png differ diff --git a/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ WishHorse26.imageset/Contents.json b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ WishHorse26.imageset/Contents.json new file mode 100644 index 00000000..4386ebfd --- /dev/null +++ b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ WishHorse26.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "preview_ WishHorse26.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ WishHorse26.imageset/preview_ WishHorse26.png b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ WishHorse26.imageset/preview_ WishHorse26.png new file mode 100644 index 00000000..fd312ebe Binary files /dev/null and b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ WishHorse26.imageset/preview_ WishHorse26.png differ diff --git a/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_AcrylicPhoto.imageset/Contents.json b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_AcrylicPhoto.imageset/Contents.json new file mode 100644 index 00000000..380836ef --- /dev/null +++ b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_AcrylicPhoto.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "preview_AcrylicPhoto.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_AcrylicPhoto.imageset/preview_AcrylicPhoto.png b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_AcrylicPhoto.imageset/preview_AcrylicPhoto.png new file mode 100644 index 00000000..a3a7431d Binary files /dev/null and b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_AcrylicPhoto.imageset/preview_AcrylicPhoto.png differ diff --git a/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ClearSketch.imageset/Contents.json b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ClearSketch.imageset/Contents.json new file mode 100644 index 00000000..9c888d90 --- /dev/null +++ b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ClearSketch.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "preview_ClearSketch.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ClearSketch.imageset/preview_ClearSketch.png b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ClearSketch.imageset/preview_ClearSketch.png new file mode 100644 index 00000000..89c4833a Binary files /dev/null and b/Keychy/Keychy/Resources/Assets.xcassets/03. Template/preview_ClearSketch.imageset/preview_ClearSketch.png differ