1. 增加从调色板中增加、删除、跳转和编辑 emoji 的功能 2. 增加通过 url 获取背景图时失败带报错 Signed-off-by: Ching <loooching@gmail.com>
126 lines
4.0 KiB
Swift
126 lines
4.0 KiB
Swift
//
|
|
// EmojiArtDocument.swift
|
|
// EmojiArt
|
|
//
|
|
// Created by ching on 2023/2/19.
|
|
//
|
|
|
|
import SwiftUI
|
|
|
|
class EmojiArtDocument: ObservableObject {
|
|
@Published private(set) var emojiArt: EmojiArtModel {
|
|
didSet {
|
|
scheduleAutosave()
|
|
if emojiArt.background != oldValue.background {
|
|
fetchBackgoundImageDataIfNecessary()
|
|
}
|
|
}
|
|
}
|
|
|
|
private var autosaveTimer: Timer?
|
|
|
|
private func scheduleAutosave() {
|
|
autosaveTimer?.invalidate()
|
|
autosaveTimer = Timer.scheduledTimer(withTimeInterval: const.coalescingInterval, repeats: false) { _ in
|
|
self.autoSave()
|
|
}
|
|
}
|
|
|
|
private enum Autosave {
|
|
static let filename = "Autosaved.emojiart"
|
|
static var url: URL? {
|
|
let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
|
|
return documentDirectory?.appendingPathComponent(filename)
|
|
}
|
|
}
|
|
|
|
private func autoSave() {
|
|
if let url = Autosave.url {
|
|
save(to: url)
|
|
}
|
|
}
|
|
|
|
private func save(to url: URL) {
|
|
let thisFunction = "\(String(describing: self)).\(#function)"
|
|
do {
|
|
let data: Data = try emojiArt.json()
|
|
print("\(thisFunction) json = \(String(data: data, encoding: .utf8) ?? "nil")")
|
|
try data.write(to: url)
|
|
print("\(thisFunction) success")
|
|
} catch {
|
|
print("\(thisFunction) error = \(error)")
|
|
}
|
|
}
|
|
|
|
init() {
|
|
if let url = Autosave.url, let autosavedEmojiArt = try? EmojiArtModel(url: url) {
|
|
emojiArt = autosavedEmojiArt
|
|
fetchBackgoundImageDataIfNecessary()
|
|
} else {
|
|
emojiArt = EmojiArtModel()
|
|
}
|
|
}
|
|
|
|
var emojis: [EmojiArtModel.Emoji] { emojiArt.emojis }
|
|
var background: EmojiArtModel.Background { emojiArt.background }
|
|
@Published var backgroundImage: UIImage?
|
|
@Published var backgroundImageFetchStatus = BackgroundImageFetchStatus.idle
|
|
|
|
enum BackgroundImageFetchStatus: Equatable {
|
|
case idle
|
|
case fetching
|
|
case failed(URL )
|
|
}
|
|
|
|
// MARK: - Intent(s)
|
|
|
|
func setBackground(_ background: EmojiArtModel.Background) {
|
|
emojiArt.background = background
|
|
print("background set to \(background)")
|
|
}
|
|
|
|
func addEmoji(_ emoji: String, at location: (x: Int, y: Int), size: CGFloat) {
|
|
emojiArt.addEmoji(emoji, at: location, size: Int(size))
|
|
}
|
|
|
|
func moveEmoji(_ emoji: EmojiArtModel.Emoji, by offset: CGSize) {
|
|
if let index = emojiArt.emojis.index(matching: emoji) {
|
|
emojiArt.emojis[index].x += Int(offset.width)
|
|
emojiArt.emojis[index].y += Int(offset.height)
|
|
}
|
|
}
|
|
|
|
func scaleEmoji(_ emoji: EmojiArtModel.Emoji, by scale: CGFloat) {
|
|
if let index = emojiArt.emojis.index(matching: emoji) {
|
|
emojiArt.emojis[index].size = Int((CGFloat(emojiArt.emojis[index].size) * scale).rounded(.toNearestOrAwayFromZero))
|
|
}
|
|
}
|
|
|
|
private func fetchBackgoundImageDataIfNecessary() {
|
|
backgroundImage = nil
|
|
switch emojiArt.background {
|
|
case .url(let url):
|
|
// fetch url
|
|
backgroundImageFetchStatus = .fetching
|
|
DispatchQueue.global(qos: .userInitiated).async {
|
|
let imageData = try? Data(contentsOf: url)
|
|
DispatchQueue.main.async { [weak self] in
|
|
if self?.emojiArt.background == EmojiArtModel.Background.url(url) {
|
|
self?.backgroundImageFetchStatus = .idle
|
|
if imageData != nil {
|
|
self?.backgroundImage = UIImage(data: imageData!)
|
|
}
|
|
if self?.backgroundImage == nil {
|
|
self?.backgroundImageFetchStatus = .failed(url)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
case .imageData(let data):
|
|
backgroundImage = UIImage(data: data)
|
|
case .blank:
|
|
break
|
|
}
|
|
}
|
|
}
|