add minimal generalized main/detail view

This commit is contained in:
Felix Förtsch
2024-08-13 00:09:16 +02:00
parent d92710eec4
commit ec2a7c15c7
5 changed files with 119 additions and 123 deletions

View File

@@ -1,104 +0,0 @@
//
// ContentView.swift
// WorkoutsPlus
//
// Created by Felix Förtsch on 10.08.24.
//
import SwiftUI
import SwiftData
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
@Query private var exercises: [Exercise]
@State private var isAddingNewExercise = false
@State private var selectedExercise: Exercise?
let initialDataSet = [
"Pull-up",
"Push-up",
"Dips",
"Rows",
"Split Squat"
]
var body: some View {
NavigationSplitView {
List {
ForEach(exercises) { exercise in
NavigationLink {
Text(exercise.name)
} label: {
Text(exercise.name)
}
}
.onDelete(perform: deleteExercises)
}
.onAppear {
if exercises.isEmpty {
loadInitialData(exercises: initialDataSet)
}
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
addExercise()
} label: {
Label("Add Exercise", systemImage: "plus")
}
}
}
.sheet(isPresented: $isAddingNewExercise) {
ExerciseDetailsView(exercise: $selectedExercise, isEditing: false)
.onDisappear {
if let selectedExercise = selectedExercise, !selectedExercise.name.isEmpty {
saveNewExercise()
}
}
}
} detail: {
Text("Select an exercise")
}
}
private func addExercise() {
selectedExercise = Exercise(name: "")
isAddingNewExercise = true
}
private func saveNewExercise() {
guard let newExercise = selectedExercise else { return }
modelContext.insert(newExercise)
try? modelContext.save()
}
private func deleteExercises(offsets: IndexSet) {
withAnimation {
for index in offsets {
modelContext.delete(exercises[index])
}
try? modelContext.save()
}
}
private func loadInitialData(exercises: [String]) {
var items: [Exercise] = []
for exercise in exercises {
let item = Exercise(name: exercise)
items.append(item)
}
for item in items {
modelContext.insert(item)
}
try? modelContext.save()
}
}
#Preview {
ContentView()
.modelContainer(for: Exercise.self, inMemory: true)
}

View File

@@ -9,11 +9,11 @@ import Foundation
import SwiftData
@Model
final class Exercise {
final class Item {
var name: String
var timestamp: Date
init(name: String, timestamp: Date = Date.now) {
init(name: String = "", timestamp: Date = Date.now) {
self.name = name
self.timestamp = timestamp
}

View File

@@ -7,33 +7,33 @@
import SwiftUI
struct ExerciseDetailsView: View {
struct ItemDetailsView: View {
@Environment(\.dismiss) private var dismiss
@Environment(\.modelContext) private var modelContext
@Binding var exercise: Exercise?
var isEditing: Bool
var item: Item?
var isPresentedAsSheet: Bool = false
var body: some View {
NavigationView {
Form {
TextField("Exercise Name", text: Binding(
get: { exercise?.name ?? "" },
get: { item?.name ?? "" },
set: { newName in
if exercise != nil {
exercise?.name = newName
if item != nil {
item?.name = newName
}
}
))
}
.navigationTitle(isEditing ? "Edit Exercise" : "Add Exercise")
.toolbar {
ToolbarItem(placement: .navigationBarLeading) {
Button("Cancel") {
dismiss()
if (isPresentedAsSheet) {
ToolbarItem(placement: .topBarLeading) {
Button("Cancel") {
dismiss()
}
}
}
ToolbarItem(placement: .navigationBarTrailing) {
ToolbarItem(placement: .topBarTrailing) {
Button("Save") {
saveItem()
dismiss()
@@ -56,7 +56,7 @@ struct ExerciseDetailsView: View {
struct ExerciseDetailsView_Previews: PreviewProvider {
static var previews: some View {
let sampleItem = Exercise(name: "Sample Item")
ExerciseDetailsView(exercise: .constant(sampleItem), isEditing: false)
let sampleItem = Item(name: "Sample Item")
ItemDetailsView(item: sampleItem)
}
}

View File

@@ -0,0 +1,100 @@
//
// ContentView.swift
// WorkoutsPlus
//
// Created by Felix Förtsch on 10.08.24.
//
import SwiftUI
import SwiftData
struct ItemLibrary: View {
@Environment(\.modelContext) private var modelContext
@Query private var items: [Item]
@State private var isPresentingNewItemSheet = false
let initialDataSet = [
"Pull-up",
"Push-up",
"Dips",
"Rows",
"Split Squat"
]
var body: some View {
NavigationView {
List {
ForEach(items) { item in
NavigationLink(destination: ItemDetailsView(item: item)) {
Text(item.name)
}
}
.onDelete(perform: deleteItem)
}
.onAppear {
if items.isEmpty {
loadInitialData(exercises: initialDataSet)
}
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
createNewItem()
} label: {
Label("Add Exercise", systemImage: "plus")
}
}
}
.sheet(isPresented: $isPresentingNewItemSheet) {
let newItem = Item(name: "")
NavigationView {
ItemDetailsView(item: newItem, isPresentedAsSheet: true)
.onDisappear {
if !newItem.name.isEmpty {
saveItem(item: newItem)
}
}
}
}
}
}
private func createNewItem() {
isPresentingNewItemSheet = true
}
private func saveItem(item: Item) {
modelContext.insert(item)
try? modelContext.save()
}
private func deleteItem(offsets: IndexSet) {
withAnimation {
for index in offsets {
modelContext.delete(items[index])
}
try? modelContext.save()
}
}
private func loadInitialData(exercises: [String]) {
var items: [Item] = []
for exercise in exercises {
let item = Item(name: exercise)
items.append(item)
}
for item in items {
modelContext.insert(item)
}
try? modelContext.save()
}
}
#Preview {
ItemLibrary()
.modelContainer(for: Item.self, inMemory: true)
}

View File

@@ -12,7 +12,7 @@ import SwiftData
struct WorkoutsPlusApp: App {
var sharedModelContainer: ModelContainer = {
let schema = Schema([
Exercise.self,
Item.self,
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
@@ -25,7 +25,7 @@ struct WorkoutsPlusApp: App {
var body: some Scene {
WindowGroup {
ContentView()
ItemLibrary()
}
.modelContainer(sharedModelContainer)
}