From a9ad7a7fa8b2a5ec34d7ab57e32256aa98044ca7 Mon Sep 17 00:00:00 2001 From: Tom Hicks Date: Thu, 25 Mar 2021 01:22:37 -0700 Subject: [PATCH] Adds MarkdownUI dependency and abilities. --- iOS/MonsterCards.xcodeproj/project.pbxproj | 41 ++++++- .../xcshareddata/swiftpm/Package.resolved | 70 +++++++++++ .../xcschemes/xcschememanagement.plist | 63 ++++++++++ .../Models/AbilityViewModel.swift | 116 ++++++++++++++++++ .../Models/MonsterViewModel.swift | 9 ++ .../MonsterCards.xcdatamodel/contents | 3 +- iOS/MonsterCards/Views/EditAbilities.swift | 64 ++++++++++ iOS/MonsterCards/Views/EditAbility.swift | 29 +++++ iOS/MonsterCards/Views/EditMonster.swift | 3 + iOS/MonsterCards/Views/EditStrings.swift | 12 +- iOS/MonsterCards/Views/MonsterDetail.swift | 12 +- 11 files changed, 415 insertions(+), 7 deletions(-) create mode 100644 iOS/MonsterCards.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 iOS/MonsterCards/Models/AbilityViewModel.swift create mode 100644 iOS/MonsterCards/Views/EditAbilities.swift create mode 100644 iOS/MonsterCards/Views/EditAbility.swift diff --git a/iOS/MonsterCards.xcodeproj/project.pbxproj b/iOS/MonsterCards.xcodeproj/project.pbxproj index cad44d6..9cbf1e7 100644 --- a/iOS/MonsterCards.xcodeproj/project.pbxproj +++ b/iOS/MonsterCards.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 52; objects = { /* Begin PBXBuildFile section */ @@ -28,6 +28,10 @@ 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 */; }; + E254F901260D07C1009295A5 /* MarkdownUI in Frameworks */ = {isa = PBXBuildFile; productRef = E254F900260D07C1009295A5 /* MarkdownUI */; }; + E254F906260D0818009295A5 /* AbilityViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E254F905260D0818009295A5 /* AbilityViewModel.swift */; }; + E254F90E260D19A0009295A5 /* EditAbilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = E254F90D260D19A0009295A5 /* EditAbilities.swift */; }; + E254F913260D1F6D009295A5 /* EditAbility.swift in Sources */ = {isa = PBXBuildFile; fileRef = E254F912260D1F6D009295A5 /* EditAbility.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 */; }; @@ -96,6 +100,9 @@ E24ACE5F2607F45E009BF703 /* EditAbilityScores.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditAbilityScores.swift; sourceTree = ""; }; E24ACE642607F55D009BF703 /* EditSavingThrows.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditSavingThrows.swift; sourceTree = ""; }; E24ACE692607F715009BF703 /* EditSkills.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditSkills.swift; sourceTree = ""; }; + E254F905260D0818009295A5 /* AbilityViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AbilityViewModel.swift; sourceTree = ""; }; + E254F90D260D19A0009295A5 /* EditAbilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditAbilities.swift; sourceTree = ""; }; + E254F912260D1F6D009295A5 /* EditAbility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditAbility.swift; sourceTree = ""; }; 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 = ""; }; E2570FBA25B1AC520055B23B /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -136,6 +143,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + E254F901260D07C1009295A5 /* MarkdownUI in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -237,6 +245,8 @@ E2570FF925B1AE020055B23B /* Collections.swift */, E2570FBA25B1AC520055B23B /* ContentView.swift */, E2570FF425B1ADEB0055B23B /* Dashboard.swift */, + E254F90D260D19A0009295A5 /* EditAbilities.swift */, + E254F912260D1F6D009295A5 /* EditAbility.swift */, E24ACE5F2607F45E009BF703 /* EditAbilityScores.swift */, E24ACE552607EE94009BF703 /* EditArmor.swift */, E24ACE4F2607326E009BF703 /* EditBasicInfo.swift */, @@ -267,6 +277,7 @@ E257101225B1B2790055B23B /* Models */ = { isa = PBXGroup; children = ( + E254F905260D0818009295A5 /* AbilityViewModel.swift */, E216B7B6260C5A9800FB205F /* ChallengeRatingViewModel.swift */, E20209E625D8DEB600EFE733 /* Enums */, E216B790260C1FE800FB205F /* LanguageViewModel.swift */, @@ -304,6 +315,9 @@ dependencies = ( ); name = MonsterCards; + packageProductDependencies = ( + E254F900260D07C1009295A5 /* MarkdownUI */, + ); productName = MonsterCards; productReference = E2570FB525B1AC520055B23B /* MonsterCards.app */; productType = "com.apple.product-type.application"; @@ -375,6 +389,9 @@ Base, ); mainGroup = E2570FAC25B1AC520055B23B; + packageReferences = ( + E254F8FF260D07C1009295A5 /* XCRemoteSwiftPackageReference "MarkdownUI" */, + ); productRefGroup = E2570FB625B1AC520055B23B /* Products */; projectDirPath = ""; projectRoot = ""; @@ -423,6 +440,7 @@ E216B799260C2DF200FB205F /* EditLanguages.swift in Sources */, E2570FBB25B1AC520055B23B /* ContentView.swift in Sources */, E24ACE502607326E009BF703 /* EditBasicInfo.swift in Sources */, + E254F90E260D19A0009295A5 /* EditAbilities.swift in Sources */, E2570FC525B1AC550055B23B /* MonsterCards.xcdatamodeld in Sources */, E2182E6425B22F8A00DFAEF8 /* Monster+CoreDataClass.swift in Sources */, E216B791260C1FE800FB205F /* LanguageViewModel.swift in Sources */, @@ -449,9 +467,11 @@ E2CB0DE1260887ED00142591 /* StringViewModel.swift in Sources */, E20209F425D8E04300EFE733 /* ProficiencyType.swift in Sources */, E2CB0DC526086E5F00142591 /* SizeType.swift in Sources */, + E254F906260D0818009295A5 /* AbilityViewModel.swift in Sources */, E2570FFA25B1AE020055B23B /* Collections.swift in Sources */, E24ACE5B2607F0F2009BF703 /* EditSpeed.swift in Sources */, E2570FB925B1AC520055B23B /* MonsterCardsApp.swift in Sources */, + E254F913260D1F6D009295A5 /* EditAbility.swift in Sources */, E216B7B7260C5A9800FB205F /* ChallengeRatingViewModel.swift in Sources */, E20209D325D8DD9600EFE733 /* Skill+CoreDataClass.swift in Sources */, E24ACE652607F55D009BF703 /* EditSavingThrows.swift in Sources */, @@ -781,6 +801,25 @@ }; /* End XCConfigurationList section */ +/* Begin XCRemoteSwiftPackageReference section */ + E254F8FF260D07C1009295A5 /* XCRemoteSwiftPackageReference "MarkdownUI" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/gonzalezreal/MarkdownUI"; + requirement = { + kind = upToNextMinorVersion; + minimumVersion = 0.5.1; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + E254F900260D07C1009295A5 /* MarkdownUI */ = { + isa = XCSwiftPackageProductDependency; + package = E254F8FF260D07C1009295A5 /* XCRemoteSwiftPackageReference "MarkdownUI" */; + productName = MarkdownUI; + }; +/* End XCSwiftPackageProductDependency section */ + /* Begin XCVersionGroup section */ E2570FC325B1AC550055B23B /* MonsterCards.xcdatamodeld */ = { isa = XCVersionGroup; diff --git a/iOS/MonsterCards.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/iOS/MonsterCards.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..4fd9a9d --- /dev/null +++ b/iOS/MonsterCards.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,70 @@ +{ + "object": { + "pins": [ + { + "package": "AttributedText", + "repositoryURL": "https://github.com/gonzalezreal/AttributedText", + "state": { + "branch": null, + "revision": "bf076de48dbb2172525486936d512e1bba062642", + "version": "0.3.0" + } + }, + { + "package": "combine-schedulers", + "repositoryURL": "https://github.com/pointfreeco/combine-schedulers", + "state": { + "branch": null, + "revision": "f1250faa1c1436ca83950ce676a4fe97a309a457", + "version": "0.4.1" + } + }, + { + "package": "MarkdownUI", + "repositoryURL": "https://github.com/gonzalezreal/MarkdownUI", + "state": { + "branch": null, + "revision": "e8931e37dcf777b4c03ca76aa09c10cf246a2ced", + "version": "0.5.1" + } + }, + { + "package": "NetworkImage", + "repositoryURL": "https://github.com/gonzalezreal/NetworkImage", + "state": { + "branch": null, + "revision": "15582b821cb097012b41b83d6219717926ec4ed6", + "version": "2.1.0" + } + }, + { + "package": "cmark", + "repositoryURL": "https://github.com/SwiftDocOrg/swift-cmark.git", + "state": { + "branch": null, + "revision": "9c8096a23f44794bde297452d87c455fc4f76d42", + "version": "0.29.0+20210102.9c8096a" + } + }, + { + "package": "SwiftCommonMark", + "repositoryURL": "https://github.com/gonzalezreal/SwiftCommonMark", + "state": { + "branch": null, + "revision": "f1575c37110a386e50da3208a04266b398bcefaa", + "version": "0.1.1" + } + }, + { + "package": "xctest-dynamic-overlay", + "repositoryURL": "https://github.com/pointfreeco/xctest-dynamic-overlay", + "state": { + "branch": null, + "revision": "603974e3909ad4b48ba04aad7e0ceee4f077a518", + "version": "0.1.0" + } + } + ] + }, + "version": 1 +} diff --git a/iOS/MonsterCards.xcodeproj/xcuserdata/tom.xcuserdatad/xcschemes/xcschememanagement.plist b/iOS/MonsterCards.xcodeproj/xcuserdata/tom.xcuserdatad/xcschemes/xcschememanagement.plist index a49defd..036dfca 100644 --- a/iOS/MonsterCards.xcodeproj/xcuserdata/tom.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/iOS/MonsterCards.xcodeproj/xcuserdata/tom.xcuserdatad/xcschemes/xcschememanagement.plist @@ -4,6 +4,69 @@ SchemeUserState + AttributedText_iOS (Playground) 1.xcscheme + + isShown + + orderHint + 11 + + AttributedText_iOS (Playground) 2.xcscheme + + isShown + + orderHint + 12 + + AttributedText_iOS (Playground).xcscheme + + isShown + + orderHint + 10 + + AttributedText_macOS (Playground) 1.xcscheme + + isShown + + orderHint + 8 + + AttributedText_macOS (Playground) 2.xcscheme + + isShown + + orderHint + 9 + + AttributedText_macOS (Playground).xcscheme + + isShown + + orderHint + 7 + + AttributedText_tvOS (Playground) 1.xcscheme + + isShown + + orderHint + 14 + + AttributedText_tvOS (Playground) 2.xcscheme + + isShown + + orderHint + 15 + + AttributedText_tvOS (Playground).xcscheme + + isShown + + orderHint + 13 + MonsterCards.xcscheme_^#shared#^_ orderHint diff --git a/iOS/MonsterCards/Models/AbilityViewModel.swift b/iOS/MonsterCards/Models/AbilityViewModel.swift new file mode 100644 index 0000000..5643800 --- /dev/null +++ b/iOS/MonsterCards/Models/AbilityViewModel.swift @@ -0,0 +1,116 @@ +// +// AbilityViewModel.swift +// MonsterCards +// +// Created by Tom Hicks on 3/25/21. +// + +import Foundation + +public class AbilityViewModel: NSObject, ObservableObject, Identifiable, NSSecureCoding { + public static var supportsSecureCoding = true + + public func encode(with coder: NSCoder) { + coder.encode(self.name, forKey: "name") + coder.encode(self.abilityDescription, forKey: "abilityDescription") + } + + public required init?(coder: NSCoder) { + self.name = coder.decodeObject(of: NSString.self, forKey: "name")! as String + self.abilityDescription = coder.decodeObject(of: NSString.self, forKey: "abilityDescription")! as String + } + + @Published public var name: String + @Published public var abilityDescription: String + + public init(_ name: String = "", _ abilityDescription: String = "") { + self.name = name + self.abilityDescription = abilityDescription + } + + public var fullText: String { + get { + return String(format: "___%@:___ %@", name, abilityDescription) + } + } + + public func renderedText(_ monster: Monster) -> String { + let strSave = monster.strengthModifier + monster.proficiencyBonus + 8 + let dexSave = monster.dexterityModifier + monster.proficiencyBonus + 8 + let conSave = monster.constitutionModifier + monster.proficiencyBonus + 8 + let intSave = monster.intelligenceModifier + monster.proficiencyBonus + 8 + let wisSave = monster.wisdomModifier + monster.proficiencyBonus + 8 + let chaSave = monster.charismaModifier + monster.proficiencyBonus + 8 + let strAttack = monster.strengthModifier + monster.proficiencyBonus + let dexAttack = monster.dexterityModifier + monster.proficiencyBonus + let conAttack = monster.constitutionModifier + monster.proficiencyBonus + let intAttack = monster.intelligenceModifier + monster.proficiencyBonus + let wisAttack = monster.wisdomModifier + monster.proficiencyBonus + let chaAttack = monster.charismaModifier + monster.proficiencyBonus + + // TODO: find the other options and implement them [WIS], [WIS STAT], [WIS DMG], [WIS STAT 1d12] + + let finalText = fullText + .replacingOccurrences(of: "[STR SAVE]", with: String(strSave)) + .replacingOccurrences(of: "[DEX SAVE]", with: String(dexSave)) + .replacingOccurrences(of: "[CON SAVE]", with: String(conSave)) + .replacingOccurrences(of: "[INT SAVE]", with: String(intSave)) + .replacingOccurrences(of: "[WIS SAVE]", with: String(wisSave)) + .replacingOccurrences(of: "[CHA SAVE]", with: String(chaSave)) + .replacingOccurrences(of: "[STR ATK]", with: String(strAttack)) + .replacingOccurrences(of: "[DEX ATK]", with: String(dexAttack)) + .replacingOccurrences(of: "[CON ATK]", with: String(conAttack)) + .replacingOccurrences(of: "[INT ATK]", with: String(intAttack)) + .replacingOccurrences(of: "[WIS ATK]", with: String(wisAttack)) + .replacingOccurrences(of: "[CHA ATK]", with: String(chaAttack)) + + return finalText + } +} + +extension AbilityViewModel: Comparable { + public static func < (lhs: AbilityViewModel, rhs: AbilityViewModel) -> Bool { + lhs.name < rhs.name + } + + public static func == (lhs: AbilityViewModel, rhs: AbilityViewModel) -> Bool { + lhs.name == rhs.name && + lhs.abilityDescription == rhs.abilityDescription + } +} + +// TODO: figure out how to add this to the set of known transformers so it will work with transformer set to NSSecureUnarchiveFromDataTransformerName +@objc(AbilityViewModelValueTransformer) +public final class AbilityViewModelValueTransformer: 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 `AbilityViewModel` 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: AbilityViewModel.self, from: data as Data) + return language + } catch { + assertionFailure("Failed to transform `Data` to `AbilityViewModel`") + return nil + } + } +} diff --git a/iOS/MonsterCards/Models/MonsterViewModel.swift b/iOS/MonsterCards/Models/MonsterViewModel.swift index bf8fe18..63c0294 100644 --- a/iOS/MonsterCards/Models/MonsterViewModel.swift +++ b/iOS/MonsterCards/Models/MonsterViewModel.swift @@ -60,6 +60,7 @@ class MonsterViewModel: ObservableObject { @Published var challengeRating: ChallengeRating @Published var customChallengeRating: String @Published var customProficiencyBonus: Int64 + @Published var abilities: [AbilityViewModel] init(_ rawMonster: Monster? = nil) { self.name = "" @@ -112,6 +113,7 @@ class MonsterViewModel: ObservableObject { self.challengeRating = .one self.customChallengeRating = "" self.customProficiencyBonus = 0 + self.abilities = [] if (rawMonster != nil) { self.copyFromMonster(monster: rawMonster!) @@ -186,7 +188,12 @@ class MonsterViewModel: ObservableObject { .sorted() self.languages = (monster.languages ?? []) + .map {LanguageViewModel($0.name, $0.speaks)} .sorted() + + // These are manually sorted in the UI + self.abilities = (monster.abilities ?? []) + .map {AbilityViewModel($0.name, $0.abilityDescription)} } func copyToMonster(monster: Monster) { @@ -262,5 +269,7 @@ class MonsterViewModel: ObservableObject { // This is necessary so core data sees the language objects as changed. Without it they won't be persisted. monster.languages = languages.map {LanguageViewModel($0.name, $0.speaks)} + + monster.abilities = abilities.map {AbilityViewModel($0.name, $0.abilityDescription)} } } diff --git a/iOS/MonsterCards/MonsterCards.xcdatamodeld/MonsterCards.xcdatamodel/contents b/iOS/MonsterCards/MonsterCards.xcdatamodeld/MonsterCards.xcdatamodel/contents index 34a6b06..7aca238 100644 --- a/iOS/MonsterCards/MonsterCards.xcdatamodeld/MonsterCards.xcdatamodel/contents +++ b/iOS/MonsterCards/MonsterCards.xcdatamodeld/MonsterCards.xcdatamodel/contents @@ -1,6 +1,7 @@ + @@ -67,7 +68,7 @@ - + \ No newline at end of file diff --git a/iOS/MonsterCards/Views/EditAbilities.swift b/iOS/MonsterCards/Views/EditAbilities.swift new file mode 100644 index 0000000..8fa7bf0 --- /dev/null +++ b/iOS/MonsterCards/Views/EditAbilities.swift @@ -0,0 +1,64 @@ +// +// EditAbilities.swift +// MonsterCards +// +// Created by Tom Hicks on 3/25/21. +// + +import SwiftUI + +struct EditAbilities: View { + @ObservedObject var viewModel: MonsterViewModel + var path: ReferenceWritableKeyPath + var title: String + + var body: some View { + List { + ForEach(viewModel[keyPath: path]) { ability in + NavigationLink( + ability.name, + destination: EditAbility(viewModel: ability)) + } + .onDelete(perform: { indexSet in + for index in indexSet { + viewModel[keyPath: path].remove(at: index) + } + }) + .onMove(perform: { indices, newOffset in + viewModel[keyPath: path].move(fromOffsets: indices, toOffset: newOffset) + + }) + } + .toolbar(content: { + ToolbarItemGroup(placement: .navigationBarTrailing) { + EditButton() + + Button( + action: { + let newAbility = AbilityViewModel() + viewModel[keyPath: path].append(newAbility) + viewModel[keyPath: path] = viewModel[keyPath: path].sorted() + }, + label: { + Image(systemName: "plus") + } + ) + } + }) + .onAppear(perform: { + viewModel[keyPath: path] = viewModel[keyPath: path].sorted() + }) + .navigationTitle(title) + + } +} + +struct EditAbilities_Previews: PreviewProvider { + static var previews: some View { + let viewModel = MonsterViewModel() + EditAbilities( + viewModel: viewModel, + path: \.abilities, + title: "Abilities") + } +} diff --git a/iOS/MonsterCards/Views/EditAbility.swift b/iOS/MonsterCards/Views/EditAbility.swift new file mode 100644 index 0000000..32b696e --- /dev/null +++ b/iOS/MonsterCards/Views/EditAbility.swift @@ -0,0 +1,29 @@ +// +// EditAbility.swift +// MonsterCards +// +// Created by Tom Hicks on 3/25/21. +// + +import SwiftUI + +struct EditAbility: View { + @ObservedObject var viewModel: AbilityViewModel + + var body: some View { + VStack { + MCTextField( + label: "Name", + value: $viewModel.name) + + TextEditor(text: $viewModel.abilityDescription) + } + } +} + +struct EditAbility_Previews: PreviewProvider { + static var previews: some View { + let viewModel = AbilityViewModel() + EditAbility(viewModel: viewModel) + } +} diff --git a/iOS/MonsterCards/Views/EditMonster.swift b/iOS/MonsterCards/Views/EditMonster.swift index 465b829..0967135 100644 --- a/iOS/MonsterCards/Views/EditMonster.swift +++ b/iOS/MonsterCards/Views/EditMonster.swift @@ -73,6 +73,9 @@ struct EditMonster: View { NavigationLink( "Challenge Rating", destination: EditChallengeRating(viewModel: monsterViewModel)) + + NavigationLink( + "Abilities", destination: EditAbilities(viewModel: monsterViewModel, path: \.abilities, title: "Abilities")) } } diff --git a/iOS/MonsterCards/Views/EditStrings.swift b/iOS/MonsterCards/Views/EditStrings.swift index 8876495..2e35e6a 100644 --- a/iOS/MonsterCards/Views/EditStrings.swift +++ b/iOS/MonsterCards/Views/EditStrings.swift @@ -33,8 +33,8 @@ struct EditStrings: View { .toolbar(content: { Button( action: { - let newDamageType = StringViewModel() - viewModel[keyPath: path].append(newDamageType) + let newString = StringViewModel() + viewModel[keyPath: path].append(newString) viewModel[keyPath: path] = viewModel[keyPath: path].sorted() }, label: { @@ -44,13 +44,17 @@ struct EditStrings: View { }) .onAppear(perform: { viewModel[keyPath: path] = viewModel[keyPath: path].sorted() - }).navigationTitle(title) + }) + .navigationTitle(title) } } struct EditStrings_Previews: PreviewProvider { static var previews: some View { let viewModel = MonsterViewModel() - EditStrings(viewModel: viewModel, path: \.damageImmunities, title: "Damage Types") + EditStrings( + viewModel: viewModel, + path: \.damageImmunities, + title: "Damage Types") } } diff --git a/iOS/MonsterCards/Views/MonsterDetail.swift b/iOS/MonsterCards/Views/MonsterDetail.swift index 53fdc5e..e395265 100644 --- a/iOS/MonsterCards/Views/MonsterDetail.swift +++ b/iOS/MonsterCards/Views/MonsterDetail.swift @@ -6,6 +6,7 @@ // import SwiftUI +import MarkdownUI struct LabeledField: View { @Environment(\.horizontalSizeClass) var sizeClass @@ -209,6 +210,7 @@ struct MonsterDetail: View { VStack (alignment: .leading) { let monsterLanguagesDescription = monster.languagesDescription let monsterChallengeRatingDescription = monster.challengeRatingDescription + let monsterAbilities: [AbilityViewModel] = monster.abilities ?? [] BasicInfoView(monster: monster) @@ -243,7 +245,15 @@ struct MonsterDetail: View { } // Abilities - + if (monsterAbilities.count > 0) { + ForEach(monsterAbilities) { ability in + VStack { + Markdown(Document(ability.renderedText(monster)/*.fullText*/)) + Divider() + } + } + } + // Actions // Legendary Actions