Adds EditSkills view bound to the monster view model's skills.

This commit is contained in:
2021-03-21 16:18:04 -07:00
parent 3a7125ee88
commit 99298d3f28
7 changed files with 178 additions and 15 deletions

44
EditSkills.swift Normal file
View File

@@ -0,0 +1,44 @@
//
// EditSkills.swift
// MonsterCards
//
// Created by Tom Hicks on 3/21/21.
//
import SwiftUI
struct EditSkills: View {
@ObservedObject var monsterViewModel: MonsterViewModel
var body: some View {
List {
ForEach(monsterViewModel.skills, id: \.self) { skill in
Text(skill.name)
}
.onDelete(perform: { indexSet in
for index in indexSet {
monsterViewModel.skills.remove(at: index)
}
})
}
.toolbar(content: {
Button(
action: {
let newSkill = SkillViewModel(nil)
newSkill.name = "New Skill"
monsterViewModel.skills.append(newSkill)
},
label: {
Image(systemName: "plus")
}
)
})
}
}
struct EditSkills_Previews: PreviewProvider {
static var previews: some View {
let viewModel = MonsterViewModel(nil)
EditSkills(monsterViewModel: viewModel)
}
}

View File

@@ -21,6 +21,7 @@
E24ACE5B2607F0F2009BF703 /* EditSpeed.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24ACE5A2607F0F2009BF703 /* EditSpeed.swift */; };
E24ACE602607F45E009BF703 /* EditAbilityScores.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24ACE5F2607F45E009BF703 /* EditAbilityScores.swift */; };
E24ACE652607F55D009BF703 /* EditSavingThrows.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24ACE642607F55D009BF703 /* EditSavingThrows.swift */; };
E24ACE6A2607F715009BF703 /* EditSkills.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24ACE692607F715009BF703 /* EditSkills.swift */; };
E2570FB925B1AC520055B23B /* MonsterCardsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2570FB825B1AC520055B23B /* MonsterCardsApp.swift */; };
E2570FBB25B1AC520055B23B /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2570FBA25B1AC520055B23B /* ContentView.swift */; };
E2570FBD25B1AC550055B23B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E2570FBC25B1AC550055B23B /* Assets.xcassets */; };
@@ -74,6 +75,7 @@
E24ACE5A2607F0F2009BF703 /* EditSpeed.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditSpeed.swift; sourceTree = "<group>"; };
E24ACE5F2607F45E009BF703 /* EditAbilityScores.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditAbilityScores.swift; sourceTree = "<group>"; };
E24ACE642607F55D009BF703 /* EditSavingThrows.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditSavingThrows.swift; sourceTree = "<group>"; };
E24ACE692607F715009BF703 /* EditSkills.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditSkills.swift; sourceTree = "<group>"; };
E2570FB525B1AC520055B23B /* MonsterCards.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MonsterCards.app; sourceTree = BUILT_PRODUCTS_DIR; };
E2570FB825B1AC520055B23B /* MonsterCardsApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonsterCardsApp.swift; sourceTree = "<group>"; };
E2570FBA25B1AC520055B23B /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
@@ -139,6 +141,7 @@
E2570FAC25B1AC520055B23B = {
isa = PBXGroup;
children = (
E24ACE692607F715009BF703 /* EditSkills.swift */,
E24ACE642607F55D009BF703 /* EditSavingThrows.swift */,
E24ACE5F2607F45E009BF703 /* EditAbilityScores.swift */,
E24ACE552607EE94009BF703 /* EditArmor.swift */,
@@ -387,6 +390,7 @@
E2570FF525B1ADEB0055B23B /* Dashboard.swift in Sources */,
E257100425B1AF4A0055B23B /* SearchBar.swift in Sources */,
E20209F525D8E04300EFE733 /* AdvantageType.swift in Sources */,
E24ACE6A2607F715009BF703 /* EditSkills.swift in Sources */,
E20209FC25D8E19100EFE733 /* MonsterViewModel.swift in Sources */,
E2570FFF25B1AE180055B23B /* Library.swift in Sources */,
E2BD703125B3BBB90058ED69 /* MCStepperField.swift in Sources */,

View File

@@ -181,16 +181,16 @@ class MonsterViewModel: ObservableObject {
monster.charismaSavingThrowAdvantageEnum = charismaSavingThrowAdvantage
monster.charismaSavingThrowProficiencyEnum = charismaSavingThrowProficiency
// // Remove missing skills from raw monster
// monster.skills?.forEach {s in
// let skill = s as! Skill
// let skillVM = skills.first { $0.isEqualTo(rawSkill: skill) }
// if (skillVM != nil) {
// skillVM!.copyToSkill(skill: skill)
// } else {
// monster.removeFromSkills(skill)
// }
// }
// Remove missing skills from raw monster
monster.skills?.forEach {s in
let skill = s as! Skill
let skillVM = skills.first { $0.isEqualTo(rawSkill: skill) }
if (skillVM != nil) {
skillVM!.copyToSkill(skill: skill)
} else {
monster.removeFromSkills(skill)
}
}
// // Add new skills to raw monster
// skills.forEach {skillVM in
// if (!(monster.skills?.contains(

View File

@@ -40,6 +40,15 @@ public class Skill: NSManagedObject {
}
}
var wrappedAdvantage: AdvantageType {
get {
return AdvantageType.init(rawValue: advantage ?? "") ?? .none
}
set {
advantage = newValue.rawValue
}
}
var modifier: Int64 {
get {
let proficiencyBonus = Double(monster?.proficiencyBonus ?? 0)

View File

@@ -6,15 +6,59 @@
//
import Foundation
import CoreData
class SkillViewModel: ObservableObject {
class SkillViewModel: ObservableObject, Hashable, Identifiable {
static func == (lhs: SkillViewModel, rhs: SkillViewModel) -> Bool {
return lhs.abilityScore == rhs.abilityScore
&& lhs.advantage == rhs.advantage
&& lhs.name == rhs.name
&& lhs.proficiency == rhs.proficiency
}
func hash(into hasher: inout Hasher) {
hasher.combine(abilityScore)
hasher.combine(advantage)
hasher.combine(name)
hasher.combine(proficiency)
}
func isEqualTo(rawSkill: Skill?) -> Bool {
if (rawSkill == nil) {
return false;
} else if (abilityScore != rawSkill!.wrappedAbilityScore) {
return false;
} else if (advantage != rawSkill!.wrappedAdvantage) {
return false;
} else if (name != rawSkill!.name) {
return false;
} else if (proficiency != rawSkill!.wrappedProficiency) {
return false;
} else {
return true
}
}
func copyToSkill(skill: Skill) {
skill.wrappedAbilityScore = abilityScore
skill.wrappedAdvantage = advantage
skill.name = name
skill.wrappedProficiency = proficiency
}
init(_ rawSkill: Skill?) {
if (rawSkill != nil) {
self.rawSkill = rawSkill
_name = rawSkill!.name ?? ""
_abilityScore = AbilityScore(rawValue: rawSkill!.abilityScoreName ?? "") ?? .strength
_proficiency = ProficiencyType(rawValue: rawSkill!.proficiency ?? "") ?? .none
_advantage = AdvantageType(rawValue: rawSkill!.advantage ?? "") ?? .none
_advantage = .none
} else {
_name = ""
_abilityScore = .strength
_proficiency = .none
_advantage = .none
}
}
@@ -35,4 +79,61 @@ class SkillViewModel: ObservableObject {
}
}
}
private var _abilityScore: AbilityScore
var abilityScore: AbilityScore {
get {
return _abilityScore
}
set {
if (newValue != _abilityScore) {
_abilityScore = newValue
// Notify changed
}
if (rawSkill != nil) {
rawSkill!.wrappedAbilityScore = newValue
}
}
}
private var _proficiency: ProficiencyType
var proficiency: ProficiencyType {
get {
return _proficiency
}
set {
if (newValue != _proficiency) {
_proficiency = newValue
// Notify changed
}
if (rawSkill != nil) {
rawSkill!.wrappedProficiency = newValue
}
}
}
private var _advantage: AdvantageType
var advantage: AdvantageType {
get {
return _advantage
}
set {
if (newValue != _advantage) {
_advantage = newValue
// Notify changed
}
if (rawSkill != nil) {
rawSkill!.wrappedAdvantage = newValue
}
}
}
func buildRawSkill(context: NSManagedObjectContext?) -> Skill {
let newSkill = context == nil ? Skill.init() : Skill.init(context: context!)
newSkill.name = name
newSkill.wrappedAbilityScore = abilityScore
newSkill.wrappedProficiency = proficiency
newSkill.wrappedAdvantage = advantage
return newSkill
}
}

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17192" systemVersion="19H114" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="YES" userDefinedModelVersionIdentifier="">
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17709" systemVersion="20D74" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="YES" userDefinedModelVersionIdentifier="">
<entity name="Monster" representedClassName="Monster" syncable="YES" codeGenerationType="category">
<attribute name="alignment" attributeType="String" defaultValueString=""/>
<attribute name="armorType" attributeType="String" defaultValueString=""/>
@@ -52,13 +52,14 @@
<relationship name="skills" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="Skill" inverseName="monster" inverseEntity="Skill"/>
</entity>
<entity name="Skill" representedClassName="Skill" syncable="YES" codeGenerationType="category">
<attribute name="abilityScoreName" attributeType="String" defaultValueString=""/>
<attribute name="abilityScoreName" attributeType="String" defaultValueString="strength"/>
<attribute name="advantage" attributeType="String" defaultValueString="none"/>
<attribute name="name" attributeType="String" defaultValueString=""/>
<attribute name="proficiency" attributeType="String" defaultValueString=""/>
<attribute name="proficiency" attributeType="String" defaultValueString="none"/>
<relationship name="monster" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Monster" inverseName="skills" inverseEntity="Monster"/>
</entity>
<elements>
<element name="Monster" positionX="-63" positionY="-18" width="128" height="778"/>
<element name="Skill" positionX="-63" positionY="135" width="128" height="103"/>
<element name="Skill" positionX="-63" positionY="135" width="128" height="14"/>
</elements>
</model>

View File

@@ -39,6 +39,10 @@ struct EditMonster: View {
NavigationLink(
"Saving Throws",
destination: EditSavingThrows(monsterViewModel: monsterViewModel))
NavigationLink(
"Skills",
destination: EditSkills(monsterViewModel: monsterViewModel))
}
.onAppear(perform: copyMonsterToLocal)
.toolbar(content: {