feat(view, viewmodels): 增加调色板按钮,增加报错提示
1. 增加从调色板中增加、删除、跳转和编辑 emoji 的功能 2. 增加通过 url 获取背景图时失败带报错 Signed-off-by: Ching <loooching@gmail.com>
This commit is contained in:
parent
ba56e249a0
commit
566f3539d1
@ -17,7 +17,10 @@
|
|||||||
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 */; };
|
||||||
|
24D4D3A729A9E02F0064E566 /* PaletteManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24D4D3A629A9E02F0064E566 /* PaletteManager.swift */; };
|
||||||
24E5FE8929A7AB4000794732 /* PaletteStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24E5FE8829A7AB4000794732 /* PaletteStore.swift */; };
|
24E5FE8929A7AB4000794732 /* PaletteStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24E5FE8829A7AB4000794732 /* PaletteStore.swift */; };
|
||||||
|
24E5FE8B29A9944600794732 /* PaletteChooser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24E5FE8A29A9944600794732 /* PaletteChooser.swift */; };
|
||||||
|
24E5FE8D29A9B25800794732 /* PaletteEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24E5FE8C29A9B25800794732 /* PaletteEditor.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
@ -33,7 +36,10 @@
|
|||||||
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>"; };
|
||||||
|
24D4D3A629A9E02F0064E566 /* PaletteManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaletteManager.swift; sourceTree = "<group>"; };
|
||||||
24E5FE8829A7AB4000794732 /* PaletteStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaletteStore.swift; sourceTree = "<group>"; };
|
24E5FE8829A7AB4000794732 /* PaletteStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaletteStore.swift; sourceTree = "<group>"; };
|
||||||
|
24E5FE8A29A9944600794732 /* PaletteChooser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaletteChooser.swift; sourceTree = "<group>"; };
|
||||||
|
24E5FE8C29A9B25800794732 /* PaletteEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaletteEditor.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@ -66,12 +72,15 @@
|
|||||||
24145E7A29A1489700ECB9D1 /* EmojiArt */ = {
|
24145E7A29A1489700ECB9D1 /* EmojiArt */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
24D4D3A629A9E02F0064E566 /* PaletteManager.swift */,
|
||||||
248D14EF29A230FE00AE4C0D /* Constants.swift */,
|
248D14EF29A230FE00AE4C0D /* Constants.swift */,
|
||||||
24145E7B29A1489700ECB9D1 /* EmojiArtApp.swift */,
|
24145E7B29A1489700ECB9D1 /* EmojiArtApp.swift */,
|
||||||
24145E7D29A1489700ECB9D1 /* EmojiArtDocumentView.swift */,
|
24145E7D29A1489700ECB9D1 /* EmojiArtDocumentView.swift */,
|
||||||
248D14F129A2435000AE4C0D /* UtilityViews.swift */,
|
248D14F129A2435000AE4C0D /* UtilityViews.swift */,
|
||||||
24145E8A29A1498500ECB9D1 /* EmojiArtModel.swift */,
|
24145E8A29A1498500ECB9D1 /* EmojiArtModel.swift */,
|
||||||
24E5FE8829A7AB4000794732 /* PaletteStore.swift */,
|
24E5FE8829A7AB4000794732 /* PaletteStore.swift */,
|
||||||
|
24E5FE8A29A9944600794732 /* PaletteChooser.swift */,
|
||||||
|
24E5FE8C29A9B25800794732 /* PaletteEditor.swift */,
|
||||||
248D14F529A243F200AE4C0D /* UtilityExtensions.swift */,
|
248D14F529A243F200AE4C0D /* UtilityExtensions.swift */,
|
||||||
248D14EB29A227C700AE4C0D /* EmojiArtDocument.swift */,
|
248D14EB29A227C700AE4C0D /* EmojiArtDocument.swift */,
|
||||||
24145E8C29A225E900ECB9D1 /* EmojiArtModel.Background.swift */,
|
24145E8C29A225E900ECB9D1 /* EmojiArtModel.Background.swift */,
|
||||||
@ -165,10 +174,13 @@
|
|||||||
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 */,
|
24E5FE8929A7AB4000794732 /* PaletteStore.swift in Sources */,
|
||||||
|
24D4D3A729A9E02F0064E566 /* PaletteManager.swift in Sources */,
|
||||||
24145E8B29A1498500ECB9D1 /* EmojiArtModel.swift in Sources */,
|
24145E8B29A1498500ECB9D1 /* EmojiArtModel.swift in Sources */,
|
||||||
|
24E5FE8D29A9B25800794732 /* PaletteEditor.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 */,
|
||||||
248D14F329A2435000AE4C0D /* UtilityViews.swift in Sources */,
|
248D14F329A2435000AE4C0D /* UtilityViews.swift in Sources */,
|
||||||
|
24E5FE8B29A9944600794732 /* PaletteChooser.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<Bucket
|
||||||
|
uuid = "E709882D-ACBF-4819-A5E7-499B9D69B8A9"
|
||||||
|
type = "1"
|
||||||
|
version = "2.0">
|
||||||
|
</Bucket>
|
||||||
@ -8,8 +8,9 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
|
|
||||||
struct const {
|
struct const {
|
||||||
static let defaultEmojiFontSize: CGFloat = 40
|
static let defaultEmojiFontSize: CGFloat = 40
|
||||||
static let coalescingInterval = 5.0
|
static let coalescingInterval = 5.0
|
||||||
|
static let paletteEditorMinWidth: CGFloat = 400
|
||||||
|
static let paletteEditorMinHeight: CGFloat = 400
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,12 +9,14 @@ import SwiftUI
|
|||||||
|
|
||||||
@main
|
@main
|
||||||
struct EmojiArtApp: App {
|
struct EmojiArtApp: App {
|
||||||
let document = EmojiArtDocument()
|
@StateObject var document = EmojiArtDocument()
|
||||||
let paletteStore = PaletteStore(named: "Default")
|
@StateObject var paletteStore = PaletteStore(named: "Default")
|
||||||
|
|
||||||
var body: some Scene {
|
var body: some Scene {
|
||||||
WindowGroup {
|
WindowGroup {
|
||||||
EmojiArtDocumentView(document: document)
|
EmojiArtDocumentView(document: document)
|
||||||
|
.environmentObject(paletteStore)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -66,9 +66,10 @@ class EmojiArtDocument: ObservableObject {
|
|||||||
@Published var backgroundImage: UIImage?
|
@Published var backgroundImage: UIImage?
|
||||||
@Published var backgroundImageFetchStatus = BackgroundImageFetchStatus.idle
|
@Published var backgroundImageFetchStatus = BackgroundImageFetchStatus.idle
|
||||||
|
|
||||||
enum BackgroundImageFetchStatus {
|
enum BackgroundImageFetchStatus: Equatable {
|
||||||
case idle
|
case idle
|
||||||
case fetching
|
case fetching
|
||||||
|
case failed(URL )
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Intent(s)
|
// MARK: - Intent(s)
|
||||||
@ -109,6 +110,9 @@ class EmojiArtDocument: ObservableObject {
|
|||||||
if imageData != nil {
|
if imageData != nil {
|
||||||
self?.backgroundImage = UIImage(data: imageData!)
|
self?.backgroundImage = UIImage(data: imageData!)
|
||||||
}
|
}
|
||||||
|
if self?.backgroundImage == nil {
|
||||||
|
self?.backgroundImageFetchStatus = .failed(url)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,14 +8,12 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct EmojiArtDocumentView: View {
|
struct EmojiArtDocumentView: View {
|
||||||
let testEmojis = "😀😃😄😁😆😅😂🤣🥲🥹☺️😊😇🙂🙃😉😌😍🥰😘😗😙😚😋😛😝😜🤪🤨🧐🤓😎🥸🤩"
|
|
||||||
|
|
||||||
@ObservedObject var document: EmojiArtDocument
|
@ObservedObject var document: EmojiArtDocument
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
documentBody
|
documentBody
|
||||||
palette
|
PaletteChooser(emojiFontSize: const.defaultEmojiFontSize)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,9 +42,31 @@ struct EmojiArtDocumentView: View {
|
|||||||
drop(providers: providers, at: location, in: geometry)
|
drop(providers: providers, at: location, in: geometry)
|
||||||
}
|
}
|
||||||
.gesture(panGesture().simultaneously(with: zoomGesture()))
|
.gesture(panGesture().simultaneously(with: zoomGesture()))
|
||||||
|
.alert(item: $alertToShow) { alertToShow in
|
||||||
|
alertToShow.alert()
|
||||||
|
}
|
||||||
|
.onChange(of: document.backgroundImageFetchStatus) { status in
|
||||||
|
switch status {
|
||||||
|
case .failed(let url):
|
||||||
|
showBackgroundImageFetchFailedAlert(url)
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@State private var alertToShow: IdentifiableAlert?
|
||||||
|
|
||||||
|
private func showBackgroundImageFetchFailedAlert(_ url: URL) {
|
||||||
|
alertToShow = IdentifiableAlert(id: "Fetch failed: " + url.absoluteString, alert: {
|
||||||
|
Alert(
|
||||||
|
title: Text("Background Image Fetch"),
|
||||||
|
message: Text("Couldn't load image from \(url)."),
|
||||||
|
dismissButton: .default(Text("OK")))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
private func drop(providers: [NSItemProvider], at location: CGPoint, in geometry: GeometryProxy) -> Bool {
|
private func drop(providers: [NSItemProvider], at location: CGPoint, in geometry: GeometryProxy) -> Bool {
|
||||||
var found = providers.loadObjects(ofType: URL.self) { url in
|
var found = providers.loadObjects(ofType: URL.self) { url in
|
||||||
document.setBackground(EmojiArtModel.Background.url(url.imageURL))
|
document.setBackground(EmojiArtModel.Background.url(url.imageURL))
|
||||||
@ -143,25 +163,6 @@ struct EmojiArtDocumentView: View {
|
|||||||
steadyStateZoomScale = min(hZoom, vZoom)
|
steadyStateZoomScale = min(hZoom, vZoom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var palette: some View {
|
|
||||||
SrcollingEmojiView(emojis: testEmojis)
|
|
||||||
.font(.system(size: const.defaultEmojiFontSize))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SrcollingEmojiView: View {
|
|
||||||
let emojis: String
|
|
||||||
var body: some View {
|
|
||||||
ScrollView(.horizontal) {
|
|
||||||
HStack {
|
|
||||||
ForEach(emojis.map { String($0) }, id: \.self) { emoji in
|
|
||||||
Text(emoji)
|
|
||||||
.onDrag { NSItemProvider(object: emoji as NSString) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ContentView_Previews: PreviewProvider {
|
struct ContentView_Previews: PreviewProvider {
|
||||||
|
|||||||
111
EmojiArt/PaletteChooser.swift
Normal file
111
EmojiArt/PaletteChooser.swift
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
//
|
||||||
|
// PaletteChooser.swift
|
||||||
|
// EmojiArt
|
||||||
|
//
|
||||||
|
// Created by ching on 2023/2/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct PaletteChooser: View {
|
||||||
|
var emojiFontSize: CGFloat = const.defaultEmojiFontSize
|
||||||
|
var emojiFont: Font { .system(size: emojiFontSize) }
|
||||||
|
|
||||||
|
@EnvironmentObject var store: PaletteStore
|
||||||
|
@State private var chosenPaletteIndex = 0
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack {
|
||||||
|
paletteButton
|
||||||
|
body(for: store.palette(at: chosenPaletteIndex)
|
||||||
|
)
|
||||||
|
}.clipped()
|
||||||
|
}
|
||||||
|
|
||||||
|
var paletteButton: some View {
|
||||||
|
Button {
|
||||||
|
withAnimation {
|
||||||
|
chosenPaletteIndex = (chosenPaletteIndex + 1) % store.palettes.count
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Image(systemName: "paintpalette")
|
||||||
|
}
|
||||||
|
.font(emojiFont)
|
||||||
|
.contextMenu { contextMenu }
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
var contextMenu: some View {
|
||||||
|
AnimatedActionButton(title: "Edit", systemImage: "pencil") {
|
||||||
|
editing = true
|
||||||
|
}
|
||||||
|
AnimatedActionButton(title: "New", systemImage: "plus") {
|
||||||
|
store.insertPalette(named: "New", emojis: "", at: chosenPaletteIndex)
|
||||||
|
editing = true
|
||||||
|
}
|
||||||
|
AnimatedActionButton(title: "Delete", systemImage: "minus.circle") {
|
||||||
|
chosenPaletteIndex = store.removePalette(at: chosenPaletteIndex)
|
||||||
|
}
|
||||||
|
AnimatedActionButton(title: "Manager", systemImage: "slider.vertical.3") {
|
||||||
|
managing = true
|
||||||
|
}
|
||||||
|
gotoMenu
|
||||||
|
}
|
||||||
|
|
||||||
|
var gotoMenu: some View {
|
||||||
|
Menu {
|
||||||
|
ForEach(store.palettes) {
|
||||||
|
palette in AnimatedActionButton(title: palette.name) {
|
||||||
|
if let index = store.palettes.index(matching: palette) {
|
||||||
|
chosenPaletteIndex = index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Label("Go To", systemImage: "text.insert")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func body(for palette: Palette) -> some View {
|
||||||
|
HStack {
|
||||||
|
Text(palette.name)
|
||||||
|
SrcollingEmojiView(emojis: palette.emojis)
|
||||||
|
.font(emojiFont)
|
||||||
|
}
|
||||||
|
.id(palette.id)
|
||||||
|
.transition(rollTransaction)
|
||||||
|
.popover(isPresented: $editing) {
|
||||||
|
PaletteEditor(palette: $store.palettes[chosenPaletteIndex])
|
||||||
|
}
|
||||||
|
.sheet(isPresented: $managing) {
|
||||||
|
PaletteManager()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@State private var editing = false
|
||||||
|
@State private var managing = false
|
||||||
|
|
||||||
|
var rollTransaction: AnyTransition {
|
||||||
|
AnyTransition.asymmetric(insertion: .offset(x: 0, y: emojiFontSize), removal: .offset(x: 0, y: -emojiFontSize))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SrcollingEmojiView: View {
|
||||||
|
let emojis: String
|
||||||
|
var body: some View {
|
||||||
|
ScrollView(.horizontal) {
|
||||||
|
HStack {
|
||||||
|
ForEach(emojis.map { String($0) }, id: \.self) { emoji in
|
||||||
|
Text(emoji)
|
||||||
|
.onDrag { NSItemProvider(object: emoji as NSString) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PaletteChooser_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
PaletteChooser()
|
||||||
|
}
|
||||||
|
}
|
||||||
69
EmojiArt/PaletteEditor.swift
Normal file
69
EmojiArt/PaletteEditor.swift
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
//
|
||||||
|
// PaletteEditor.swift
|
||||||
|
// EmojiArt
|
||||||
|
//
|
||||||
|
// Created by ching on 2023/2/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct PaletteEditor: View {
|
||||||
|
@Binding var palette: Palette
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Form {
|
||||||
|
nameSection
|
||||||
|
addEmojiSection
|
||||||
|
removeEmojiSection
|
||||||
|
}
|
||||||
|
.frame(minWidth: const.paletteEditorMinWidth, minHeight: const.paletteEditorMinHeight)
|
||||||
|
.navigationTitle("Edit \(palette.name)")
|
||||||
|
}
|
||||||
|
|
||||||
|
var nameSection: some View {
|
||||||
|
Section(header: Text("Name")) {
|
||||||
|
TextField("Name", text: $palette.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@State private var emojiToAdd = ""
|
||||||
|
|
||||||
|
var addEmojiSection: some View {
|
||||||
|
Section(header: Text("Add Emoji")) {
|
||||||
|
TextField("", text: $emojiToAdd)
|
||||||
|
.onChange(of: emojiToAdd) { emojis in
|
||||||
|
addEmojis(emojis)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addEmojis(_ emojis: String) {
|
||||||
|
palette.emojis = (emojis + palette.emojis)
|
||||||
|
.filter { $0.isEmoji }
|
||||||
|
}
|
||||||
|
|
||||||
|
var removeEmojiSection: some View {
|
||||||
|
Section(header: Text("Remove Emoji")) {
|
||||||
|
let emojis = palette.emojis.map { String($0) }
|
||||||
|
LazyVGrid(columns: [GridItem(.adaptive(minimum: const.defaultEmojiFontSize))]) {
|
||||||
|
ForEach(emojis, id: \.self) { emoji in
|
||||||
|
Text(emoji)
|
||||||
|
.onTapGesture {
|
||||||
|
withAnimation {
|
||||||
|
palette.emojis.removeAll(where: { String($0) == emoji })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PaletteEditor_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
PaletteEditor(palette: .constant(PaletteStore(named: "Priview").palette(at: 1)))
|
||||||
|
.previewLayout(.fixed(width: 350, height: 350))
|
||||||
|
PaletteEditor(palette: .constant(PaletteStore(named: "Priview").palette(at: 1)))
|
||||||
|
.previewLayout(.fixed(width: 350, height: 650))
|
||||||
|
}
|
||||||
|
}
|
||||||
49
EmojiArt/PaletteManager.swift
Normal file
49
EmojiArt/PaletteManager.swift
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
//
|
||||||
|
// PaletteManager.swift
|
||||||
|
// EmojiArt
|
||||||
|
//
|
||||||
|
// Created by ching on 2023/2/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct PaletteManager: View {
|
||||||
|
@EnvironmentObject var store: PaletteStore
|
||||||
|
@Environment(\.colorScheme) var colorScheme
|
||||||
|
@State private var editMode: EditMode = .inactive
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
NavigationView {
|
||||||
|
List {
|
||||||
|
ForEach(store.palettes) { palette in
|
||||||
|
NavigationLink(destination: PaletteEditor(palette: $store.palettes[palette])) {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text(palette.name)
|
||||||
|
Text(palette.emojis)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onDelete { indexSet in
|
||||||
|
store.palettes.remove(atOffsets: indexSet)
|
||||||
|
}
|
||||||
|
.onMove { indexSet, newOffset in
|
||||||
|
store.palettes.move(fromOffsets: indexSet, toOffset: newOffset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle("Manage Palettes")
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
.toolbar {
|
||||||
|
EditButton()
|
||||||
|
}
|
||||||
|
.environment(\.editMode, $editMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PaletteManager_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
PaletteManager()
|
||||||
|
.previewDevice("iPhone 14 Pro Max")
|
||||||
|
.environmentObject(PaletteStore(named: "preview"))
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
struct Palette: Identifiable, Codable {
|
struct Palette: Identifiable, Codable, Hashable {
|
||||||
var name: String
|
var name: String
|
||||||
var emojis: String
|
var emojis: String
|
||||||
var id: Int
|
var id: Int
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user