add illustration image for worlds

This commit is contained in:
Giovanni committed 2026-09-05 13:34:07 +02:00
1 parent 85259446b1
commit 21614116fc
7 files changed
+158 -53

No files matched your search

@@ -10,5 +10,13 @@
<integer>0</integer> <integer>0</integer>
</dict> </dict>
</dict> </dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>2C96045430463C2E0003A2A8</key>
<dict>
<key>primary</key>
<true/>
</dict>
</dict>
</dict> </dict>
</plist> </plist>
+11 -4
View File
@@ -36,11 +36,18 @@ struct WorldCardView : View {
.padding(.horizontal, 4) .padding(.horizontal, 4)
} }
.frame(height: 220) .frame(height: 250)
.background { .background {
Image(systemName: "globe.europe.africa") if let dataImage = world.illustrationData,
.resizable() let uiImage = UIImage(data: dataImage) {
.scaledToFill() Image(uiImage: uiImage)
.resizable()
.scaledToFill()
}else {
Image(systemName: "globe.europe.africa")
.resizable()
.scaledToFill()
}
} }
.clipShape(RoundedRectangle(cornerRadius: 20)) .clipShape(RoundedRectangle(cornerRadius: 20))
.contentShape(RoundedRectangle(cornerRadius: 20)) .contentShape(RoundedRectangle(cornerRadius: 20))
+4 -2
View File
@@ -15,18 +15,20 @@ class World {
var updatedAt : Date var updatedAt : Date
var name : String var name : String
var overview : String var overview : String
@Attribute(.externalStorage) var illustrationData : Data?
init(name: String, overview: String) { init(name: String, overview: String, illustrationData: Data?) {
self.id = UUID() self.id = UUID()
self.name = name self.name = name
self.overview = overview self.overview = overview
self.createdAt = Date() self.createdAt = Date()
self.updatedAt = Date() self.updatedAt = Date()
self.illustrationData = illustrationData
} }
func duplicate() -> World { func duplicate() -> World {
let newWorld = World( let newWorld = World(
name: self.name, overview: self.overview name: self.name, overview: self.overview, illustrationData: self.illustrationData
) )
return newWorld return newWorld
@@ -0,0 +1,37 @@
//
// IllustrationSection.swift
// WorldBuilder
//
// Created by Giovanni Josserand on 05/09/2026.
//
import SwiftUI
import PhotosUI
struct IllustrationSection : View {
@Binding var selectedItem: PhotosPickerItem?
@Binding var selectedImageData: Data?
var body: some View {
Section("Illustration") {
if let selectedImageData, let uiImage = UIImage(data: selectedImageData) {
Image(uiImage: uiImage)
.resizable()
.scaledToFill()
.frame(height: 150)
.clipShape(RoundedRectangle(cornerRadius: 10))
.listRowInsets(EdgeInsets())
}
PhotosPicker(
selection: $selectedItem,
matching: .any(of: [.images, .not(.screenshots)])
) {
Label(
selectedImageData == nil ? "Sélectionner une photo" : "Modifier la photo",
systemImage: "photo"
)
}
}
}
}
@@ -0,0 +1,25 @@
//
// InformationsSection.swift
// WorldBuilder
//
// Created by Giovanni Josserand on 05/09/2026.
//
import SwiftUI
struct InformationsSection : View {
@Binding var worldName : String
@Binding var worldOverview : String
var body: some View {
Section("Informations") {
TextField("Nom du monde", text: $worldName)
TextField(
"Description",
text: $worldOverview,
axis: .vertical
)
}
}
}
@@ -5,8 +5,14 @@
// Created by Giovanni Josserand on 01/09/2026. // Created by Giovanni Josserand on 01/09/2026.
// //
//
// CreateWorldView.swift
// WorldBuilder
//
import SwiftUI import SwiftUI
import SwiftData import SwiftData
import PhotosUI
struct CreateWorldView: View { struct CreateWorldView: View {
@State private var worldName = "" @State private var worldName = ""
@@ -19,28 +25,38 @@ struct CreateWorldView: View {
@State private var showSaveError = false @State private var showSaveError = false
@State private var saveErrorMessage = "" @State private var saveErrorMessage = ""
@State private var selectedItem: PhotosPickerItem?
@State private var selectedImageData: Data?
var body: some View { var body: some View {
Form { Form {
Section("Informations") { InformationsSection(
TextField("Nom du monde", text: $worldName) worldName: $worldName,
worldOverview: $worldOverview
)
TextField( IllustrationSection(
"Description", selectedItem: $selectedItem,
text: $worldOverview, selectedImageData: $selectedImageData
axis: .vertical )
)
}
Section { Section {
Button("Créer le monde") { Button("Créer le monde") {
let world = World(name: worldName, overview: worldOverview) let world = World(
name: worldName,
overview: worldOverview,
illustrationData: selectedImageData
)
context.insert(world) context.insert(world)
do { do {
try context.save() try context.save()
router.selectedTab = .worldsList router.selectedTab = .worldsList
dismiss() dismiss()
}catch { } catch {
saveErrorMessage = "Impossible de sauvegarder le monde : \(error.localizedDescription)" saveErrorMessage =
"Impossible de sauvegarder le monde : \(error.localizedDescription)"
showSaveError = true showSaveError = true
} }
} }
@@ -61,5 +77,12 @@ struct CreateWorldView: View {
} message: { } message: {
Text(saveErrorMessage) Text(saveErrorMessage)
} }
.onChange(of: selectedItem) { _, newItem in
Task {
if let data = try? await newItem?.loadTransferable(type: Data.self) {
selectedImageData = data
}
}
}
} }
} }
@@ -19,52 +19,55 @@ struct WorldListView: View {
var body: some View { var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 24){ VStack(alignment: .leading, spacing: 24){
WorldListHeaderView(worlds: worlds)
if worlds.isEmpty { if worlds.isEmpty {
EmptyWorldView() EmptyWorldView()
}else{ }else{
LazyVGrid(columns: columns){ ScrollView {
ForEach(worlds) {world in LazyVGrid(columns: columns){
if isSelecting { ForEach(worlds) {world in
WorldCardView( if isSelecting {
world: world, WorldCardView(
isSelected: selectedWorlds.contains(world.id) world: world,
) isSelected: selectedWorlds.contains(world.id)
.onTapGesture { )
if selectedWorlds.contains(world.id) { .onTapGesture {
selectedWorlds.remove(world.id) if selectedWorlds.contains(world.id) {
} else { selectedWorlds.remove(world.id)
selectedWorlds.insert(world.id) } else {
selectedWorlds.insert(world.id)
}
} }
}
}else { }else {
NavigationLink{ NavigationLink{
WorldOverviewView() WorldOverviewView()
} label: {
WorldCardView(world: world)
}
.contextMenu{
Button {
context.insert(world.duplicate())
} label: { } label: {
Label("Dupliquer", systemImage: "plus.square.on.square") WorldCardView(world: world)
} }
.contextMenu{
Button {
context.insert(world.duplicate())
} label: {
Label("Dupliquer", systemImage: "plus.square.on.square")
}
Button(role: .destructive){ Button(role: .destructive){
context.delete(world) context.delete(world)
}label : { }label : {
Label("Supprimer", systemImage: "trash") Label("Supprimer", systemImage: "trash")
}
} }
} }
} }
} }
} }
.scrollIndicators(.hidden)
} }
} }
.navigationTitle("Bibliothèque")
.navigationBarTitleDisplayMode(.inline)
.padding(.horizontal, 8) .padding(.horizontal, 8)
.frame(maxHeight: .infinity, alignment: .topLeading) .frame(maxHeight: .infinity, alignment: .topLeading)
.modifier( .modifier(
@@ -74,13 +77,13 @@ struct WorldListView: View {
worlds: worlds worlds: worlds
) )
) )
} .modifier(
.modifier( WorldListToolbarModifier(
WorldListToolbarModifier( isSelecting: $isSelecting,
isSelecting: $isSelecting, selectedWorlds: $selectedWorlds,
selectedWorlds: $selectedWorlds, worldsIsEmpty: worlds.isEmpty
worldsIsEmpty: worlds.isEmpty )
) )
)
} }
} }