feat(view model): 增加自定义调色板

可以自动保存和加载调色板

Signed-off-by: Ching <loooching@gmail.com>
This commit is contained in:
Ching 2023-02-23 23:46:48 +08:00
parent ac3031dd05
commit ba56e249a0
3 changed files with 85 additions and 0 deletions

View File

@ -17,6 +17,7 @@
248D14F029A230FE00AE4C0D /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 248D14EF29A230FE00AE4C0D /* Constants.swift */; }; 248D14F029A230FE00AE4C0D /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 248D14EF29A230FE00AE4C0D /* Constants.swift */; };
248D14F329A2435000AE4C0D /* UtilityViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 248D14F129A2435000AE4C0D /* UtilityViews.swift */; }; 248D14F329A2435000AE4C0D /* UtilityViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 248D14F129A2435000AE4C0D /* UtilityViews.swift */; };
248D14F629A243F200AE4C0D /* UtilityExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 248D14F529A243F200AE4C0D /* UtilityExtensions.swift */; }; 248D14F629A243F200AE4C0D /* UtilityExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 248D14F529A243F200AE4C0D /* UtilityExtensions.swift */; };
24E5FE8929A7AB4000794732 /* PaletteStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24E5FE8829A7AB4000794732 /* PaletteStore.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
@ -32,6 +33,7 @@
248D14EF29A230FE00AE4C0D /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; }; 248D14EF29A230FE00AE4C0D /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
248D14F129A2435000AE4C0D /* UtilityViews.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UtilityViews.swift; sourceTree = "<group>"; }; 248D14F129A2435000AE4C0D /* UtilityViews.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UtilityViews.swift; sourceTree = "<group>"; };
248D14F529A243F200AE4C0D /* UtilityExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UtilityExtensions.swift; sourceTree = "<group>"; }; 248D14F529A243F200AE4C0D /* UtilityExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UtilityExtensions.swift; sourceTree = "<group>"; };
24E5FE8829A7AB4000794732 /* PaletteStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaletteStore.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@ -69,6 +71,7 @@
24145E7D29A1489700ECB9D1 /* EmojiArtDocumentView.swift */, 24145E7D29A1489700ECB9D1 /* EmojiArtDocumentView.swift */,
248D14F129A2435000AE4C0D /* UtilityViews.swift */, 248D14F129A2435000AE4C0D /* UtilityViews.swift */,
24145E8A29A1498500ECB9D1 /* EmojiArtModel.swift */, 24145E8A29A1498500ECB9D1 /* EmojiArtModel.swift */,
24E5FE8829A7AB4000794732 /* PaletteStore.swift */,
248D14F529A243F200AE4C0D /* UtilityExtensions.swift */, 248D14F529A243F200AE4C0D /* UtilityExtensions.swift */,
248D14EB29A227C700AE4C0D /* EmojiArtDocument.swift */, 248D14EB29A227C700AE4C0D /* EmojiArtDocument.swift */,
24145E8C29A225E900ECB9D1 /* EmojiArtModel.Background.swift */, 24145E8C29A225E900ECB9D1 /* EmojiArtModel.Background.swift */,
@ -161,6 +164,7 @@
24145E7C29A1489700ECB9D1 /* EmojiArtApp.swift in Sources */, 24145E7C29A1489700ECB9D1 /* EmojiArtApp.swift in Sources */,
248D14EC29A227C700AE4C0D /* EmojiArtDocument.swift in Sources */, 248D14EC29A227C700AE4C0D /* EmojiArtDocument.swift in Sources */,
248D14F029A230FE00AE4C0D /* Constants.swift in Sources */, 248D14F029A230FE00AE4C0D /* Constants.swift in Sources */,
24E5FE8929A7AB4000794732 /* PaletteStore.swift in Sources */,
24145E8B29A1498500ECB9D1 /* EmojiArtModel.swift in Sources */, 24145E8B29A1498500ECB9D1 /* EmojiArtModel.swift in Sources */,
24145E8D29A225E900ECB9D1 /* EmojiArtModel.Background.swift in Sources */, 24145E8D29A225E900ECB9D1 /* EmojiArtModel.Background.swift in Sources */,
248D14F629A243F200AE4C0D /* UtilityExtensions.swift in Sources */, 248D14F629A243F200AE4C0D /* UtilityExtensions.swift in Sources */,

View File

@ -10,6 +10,8 @@ import SwiftUI
@main @main
struct EmojiArtApp: App { struct EmojiArtApp: App {
let document = EmojiArtDocument() let document = EmojiArtDocument()
let paletteStore = PaletteStore(named: "Default")
var body: some Scene { var body: some Scene {
WindowGroup { WindowGroup {
EmojiArtDocumentView(document: document) EmojiArtDocumentView(document: document)

View File

@ -0,0 +1,79 @@
//
// PaletteStore.swift
// EmojiArt
//
// Created by ching on 2023/2/23.
//
import SwiftUI
struct Palette: Identifiable, Codable {
var name: String
var emojis: String
var id: Int
fileprivate init(name: String, emojis: String, id: Int) {
self.name = name
self.emojis = emojis
self.id = id
}
}
class PaletteStore: ObservableObject {
let name: String
@Published var palettes = [Palette]() {
didSet {
storeInUserDefaults()
}
}
private var userDefaultsKey: String {
"PaletteStore:" + name
}
private func storeInUserDefaults() {
UserDefaults.standard.set(try? JSONEncoder().encode(palettes), forKey: userDefaultsKey)
}
private func restoreFromUserDefaults() {
if let jsonData = UserDefaults.standard.data(forKey: userDefaultsKey),
let decodedPalettes = try? JSONDecoder().decode(Array<Palette>.self, from: jsonData) {
palettes = decodedPalettes
}
}
init(named name: String) {
self.name = name
restoreFromUserDefaults()
if palettes.isEmpty {
print("using built-in palettes")
insertPalette(named: "Vehicles", emojis: "🚗🚕🚙🚌🚎🏎🚓🚑🚒🚐🛻🚚🚛🚜🦯🦽🦼🛴🚲🛵🏍🛺🚨🚔🚍🚘🚖🛞🚡🚠🚟🚃🚋🚞🚝🚄🚅🚈🚂🚆🚇🚊🚉✈️🛫🛬🛩")
insertPalette(named: "Sports", emojis: "⚽️🏀🏈⚾️🥎🎾🏐🏉🥏🎱🪀🏓🏸🏒🏑🥍🏏")
} else {
print("loaded palettes")
}
}
// Intent
func palette(at index: Int) -> Palette {
let safeIndex = min(max(index, 0), palettes.count - 1)
return palettes[safeIndex]
}
@discardableResult
func removePalette(at index: Int) -> Int {
if palettes.count > 1, palettes.indices.contains(index) {
palettes.remove(at: index)
}
return index % palettes.count
}
func insertPalette(named name: String, emojis: String? = nil, at index: Int = 0) {
let unique = (palettes.max(by: { $0.id < $1.id })?.id ?? 0) + 1
let palette = Palette(name: name, emojis: emojis ?? "", id: unique)
let safeIndex = min(max(index, 0), palettes.count)
palettes.insert(palette, at: safeIndex)
}
}