Adds languages to the editor.

This commit is contained in:
2021-03-24 22:29:54 -07:00
parent af1cd55bc9
commit 5e0b998b70
10 changed files with 245 additions and 23 deletions

View File

@@ -15,6 +15,9 @@
E20209FC25D8E19100EFE733 /* MonsterViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E20209FA25D8E19100EFE733 /* MonsterViewModel.swift */; }; E20209FC25D8E19100EFE733 /* MonsterViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E20209FA25D8E19100EFE733 /* MonsterViewModel.swift */; };
E210B83A25B42D980083EAC5 /* MCProficiencyPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = E210B83925B42D980083EAC5 /* MCProficiencyPicker.swift */; }; E210B83A25B42D980083EAC5 /* MCProficiencyPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = E210B83925B42D980083EAC5 /* MCProficiencyPicker.swift */; };
E210B83F25B42DAB0083EAC5 /* MCAdvantagePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = E210B83E25B42DAB0083EAC5 /* MCAdvantagePicker.swift */; }; E210B83F25B42DAB0083EAC5 /* MCAdvantagePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = E210B83E25B42DAB0083EAC5 /* MCAdvantagePicker.swift */; };
E216B791260C1FE800FB205F /* LanguageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E216B790260C1FE800FB205F /* LanguageViewModel.swift */; };
E216B799260C2DF200FB205F /* EditLanguages.swift in Sources */ = {isa = PBXBuildFile; fileRef = E216B798260C2DF200FB205F /* EditLanguages.swift */; };
E216B79E260C396F00FB205F /* EditLanguage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E216B79D260C396F00FB205F /* EditLanguage.swift */; };
E2182E6425B22F8A00DFAEF8 /* Monster+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2182E6225B22F8A00DFAEF8 /* Monster+CoreDataClass.swift */; }; E2182E6425B22F8A00DFAEF8 /* Monster+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2182E6225B22F8A00DFAEF8 /* Monster+CoreDataClass.swift */; };
E24ACE502607326E009BF703 /* EditBasicInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24ACE4F2607326E009BF703 /* EditBasicInfo.swift */; }; E24ACE502607326E009BF703 /* EditBasicInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24ACE4F2607326E009BF703 /* EditBasicInfo.swift */; };
E24ACE562607EE94009BF703 /* EditArmor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24ACE552607EE94009BF703 /* EditArmor.swift */; }; E24ACE562607EE94009BF703 /* EditArmor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24ACE552607EE94009BF703 /* EditArmor.swift */; };
@@ -77,6 +80,9 @@
E20209FA25D8E19100EFE733 /* MonsterViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MonsterViewModel.swift; sourceTree = "<group>"; }; E20209FA25D8E19100EFE733 /* MonsterViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MonsterViewModel.swift; sourceTree = "<group>"; };
E210B83925B42D980083EAC5 /* MCProficiencyPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MCProficiencyPicker.swift; sourceTree = "<group>"; }; E210B83925B42D980083EAC5 /* MCProficiencyPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MCProficiencyPicker.swift; sourceTree = "<group>"; };
E210B83E25B42DAB0083EAC5 /* MCAdvantagePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MCAdvantagePicker.swift; sourceTree = "<group>"; }; E210B83E25B42DAB0083EAC5 /* MCAdvantagePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MCAdvantagePicker.swift; sourceTree = "<group>"; };
E216B790260C1FE800FB205F /* LanguageViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageViewModel.swift; sourceTree = "<group>"; };
E216B798260C2DF200FB205F /* EditLanguages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditLanguages.swift; sourceTree = "<group>"; };
E216B79D260C396F00FB205F /* EditLanguage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditLanguage.swift; sourceTree = "<group>"; };
E2182E6225B22F8A00DFAEF8 /* Monster+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Monster+CoreDataClass.swift"; sourceTree = "<group>"; }; E2182E6225B22F8A00DFAEF8 /* Monster+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Monster+CoreDataClass.swift"; sourceTree = "<group>"; };
E24ACE4F2607326E009BF703 /* EditBasicInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditBasicInfo.swift; sourceTree = "<group>"; }; E24ACE4F2607326E009BF703 /* EditBasicInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditBasicInfo.swift; sourceTree = "<group>"; };
E24ACE552607EE94009BF703 /* EditArmor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditArmor.swift; sourceTree = "<group>"; }; E24ACE552607EE94009BF703 /* EditArmor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditArmor.swift; sourceTree = "<group>"; };
@@ -180,15 +186,15 @@
E2570FB725B1AC520055B23B /* MonsterCards */ = { E2570FB725B1AC520055B23B /* MonsterCards */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
E2570FB825B1AC520055B23B /* MonsterCardsApp.swift */,
E2D473FB25B5328800CB36D7 /* Helpers */,
E257101225B1B2790055B23B /* Models */,
E2570FEB25B1ADA90055B23B /* Views */,
E2570FBC25B1AC550055B23B /* Assets.xcassets */, E2570FBC25B1AC550055B23B /* Assets.xcassets */,
E2570FC125B1AC550055B23B /* Persistence.swift */, E2D473FB25B5328800CB36D7 /* Helpers */,
E2570FC625B1AC550055B23B /* Info.plist */, E2570FC625B1AC550055B23B /* Info.plist */,
E257101225B1B2790055B23B /* Models */,
E2570FC325B1AC550055B23B /* MonsterCards.xcdatamodeld */, E2570FC325B1AC550055B23B /* MonsterCards.xcdatamodeld */,
E2570FB825B1AC520055B23B /* MonsterCardsApp.swift */,
E2570FC125B1AC550055B23B /* Persistence.swift */,
E2570FBE25B1AC550055B23B /* Preview Content */, E2570FBE25B1AC550055B23B /* Preview Content */,
E2570FEB25B1ADA90055B23B /* Views */,
); );
path = MonsterCards; path = MonsterCards;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -228,12 +234,14 @@
E24ACE5F2607F45E009BF703 /* EditAbilityScores.swift */, E24ACE5F2607F45E009BF703 /* EditAbilityScores.swift */,
E24ACE552607EE94009BF703 /* EditArmor.swift */, E24ACE552607EE94009BF703 /* EditArmor.swift */,
E24ACE4F2607326E009BF703 /* EditBasicInfo.swift */, E24ACE4F2607326E009BF703 /* EditBasicInfo.swift */,
E2CB0DE526088CE400142591 /* EditStrings.swift */, E216B79D260C396F00FB205F /* EditLanguage.swift */,
E216B798260C2DF200FB205F /* EditLanguages.swift */,
E2B5285825B3028700AAA69E /* EditMonster.swift */, E2B5285825B3028700AAA69E /* EditMonster.swift */,
E24ACE642607F55D009BF703 /* EditSavingThrows.swift */, E24ACE642607F55D009BF703 /* EditSavingThrows.swift */,
E2CB0DB226080C0500142591 /* EditSkill.swift */, E2CB0DB226080C0500142591 /* EditSkill.swift */,
E24ACE692607F715009BF703 /* EditSkills.swift */, E24ACE692607F715009BF703 /* EditSkills.swift */,
E24ACE5A2607F0F2009BF703 /* EditSpeed.swift */, E24ACE5A2607F0F2009BF703 /* EditSpeed.swift */,
E2CB0DE526088CE400142591 /* EditStrings.swift */,
E2570FFE25B1AE180055B23B /* Library.swift */, E2570FFE25B1AE180055B23B /* Library.swift */,
E2CB0DB726081A2F00142591 /* MCAbilityScorePicker.swift */, E2CB0DB726081A2F00142591 /* MCAbilityScorePicker.swift */,
E210B83E25B42DAB0083EAC5 /* MCAdvantagePicker.swift */, E210B83E25B42DAB0083EAC5 /* MCAdvantagePicker.swift */,
@@ -251,12 +259,13 @@
E257101225B1B2790055B23B /* Models */ = { E257101225B1B2790055B23B /* Models */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
E2CB0DE0260887ED00142591 /* StringViewModel.swift */,
E20209E625D8DEB600EFE733 /* Enums */, E20209E625D8DEB600EFE733 /* Enums */,
E216B790260C1FE800FB205F /* LanguageViewModel.swift */,
E2182E6225B22F8A00DFAEF8 /* Monster+CoreDataClass.swift */, E2182E6225B22F8A00DFAEF8 /* Monster+CoreDataClass.swift */,
E20209FA25D8E19100EFE733 /* MonsterViewModel.swift */, E20209FA25D8E19100EFE733 /* MonsterViewModel.swift */,
E20209D225D8DD9600EFE733 /* Skill+CoreDataClass.swift */, E20209D225D8DD9600EFE733 /* Skill+CoreDataClass.swift */,
E20209F925D8E19100EFE733 /* SkillViewModel.swift */, E20209F925D8E19100EFE733 /* SkillViewModel.swift */,
E2CB0DE0260887ED00142591 /* StringViewModel.swift */,
); );
path = Models; path = Models;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -402,10 +411,12 @@
E20209FB25D8E19100EFE733 /* SkillViewModel.swift in Sources */, E20209FB25D8E19100EFE733 /* SkillViewModel.swift in Sources */,
E24ACE602607F45E009BF703 /* EditAbilityScores.swift in Sources */, E24ACE602607F45E009BF703 /* EditAbilityScores.swift in Sources */,
E2570FC225B1AC550055B23B /* Persistence.swift in Sources */, E2570FC225B1AC550055B23B /* Persistence.swift in Sources */,
E216B799260C2DF200FB205F /* EditLanguages.swift in Sources */,
E2570FBB25B1AC520055B23B /* ContentView.swift in Sources */, E2570FBB25B1AC520055B23B /* ContentView.swift in Sources */,
E24ACE502607326E009BF703 /* EditBasicInfo.swift in Sources */, E24ACE502607326E009BF703 /* EditBasicInfo.swift in Sources */,
E2570FC525B1AC550055B23B /* MonsterCards.xcdatamodeld in Sources */, E2570FC525B1AC550055B23B /* MonsterCards.xcdatamodeld in Sources */,
E2182E6425B22F8A00DFAEF8 /* Monster+CoreDataClass.swift in Sources */, E2182E6425B22F8A00DFAEF8 /* Monster+CoreDataClass.swift in Sources */,
E216B791260C1FE800FB205F /* LanguageViewModel.swift in Sources */,
E210B83A25B42D980083EAC5 /* MCProficiencyPicker.swift in Sources */, E210B83A25B42D980083EAC5 /* MCProficiencyPicker.swift in Sources */,
E2570FF025B1ADC10055B23B /* Search.swift in Sources */, E2570FF025B1ADC10055B23B /* Search.swift in Sources */,
E257100925B1B2480055B23B /* MonsterDetail.swift in Sources */, E257100925B1B2480055B23B /* MonsterDetail.swift in Sources */,
@@ -420,6 +431,7 @@
E24ACE6A2607F715009BF703 /* EditSkills.swift in Sources */, E24ACE6A2607F715009BF703 /* EditSkills.swift in Sources */,
E20209FC25D8E19100EFE733 /* MonsterViewModel.swift in Sources */, E20209FC25D8E19100EFE733 /* MonsterViewModel.swift in Sources */,
E2570FFF25B1AE180055B23B /* Library.swift in Sources */, E2570FFF25B1AE180055B23B /* Library.swift in Sources */,
E216B79E260C396F00FB205F /* EditLanguage.swift in Sources */,
E2BD703125B3BBB90058ED69 /* MCStepperField.swift in Sources */, E2BD703125B3BBB90058ED69 /* MCStepperField.swift in Sources */,
E2CB0DE626088CE400142591 /* EditStrings.swift in Sources */, E2CB0DE626088CE400142591 /* EditStrings.swift in Sources */,
E2CB0DB326080C0500142591 /* EditSkill.swift in Sources */, E2CB0DB326080C0500142591 /* EditSkill.swift in Sources */,

View File

@@ -0,0 +1,79 @@
//
// LanguageViewModel.swift
// MonsterCards
//
// Created by Tom Hicks on 3/24/21.
//
import Foundation
// TODO: split this into separate Model and ViewModel classes later.
public class LanguageViewModel : NSObject, ObservableObject, Comparable, Identifiable, NSSecureCoding {
public static var supportsSecureCoding = true
public func encode(with coder: NSCoder) {
coder.encode(self.name, forKey: "name")
coder.encode(self.speaks, forKey: "speaks")
}
public required init?(coder: NSCoder) {
self.name = coder.decodeObject(of: NSString.self, forKey: "name")! as String
self.speaks = coder.decodeBool(forKey: "speaks")
}
public static func < (lhs: LanguageViewModel, rhs: LanguageViewModel) -> Bool {
lhs.name < rhs.name
}
public static func == (lhs: LanguageViewModel, rhs: LanguageViewModel) -> Bool {
lhs.name == rhs.name &&
lhs.speaks == rhs.speaks
}
@Published var name: String
@Published var speaks: Bool
init(
_ name: String = "",
_ speaks: Bool = true
) {
self.name = name
self.speaks = speaks
}
}
// TODO: figure out how to add this to the set of known transformers so it will work with transformer set to NSSecureUnarchiveFromDataTransformerName
@objc(LanguageViewModelValueTransformer)
public final class LanguageViewModelValueTransformer: ValueTransformer {
override public class func transformedValueClass() -> AnyClass {
return NSArray.self
}
override public class func allowsReverseTransformation() -> Bool {
return true
}
override public func transformedValue(_ value: Any?) -> Any? {
guard let language = value as? NSArray else { return nil }
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: language, requiringSecureCoding: true)
return data
} catch {
assertionFailure("Failed to transform `LanguageViewModel` to `Data`")
return nil
}
}
override public func reverseTransformedValue(_ value: Any?) -> Any? {
guard let data = value as? NSData else { return nil }
do {
let language = try NSKeyedUnarchiver.unarchivedArrayOfObjects(ofClass: LanguageViewModel.self, from: data as Data)
return language
} catch {
assertionFailure("Failed to transform `Data` to `LanguageViewModel`")
return nil
}
}
}

View File

@@ -698,8 +698,51 @@ public class Monster: NSManagedObject {
var languagesDescription: String { var languagesDescription: String {
get { get {
let sortedLanguages = self.languagesArray.sorted() let spokenLanguages = (self.languages ?? [])
return StringHelper.oxfordJoin(sortedLanguages, ", ", ", and ", " and ") .filter({ $0.speaks })
.map({$0.name})
.sorted()
let understoodLanguages = (self.languages ?? [])
.filter({ !$0.speaks })
.map({$0.name})
.sorted()
let understandsButText = understandsBut?.isEmpty ?? false
? ""
: String(format: " but %@", understandsBut!)
let telepathyText = telepathy > 0
? String(format: ", telepathy %d ft.", telepathy)
: ""
if (spokenLanguages.count > 0) {
if (understoodLanguages.count > 0) {
return String(
format:"%@ and understands %@%@%@",
StringHelper.oxfordJoin(spokenLanguages),
StringHelper.oxfordJoin(understoodLanguages),
understandsButText,
telepathyText)
} else {
return String(
format: "%@%@%@",
StringHelper.oxfordJoin(spokenLanguages),
understandsButText,
telepathyText)
}
} else {
if (understoodLanguages.count > 0) {
return String(
format: "understands %@%@%@",
StringHelper.oxfordJoin(understoodLanguages),
understandsButText,
telepathyText)
} else if (telepathy > 0){
return String(format: "telepathy %d ft.", telepathy)
} else {
return ""
}
}
} }
} }

View File

@@ -54,6 +54,9 @@ class MonsterViewModel: ObservableObject {
@Published var damageVulnerabilities: [StringViewModel] @Published var damageVulnerabilities: [StringViewModel]
@Published var conditionImmunities: [StringViewModel] @Published var conditionImmunities: [StringViewModel]
@Published var senses: [StringViewModel] @Published var senses: [StringViewModel]
@Published var languages: [LanguageViewModel]
@Published var telepathy: Int64
@Published var understandsBut: String
init(_ rawMonster: Monster? = nil) { init(_ rawMonster: Monster? = nil) {
self.name = "" self.name = ""
@@ -100,6 +103,9 @@ class MonsterViewModel: ObservableObject {
self.damageVulnerabilities = [] self.damageVulnerabilities = []
self.conditionImmunities = [] self.conditionImmunities = []
self.senses = [] self.senses = []
self.languages = []
self.telepathy = 0
self.understandsBut = ""
if (rawMonster != nil) { if (rawMonster != nil) {
self.copyFromMonster(monster: rawMonster!) self.copyFromMonster(monster: rawMonster!)
@@ -145,6 +151,8 @@ class MonsterViewModel: ObservableObject {
self.charismaScore = monster.charismaScore self.charismaScore = monster.charismaScore
self.charismaSavingThrowAdvantage = monster.charismaSavingThrowAdvantageEnum self.charismaSavingThrowAdvantage = monster.charismaSavingThrowAdvantageEnum
self.charismaSavingThrowProficiency = monster.charismaSavingThrowProficiencyEnum self.charismaSavingThrowProficiency = monster.charismaSavingThrowProficiencyEnum
self.telepathy = monster.telepathy
self.understandsBut = monster.understandsBut ?? ""
self.skills = (monster.skills?.allObjects.map {SkillViewModel(($0 as! Skill))})!.sorted() self.skills = (monster.skills?.allObjects.map {SkillViewModel(($0 as! Skill))})!.sorted()
self.damageImmunities = (monster.damageImmunities ?? []) self.damageImmunities = (monster.damageImmunities ?? [])
@@ -166,6 +174,9 @@ class MonsterViewModel: ObservableObject {
self.senses = (monster.senses ?? []) self.senses = (monster.senses ?? [])
.map {StringViewModel($0)} .map {StringViewModel($0)}
.sorted() .sorted()
self.languages = (monster.languages ?? [])
.sorted()
} }
func copyToMonster(monster: Monster) { func copyToMonster(monster: Monster) {
@@ -207,6 +218,8 @@ class MonsterViewModel: ObservableObject {
monster.charismaScore = charismaScore monster.charismaScore = charismaScore
monster.charismaSavingThrowAdvantageEnum = charismaSavingThrowAdvantage monster.charismaSavingThrowAdvantageEnum = charismaSavingThrowAdvantage
monster.charismaSavingThrowProficiencyEnum = charismaSavingThrowProficiency monster.charismaSavingThrowProficiencyEnum = charismaSavingThrowProficiency
monster.telepathy = telepathy
monster.understandsBut = understandsBut
// Remove missing skills from raw monster // Remove missing skills from raw monster
monster.skills?.forEach {s in monster.skills?.forEach {s in
@@ -233,5 +246,6 @@ class MonsterViewModel: ObservableObject {
monster.damageResistances = damageResistances.map {$0.name} monster.damageResistances = damageResistances.map {$0.name}
monster.damageVulnerabilities = damageVulnerabilities.map {$0.name} monster.damageVulnerabilities = damageVulnerabilities.map {$0.name}
monster.senses = senses.map {$0.name} monster.senses = senses.map {$0.name}
monster.languages = languages.map {LanguageViewModel($0.name, $0.speaks)}
} }
} }

View File

@@ -36,6 +36,7 @@
<attribute name="intelligenceSavingThrowProficiency" attributeType="String" defaultValueString="none"/> <attribute name="intelligenceSavingThrowProficiency" attributeType="String" defaultValueString="none"/>
<attribute name="intelligenceScore" attributeType="Integer 64" defaultValueString="10" usesScalarValueType="YES"/> <attribute name="intelligenceScore" attributeType="Integer 64" defaultValueString="10" usesScalarValueType="YES"/>
<attribute name="isBlind" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/> <attribute name="isBlind" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="languages" optional="YES" attributeType="Transformable" valueTransformerName="LanguageViewModelValueTransformer" customClassName="[LanguageViewModel]"/>
<attribute name="name" attributeType="String" defaultValueString="Unnamed Monster"/> <attribute name="name" attributeType="String" defaultValueString="Unnamed Monster"/>
<attribute name="naturalArmorBonus" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/> <attribute name="naturalArmorBonus" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="otherArmorDescription" attributeType="String" defaultValueString=""/> <attribute name="otherArmorDescription" attributeType="String" defaultValueString=""/>
@@ -47,6 +48,7 @@
<attribute name="strengthScore" attributeType="Integer 64" defaultValueString="10" usesScalarValueType="YES"/> <attribute name="strengthScore" attributeType="Integer 64" defaultValueString="10" usesScalarValueType="YES"/>
<attribute name="subtype" attributeType="String" defaultValueString=""/> <attribute name="subtype" attributeType="String" defaultValueString=""/>
<attribute name="swimSpeed" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/> <attribute name="swimSpeed" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="telepathy" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="tremorsenseDistance" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/> <attribute name="tremorsenseDistance" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="truesightDistance" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/> <attribute name="truesightDistance" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="type" attributeType="String" defaultValueString=""/> <attribute name="type" attributeType="String" defaultValueString=""/>
@@ -64,7 +66,7 @@
<relationship name="monster" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Monster" inverseName="skills" inverseEntity="Monster"/> <relationship name="monster" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Monster" inverseName="skills" inverseEntity="Monster"/>
</entity> </entity>
<elements> <elements>
<element name="Monster" positionX="-63" positionY="-18" width="128" height="839"/> <element name="Monster" positionX="-63" positionY="-18" width="128" height="869"/>
<element name="Skill" positionX="-63" positionY="135" width="128" height="14"/> <element name="Skill" positionX="-63" positionY="135" width="128" height="14"/>
</elements> </elements>
</model> </model>

View File

@@ -38,19 +38,6 @@ struct PersistenceController {
} }
container.loadPersistentStores(completionHandler: { (storeDescription, error) in container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? { if let error = error as NSError? {
// NSPersistentStoreCoordinator.destroyPersistentStore(storeDes)
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)") fatalError("Unresolved error \(error), \(error.userInfo)")
} }
}) })

View File

@@ -0,0 +1,28 @@
//
// EditLanguage.swift
// MonsterCards
//
// Created by Tom Hicks on 3/24/21.
//
import SwiftUI
struct EditLanguage: View {
@ObservedObject var viewModel: LanguageViewModel
var body: some View {
MCTextField(
label: "Name",
value: $viewModel.name)
.autocapitalization(.none)
Toggle("Speaks", isOn: $viewModel.speaks)
}
}
struct EditLanguage_Previews: PreviewProvider {
static var previews: some View {
let viewModel = LanguageViewModel()
EditLanguage(viewModel: viewModel)
}
}

View File

@@ -0,0 +1,48 @@
//
// EditLanguages.swift
// MonsterCards
//
// Created by Tom Hicks on 3/24/21.
//
import SwiftUI
struct EditLanguages: View {
@ObservedObject var viewModel: MonsterViewModel
var body: some View {
let sortedLanguages = viewModel.languages.sorted()
List {
MCTextField(
label: "Understands But",
value: $viewModel.understandsBut)
MCStepperField(label: "Telepathy", prefix: "", step: 5, suffix: " ft.", value: $viewModel.telepathy)
ForEach(sortedLanguages/*viewModel.languages*/) { language in
NavigationLink(language.name, destination: EditLanguage(viewModel: language))
}
}
.toolbar(content: {
Button(
action: {
let newLanguage = LanguageViewModel("English")
viewModel.languages.append(newLanguage)
viewModel.languages = viewModel.languages.sorted()
},
label: {
Image(systemName: "plus")
}
)
})
.onAppear(perform: {
viewModel.languages = viewModel.languages.sorted()
})
}
}
struct EditLanguages_Previews: PreviewProvider {
static var previews: some View {
let viewModel = MonsterViewModel()
EditLanguages(viewModel: viewModel)
}
}

View File

@@ -65,6 +65,10 @@ struct EditMonster: View {
NavigationLink( NavigationLink(
"Senses", "Senses",
destination: EditStrings(viewModel: monsterViewModel, path: \.senses, title: "Senses")) destination: EditStrings(viewModel: monsterViewModel, path: \.senses, title: "Senses"))
NavigationLink(
"Languages",
destination: EditLanguages(viewModel: monsterViewModel))
} }
} }

View File

@@ -302,6 +302,11 @@ struct MonsterDetail_Previews: PreviewProvider {
monster.wisdomSavingThrowProficiencyEnum = ProficiencyType.proficient monster.wisdomSavingThrowProficiencyEnum = ProficiencyType.proficient
monster.charismaSavingThrowAdvantageEnum = AdvantageType.disadvantage monster.charismaSavingThrowAdvantageEnum = AdvantageType.disadvantage
monster.charismaSavingThrowProficiencyEnum = ProficiencyType.none monster.charismaSavingThrowProficiencyEnum = ProficiencyType.none
monster.telepathy = 1
monster.languages = [
LanguageViewModel("English", true),
LanguageViewModel("French", false)
]
return Group { return Group {
MonsterDetail(monster: monster) MonsterDetail(monster: monster)