Convertes to Swift and SwiftUI
This commit is contained in:
21
iOS/MonsterCards/Views/Collections.swift
Normal file
21
iOS/MonsterCards/Views/Collections.swift
Normal file
@@ -0,0 +1,21 @@
|
||||
//
|
||||
// Collections.swift
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 1/15/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct Collections: View {
|
||||
// @State var allCollections: [MonsterCollection] = []
|
||||
var body: some View {
|
||||
Text("Collections")
|
||||
}
|
||||
}
|
||||
|
||||
struct Collections_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Collections().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
//
|
||||
// CollectionsViewController.h
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 9/4/20.
|
||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface CollectionsViewController : UIViewController
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -1,32 +0,0 @@
|
||||
//
|
||||
// CollectionsViewController.m
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 9/4/20.
|
||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "CollectionsViewController.h"
|
||||
|
||||
@interface CollectionsViewController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation CollectionsViewController
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
// Do any additional setup after loading the view.
|
||||
}
|
||||
|
||||
/*
|
||||
#pragma mark - Navigation
|
||||
|
||||
// In a storyboard-based application, you will often want to do a little preparation before navigation
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
||||
// Get the new view controller using [segue destinationViewController].
|
||||
// Pass the selected object to the new view controller.
|
||||
}
|
||||
*/
|
||||
|
||||
@end
|
||||
106
iOS/MonsterCards/Views/ContentView.swift
Normal file
106
iOS/MonsterCards/Views/ContentView.swift
Normal file
@@ -0,0 +1,106 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 1/15/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import CoreData
|
||||
|
||||
struct ContentView: View {
|
||||
@Environment(\.managedObjectContext) private var viewContext
|
||||
|
||||
var body: some View {
|
||||
TabView {
|
||||
Search()
|
||||
.tabItem {
|
||||
Image(systemName: "magnifyingglass")
|
||||
Text("Search")
|
||||
}
|
||||
Dashboard()
|
||||
.tabItem {
|
||||
Image(systemName: "rectangle.3.offgrid.fill")
|
||||
Text("Dashboard")
|
||||
|
||||
}
|
||||
Collections()
|
||||
.tabItem {
|
||||
Image(systemName: "tray.full.fill")
|
||||
Text("Collections")
|
||||
}
|
||||
Library()
|
||||
.tabItem {
|
||||
Image(systemName: "book.fill")
|
||||
Text("Library")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @FetchRequest(
|
||||
// sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)],
|
||||
// animation: .default)
|
||||
// private var items: FetchedResults<Item>
|
||||
//
|
||||
// var body: some View {
|
||||
// List {
|
||||
// ForEach(items) { item in
|
||||
// Text("Item at \(item.timestamp!, formatter: itemFormatter)")
|
||||
// }
|
||||
// .onDelete(perform: deleteItems)
|
||||
// }
|
||||
// .toolbar {
|
||||
// #if os(iOS)
|
||||
// EditButton()
|
||||
// #endif
|
||||
//
|
||||
// Button(action: addItem) {
|
||||
// Label("Add Item", systemImage: "plus")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private func addItem() {
|
||||
// withAnimation {
|
||||
// let newItem = Item(context: viewContext)
|
||||
// newItem.timestamp = Date()
|
||||
//
|
||||
// do {
|
||||
// try viewContext.save()
|
||||
// } catch {
|
||||
// // 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.
|
||||
// let nsError = error as NSError
|
||||
// fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private func deleteItems(offsets: IndexSet) {
|
||||
// withAnimation {
|
||||
// offsets.map { items[$0] }.forEach(viewContext.delete)
|
||||
//
|
||||
// do {
|
||||
// try viewContext.save()
|
||||
// } catch {
|
||||
// // 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.
|
||||
// let nsError = error as NSError
|
||||
// fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
//private let itemFormatter: DateFormatter = {
|
||||
// let formatter = DateFormatter()
|
||||
// formatter.dateStyle = .short
|
||||
// formatter.timeStyle = .medium
|
||||
// return formatter
|
||||
//}()
|
||||
|
||||
struct ContentView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
|
||||
}
|
||||
}
|
||||
20
iOS/MonsterCards/Views/Dashboard.swift
Normal file
20
iOS/MonsterCards/Views/Dashboard.swift
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// Dashboard.swift
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 1/15/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct Dashboard: View {
|
||||
var body: some View {
|
||||
Text("Dashboard")
|
||||
}
|
||||
}
|
||||
|
||||
struct Dashboard_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Dashboard().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
//
|
||||
// DashboardViewController.h
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 9/4/20.
|
||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface DashboardViewController : UIViewController
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -1,32 +0,0 @@
|
||||
//
|
||||
// DashboardViewController.m
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 9/4/20.
|
||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "DashboardViewController.h"
|
||||
|
||||
@interface DashboardViewController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation DashboardViewController
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
// Do any additional setup after loading the view.
|
||||
}
|
||||
|
||||
/*
|
||||
#pragma mark - Navigation
|
||||
|
||||
// In a storyboard-based application, you will often want to do a little preparation before navigation
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
||||
// Get the new view controller using [segue destinationViewController].
|
||||
// Pass the selected object to the new view controller.
|
||||
}
|
||||
*/
|
||||
|
||||
@end
|
||||
425
iOS/MonsterCards/Views/EditMonster.swift
Normal file
425
iOS/MonsterCards/Views/EditMonster.swift
Normal file
@@ -0,0 +1,425 @@
|
||||
//
|
||||
// EditMonster.swift
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 1/16/21.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
import SwiftUI
|
||||
|
||||
struct EditMonster: View {
|
||||
// TODO: add challengeRating/challengeRatingEnum and customChallengeRating maybe in basicInfo
|
||||
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
|
||||
@Environment(\.managedObjectContext) private var viewContext
|
||||
|
||||
var monster: Monster
|
||||
|
||||
@State private var monsterName: String = ""
|
||||
@State private var monsterSize: String = ""
|
||||
@State private var monsterType: String = ""
|
||||
@State private var monsterSubtype: String = ""
|
||||
@State private var monsterAlignment: String = ""
|
||||
@State private var monsterHitDice: Int64 = 0
|
||||
@State private var monsterHasCustomHP: Bool = false
|
||||
@State private var monsterCustomHP: String = ""
|
||||
@State private var monsterArmorType: String = ""
|
||||
@State private var monsterHasShield: Bool = false
|
||||
@State private var monsterNaturalArmorBonus: Int64 = 0
|
||||
@State private var monsterCustomArmor: String = ""
|
||||
@State private var monsterBaseSpeed: Int64 = 0
|
||||
@State private var monsterBurrowSpeed: Int64 = 0
|
||||
@State private var monsterClimbSpeed: Int64 = 0
|
||||
@State private var monsterFlySpeed: Int64 = 0
|
||||
@State private var monsterCanHover: Bool = false
|
||||
@State private var monsterSwimSpeed: Int64 = 0
|
||||
@State private var monsterHasCustomSpeed: Bool = false
|
||||
@State private var monsterCustomSpeed: String = ""
|
||||
@State private var monsterStrengthScore: Int64 = 10
|
||||
@State private var monsterDexterityScore: Int64 = 10
|
||||
@State private var monsterConstitutionScore: Int64 = 10
|
||||
@State private var monsterIntelligenceScore: Int64 = 10
|
||||
@State private var monsterWisdomScore: Int64 = 10
|
||||
@State private var monsterCharismaScore: Int64 = 10
|
||||
|
||||
@State private var monsterStrengthSavingThrowProficiency: ProficiencyType = .none
|
||||
@State private var monsterStrengthSavingThrowAdvantage: AdvantageType = .none
|
||||
@State private var monsterDexteritySavingThrowProficiency: ProficiencyType = .none
|
||||
@State private var monsterDexteritySavingThrowAdvantage: AdvantageType = .none
|
||||
@State private var monsterConstitutionSavingThrowProficiency: ProficiencyType = .none
|
||||
@State private var monsterConstitutionSavingThrowAdvantage: AdvantageType = .none
|
||||
@State private var monsterIntelligenceSavingThrowProficiency: ProficiencyType = .none
|
||||
@State private var monsterIntelligenceSavingThrowAdvantage: AdvantageType = .none
|
||||
@State private var monsterWisdomSavingThrowProficiency: ProficiencyType = .none
|
||||
@State private var monsterWisdomSavingThrowAdvantage: AdvantageType = .none
|
||||
@State private var monsterCharismaSavingThrowProficiency: ProficiencyType = .none
|
||||
@State private var monsterCharismaSavingThrowAdvantage: AdvantageType = .none
|
||||
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
Section(header: Text("Basic Info")) {
|
||||
// Editable Text field bound to monster.name
|
||||
MCTextField(
|
||||
label: "Name",
|
||||
value: $monsterName)
|
||||
|
||||
// Editable Text field bound to monster.size
|
||||
MCTextField(
|
||||
label: "Size",
|
||||
value: $monsterSize)
|
||||
|
||||
// Editable Text field bound to monster.type
|
||||
MCTextField(
|
||||
label: "Type",
|
||||
value: $monsterType)
|
||||
|
||||
// Editable Text field bound to monster.subType
|
||||
MCTextField(
|
||||
label: "Subtype",
|
||||
value: $monsterSubtype)
|
||||
|
||||
// Editable Text field bound to monster.alignment
|
||||
MCTextField(
|
||||
label: "Alignment",
|
||||
value: $monsterAlignment)
|
||||
|
||||
// Number with -/+ buttons bound to monster.hitDice
|
||||
MCStepperField(
|
||||
label: "Hit Dice",
|
||||
value: $monsterHitDice)
|
||||
|
||||
// Toggle bound to monster.hasCustomHP?
|
||||
Toggle(
|
||||
"Has Custom HP",
|
||||
isOn:$monsterHasCustomHP)
|
||||
|
||||
// Editable Text field bound to monster.customHpText?
|
||||
MCTextField(
|
||||
label: "Custom HP",
|
||||
value: $monsterCustomHP)
|
||||
|
||||
}
|
||||
.textCase(nil)
|
||||
|
||||
Section(header: Text("Armor")) {
|
||||
// Armor Type select bound to monster.armorType?
|
||||
// TODO: this should be a select/dropdown
|
||||
MCTextField(
|
||||
label: "Armor Type",
|
||||
value: $monsterArmorType)
|
||||
|
||||
// Toggle bound to monster.hasShield?
|
||||
Toggle(
|
||||
"Has Shield",
|
||||
isOn: $monsterHasShield)
|
||||
|
||||
// Number with -/+ buttons bound to monster.naturalArmorBonus
|
||||
MCStepperField(
|
||||
label: "Natural Armor Bonus",
|
||||
value: $monsterNaturalArmorBonus)
|
||||
|
||||
// Editable Text field bound to monster.customArmorText?
|
||||
MCTextField(
|
||||
label: "Custom Armor",
|
||||
value: $monsterCustomArmor)
|
||||
|
||||
}
|
||||
.textCase(nil)
|
||||
|
||||
Section(header: Text("Speed")) {
|
||||
// Number bound to monster.baseSpeed
|
||||
MCStepperField(
|
||||
label: "Base",
|
||||
step: 5,
|
||||
suffix: " ft.",
|
||||
value: $monsterBaseSpeed)
|
||||
|
||||
// Number bound to monster.burrowSpeed
|
||||
MCStepperField(
|
||||
label: "Burrow",
|
||||
step: 5,
|
||||
suffix: " ft.",
|
||||
value: $monsterBurrowSpeed)
|
||||
|
||||
// Number bound to monster.climbSpeed
|
||||
MCStepperField(
|
||||
label: "Climb",
|
||||
step: 5,
|
||||
suffix: " ft.",
|
||||
value: $monsterClimbSpeed)
|
||||
|
||||
// Number bound to monster.flySpeed
|
||||
MCStepperField(
|
||||
label: "Fly",
|
||||
step: 5,
|
||||
suffix: " ft.",
|
||||
value: $monsterFlySpeed)
|
||||
|
||||
// Toggle bound to monster.canHover
|
||||
Toggle(
|
||||
"Can Hover",
|
||||
isOn: $monsterCanHover)
|
||||
|
||||
// Number bound to monster.swimSpeed
|
||||
MCStepperField(
|
||||
label: "Swim",
|
||||
step: 5,
|
||||
suffix: " ft.",
|
||||
value: $monsterSwimSpeed)
|
||||
|
||||
// Toggle bound to monster.hasCustomSpeed
|
||||
Toggle(
|
||||
"Has Custom Speed",
|
||||
isOn: $monsterHasCustomSpeed)
|
||||
|
||||
// Editable Text field bound to monster.customSpeedText
|
||||
MCTextField(
|
||||
label: "Custom Speed",
|
||||
value: $monsterCustomSpeed)
|
||||
}
|
||||
.textCase(nil)
|
||||
|
||||
Section(header: Text("Ability Scores")) {
|
||||
MCStepperField(
|
||||
label: "STR",
|
||||
value: $monsterStrengthScore)
|
||||
MCStepperField(
|
||||
label: "DEX",
|
||||
value: $monsterDexterityScore)
|
||||
MCStepperField(
|
||||
label: "CON",
|
||||
value: $monsterConstitutionScore)
|
||||
MCStepperField(
|
||||
label: "INT",
|
||||
value: $monsterIntelligenceScore)
|
||||
MCStepperField(
|
||||
label: "WIS",
|
||||
value: $monsterWisdomScore)
|
||||
MCStepperField(
|
||||
label: "CHA",
|
||||
value: $monsterCharismaScore)
|
||||
}
|
||||
.textCase(nil)
|
||||
|
||||
Section(header: Text("Saving Throws")) {
|
||||
VStack {
|
||||
MCAdvantagePicker(
|
||||
label: "Strength Advantage",
|
||||
value: $monsterStrengthSavingThrowAdvantage)
|
||||
|
||||
MCProficiencyPicker(
|
||||
label: "Strength Proficiency",
|
||||
value: $monsterStrengthSavingThrowProficiency)
|
||||
}
|
||||
VStack {
|
||||
MCAdvantagePicker(
|
||||
label: "Dexterity Advantage",
|
||||
value: $monsterDexteritySavingThrowAdvantage)
|
||||
|
||||
MCProficiencyPicker(
|
||||
label: "Dexterity Proficiency",
|
||||
value: $monsterDexteritySavingThrowProficiency)
|
||||
}
|
||||
VStack {
|
||||
MCAdvantagePicker(
|
||||
label: "Constitution Advantage",
|
||||
value: $monsterConstitutionSavingThrowAdvantage)
|
||||
|
||||
MCProficiencyPicker(
|
||||
label: "Constitution Proficiency",
|
||||
value: $monsterConstitutionSavingThrowProficiency)
|
||||
}
|
||||
VStack {
|
||||
MCAdvantagePicker(
|
||||
label: "Intelligence Advantage",
|
||||
value: $monsterIntelligenceSavingThrowAdvantage)
|
||||
|
||||
MCProficiencyPicker(
|
||||
label: "Intelligence Proficiency",
|
||||
value: $monsterIntelligenceSavingThrowProficiency)
|
||||
}
|
||||
VStack {
|
||||
MCAdvantagePicker(
|
||||
label: "Wisdom Advantage",
|
||||
value: $monsterWisdomSavingThrowAdvantage)
|
||||
|
||||
MCProficiencyPicker(
|
||||
label: "Wisdom Proficiency",
|
||||
value: $monsterWisdomSavingThrowProficiency)
|
||||
}
|
||||
VStack {
|
||||
MCAdvantagePicker(
|
||||
label: "Charisma Advantage",
|
||||
value: $monsterCharismaSavingThrowAdvantage)
|
||||
|
||||
MCProficiencyPicker(
|
||||
label: "Charisma Proficiency",
|
||||
value: $monsterCharismaSavingThrowProficiency)
|
||||
}
|
||||
}
|
||||
.textCase(nil)
|
||||
}
|
||||
.onAppear(perform: copyMonsterToLocal)
|
||||
.toolbar(content: {
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
Button("Save", action: saveMonster)
|
||||
}
|
||||
ToolbarItem(placement: ToolbarItemPlacement.cancellationAction) {
|
||||
Button("Cancel", action: cancel)
|
||||
}
|
||||
})
|
||||
.navigationTitle(monster.name ?? "")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationBarBackButtonHidden(true)
|
||||
}
|
||||
|
||||
private func dismissView() {
|
||||
self.presentationMode.wrappedValue.dismiss()
|
||||
}
|
||||
|
||||
private func saveMonster() {
|
||||
copyLocalToMonster()
|
||||
|
||||
do {
|
||||
try viewContext.save()
|
||||
} catch {
|
||||
// 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.
|
||||
let nsError = error as NSError
|
||||
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
|
||||
}
|
||||
// TODO: save coredata context
|
||||
dismissView()
|
||||
}
|
||||
|
||||
private func cancel() {
|
||||
dismissView()
|
||||
}
|
||||
|
||||
private func copyMonsterToLocal() {
|
||||
monsterName = monster.name ?? ""
|
||||
monsterSize = monster.size ?? ""
|
||||
monsterType = monster.type ?? ""
|
||||
monsterSubtype = monster.subtype ?? ""
|
||||
monsterAlignment = monster.alignment ?? ""
|
||||
monsterHitDice = monster.hitDice
|
||||
monsterHasCustomHP = monster.hasCustomHP
|
||||
monsterCustomHP = monster.customHP ?? ""
|
||||
monsterArmorType = monster.armorType ?? ""
|
||||
monsterHasShield = monster.hasShield
|
||||
monsterNaturalArmorBonus = monster.naturalArmorBonus
|
||||
monsterCustomArmor = monster.customArmor ?? ""
|
||||
monsterBaseSpeed = monster.baseSpeed
|
||||
monsterBurrowSpeed = monster.burrowSpeed
|
||||
monsterClimbSpeed = monster.climbSpeed
|
||||
monsterFlySpeed = monster.flySpeed
|
||||
monsterCanHover = monster.canHover
|
||||
monsterSwimSpeed = monster.swimSpeed
|
||||
monsterHasCustomSpeed = monster.hasCustomSpeed
|
||||
monsterCustomSpeed = monster.customSpeed ?? ""
|
||||
monsterStrengthScore = monster.strengthScore
|
||||
monsterDexterityScore = monster.dexterityScore
|
||||
monsterConstitutionScore = monster.constitutionScore
|
||||
monsterIntelligenceScore = monster.intelligenceScore
|
||||
monsterWisdomScore = monster.wisdomScore
|
||||
monsterCharismaScore = monster.charismaScore
|
||||
monsterStrengthSavingThrowProficiency = ProficiencyType.init(rawValue: monster.strengthSavingThrowProficiency ?? "") ?? .none
|
||||
monsterStrengthSavingThrowAdvantage = AdvantageType(rawValue: monster.strengthSavingThrowAdvantage ?? "") ?? .none
|
||||
monsterDexteritySavingThrowProficiency = ProficiencyType(rawValue: monster.dexteritySavingThrowProficiency ?? "") ?? .none
|
||||
monsterDexteritySavingThrowAdvantage = AdvantageType(rawValue: monster.dexteritySavingThrowAdvantage ?? "") ?? .none
|
||||
monsterConstitutionSavingThrowProficiency = ProficiencyType(rawValue: monster.constitutionSavingThrowProficiency ?? "") ?? .none
|
||||
monsterConstitutionSavingThrowAdvantage = AdvantageType(rawValue: monster.constitutionSavingThrowAdvantage ?? "") ?? .none
|
||||
monsterIntelligenceSavingThrowProficiency = ProficiencyType(rawValue: monster.intelligenceSavingThrowProficiency ?? "") ?? .none
|
||||
monsterIntelligenceSavingThrowAdvantage = AdvantageType(rawValue: monster.intelligenceSavingThrowAdvantage ?? "") ?? .none
|
||||
monsterWisdomSavingThrowProficiency = ProficiencyType(rawValue: monster.wisdomSavingThrowProficiency ?? "") ?? .none
|
||||
monsterWisdomSavingThrowAdvantage = AdvantageType(rawValue: monster.wisdomSavingThrowAdvantage ?? "") ?? .none
|
||||
monsterCharismaSavingThrowProficiency = ProficiencyType(rawValue: monster.charismaSavingThrowProficiency ?? "") ?? .none
|
||||
monsterCharismaSavingThrowAdvantage = AdvantageType(rawValue: monster.charismaSavingThrowAdvantage ?? "") ?? .none
|
||||
}
|
||||
|
||||
private func copyLocalToMonster() {
|
||||
monster.name = monsterName
|
||||
monster.size = monsterSize
|
||||
monster.type = monsterType
|
||||
monster.subtype = monsterSubtype
|
||||
monster.alignment = monsterAlignment
|
||||
monster.hitDice = monsterHitDice
|
||||
monster.hasCustomHP = monsterHasCustomHP
|
||||
monster.customHP = monsterCustomHP
|
||||
monster.armorType = monsterArmorType
|
||||
monster.hasShield = monsterHasShield
|
||||
monster.naturalArmorBonus = monsterNaturalArmorBonus
|
||||
monster.customArmor = monsterCustomArmor
|
||||
monster.baseSpeed = monsterBaseSpeed
|
||||
monster.burrowSpeed = monsterBurrowSpeed
|
||||
monster.climbSpeed = monsterClimbSpeed
|
||||
monster.flySpeed = monsterFlySpeed
|
||||
monster.canHover = monsterCanHover
|
||||
monster.swimSpeed = monsterSwimSpeed
|
||||
monster.hasCustomSpeed = monsterHasCustomSpeed
|
||||
monster.customSpeed = monsterCustomSpeed
|
||||
monster.strengthScore = monsterStrengthScore
|
||||
monster.dexterityScore = monsterDexterityScore
|
||||
monster.constitutionScore = monsterConstitutionScore
|
||||
monster.intelligenceScore = monsterIntelligenceScore
|
||||
monster.wisdomScore = monsterWisdomScore
|
||||
monster.charismaScore = monsterCharismaScore
|
||||
monster.strengthSavingThrowProficiency = monsterStrengthSavingThrowProficiency.rawValue
|
||||
monster.strengthSavingThrowAdvantage = monsterStrengthSavingThrowAdvantage.rawValue
|
||||
monster.dexteritySavingThrowProficiency = monsterDexteritySavingThrowProficiency.rawValue
|
||||
monster.dexteritySavingThrowAdvantage = monsterDexteritySavingThrowAdvantage.rawValue
|
||||
monster.constitutionSavingThrowProficiency = monsterConstitutionSavingThrowProficiency.rawValue
|
||||
monster.constitutionSavingThrowAdvantage = monsterConstitutionSavingThrowAdvantage.rawValue
|
||||
monster.intelligenceSavingThrowProficiency = monsterIntelligenceSavingThrowProficiency.rawValue
|
||||
monster.intelligenceSavingThrowAdvantage = monsterIntelligenceSavingThrowAdvantage.rawValue
|
||||
monster.wisdomSavingThrowProficiency = monsterWisdomSavingThrowProficiency.rawValue
|
||||
monster.wisdomSavingThrowAdvantage = monsterWisdomSavingThrowAdvantage.rawValue
|
||||
monster.charismaSavingThrowProficiency = monsterCharismaSavingThrowProficiency.rawValue
|
||||
monster.charismaSavingThrowAdvantage = monsterCharismaSavingThrowAdvantage.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
struct EditMonster_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let context = PersistenceController.preview.container.viewContext
|
||||
let monster = Monster.init(context: context)
|
||||
|
||||
monster.name = "Steve"
|
||||
monster.size = "Medium"
|
||||
monster.type = "humanoid"
|
||||
monster.subtype = "human"
|
||||
monster.alignment = "LG"
|
||||
monster.hitDice = 6
|
||||
monster.hasCustomHP = true
|
||||
monster.customHP = "12 (1d10)+2"
|
||||
monster.baseSpeed = 5
|
||||
monster.burrowSpeed = 10
|
||||
monster.climbSpeed = 15
|
||||
monster.flySpeed = 20
|
||||
monster.swimSpeed = 25
|
||||
monster.canHover = true
|
||||
monster.hasCustomSpeed = false
|
||||
monster.customSpeed = "walk: 5 ft."
|
||||
monster.strengthScore = 8
|
||||
monster.dexterityScore = 10
|
||||
monster.constitutionScore = 12
|
||||
monster.intelligenceScore = 14
|
||||
monster.wisdomScore = 16
|
||||
monster.charismaScore = 18
|
||||
monster.strengthSavingThrowAdvantage = AdvantageType.none.rawValue
|
||||
monster.strengthSavingThrowProficiency = ProficiencyType.none.rawValue
|
||||
monster.dexteritySavingThrowAdvantage = AdvantageType.advantage.rawValue
|
||||
monster.dexteritySavingThrowProficiency = ProficiencyType.proficient.rawValue
|
||||
monster.constitutionSavingThrowAdvantage = AdvantageType.disadvantage.rawValue
|
||||
monster.constitutionSavingThrowProficiency = ProficiencyType.expertise.rawValue
|
||||
monster.intelligenceSavingThrowAdvantage = AdvantageType.none.rawValue
|
||||
monster.intelligenceSavingThrowProficiency = ProficiencyType.expertise.rawValue
|
||||
monster.wisdomSavingThrowAdvantage = AdvantageType.advantage.rawValue
|
||||
monster.wisdomSavingThrowProficiency = ProficiencyType.proficient.rawValue
|
||||
monster.charismaSavingThrowAdvantage = AdvantageType.disadvantage.rawValue
|
||||
monster.charismaSavingThrowProficiency = ProficiencyType.none.rawValue
|
||||
|
||||
return EditMonster(monster: monster).environment(\.managedObjectContext, context)
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
//
|
||||
// EditMonsterViewController.h
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 9/8/20.
|
||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "Monster.h"
|
||||
#import "MCShortStringFieldTableViewCell.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface EditMonsterViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, MCFormFieldDelegate>
|
||||
|
||||
@property Monster* originalMonster;
|
||||
@property (weak, nonatomic) IBOutlet UITableView *monsterTableView;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -1,719 +0,0 @@
|
||||
//
|
||||
// EditMonsterViewController.m
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 9/8/20.
|
||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "EditMonsterViewController.h"
|
||||
#import "MCBooleanFieldTableViewCell.h"
|
||||
#import "MCIntegerFieldTableViewCell.h"
|
||||
#import "MCRadioFieldTableViewCell.h"
|
||||
#import "MCSelectFieldTableViewCell.h"
|
||||
#import "MCShortStringFieldTableViewCell.h"
|
||||
#import "AppDelegate.h"
|
||||
|
||||
@interface EditMonsterViewController ()
|
||||
|
||||
@property Monster* editingMonster;
|
||||
|
||||
@end
|
||||
|
||||
const int kSectionIndexBasicInfo = 0;
|
||||
const int kSectionIndexArmor = 1;
|
||||
const int kSectionIndexSpeed = 2;
|
||||
const int kSectionIndexAbilityScores = 3;
|
||||
const int kSectionIndexSavingThrows = 4;
|
||||
|
||||
const int kBasicInfoSectionRowIndexName = 0;
|
||||
const int kBasicInfoSectionRowIndexSize = 1;
|
||||
const int kBasicInfoSectionRowIndexType = 2;
|
||||
const int kBasicInfoSectionRowIndexSubtype = 3;
|
||||
const int kBasicInfoSectionRowIndexAlignment = 4;
|
||||
const int kBasicInfoSectionRowIndexHitDice = 5;
|
||||
const int kBasicInfoSectionRowIndexCustomHP = 6;
|
||||
const int kBasicInfoSectionRowIndexCustomHPText = 7;
|
||||
|
||||
const int kArmorSectionRowIndexArmorType = 0;
|
||||
const int kArmorSectionRowIndexHasShield = 1;
|
||||
const int kArmorSectionRowIndexNaturalArmorBonus = 2;
|
||||
const int kArmorSectionRowIndexCustomArmor = 3;
|
||||
|
||||
const int kSpeedSectionRowIndexBaseSpeed = 0;
|
||||
const int kSpeedSectionRowIndexBurrowSpeed = 1;
|
||||
const int kSpeedSectionRowIndexClimbSpeed = 2;
|
||||
const int kSpeedSectionRowIndexFlySpeed = 3;
|
||||
const int kSpeedSectionRowIndexCanHover = 4;
|
||||
const int kSpeedSectionRowIndexSwimSpeed = 5;
|
||||
const int kSpeedSectionRowIndexHasCustomSpeed = 6;
|
||||
const int kSpeedSectionRowIndexCustomSpeed = 7;
|
||||
|
||||
const int kAbilityScoreSectionRowIndexStrength = 0;
|
||||
const int kAbilityScoreSectionRowIndexDexterity = 1;
|
||||
const int kAbilityScoreSectionRowIndexConstitution = 2;
|
||||
const int kAbilityScoreSectionRowIndexIntelligence = 3;
|
||||
const int kAbilityScoreSectionRowIndexWisdom = 4;
|
||||
const int kAbilityScoreSectionRowIndexCharisma = 5;
|
||||
|
||||
const int kSavingThrowsSectionRowIndexStrengthProficiency = 0;
|
||||
const int kSavingThrowsSectionRowIndexStrengthAdvantage = 1;
|
||||
const int kSavingThrowsSectionRowIndexDexterityProficiency = 2;
|
||||
const int kSavingThrowsSectionRowIndexDexterityAdvantage = 3;
|
||||
const int kSavingThrowsSectionRowIndexConstitutionProficiency = 4;
|
||||
const int kSavingThrowsSectionRowIndexConstitutionAdvantage = 5;
|
||||
const int kSavingThrowsSectionRowIndexIntelligenceProficiency = 6;
|
||||
const int kSavingThrowsSectionRowIndexIntelligenceAdvantage = 7;
|
||||
const int kSavingThrowsSectionRowIndexWisdomProficiency = 8;
|
||||
const int kSavingThrowsSectionRowIndexWisdomAdvantage = 9;
|
||||
const int kSavingThrowsSectionRowIndexCharismaProficiency = 10;
|
||||
const int kSavingThrowsSectionRowIndexCharismaAdvantage = 11;
|
||||
|
||||
NSString *const kIdentifierName = @"monster.name";
|
||||
NSString *const kIdentifierSize = @"monster.size";
|
||||
NSString *const kIdentifierType = @"monster.type";
|
||||
NSString *const kIdentifierSubtype = @"monster.subtype";
|
||||
NSString *const kIdentifierAlignment = @"monster.alignment";
|
||||
NSString *const kIdentifierCustomHP = @"monster.customHPText";
|
||||
NSString *const kIdentifierCustomSpeed = @"monster.customSpeed";
|
||||
NSString *const kIdentifierCustomArmor = @"monster.customArmor";
|
||||
NSString *const kIdentifierStrengthScore = @"monster.strengthScore";
|
||||
NSString *const kIdentiferDexterityScore = @"monster.dexterityScore";
|
||||
NSString *const kIdentifierConstitutionScore = @"monster.constitutionScore";
|
||||
NSString *const kIdentifierIntelligenceScore = @"monster.intelligenceScore";
|
||||
NSString *const kIdentifierWisdomScore = @"monster.wisdomScore";
|
||||
NSString *const kIdentifierCharismaScore = @"monster.charismaScore";
|
||||
NSString *const kIdentifierHitDice = @"monster.hitDice";
|
||||
NSString *const kIdentifierBaseSpeed = @"monster.baseSpeed";
|
||||
NSString *const kIdentifierBurrowSpeed = @"monster.burrowSpeed";
|
||||
NSString *const kIdentifierClimbSpeed = @"monster.climbSpeed";
|
||||
NSString *const kIdentifierFlySpeed = @"monster.flySpeed";
|
||||
NSString *const kIdentifierSwimSpeed = @"monster.swimSpeed";
|
||||
NSString *const kIdentifierNaturalArmorBonus = @"monster.naturalArmorBonus";
|
||||
NSString *const kIdentifierHasCustomHP = @"monster.customHP";
|
||||
NSString *const kIdentifierCanHover = @"monster.canHover";
|
||||
NSString *const kIdentifierHasCustomSpeed = @"monster.hasCustomSpeed";
|
||||
NSString *const kIdentifierHasShield = @"monster.hasShield";
|
||||
NSString *const kIdentifierArmorType = @"monster.armorType";
|
||||
NSString *const kIdentifierStrengthSavingThrowAdvantage = @"monster.savingThrows.strength.advantage";
|
||||
NSString *const kIdentifierStrengthSavingThrowProficiency = @"monster.savingThrows.strength.proficiency";
|
||||
NSString *const kIdentifierDexteritySavingThrowAdvantage = @"monster.savingThrows.dexterity.advantage";
|
||||
NSString *const kIdentifierDexteritySavingThrowProficiency = @"monster.savingThrows.dexterity.proficiency";
|
||||
NSString *const kIdentifierConstitutionSavingThrowAdvantage = @"monster.savingThrows.constitution.advantage";
|
||||
NSString *const kIdentifierConstitutionSavingThrowProficiency = @"monster.savingThrows.constitution.proficiency";
|
||||
NSString *const kIdentifierIntelligenceSavingThrowAdvantage = @"monster.savingThrows.intelligence.advantage";
|
||||
NSString *const kIdentifierIntelligenceSavingThrowProficiency = @"monster.savingThrows.intelligence.proficiency";
|
||||
NSString *const kIdentifierWisdomSavingThrowAdvantage = @"monster.savingThrows.wisdom.advantage";
|
||||
NSString *const kIdentifierWisdomSavingThrowProficiency = @"monster.savingThrows.wisdom.proficiency";
|
||||
NSString *const kIdentifierCharismaSavingThrowAdvantage = @"monster.savingThrows.charisma.advantage";
|
||||
NSString *const kIdentifierCharismaSavingThrowProficiency = @"monster.savingThrows.charisma.proficiency";
|
||||
|
||||
@implementation EditMonsterViewController {
|
||||
NSManagedObjectContext *_context;
|
||||
NSArray<MCChoice*>* _armorTypes;
|
||||
NSArray<MCChoice*>* _proficiencyTypes;
|
||||
NSArray<MCChoice*>* _advantageTypes;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
AppDelegate *appDelegate = (AppDelegate*)UIApplication.sharedApplication.delegate;
|
||||
_context = appDelegate.persistentContainer.viewContext;
|
||||
|
||||
_armorTypes = [NSArray arrayWithObjects:
|
||||
[MCChoice choiceWithLabel:NSLocalizedString(@"None", @"")
|
||||
andValue:kArmorNameNone],
|
||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Natural Armor", @"")
|
||||
andValue:kArmorNameNaturalArmor],
|
||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Mage Armor", @"")
|
||||
andValue:kArmorNameMageArmor],
|
||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Padded", @"")
|
||||
andValue:kArmorNamePadded],
|
||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Leather", @"")
|
||||
andValue:kArmorNameLeather],
|
||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Studded", @"")
|
||||
andValue:kArmorNameStuddedLeather],
|
||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Hide", @"")
|
||||
andValue:kArmorNameHide],
|
||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Chain Shirt", @"")
|
||||
andValue:kArmorNameChainShirt],
|
||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Scale Mail", @"")
|
||||
andValue:kArmorNameScaleMail],
|
||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Breastplate", @"")
|
||||
andValue:kArmorNameBreastplate],
|
||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Half Plate", @"")
|
||||
andValue:kArmorNameHalfPlate],
|
||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Ring Mail", @"")
|
||||
andValue:kArmorNameRingMail],
|
||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Chain Mail", @"")
|
||||
andValue:kArmorNameChainMail],
|
||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Splint", @"")
|
||||
andValue:kArmorNameSplintMail],
|
||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Plate", @"")
|
||||
andValue:kArmorNamePlateMail],
|
||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Other", @"")
|
||||
andValue:kArmorNameOther],
|
||||
nil];
|
||||
_proficiencyTypes = [NSArray arrayWithObjects:
|
||||
[MCChoice choiceWithLabel:NSLocalizedString(@"None", @"")
|
||||
andValue:kProficiencyTypeNone],
|
||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Proficient", @"")
|
||||
andValue:kProficiencyTypeProficient],
|
||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Expertise", @"")
|
||||
andValue:kProficiencyTypeExpertise],
|
||||
nil];
|
||||
_advantageTypes = [NSArray arrayWithObjects:
|
||||
[MCChoice choiceWithLabel:NSLocalizedString(@"None", @"")
|
||||
andValue:kAdvantageTypeNone],
|
||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Advantage", @"")
|
||||
andValue:kAdvantageTypeAdvantage],
|
||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Disadvantage", @"")
|
||||
andValue:kAdvantageTypeDisadvantage],
|
||||
nil];
|
||||
|
||||
self.monsterTableView.allowsSelection = NO;
|
||||
self.monsterTableView.allowsSelectionDuringEditing = NO;
|
||||
self.monsterTableView.dataSource = self;
|
||||
self.monsterTableView.delegate = self;
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[super viewWillAppear:animated];
|
||||
self.editingMonster = [[Monster alloc] initWithMonster:self.originalMonster];
|
||||
}
|
||||
|
||||
- (UITableViewCell*) makeSafeCell {
|
||||
#if DEBUG
|
||||
return nil;
|
||||
#else
|
||||
return [[UITableViewCell alloc] init];
|
||||
#endif
|
||||
}
|
||||
|
||||
- (MCShortStringFieldTableViewCell*) makeShortStringCellFromTableView:(UITableView*)tableView
|
||||
withIdentifier:(NSString*)identifier
|
||||
label:(NSString*)label
|
||||
andInitialValue:(NSString*)initialValue {
|
||||
MCShortStringFieldTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MCShortStringField"];
|
||||
if (!cell || ![cell isKindOfClass:[MCShortStringFieldTableViewCell class]]) {
|
||||
return nil;
|
||||
}
|
||||
cell.delegate = self;
|
||||
cell.identifier = identifier;
|
||||
cell.label = label;
|
||||
cell.value = initialValue;
|
||||
|
||||
// TODO: move these to better properties on MCShortStringFieldTableViewCell they should be stored via label and initialValue/value.
|
||||
cell.textField.text = initialValue;
|
||||
cell.textField.placeholder = label;
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (MCIntegerFieldTableViewCell*) makeIntegerCellFromTableView:(UITableView*)tableView
|
||||
withIdentifier:(NSString*)identifier
|
||||
label:(NSString*)label
|
||||
andInitialValue:(int)initialValue {
|
||||
MCIntegerFieldTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MCIntegerField"];
|
||||
if (!cell || ![cell isKindOfClass:[MCIntegerFieldTableViewCell class]]) {
|
||||
return nil;
|
||||
}
|
||||
cell.delegate = self;
|
||||
cell.identifier = identifier;
|
||||
cell.label = label;
|
||||
cell.value = initialValue;
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (MCBooleanFieldTableViewCell*) makeBooleanCellFromTableView:(UITableView*)tableView
|
||||
withIdentifier:(NSString*)identifier
|
||||
label:(NSString*)label
|
||||
andInitialValue:(BOOL)initialValue {
|
||||
MCBooleanFieldTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MCBooleanField"];
|
||||
if (!cell || ![cell isKindOfClass:[MCBooleanFieldTableViewCell class]]) {
|
||||
return nil;
|
||||
}
|
||||
cell.delegate = self;
|
||||
cell.identifier = identifier;
|
||||
cell.label = label;
|
||||
cell.value = initialValue;
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (MCSelectFieldTableViewCell*) makeSelectCellFromTableView:(UITableView*)tableView
|
||||
withIdentifier:(NSString*)identifier
|
||||
label:(NSString*)label
|
||||
initialValue:(NSObject*)initialValue
|
||||
andChoices:(NSArray*)choices {
|
||||
MCSelectFieldTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MCSelectField"];
|
||||
if (!cell || ![cell isKindOfClass:[MCSelectFieldTableViewCell class]]) {
|
||||
return nil;
|
||||
}
|
||||
cell.delegate = self;
|
||||
cell.identifier = identifier;
|
||||
cell.label = label;
|
||||
cell.selectedValue = initialValue;
|
||||
cell.choices = choices;
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (MCRadioFieldTableViewCell*) makeRadioCellFromTableView:(UITableView*)tableView
|
||||
withIdentifier:(NSString*)identifier
|
||||
label:(NSString*)label
|
||||
initialValue:(NSObject*)initialValue
|
||||
andChoices:(NSArray*)choices {
|
||||
MCRadioFieldTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MCRadioField"];
|
||||
if (!cell || ![cell isKindOfClass:[MCRadioFieldTableViewCell class]]) {
|
||||
return nil;
|
||||
}
|
||||
cell.delegate = self;
|
||||
cell.identifier = identifier;
|
||||
cell.label = label;
|
||||
// TODO: possibly swap these two
|
||||
cell.selectedValue = initialValue;
|
||||
cell.choices = choices;
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
#pragma mark - Navigation
|
||||
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
||||
if ([@"DiscardChanges" isEqualToString:segue.identifier]) {
|
||||
[_context rollback];
|
||||
} else if ([@"SaveChanges" isEqualToString:segue.identifier]) {
|
||||
// TODO: this should use a method on originalMonster to copy values from editingMonster or pass the new monster back some way. Core Data would save and probably trigger a refresh in the receiving view.
|
||||
[self.originalMonster copyFromMonster:self.editingMonster];
|
||||
[_context refreshObject:self.editingMonster mergeChanges:NO];
|
||||
[_context save:nil];
|
||||
} else {
|
||||
NSLog(@"Unknown Segue %@", segue.identifier);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDataSource
|
||||
|
||||
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
switch(section) {
|
||||
case kSectionIndexBasicInfo:
|
||||
// Section 0 is basic info
|
||||
// * Name
|
||||
// * Size
|
||||
// * Type
|
||||
// * Subtype
|
||||
// * Alignment
|
||||
return 8;
|
||||
case kSectionIndexArmor:
|
||||
return 4;
|
||||
case kSectionIndexSpeed:
|
||||
return 8;
|
||||
case kSectionIndexAbilityScores:
|
||||
return 6;
|
||||
case kSectionIndexSavingThrows:
|
||||
return 12; // 12
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
|
||||
- (NSString *)tableView:(UITableView *)tableView
|
||||
titleForHeaderInSection:(NSInteger)section {
|
||||
switch(section) {
|
||||
case kSectionIndexBasicInfo:
|
||||
return NSLocalizedString(@"Basic Info", @"Section title");
|
||||
case kSectionIndexArmor:
|
||||
return NSLocalizedString(@"Armor", @"Section title");
|
||||
case kSectionIndexSpeed:
|
||||
return NSLocalizedString(@"Speed", @"Section title");
|
||||
case kSectionIndexAbilityScores:
|
||||
return NSLocalizedString(@"Ability Scores", @"Section title");
|
||||
case kSectionIndexSavingThrows:
|
||||
return NSLocalizedString(@"Saving Throws", @"Section title");
|
||||
default:
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
|
||||
|
||||
UITableViewCell *newCell = nil;
|
||||
|
||||
switch (indexPath.section) {
|
||||
case kSectionIndexBasicInfo:
|
||||
switch (indexPath.row) {
|
||||
case kBasicInfoSectionRowIndexName:
|
||||
newCell = [self makeShortStringCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierName
|
||||
label:NSLocalizedString(@"Name", @"Placeholder text for the name of a monster or NPC.")
|
||||
andInitialValue:self.editingMonster.name];
|
||||
break;
|
||||
case kBasicInfoSectionRowIndexSize:
|
||||
newCell = [self makeShortStringCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierSize
|
||||
label:NSLocalizedString(@"Size", @"Placehodler text for the size of a monster or NPC.")
|
||||
andInitialValue:self.editingMonster.size];
|
||||
break;
|
||||
case kBasicInfoSectionRowIndexType:
|
||||
newCell = [self makeShortStringCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierType
|
||||
label:NSLocalizedString(@"Type", @"Placehodler text for the type of a monster or NPC.")
|
||||
andInitialValue:self.editingMonster.type];
|
||||
break;
|
||||
case kBasicInfoSectionRowIndexSubtype:
|
||||
newCell = [self makeShortStringCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierSubtype
|
||||
label:NSLocalizedString(@"Subtype", @"Placeholder text for the subtype of a monster or NPC.")
|
||||
andInitialValue:self.editingMonster.subtype];
|
||||
break;
|
||||
case kBasicInfoSectionRowIndexAlignment:
|
||||
newCell = [self makeShortStringCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierAlignment
|
||||
label: NSLocalizedString(@"Alignment", @"Placeholder text for the alignment of a monster or NPC.")
|
||||
andInitialValue:self.editingMonster.alignment];
|
||||
break;
|
||||
case kBasicInfoSectionRowIndexHitDice:
|
||||
newCell = [self makeIntegerCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierHitDice
|
||||
label:NSLocalizedString(@"Hit Dice", @"")
|
||||
andInitialValue:self.editingMonster.hitDice];
|
||||
break;
|
||||
case kBasicInfoSectionRowIndexCustomHP:
|
||||
newCell = [self makeBooleanCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierHasCustomHP
|
||||
label:NSLocalizedString(@"Custom HP", @"")
|
||||
andInitialValue:self.editingMonster.customHP];
|
||||
break;
|
||||
case kBasicInfoSectionRowIndexCustomHPText:
|
||||
newCell = [self makeShortStringCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierCustomHP
|
||||
label:NSLocalizedString(@"Custom HP Text", @"")
|
||||
andInitialValue:self.editingMonster.hpText];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case kSectionIndexArmor:
|
||||
switch (indexPath.row) {
|
||||
case kArmorSectionRowIndexArmorType:
|
||||
newCell = [self makeSelectCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierArmorType
|
||||
label:NSLocalizedString(@"Type", @"")
|
||||
initialValue:self.editingMonster.armorType
|
||||
andChoices:_armorTypes];
|
||||
break;
|
||||
case kArmorSectionRowIndexHasShield:
|
||||
newCell = [self makeBooleanCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierHasShield
|
||||
label:NSLocalizedString(@"Shield", @"")
|
||||
andInitialValue:self.editingMonster.hasShield];
|
||||
break;
|
||||
case kArmorSectionRowIndexCustomArmor:
|
||||
newCell = [self makeShortStringCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierCustomArmor
|
||||
label:NSLocalizedString(@"Custom Armor", @"")
|
||||
andInitialValue:self.editingMonster.customArmor];
|
||||
break;
|
||||
case kArmorSectionRowIndexNaturalArmorBonus:
|
||||
newCell = [self makeIntegerCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierNaturalArmorBonus
|
||||
label:NSLocalizedString(@"Natural Armor Bonus", @"")
|
||||
andInitialValue:self.editingMonster.naturalArmorBonus];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case kSectionIndexSpeed:
|
||||
switch (indexPath.row) {
|
||||
case kSpeedSectionRowIndexBaseSpeed:
|
||||
newCell = [self makeIntegerCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierBaseSpeed
|
||||
label:NSLocalizedString(@"Base", @"")
|
||||
andInitialValue:self.editingMonster.baseSpeed];
|
||||
break;
|
||||
case kSpeedSectionRowIndexBurrowSpeed:
|
||||
newCell = [self makeIntegerCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierBurrowSpeed
|
||||
label:NSLocalizedString(@"Burrow", @"")
|
||||
andInitialValue:self.editingMonster.burrowSpeed];
|
||||
break;
|
||||
case kSpeedSectionRowIndexClimbSpeed:
|
||||
newCell = [self makeIntegerCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierClimbSpeed
|
||||
label:NSLocalizedString(@"Climb", @"")
|
||||
andInitialValue:self.editingMonster.climbSpeed];
|
||||
break;
|
||||
case kSpeedSectionRowIndexFlySpeed:
|
||||
newCell = [self makeIntegerCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierFlySpeed
|
||||
label:NSLocalizedString(@"Fly", @"")
|
||||
andInitialValue:self.editingMonster.flySpeed];
|
||||
break;
|
||||
case kSpeedSectionRowIndexCanHover:
|
||||
newCell = [self makeBooleanCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierCanHover
|
||||
label:NSLocalizedString(@"Hover", @"")
|
||||
andInitialValue:self.editingMonster.canHover];
|
||||
break;
|
||||
case kSpeedSectionRowIndexSwimSpeed:
|
||||
newCell = [self makeIntegerCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierSwimSpeed
|
||||
label:NSLocalizedString(@"Swim", @"")
|
||||
andInitialValue:self.editingMonster.swimSpeed];
|
||||
break;
|
||||
case kSpeedSectionRowIndexHasCustomSpeed:
|
||||
newCell = [self makeBooleanCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierHasCustomSpeed
|
||||
label:NSLocalizedString(@"Custom Speed", @"")
|
||||
andInitialValue:self.editingMonster.hasCustomSpeed];
|
||||
break;
|
||||
case kSpeedSectionRowIndexCustomSpeed:
|
||||
newCell = [self makeShortStringCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierCustomSpeed
|
||||
label:NSLocalizedString(@"Custom Speed", @"")
|
||||
andInitialValue:self.editingMonster.customSpeed];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case kSectionIndexAbilityScores:
|
||||
switch (indexPath.row) {
|
||||
case kAbilityScoreSectionRowIndexStrength:
|
||||
newCell = [self makeIntegerCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierStrengthScore
|
||||
label:NSLocalizedString(@"STR", @"Placeholder abbreviation for the strength score of a monster or NPC.")
|
||||
andInitialValue:self.editingMonster.strengthScore];
|
||||
break;
|
||||
case kAbilityScoreSectionRowIndexDexterity:
|
||||
newCell = [self makeIntegerCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentiferDexterityScore
|
||||
label:NSLocalizedString(@"DEX", @"Placeholder abbreviation for the dexterity score of a monster or NPC.")
|
||||
andInitialValue:self.editingMonster.dexterityScore];
|
||||
break;
|
||||
case kAbilityScoreSectionRowIndexConstitution:
|
||||
newCell = [self makeIntegerCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierConstitutionScore
|
||||
label:NSLocalizedString(@"CON", @"Placeholder abbreviation for the constitution score of a monster or NPC.")
|
||||
andInitialValue:self.editingMonster.constitutionScore];
|
||||
break;
|
||||
case kAbilityScoreSectionRowIndexIntelligence:
|
||||
newCell = [self makeIntegerCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierIntelligenceScore
|
||||
label:NSLocalizedString(@"INT", @"Placeholder abbreviation for the intelligence score of a monster or NPC.")
|
||||
andInitialValue:self.editingMonster.intelligenceScore];
|
||||
break;
|
||||
case kAbilityScoreSectionRowIndexWisdom:
|
||||
newCell = [self makeIntegerCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierWisdomScore
|
||||
label:NSLocalizedString(@"WIS", @"Placeholder abbreviation for the wisdom score of a monster or NPC.")
|
||||
andInitialValue:self.editingMonster.wisdomScore];
|
||||
break;
|
||||
case kAbilityScoreSectionRowIndexCharisma:
|
||||
newCell = [self makeIntegerCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierCharismaScore
|
||||
label:NSLocalizedString(@"CHA", @"Placeholder abbreviation for the charisma score of a monster or NPC.")
|
||||
andInitialValue:self.editingMonster.charismaScore];
|
||||
break;
|
||||
|
||||
}
|
||||
break;
|
||||
case kSectionIndexSavingThrows:
|
||||
switch (indexPath.row) {
|
||||
case kSavingThrowsSectionRowIndexStrengthProficiency:
|
||||
newCell = [self makeRadioCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierStrengthSavingThrowProficiency
|
||||
label:NSLocalizedString(@"Strength Proficiency", @"")
|
||||
initialValue:self.editingMonster.strengthSavingThrowProficiency
|
||||
andChoices:_proficiencyTypes];
|
||||
break;
|
||||
case kSavingThrowsSectionRowIndexStrengthAdvantage:
|
||||
newCell = [self makeRadioCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierStrengthSavingThrowAdvantage
|
||||
label:NSLocalizedString(@"Strength Advantage", @"")
|
||||
initialValue:self.editingMonster.strengthSavingThrowAdvantage
|
||||
andChoices:_advantageTypes];
|
||||
break;
|
||||
case kSavingThrowsSectionRowIndexDexterityProficiency:
|
||||
newCell = [self makeRadioCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierDexteritySavingThrowProficiency
|
||||
label:NSLocalizedString(@"Dexterity Proficiency", @"")
|
||||
initialValue:self.editingMonster.dexteritySavingThrowProficiency
|
||||
andChoices:_proficiencyTypes];
|
||||
break;
|
||||
case kSavingThrowsSectionRowIndexDexterityAdvantage:
|
||||
newCell = [self makeRadioCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierDexteritySavingThrowAdvantage
|
||||
label:NSLocalizedString(@"Dexterity Advantage", @"")
|
||||
initialValue:self.editingMonster.dexteritySavingThrowAdvantage
|
||||
andChoices:_advantageTypes];
|
||||
break;
|
||||
case kSavingThrowsSectionRowIndexConstitutionProficiency:
|
||||
newCell = [self makeRadioCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierConstitutionSavingThrowProficiency
|
||||
label:NSLocalizedString(@"Constitution Proficiency", @"")
|
||||
initialValue:self.editingMonster.constitutionSavingThrowProficiency
|
||||
andChoices:_proficiencyTypes];
|
||||
break;
|
||||
case kSavingThrowsSectionRowIndexConstitutionAdvantage:
|
||||
newCell = [self makeRadioCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierConstitutionSavingThrowAdvantage
|
||||
label:NSLocalizedString(@"Constitution Advantage", @"")
|
||||
initialValue:self.editingMonster.constitutionSavingThrowAdvantage
|
||||
andChoices:_advantageTypes];
|
||||
break;
|
||||
case kSavingThrowsSectionRowIndexIntelligenceProficiency:
|
||||
newCell = [self makeRadioCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierIntelligenceSavingThrowProficiency
|
||||
label:NSLocalizedString(@"Intelligence Proficiency", @"")
|
||||
initialValue:self.editingMonster.intelligenceSavingThrowProficiency
|
||||
andChoices:_proficiencyTypes];
|
||||
break;
|
||||
case kSavingThrowsSectionRowIndexIntelligenceAdvantage:
|
||||
newCell = [self makeRadioCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierIntelligenceSavingThrowAdvantage
|
||||
label:NSLocalizedString(@"Intelligence Advantage", @"")
|
||||
initialValue:self.editingMonster.intelligenceSavingThrowAdvantage
|
||||
andChoices:_advantageTypes];
|
||||
break;
|
||||
case kSavingThrowsSectionRowIndexWisdomProficiency:
|
||||
newCell = [self makeRadioCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierWisdomSavingThrowProficiency
|
||||
label:NSLocalizedString(@"Wisdom Proficiency", @"")
|
||||
initialValue:self.editingMonster.wisdomSavingThrowProficiency
|
||||
andChoices:_proficiencyTypes];
|
||||
break;
|
||||
case kSavingThrowsSectionRowIndexWisdomAdvantage:
|
||||
newCell = [self makeRadioCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierWisdomSavingThrowAdvantage
|
||||
label:NSLocalizedString(@"Wisdom Advantage", @"")
|
||||
initialValue:self.editingMonster.wisdomSavingThrowAdvantage
|
||||
andChoices:_advantageTypes];
|
||||
break;
|
||||
case kSavingThrowsSectionRowIndexCharismaProficiency:
|
||||
newCell = [self makeRadioCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierCharismaSavingThrowProficiency
|
||||
label:NSLocalizedString(@"Charisma Proficiency", @"")
|
||||
initialValue:self.editingMonster.charismaSavingThrowProficiency
|
||||
andChoices:_proficiencyTypes];
|
||||
break;
|
||||
case kSavingThrowsSectionRowIndexCharismaAdvantage:
|
||||
newCell = [self makeRadioCellFromTableView:self.monsterTableView
|
||||
withIdentifier:kIdentifierCharismaSavingThrowAdvantage
|
||||
label:NSLocalizedString(@"Charisma Advantage", @"")
|
||||
initialValue:self.editingMonster.charismaSavingThrowAdvantage
|
||||
andChoices:_advantageTypes];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!newCell) {
|
||||
NSLog(@"ERROR: Unable to build a cell for %@", indexPath);
|
||||
newCell = [self makeSafeCell];
|
||||
}
|
||||
|
||||
return newCell;
|
||||
}
|
||||
|
||||
#pragma mark - MCShortStringFieldDelegate
|
||||
|
||||
- (void)editableValueDidChange:(NSObject*)value forIdentifier:(NSString*)identifier andType:(NSString*)type {
|
||||
if ([kMCFieldValueTypeString isEqualToString:type]) {
|
||||
if ([kIdentifierName isEqualToString:identifier]) {
|
||||
self.editingMonster.name = (NSString*)value;
|
||||
} else if ([kIdentifierSize isEqualToString:identifier]) {
|
||||
self.editingMonster.size = (NSString*)value;
|
||||
} else if ([kIdentifierType isEqualToString:identifier]) {
|
||||
self.editingMonster.type = (NSString*)value;
|
||||
} else if ([kIdentifierSubtype isEqualToString:identifier]) {
|
||||
self.editingMonster.subtype = (NSString*)value;
|
||||
} else if ([kIdentifierAlignment isEqualToString:identifier]) {
|
||||
self.editingMonster.alignment = (NSString*)value;
|
||||
} else if ([kIdentifierCustomHP isEqualToString:identifier]) {
|
||||
self.editingMonster.hpText = (NSString*)value;
|
||||
} else if ([kIdentifierCustomSpeed isEqualToString:identifier]) {
|
||||
self.editingMonster.customSpeed = (NSString*)value;
|
||||
} else if ([kIdentifierCustomArmor isEqualToString:identifier]) {
|
||||
self.editingMonster.customArmor = (NSString*)value;
|
||||
}
|
||||
}
|
||||
if ([kMCFieldValueTypeInteger isEqualToString:type]) {
|
||||
if ([kIdentifierStrengthScore isEqualToString:identifier]) {
|
||||
self.editingMonster.strengthScore = [(NSNumber*)value intValue];
|
||||
} else if ([kIdentiferDexterityScore isEqualToString:identifier]) {
|
||||
self.editingMonster.dexterityScore = [(NSNumber*)value intValue];
|
||||
} else if ([kIdentifierConstitutionScore isEqualToString:identifier]) {
|
||||
self.editingMonster.constitutionScore = [(NSNumber*)value intValue];
|
||||
} else if ([kIdentifierIntelligenceScore isEqualToString:identifier]) {
|
||||
self.editingMonster.intelligenceScore = [(NSNumber*)value intValue];
|
||||
} else if ([kIdentifierWisdomScore isEqualToString:identifier]) {
|
||||
self.editingMonster.wisdomScore = [(NSNumber*)value intValue];
|
||||
} else if ([kIdentifierCharismaScore isEqualToString:identifier]) {
|
||||
self.editingMonster.charismaScore = [(NSNumber*)value intValue];
|
||||
} else if ([kIdentifierHitDice isEqualToString:identifier]) {
|
||||
self.editingMonster.hitDice = [(NSNumber*)value intValue];
|
||||
} else if ([kIdentifierBaseSpeed isEqualToString:identifier]) {
|
||||
self.editingMonster.baseSpeed = [(NSNumber*)value intValue];
|
||||
} else if ([kIdentifierBurrowSpeed isEqualToString:identifier]) {
|
||||
self.editingMonster.burrowSpeed = [(NSNumber*)value intValue];
|
||||
} else if ([kIdentifierClimbSpeed isEqualToString:identifier]) {
|
||||
self.editingMonster.climbSpeed = [(NSNumber*)value intValue];
|
||||
} else if ([kIdentifierFlySpeed isEqualToString:identifier]) {
|
||||
self.editingMonster.flySpeed = [(NSNumber*)value intValue];
|
||||
} else if ([kIdentifierSwimSpeed isEqualToString:identifier]) {
|
||||
self.editingMonster.swimSpeed = [(NSNumber*)value intValue];
|
||||
} else if ([kIdentifierNaturalArmorBonus isEqualToString:identifier]) {
|
||||
self.editingMonster.naturalArmorBonus = [(NSNumber*)value intValue];
|
||||
}
|
||||
}
|
||||
if ([kMCFieldValueTypeBoolean isEqualToString:type]) {
|
||||
if ([kIdentifierHasCustomHP isEqualToString:identifier]) {
|
||||
self.editingMonster.customHP = [(NSNumber*)value boolValue];
|
||||
} else if ([kIdentifierCanHover isEqualToString:identifier]) {
|
||||
self.editingMonster.canHover = [(NSNumber*)value boolValue];
|
||||
} else if ([kIdentifierHasCustomSpeed isEqualToString:identifier]) {
|
||||
self.editingMonster.hasCustomSpeed = [(NSNumber*)value boolValue];
|
||||
} else if ([kIdentifierHasShield isEqualToString:identifier]) {
|
||||
self.editingMonster.hasShield = [(NSNumber*)value boolValue];
|
||||
}
|
||||
}
|
||||
if ([kMCFieldValueTypeChoice isEqualToString:type]) {
|
||||
if ([kIdentifierArmorType isEqualToString:identifier]) {
|
||||
self.editingMonster.armorType = (NSString*)value;
|
||||
} else if ([kIdentifierStrengthSavingThrowAdvantage isEqualToString:identifier]) {
|
||||
self.editingMonster.strengthSavingThrowAdvantage = (NSString*)value;
|
||||
} else if ([kIdentifierStrengthSavingThrowProficiency isEqualToString:identifier]) {
|
||||
self.editingMonster.strengthSavingThrowProficiency = (NSString*)value;
|
||||
} else if ([kIdentifierDexteritySavingThrowAdvantage isEqualToString:identifier]) {
|
||||
self.editingMonster.dexteritySavingThrowAdvantage = (NSString*)value;
|
||||
} else if ([kIdentifierDexteritySavingThrowProficiency isEqualToString:identifier]) {
|
||||
self.editingMonster.dexteritySavingThrowProficiency = (NSString*)value;
|
||||
} else if ([kIdentifierConstitutionSavingThrowAdvantage isEqualToString:identifier]) {
|
||||
self.editingMonster.constitutionSavingThrowAdvantage = (NSString*)value;
|
||||
} else if ([kIdentifierConstitutionSavingThrowProficiency isEqualToString:identifier]) {
|
||||
self.editingMonster.constitutionSavingThrowProficiency = (NSString*)value;
|
||||
} else if ([kIdentifierIntelligenceSavingThrowAdvantage isEqualToString:identifier]) {
|
||||
self.editingMonster.intelligenceSavingThrowAdvantage = (NSString*)value;
|
||||
} else if ([kIdentifierIntelligenceSavingThrowProficiency isEqualToString:identifier]) {
|
||||
self.editingMonster.intelligenceSavingThrowProficiency = (NSString*)value;
|
||||
} else if ([kIdentifierWisdomSavingThrowAdvantage isEqualToString:identifier]) {
|
||||
self.editingMonster.wisdomSavingThrowAdvantage = (NSString*)value;
|
||||
} else if ([kIdentifierWisdomSavingThrowProficiency isEqualToString:identifier]) {
|
||||
self.editingMonster.wisdomSavingThrowProficiency = (NSString*)value;
|
||||
} else if ([kIdentifierCharismaSavingThrowAdvantage isEqualToString:identifier]) {
|
||||
self.editingMonster.charismaSavingThrowAdvantage = (NSString*)value;
|
||||
} else if ([kIdentifierCharismaSavingThrowProficiency isEqualToString:identifier]) {
|
||||
self.editingMonster.charismaSavingThrowProficiency = (NSString*)value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return UITableViewAutomaticDimension;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,26 +0,0 @@
|
||||
//
|
||||
// MCBooleanFieldTableViewCell.h
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 9/25/20.
|
||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "MCFormFieldDelegate.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MCBooleanFieldTableViewCell : UITableViewCell
|
||||
|
||||
@property NSString* identifier;
|
||||
@property NSString* label;
|
||||
@property BOOL value;
|
||||
|
||||
@property (weak, nonatomic) id<MCFormFieldDelegate> delegate;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *labelView;
|
||||
@property (weak, nonatomic) IBOutlet UISwitch *switchView;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -1,58 +0,0 @@
|
||||
//
|
||||
// MCBooleanFieldTableViewCell.m
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 9/25/20.
|
||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MCBooleanFieldTableViewCell.h"
|
||||
|
||||
@implementation MCBooleanFieldTableViewCell
|
||||
|
||||
@synthesize value = _value;
|
||||
|
||||
- (void)setValue:(BOOL)value {
|
||||
if (value != _value) {
|
||||
_value = value;
|
||||
if (self.switchView) {
|
||||
self.switchView.on = _value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)value {
|
||||
return _value;
|
||||
}
|
||||
|
||||
@synthesize label = _label;
|
||||
|
||||
- (void)setLabel:(NSString*)label {
|
||||
if (![_label isEqualToString:label]) {
|
||||
_label = label;
|
||||
if (self.labelView) {
|
||||
self.labelView.text = label;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString*)label {
|
||||
return _label;
|
||||
}
|
||||
|
||||
- (void)awakeFromNib {
|
||||
[super awakeFromNib];
|
||||
self.switchView.on = self.value;
|
||||
self.labelView.text = self.label;
|
||||
}
|
||||
|
||||
- (IBAction)valueChanged:(id)sender {
|
||||
self.value = self.switchView.on;
|
||||
if (self.delegate != nil) {
|
||||
[self.delegate editableValueDidChange:[NSNumber numberWithBool:self.value]
|
||||
forIdentifier:self.identifier
|
||||
andType:kMCFieldValueTypeBoolean];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,28 +0,0 @@
|
||||
//
|
||||
// MCChoice.h
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 9/26/20.
|
||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MCChoice : NSObject
|
||||
|
||||
@property NSString* label;
|
||||
@property NSObject* value;
|
||||
|
||||
+(id)choiceWithLabel:(NSString*)label
|
||||
andValue:(NSObject*)value;
|
||||
|
||||
-(id)initWithLabel:(NSString*)label
|
||||
andValue:(NSObject*)value;
|
||||
|
||||
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -1,29 +0,0 @@
|
||||
//
|
||||
// MCChoice.m
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 9/26/20.
|
||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MCChoice.h"
|
||||
|
||||
@implementation MCChoice
|
||||
|
||||
+(id)choiceWithLabel:(NSString*)label
|
||||
andValue:(NSObject*)value {
|
||||
return [[MCChoice alloc] initWithLabel:label
|
||||
andValue:value];
|
||||
}
|
||||
|
||||
-(id)initWithLabel:(NSString*)label
|
||||
andValue:(NSObject*)value {
|
||||
self = [super init];
|
||||
|
||||
self.label = label;
|
||||
self.value = value;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,17 +0,0 @@
|
||||
//
|
||||
// MCFormFieldConstants.h
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 9/17/20.
|
||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MCFormFieldConstants_h
|
||||
#define MCFormFieldConstants_h
|
||||
|
||||
extern NSString* const kMCFieldValueTypeInteger;
|
||||
extern NSString* const kMCFieldValueTypeString;
|
||||
extern NSString* const kMCFieldValueTypeBoolean;
|
||||
extern NSString* const kMCFieldValueTypeChoice;
|
||||
|
||||
#endif /* MCFormFieldConstants_h */
|
||||
@@ -1,15 +0,0 @@
|
||||
//
|
||||
// MCFormFieldConstants.m
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 9/17/20.
|
||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "MCFormFieldConstants.h"
|
||||
|
||||
NSString* const kMCFieldValueTypeInteger = @"Integer";
|
||||
NSString* const kMCFieldValueTypeString = @"String";
|
||||
NSString* const kMCFieldValueTypeBoolean = @"Boolean";
|
||||
NSString* const kMCFieldValueTypeChoice = @"Choice";
|
||||
@@ -1,22 +0,0 @@
|
||||
//
|
||||
// MCFormFieldDelegate.h
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 9/9/20.
|
||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MCFormFieldDelegate_h
|
||||
#define MCFormFieldDelegate_h
|
||||
|
||||
#import "MCFormFieldConstants.h"
|
||||
|
||||
@protocol MCFormFieldDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
-(void)editableValueDidChange:(NSObject*)value forIdentifier:(NSString*)identifier andType:(NSString*)type;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#endif /* MCFormFieldDelegate_h */
|
||||
@@ -1,28 +0,0 @@
|
||||
//
|
||||
// MCIntegerFieldTableViewCell.h
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 9/17/20.
|
||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "MCFormFieldDelegate.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MCIntegerFieldTableViewCell : UITableViewCell <UITextFieldDelegate>
|
||||
|
||||
@property NSString* identifier;
|
||||
@property NSString* label;
|
||||
@property int value;
|
||||
|
||||
@property (weak, nonatomic) id<MCFormFieldDelegate> delegate;
|
||||
@property (weak, nonatomic) IBOutlet UITextField *textField;
|
||||
@property (weak, nonatomic) IBOutlet UIStepper *stepper;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *labelView;
|
||||
- (IBAction)stepperValueChanged:(id)sender;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -1,72 +0,0 @@
|
||||
//
|
||||
// MCIntegerFieldTableViewCell.m
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 9/17/20.
|
||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MCIntegerFieldTableViewCell.h"
|
||||
|
||||
@implementation MCIntegerFieldTableViewCell
|
||||
|
||||
@synthesize value = _value;
|
||||
|
||||
-(void) setValue:(int)number {
|
||||
if (_value != number) {
|
||||
NSNumber *newValue = [NSNumber numberWithInt:number];
|
||||
_value = number;
|
||||
if (self.textField) {
|
||||
self.textField.text = [newValue stringValue];
|
||||
}
|
||||
if (self.stepper) {
|
||||
self.stepper.value = number;
|
||||
}
|
||||
if (self.delegate) {
|
||||
[self.delegate editableValueDidChange:newValue
|
||||
forIdentifier:self.identifier
|
||||
andType:kMCFieldValueTypeInteger];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (int) value {
|
||||
return _value;
|
||||
}
|
||||
|
||||
@synthesize label = _label;
|
||||
|
||||
- (void)setLabel:(NSString*)label {
|
||||
if (![_label isEqualToString:label]) {
|
||||
_label = label;
|
||||
if (self.textField) {
|
||||
self.textField.placeholder = label;
|
||||
}
|
||||
if (self.labelView) {
|
||||
self.labelView.text = label;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString*)label {
|
||||
return _label;
|
||||
}
|
||||
|
||||
- (void)awakeFromNib {
|
||||
[super awakeFromNib];
|
||||
[self.textField addTarget:self
|
||||
action:@selector(textFieldValueChanged:)
|
||||
forControlEvents:UIControlEventEditingChanged];
|
||||
self.textField.text = [[NSNumber numberWithInt:_value] stringValue];
|
||||
self.stepper.value = _value;
|
||||
}
|
||||
|
||||
- (void)textFieldValueChanged:(UITextField*)textField {
|
||||
self.value = [textField.text intValue];
|
||||
}
|
||||
|
||||
- (IBAction)stepperValueChanged:(id)sender {
|
||||
self.value = self.stepper.value;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,29 +0,0 @@
|
||||
//
|
||||
// MCRadioFieldTableViewCell.h
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 9/26/20.
|
||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "MCFormFieldDelegate.h"
|
||||
#import "MCChoice.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MCRadioFieldTableViewCell : UITableViewCell
|
||||
|
||||
@property NSString* identifier;
|
||||
@property NSString* label;
|
||||
@property NSObject* selectedValue;
|
||||
@property NSArray<MCChoice*>* choices;
|
||||
|
||||
@property (weak, nonatomic) id<MCFormFieldDelegate> delegate;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *labelView;
|
||||
@property (weak, nonatomic) IBOutlet UISegmentedControl *segmentedControl;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -1,131 +0,0 @@
|
||||
//
|
||||
// MCRadioFieldTableViewCell.m
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 9/26/20.
|
||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MCRadioFieldTableViewCell.h"
|
||||
|
||||
@implementation MCRadioFieldTableViewCell {
|
||||
MCChoice* _selectedChoice;
|
||||
}
|
||||
|
||||
-(MCChoice*)findChoiceWithValue:(NSObject*)value
|
||||
inArray:(NSArray*)array {
|
||||
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id element, NSDictionary *bindings) {
|
||||
if (![element isKindOfClass:[MCChoice class]]) {
|
||||
return NO;
|
||||
}
|
||||
MCChoice *choice = (MCChoice*)element;
|
||||
return [choice.value isEqual:value];
|
||||
}];
|
||||
NSArray<MCChoice*> *matchingChoices = [array filteredArrayUsingPredicate:predicate];
|
||||
MCChoice *foundChoice = matchingChoices.count > 0 ? matchingChoices.firstObject : nil;
|
||||
return foundChoice;
|
||||
}
|
||||
|
||||
-(void)notifyChangedValue {
|
||||
NSUInteger selectedIndex = [_choices indexOfObject:_selectedChoice];
|
||||
[self.segmentedControl setSelectedSegmentIndex:selectedIndex];
|
||||
|
||||
if (_delegate) {
|
||||
[_delegate editableValueDidChange:_selectedValue
|
||||
forIdentifier:_identifier
|
||||
andType:kMCFieldValueTypeChoice];
|
||||
}
|
||||
}
|
||||
|
||||
-(void)updateSegments {
|
||||
if (_segmentedControl) {
|
||||
[_segmentedControl removeAllSegments];
|
||||
int index = 0;
|
||||
for (MCChoice *choice in _choices) {
|
||||
[_segmentedControl insertSegmentWithTitle:choice.label atIndex:index animated:NO];
|
||||
index++;
|
||||
}
|
||||
_segmentedControl.selectedSegmentIndex = [_choices indexOfObject:_selectedChoice];
|
||||
}
|
||||
}
|
||||
|
||||
@synthesize choices = _choices;
|
||||
-(void)setChoices:(NSArray<MCChoice*>*)choices {
|
||||
MCChoice *foundChoice = [self findChoiceWithValue:_selectedValue
|
||||
inArray:choices];
|
||||
if ([_choices isEqualToArray:choices]) {
|
||||
// Choices are equivalent so selected value. Pointer may have changed but content hasn't.
|
||||
_selectedChoice = foundChoice;
|
||||
} else if (foundChoice) {
|
||||
// Choices are different but selected value is in the new choices. Pointer may have changed but content hasn't.
|
||||
_selectedChoice = foundChoice;
|
||||
} else {
|
||||
// Choices are different and selected value is not in the new choices. Select the first choice or nil if there are none.
|
||||
_selectedChoice = [choices firstObject];
|
||||
}
|
||||
_choices = choices;
|
||||
|
||||
if (_selectedValue != foundChoice.value) {
|
||||
self.selectedValue = foundChoice.value;
|
||||
}
|
||||
|
||||
[self updateSegments];
|
||||
}
|
||||
-(NSArray<MCChoice*>*)choices {
|
||||
return _choices;
|
||||
}
|
||||
|
||||
@synthesize label = _label;
|
||||
-(void)setLabel:(NSString*)label {
|
||||
if (![_label isEqualToString:label]) {
|
||||
_label = label;
|
||||
}
|
||||
if (_labelView && ![_labelView.text isEqualToString:label]) {
|
||||
_labelView.text = label;
|
||||
}
|
||||
}
|
||||
-(NSString*)label {
|
||||
return _label;
|
||||
}
|
||||
|
||||
@synthesize selectedValue = _selectedValue;
|
||||
-(void)setSelectedValue:(NSObject*)value {
|
||||
NSObject *newValue = nil;
|
||||
MCChoice *foundChoice = [self findChoiceWithValue:value inArray:_choices];
|
||||
if (!_choices) {
|
||||
newValue = value;
|
||||
} else if (!foundChoice) {
|
||||
newValue = nil;
|
||||
} else {
|
||||
newValue = foundChoice.value;
|
||||
}
|
||||
_selectedChoice = foundChoice;
|
||||
if (_selectedValue != newValue) {
|
||||
_selectedValue = newValue;
|
||||
[self notifyChangedValue];
|
||||
}
|
||||
}
|
||||
-(NSObject*)selectedValue {
|
||||
return _selectedValue;
|
||||
}
|
||||
|
||||
- (void)awakeFromNib {
|
||||
[super awakeFromNib];
|
||||
// Initialization code
|
||||
}
|
||||
|
||||
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
|
||||
[super setSelected:selected animated:animated];
|
||||
|
||||
// Configure the view for the selected state
|
||||
}
|
||||
|
||||
- (IBAction)selectedSegmentChanged:(id)sender {
|
||||
NSInteger selectedIndex = _segmentedControl.selectedSegmentIndex;
|
||||
MCChoice *newChoice = [_choices objectAtIndex:selectedIndex];
|
||||
_selectedChoice = newChoice;
|
||||
_selectedValue = _selectedChoice.value;
|
||||
[self notifyChangedValue];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,29 +0,0 @@
|
||||
//
|
||||
// MCSelectFieldTableViewCell.h
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 9/26/20.
|
||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "MCFormFieldDelegate.h"
|
||||
#import "MCChoice.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MCSelectFieldTableViewCell : UITableViewCell<UIPickerViewDataSource, UIPickerViewDelegate, UITextFieldDelegate>
|
||||
|
||||
@property NSString* identifier;
|
||||
@property NSString* label;
|
||||
@property NSObject* selectedValue;
|
||||
@property NSArray<MCChoice*>* choices;
|
||||
|
||||
@property (weak, nonatomic) id<MCFormFieldDelegate> delegate;
|
||||
@property (weak, nonatomic) IBOutlet UITextField *textField;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *labelView;
|
||||
@property (nonatomic) UIPickerView *pickerView;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -1,182 +0,0 @@
|
||||
//
|
||||
// MCSelectFieldTableViewCell.m
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 9/26/20.
|
||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MCSelectFieldTableViewCell.h"
|
||||
|
||||
@implementation MCSelectFieldTableViewCell {
|
||||
MCChoice* _selectedChoice;
|
||||
}
|
||||
|
||||
-(MCChoice*)findChoiceWithValue:(NSObject*)value
|
||||
inArray:(NSArray*)array {
|
||||
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id element, NSDictionary *bindings) {
|
||||
if (![element isKindOfClass:[MCChoice class]]) {
|
||||
return NO;
|
||||
}
|
||||
MCChoice *choice = (MCChoice*)element;
|
||||
return [choice.value isEqual:value];
|
||||
}];
|
||||
NSArray<MCChoice*> *matchingChoices = [array filteredArrayUsingPredicate:predicate];
|
||||
MCChoice *foundChoice = matchingChoices.count > 0 ? matchingChoices.firstObject : nil;
|
||||
return foundChoice;
|
||||
}
|
||||
|
||||
-(void)notifyChangedValue {
|
||||
[self updateView];
|
||||
|
||||
if (_delegate) {
|
||||
[_delegate editableValueDidChange:_selectedValue
|
||||
forIdentifier:_identifier
|
||||
andType:kMCFieldValueTypeChoice];
|
||||
}
|
||||
}
|
||||
|
||||
-(void)updateView {
|
||||
self.textField.text = _selectedChoice.label;
|
||||
if (_choices && _choices.count > 0) {
|
||||
NSInteger selectedRow = [_choices indexOfObject:_selectedChoice];
|
||||
if (selectedRow != NSNotFound) {
|
||||
[self.pickerView selectRow:selectedRow inComponent:0 animated:YES];
|
||||
} else {
|
||||
[self.pickerView selectRow:0 inComponent:0 animated:YES];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@synthesize choices = _choices;
|
||||
-(void)setChoices:(NSArray<MCChoice*>*)choices {
|
||||
MCChoice *foundChoice = [self findChoiceWithValue:_selectedValue
|
||||
inArray:choices];
|
||||
if ([_choices isEqualToArray:choices]) {
|
||||
// Choices are equivalent so selected value. Pointer may have changed but content hasn't.
|
||||
_selectedChoice = foundChoice;
|
||||
} else if (foundChoice) {
|
||||
// Choices are different but selected value is in the new choices. Pointer may have changed but content hasn't.
|
||||
_selectedChoice = foundChoice;
|
||||
} else {
|
||||
// Choices are different and selected value is not in the new choices. Select the first choice or nil if there are none.
|
||||
_selectedChoice = [choices firstObject];
|
||||
}
|
||||
_choices = choices;
|
||||
|
||||
if (_selectedValue != foundChoice.value) {
|
||||
self.selectedValue = foundChoice.value;
|
||||
}
|
||||
|
||||
[self updateView];
|
||||
}
|
||||
-(NSArray<MCChoice*>*)choices {
|
||||
return _choices;
|
||||
}
|
||||
|
||||
@synthesize label = _label;
|
||||
-(void)setLabel:(NSString*)label {
|
||||
if (![_label isEqualToString:label]) {
|
||||
_label = label;
|
||||
}
|
||||
if (_labelView && ![_labelView.text isEqualToString:label]) {
|
||||
_labelView.text = label;
|
||||
}
|
||||
}
|
||||
-(NSString*)label {
|
||||
return _label;
|
||||
}
|
||||
|
||||
|
||||
@synthesize selectedValue = _selectedValue;
|
||||
-(void)setSelectedValue:(NSObject*)value {
|
||||
NSObject *newValue = nil;
|
||||
MCChoice *foundChoice = [self findChoiceWithValue:value inArray:_choices];
|
||||
if (!_choices) {
|
||||
newValue = value;
|
||||
} else if (!foundChoice) {
|
||||
foundChoice = [_choices firstObject];
|
||||
newValue = foundChoice.value;
|
||||
} else {
|
||||
newValue = foundChoice.value;
|
||||
}
|
||||
_selectedChoice = foundChoice;
|
||||
if (_selectedValue != newValue) {
|
||||
_selectedValue = newValue;
|
||||
[self notifyChangedValue];
|
||||
}
|
||||
}
|
||||
-(NSObject*)selectedValue {
|
||||
return _selectedValue;
|
||||
}
|
||||
|
||||
- (void)awakeFromNib {
|
||||
[super awakeFromNib];
|
||||
self.pickerView = [[UIPickerView alloc] init];
|
||||
self.pickerView.delegate = self;
|
||||
self.pickerView.dataSource = self;
|
||||
self.textField.inputView = self.pickerView;
|
||||
self.pickerView.translatesAutoresizingMaskIntoConstraints = NO;
|
||||
|
||||
UIToolbar *toolbar = [[UIToolbar alloc] init];
|
||||
[toolbar sizeToFit];
|
||||
|
||||
UIBarButtonItem *button =
|
||||
[[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Done", @"Button label")
|
||||
style:UIBarButtonItemStylePlain
|
||||
target:self
|
||||
action:@selector(choiceSelected)];
|
||||
[toolbar setItems:@[button]
|
||||
animated:true];
|
||||
[toolbar setUserInteractionEnabled:YES];
|
||||
self.textField.inputAccessoryView = toolbar;
|
||||
self.textField.hidden = NO;
|
||||
self.textField.text = _selectedChoice.label;
|
||||
self.textField.delegate = self;
|
||||
}
|
||||
|
||||
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
|
||||
[super setSelected:selected animated:animated];
|
||||
|
||||
// Configure the view for the selected state
|
||||
}
|
||||
|
||||
- (void)choiceSelected {
|
||||
[self endEditing:true];
|
||||
}
|
||||
|
||||
#pragma mark - UIPickerViewDataSource
|
||||
|
||||
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (NSInteger)pickerView:(UIPickerView *)pickerView
|
||||
numberOfRowsInComponent:(NSInteger)component {
|
||||
return [_choices count];
|
||||
}
|
||||
|
||||
#pragma mark - UIPickerViewDelegate
|
||||
|
||||
- (NSString *)pickerView:(UIPickerView *)pickerView
|
||||
titleForRow:(NSInteger)row
|
||||
forComponent:(NSInteger)component {
|
||||
return [_choices objectAtIndex:row].label;
|
||||
}
|
||||
|
||||
- (void)pickerView:(UIPickerView *)pickerView
|
||||
didSelectRow:(NSInteger)row
|
||||
inComponent:(NSInteger)component {
|
||||
|
||||
_selectedChoice = [_choices objectAtIndex:row];
|
||||
self.textField.text = _selectedChoice.label;
|
||||
self.selectedValue = _selectedChoice.value;
|
||||
}
|
||||
|
||||
#pragma mark - UITextFieldDelegate
|
||||
|
||||
- (void)textFieldDidBeginEditing:(UITextField *)textField {
|
||||
[self updateView];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,25 +0,0 @@
|
||||
//
|
||||
// EditableShortStringTableViewCell.h
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 9/9/20.
|
||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "MCFormFieldDelegate.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MCShortStringFieldTableViewCell : UITableViewCell
|
||||
|
||||
@property NSString* identifier;
|
||||
@property NSString* label;
|
||||
@property NSString* value;
|
||||
|
||||
@property (weak, nonatomic) id<MCFormFieldDelegate> delegate;
|
||||
@property (weak, nonatomic) IBOutlet UITextField *textField;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -1,57 +0,0 @@
|
||||
//
|
||||
// EditableShortStringTableViewCell.m
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 9/9/20.
|
||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MCShortStringFieldTableViewCell.h"
|
||||
|
||||
@implementation MCShortStringFieldTableViewCell
|
||||
|
||||
@synthesize value = _value;
|
||||
|
||||
- (void)setValue:(NSString*)value {
|
||||
if (![_value isEqualToString:value]) {
|
||||
_value = value;
|
||||
if (self.textField) {
|
||||
self.textField.text = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString*)value {
|
||||
return _value;
|
||||
}
|
||||
|
||||
@synthesize label = _label;
|
||||
|
||||
- (void)setLabel:(NSString*)label {
|
||||
if (![_label isEqualToString:label]) {
|
||||
_label = label;
|
||||
if (self.textField) {
|
||||
self.textField.placeholder = label;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString*)label {
|
||||
return _label;
|
||||
}
|
||||
|
||||
- (void)awakeFromNib {
|
||||
[super awakeFromNib];
|
||||
[self.textField addTarget:self action:@selector(valueChanged:) forControlEvents:UIControlEventEditingChanged];
|
||||
}
|
||||
|
||||
- (void)valueChanged:(UITextField*)textField {
|
||||
NSString *newValue = textField.text;
|
||||
if (self.delegate != nil) {
|
||||
[self.delegate editableValueDidChange:newValue
|
||||
forIdentifier:self.identifier
|
||||
andType:kMCFieldValueTypeString];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
60
iOS/MonsterCards/Views/Library.swift
Normal file
60
iOS/MonsterCards/Views/Library.swift
Normal file
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// Library.swift
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 1/15/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct Library: View {
|
||||
@Environment(\.managedObjectContext) private var viewContext
|
||||
|
||||
@FetchRequest(
|
||||
sortDescriptors: [
|
||||
NSSortDescriptor(keyPath: \Monster.name, ascending: true),
|
||||
],
|
||||
animation: .default)
|
||||
var allMonsters: FetchedResults<Monster>
|
||||
|
||||
var body: some View {
|
||||
NavigationView{
|
||||
List(allMonsters) { monster in
|
||||
NavigationLink(destination: MonsterDetail(monster: monster)) {
|
||||
Text(monster.name ?? "")
|
||||
}
|
||||
}
|
||||
.navigationTitle("Library")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar(content: {
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
Button(action: addMonster) {
|
||||
Image(systemName:"plus")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private func addMonster() {
|
||||
withAnimation {
|
||||
let newItem = Monster(context: viewContext)
|
||||
newItem.name = "Unnamed Monster"
|
||||
|
||||
do {
|
||||
try viewContext.save()
|
||||
} catch {
|
||||
// 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.
|
||||
let nsError = error as NSError
|
||||
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Library_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
return Library().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
//
|
||||
// LibraryViewController.h
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 9/4/20.
|
||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface LibraryViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource>
|
||||
|
||||
@property (strong, nonatomic) IBOutlet UITableView *monstersTable;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -1,94 +0,0 @@
|
||||
//
|
||||
// LibraryViewController.m
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 9/4/20.
|
||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "LibraryViewController.h"
|
||||
#import "Monster.h"
|
||||
#import "MonsterViewController.h"
|
||||
#import "AppDelegate.h"
|
||||
|
||||
@interface LibraryViewController ()
|
||||
|
||||
@property NSArray* allMonsters;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LibraryViewController {
|
||||
NSManagedObjectContext *_context;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
AppDelegate *appDelegate = (AppDelegate*)UIApplication.sharedApplication.delegate;
|
||||
_context = appDelegate.persistentContainer.viewContext;
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
self.allMonsters = [_context executeFetchRequest:[Monster fetchRequest] error:nil];
|
||||
[self.monstersTable reloadData];
|
||||
}
|
||||
|
||||
- (IBAction)addNewMonster:(id)sender {
|
||||
Monster *monster = [[Monster alloc] initWithContext:_context];
|
||||
monster.name = NSLocalizedString(@"Unnamed Monster", @"The default name of a new monster.");
|
||||
self.allMonsters = [self.allMonsters arrayByAddingObject:monster];
|
||||
//DispatchQueue.main.async{"code here"}
|
||||
[_context save:nil];
|
||||
[self.monstersTable reloadData];
|
||||
}
|
||||
|
||||
#pragma mark - Navigation
|
||||
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
||||
if ([@"ShowMonsterDetail" isEqualToString:segue.identifier]) {
|
||||
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
|
||||
if ([segue.destinationViewController isKindOfClass:[MonsterViewController class]]) {
|
||||
MonsterViewController *vc = (MonsterViewController*)segue.destinationViewController;
|
||||
vc.monster = [self.allMonsters objectAtIndex:indexPath.row];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDataSource
|
||||
|
||||
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return [self.allMonsters count];
|
||||
}
|
||||
|
||||
- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
|
||||
static NSString *simpleTableIdentifier = @"MonsterCell";
|
||||
|
||||
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
|
||||
|
||||
if (cell == nil) {
|
||||
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
|
||||
}
|
||||
|
||||
Monster *monster = (Monster*)[self.allMonsters objectAtIndex:indexPath.row];
|
||||
|
||||
cell.textLabel.text = monster.name;
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDelegate
|
||||
|
||||
- (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView
|
||||
trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
UIContextualAction *action = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleDestructive title:NSLocalizedString(@"Delete", @"Command to delete an object.") handler:^(UIContextualAction *action, __kindof UIView *sourceView, void (^completionHandler)(BOOL actionPerformed)) {
|
||||
Monster *monster = [self.allMonsters objectAtIndex:indexPath.row];
|
||||
[self->_context deleteObject:monster];
|
||||
[self->_context save:nil];
|
||||
self.allMonsters = [self->_context executeFetchRequest:[Monster fetchRequest] error:nil];
|
||||
[self.tableView reloadData];
|
||||
}];
|
||||
|
||||
UISwipeActionsConfiguration *config = [UISwipeActionsConfiguration configurationWithActions:[NSArray arrayWithObject:action]];
|
||||
return config;
|
||||
}
|
||||
|
||||
@end
|
||||
34
iOS/MonsterCards/Views/MCAdvantagePicker.swift
Normal file
34
iOS/MonsterCards/Views/MCAdvantagePicker.swift
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// MCAdvantagePicker.swift
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 1/17/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct MCAdvantagePicker: View {
|
||||
var label: String = ""
|
||||
var value: Binding<AdvantageType>
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
Text(label)
|
||||
.font(.caption2)
|
||||
Picker(
|
||||
selection: value,
|
||||
label: Text(label)) {
|
||||
ForEach(AdvantageType.allCases) { advType in
|
||||
Text(advType.displayName).tag(advType)
|
||||
}
|
||||
}
|
||||
.pickerStyle(SegmentedPickerStyle())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MCAdvantagePicker_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
MCAdvantagePicker(value: .constant(AdvantageType.none))
|
||||
}
|
||||
}
|
||||
34
iOS/MonsterCards/Views/MCProficiencyPicker.swift
Normal file
34
iOS/MonsterCards/Views/MCProficiencyPicker.swift
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// MCProficiencyPicker.swift
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 1/17/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct MCProficiencyPicker: View {
|
||||
var label: String = ""
|
||||
var value: Binding<ProficiencyType>
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
Text(label)
|
||||
.font(.caption2)
|
||||
Picker(
|
||||
selection: value,
|
||||
label: Text(label)) {
|
||||
ForEach(ProficiencyType.allCases) { profType in
|
||||
Text(profType.displayName).tag(profType)
|
||||
}
|
||||
}
|
||||
.pickerStyle(SegmentedPickerStyle())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MCProficiencyPicker_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
MCProficiencyPicker(value: .constant(ProficiencyType.none))
|
||||
}
|
||||
}
|
||||
37
iOS/MonsterCards/Views/MCStepperField.swift
Normal file
37
iOS/MonsterCards/Views/MCStepperField.swift
Normal file
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// MCStepperField.swift
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 1/16/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct MCStepperField: View {
|
||||
var label: String = ""
|
||||
var prefix: String = ""
|
||||
var step: Int = 1
|
||||
var suffix: String = ""
|
||||
var value: Binding<Int64>
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
Text(label)
|
||||
.font(.caption2)
|
||||
Stepper(
|
||||
value: value,
|
||||
step: step
|
||||
) {
|
||||
Text("\(prefix)\(value.wrappedValue)\(suffix)" as String)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MCStepperField_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
MCStepperField(
|
||||
label: "Hit Dice",
|
||||
value: .constant(4))
|
||||
}
|
||||
}
|
||||
28
iOS/MonsterCards/Views/MCTextField.swift
Normal file
28
iOS/MonsterCards/Views/MCTextField.swift
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// MCTextField.swift
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 1/16/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct MCTextField: View {
|
||||
var label: String
|
||||
var value: Binding<String>
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
Text(label)
|
||||
.font(.caption2)
|
||||
TextField(label, text: value)
|
||||
.autocapitalization(/*@START_MENU_TOKEN@*/.none/*@END_MENU_TOKEN@*/)
|
||||
// .padding(.top, -4)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MCTextField_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
MCTextField(label: "Name", value: .constant("Ted"))
|
||||
}
|
||||
}
|
||||
177
iOS/MonsterCards/Views/MonsterDetail.swift
Normal file
177
iOS/MonsterCards/Views/MonsterDetail.swift
Normal file
@@ -0,0 +1,177 @@
|
||||
//
|
||||
// MonsterDetail.swift
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 1/15/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct LabeledField<Content: View>: View {
|
||||
let content: Content
|
||||
let label: String
|
||||
|
||||
@inlinable public init(
|
||||
_ label: String,
|
||||
@ViewBuilder content: () -> Content) {
|
||||
self.content = content()
|
||||
self.label = label
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
HStack(alignment: .top) {
|
||||
Text(label)
|
||||
.fontWeight(.bold)
|
||||
content
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SectionDivider: View {
|
||||
var body: some View {
|
||||
Image("section-divider") // divider
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.padding(.vertical, 4)
|
||||
}
|
||||
}
|
||||
|
||||
struct SmallAbilityScore: View {
|
||||
var label: String
|
||||
var score: Int64
|
||||
var modifier: Int
|
||||
|
||||
public init(
|
||||
_ label: String,
|
||||
_ score: Int64,
|
||||
_ modifier: Int) {
|
||||
self.label = label
|
||||
self.score = score
|
||||
self.modifier = modifier
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
Text(label)
|
||||
.fontWeight(.bold)
|
||||
Text(String(format: "%d (%+d)", score, modifier))
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
}
|
||||
|
||||
struct MonsterDetail: View {
|
||||
let kTextColor: Color = Color(hex: 0x982818)
|
||||
|
||||
var monster: Monster
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
VStack (alignment: .leading) {
|
||||
// meta: "(large humanoid (elf) lawful evil"
|
||||
Text(monster.meta)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
SectionDivider()
|
||||
|
||||
// AC
|
||||
LabeledField("Armor Class") {
|
||||
Text(monster.armorClassDescription)// armor class
|
||||
}
|
||||
// HP
|
||||
LabeledField("Hit Points") {
|
||||
Text(monster.hitPoints) // hit points
|
||||
}
|
||||
// Speed
|
||||
LabeledField("Speed") {
|
||||
Text(monster.speed) // speed
|
||||
}
|
||||
|
||||
SectionDivider()
|
||||
|
||||
// Ability Scores
|
||||
HStack {
|
||||
SmallAbilityScore("STR", monster.strengthScore, monster.strengthModifier)
|
||||
SmallAbilityScore("DEX", monster.dexterityScore, monster.dexterityModifier)
|
||||
SmallAbilityScore("CON", monster.constitutionScore, monster.constitutionModifier)
|
||||
SmallAbilityScore("INT", monster.intelligenceScore, monster.intelligenceModifier)
|
||||
SmallAbilityScore("WIS", monster.wisdomScore, monster.wisdomModifier)
|
||||
SmallAbilityScore("CHA", monster.charismaScore, monster.charismaModifier)
|
||||
}
|
||||
|
||||
SectionDivider()
|
||||
|
||||
let savingThrowsDescription = monster.savingThrowsDescription
|
||||
if (!savingThrowsDescription.isEmpty) {
|
||||
LabeledField("Saving Throws") {
|
||||
Text(monster.savingThrowsDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.horizontal)
|
||||
.foregroundColor(kTextColor)
|
||||
}
|
||||
.toolbar(content: {
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
NavigationLink("Edit", destination: EditMonster(monster: monster))
|
||||
}
|
||||
})
|
||||
.navigationTitle(monster.name ?? "")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
|
||||
private func editMonster() {
|
||||
print("Edit Monster pressed")
|
||||
}
|
||||
}
|
||||
|
||||
struct MonsterDetail_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let context = PersistenceController.preview.container.viewContext
|
||||
let monster = Monster.init(context: context)
|
||||
monster.name = "Steve"
|
||||
monster.size = "Medium"
|
||||
monster.type = "humanoid"
|
||||
monster.subtype = "human"
|
||||
monster.alignment = "LG"
|
||||
monster.hitDice = 6
|
||||
monster.hasCustomHP = true
|
||||
monster.customHP = "12 (1d10)+2"
|
||||
monster.baseSpeed = 5
|
||||
monster.burrowSpeed = 10
|
||||
monster.climbSpeed = 15
|
||||
monster.flySpeed = 20
|
||||
monster.swimSpeed = 25
|
||||
monster.canHover = true
|
||||
monster.hasCustomSpeed = false
|
||||
monster.customSpeed = "walk: 5 ft."
|
||||
monster.strengthScore = 8
|
||||
monster.dexterityScore = 10
|
||||
monster.constitutionScore = 12
|
||||
monster.intelligenceScore = 14
|
||||
monster.wisdomScore = 16
|
||||
monster.charismaScore = 18
|
||||
monster.strengthSavingThrowAdvantageEnum = AdvantageType.none
|
||||
monster.strengthSavingThrowProficiencyEnum = ProficiencyType.none
|
||||
monster.dexteritySavingThrowAdvantageEnum = AdvantageType.advantage
|
||||
monster.dexteritySavingThrowProficiencyEnum = ProficiencyType.proficient
|
||||
monster.constitutionSavingThrowAdvantageEnum = AdvantageType.disadvantage
|
||||
monster.constitutionSavingThrowProficiencyEnum = ProficiencyType.expertise
|
||||
monster.intelligenceSavingThrowAdvantageEnum = AdvantageType.none
|
||||
monster.intelligenceSavingThrowProficiencyEnum = ProficiencyType.expertise
|
||||
monster.wisdomSavingThrowAdvantageEnum = AdvantageType.advantage
|
||||
monster.wisdomSavingThrowProficiencyEnum = ProficiencyType.proficient
|
||||
monster.charismaSavingThrowAdvantageEnum = AdvantageType.disadvantage
|
||||
monster.charismaSavingThrowProficiencyEnum = ProficiencyType.none
|
||||
|
||||
return Group {
|
||||
MonsterDetail(monster: monster)
|
||||
.environment(\.managedObjectContext, context)
|
||||
.previewDevice("iPod touch (7th generation)")
|
||||
MonsterDetail(monster: monster)
|
||||
.environment(\.managedObjectContext, context)
|
||||
.previewDevice("iPad Pro (11-inch) (2nd generation)")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
//
|
||||
// MonsterViewController.h
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 9/4/20.
|
||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "Monster.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface MonsterViewController : UIViewController
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UILabel *monsterName;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *monsterMeta;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *monsterArmorClass;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *monsterHitPoints;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *monsterSpeed;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *monsterStrength;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *monsterDexterity;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *monsterConstitution;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *monsterIntelligence;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *monsterWisdom;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *monsterCharisma;
|
||||
@property (weak, nonatomic) IBOutlet UILabel *monsterSavingThrows;
|
||||
|
||||
@property Monster* monster;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -1,134 +0,0 @@
|
||||
//
|
||||
// MonsterViewController.m
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 9/4/20.
|
||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MonsterViewController.h"
|
||||
#import "EditMonsterViewController.h"
|
||||
#import "HTMLHelper.h"
|
||||
#import "AppDelegate.h"
|
||||
|
||||
@interface MonsterViewController ()
|
||||
|
||||
@end
|
||||
|
||||
NSString *const defaultFontFamily = @"helvetica";
|
||||
NSString *const defaultFontSize = @"12pt";
|
||||
NSString *const defaultTextColor = @"#9B2818";
|
||||
|
||||
NSString* makeHTMLFragmentString(NSString* format, ...) {
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
NSString *childString = [[NSString alloc] initWithFormat:format arguments:args];
|
||||
va_end(args);
|
||||
|
||||
NSString *formattedString = [NSString stringWithFormat:@"<span style=\"font-family: %@; font-size: %@; color: %@;\">%@</span>", defaultFontFamily, defaultFontSize, defaultTextColor, childString];
|
||||
return formattedString;
|
||||
}
|
||||
|
||||
@implementation MonsterViewController {
|
||||
NSManagedObjectContext *_context;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
AppDelegate *appDelegate = (AppDelegate*)UIApplication.sharedApplication.delegate;
|
||||
_context = appDelegate.persistentContainer.viewContext;
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
[_context refreshObject:self.monster mergeChanges:NO];
|
||||
if (self.monsterName != nil) {
|
||||
self.monsterName.text = self.monster.name;
|
||||
} else if (self.navigationItem != nil) {
|
||||
self.navigationItem.title = self.monster.name;
|
||||
}
|
||||
if (self.monsterName != nil) {
|
||||
self.monsterName.text = self.monster.name;
|
||||
} else if (self.navigationItem != nil) {
|
||||
if (self.monster.name == nil) {
|
||||
self.navigationItem.title = @"Unnamed Monster";
|
||||
} else {
|
||||
self.navigationItem.title = self.monster.name;
|
||||
}
|
||||
}
|
||||
if (self.monsterMeta != nil) {
|
||||
NSString *metaText = self.monster.meta;
|
||||
if (metaText == nil) {
|
||||
self.monsterMeta.text = @"";
|
||||
} else {
|
||||
self.monsterMeta.text = metaText;
|
||||
}
|
||||
}
|
||||
if (self.monsterArmorClass != nil) {
|
||||
NSString *armorClassDescription = self.monster.armorClassDescription;
|
||||
if (armorClassDescription == nil) {
|
||||
self.monsterArmorClass.text = @"";
|
||||
} else {
|
||||
self.monsterArmorClass.attributedText = [HTMLHelper attributedStringFromHTML:makeHTMLFragmentString(@"<b>Armor Class</b> %@</span>", armorClassDescription)];
|
||||
}
|
||||
}
|
||||
if (self.monsterHitPoints != nil) {
|
||||
NSString *hitPointsDescription = self.monster.hitPointsDescription;
|
||||
if (hitPointsDescription == nil) {
|
||||
self.monsterHitPoints.text = @"";
|
||||
} else {
|
||||
self.monsterHitPoints.attributedText = [HTMLHelper attributedStringFromHTML:makeHTMLFragmentString(@"<b>Hit Points</b> %@", hitPointsDescription)];
|
||||
}
|
||||
}
|
||||
if (self.monsterSpeed != nil) {
|
||||
NSString *speedDescription = self.monster.speedDescription;
|
||||
if (speedDescription == nil) {
|
||||
self.monsterSpeed.text = @"";
|
||||
} else {
|
||||
self.monsterSpeed.attributedText = [HTMLHelper attributedStringFromHTML:makeHTMLFragmentString(@"<b>Speed</b> %@", speedDescription)];
|
||||
}
|
||||
}
|
||||
if (self.monsterStrength) {
|
||||
self.monsterStrength.text = self.monster.strengthDescription;
|
||||
}
|
||||
if (self.monsterDexterity) {
|
||||
self.monsterDexterity.text = self.monster.dexterityDescription;
|
||||
}
|
||||
if (self.monsterConstitution) {
|
||||
self.monsterConstitution.text = self.monster.constitutionDescription;
|
||||
}
|
||||
if (self.monsterIntelligence) {
|
||||
self.monsterIntelligence.text = self.monster.intelligenceDescription;
|
||||
}
|
||||
if (self.monsterWisdom) {
|
||||
self.monsterWisdom.text = self.monster.wisdomDescription;
|
||||
}
|
||||
if (self.monsterCharisma) {
|
||||
self.monsterCharisma.text = self.monster.charismaDescription;
|
||||
}
|
||||
if (self.monsterSavingThrows) {
|
||||
NSString *savingThrowsDescription = self.monster.savingThrowsDescription;
|
||||
if (savingThrowsDescription == nil) {
|
||||
self.monsterSavingThrows.text = @"";
|
||||
} else {
|
||||
self.monsterSavingThrows.attributedText = [HTMLHelper attributedStringFromHTML:makeHTMLFragmentString(@"<b>Saving Throws</b> %@", savingThrowsDescription)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)unwindWithSegue:(UIStoryboardSegue *)unwindSegue {
|
||||
// UIViewController *sourceViewController = unwindSegue.sourceViewController;
|
||||
// Use data from the view controller which initiated the unwind segue
|
||||
}
|
||||
|
||||
#pragma mark - Navigation
|
||||
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
||||
if ([@"EditMonster" isEqualToString:segue.identifier]) {
|
||||
if ([segue.destinationViewController isKindOfClass:[EditMonsterViewController class]]) {
|
||||
EditMonsterViewController *vc = (EditMonsterViewController*)segue.destinationViewController;
|
||||
vc.originalMonster = self.monster;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
41
iOS/MonsterCards/Views/Search.swift
Normal file
41
iOS/MonsterCards/Views/Search.swift
Normal file
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// Search.swift
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 1/15/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct Search: View {
|
||||
@State private var searchText = ""
|
||||
|
||||
@Environment(\.managedObjectContext) var managedObjectContext
|
||||
@FetchRequest(
|
||||
sortDescriptors: [
|
||||
NSSortDescriptor(keyPath: \Monster.name, ascending: true),
|
||||
],
|
||||
animation: .default)
|
||||
var allMonsters: FetchedResults<Monster>
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
List {
|
||||
SearchBar(text: $searchText)
|
||||
.padding(.top, -30)
|
||||
|
||||
ForEach(allMonsters.filter({searchText.isEmpty ? true : $0.name?.containsCaseInsensitive(searchText) ?? false })) { monster in
|
||||
NavigationLink(destination: MonsterDetail(monster: monster)) {
|
||||
Text(monster.name ?? "")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Search_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Search().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
|
||||
}
|
||||
}
|
||||
63
iOS/MonsterCards/Views/SearchBar.swift
Normal file
63
iOS/MonsterCards/Views/SearchBar.swift
Normal file
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// SearchBar.swift
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 1/15/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
|
||||
struct SearchBar: UIViewRepresentable {
|
||||
|
||||
@Binding var text: String
|
||||
|
||||
class Coordinator: NSObject, UISearchBarDelegate {
|
||||
@Binding var text: String
|
||||
|
||||
init(text: Binding<String>) {
|
||||
_text = text
|
||||
}
|
||||
|
||||
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
|
||||
text = searchText
|
||||
}
|
||||
|
||||
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
|
||||
UIApplication.shared.endEditing()
|
||||
}
|
||||
}
|
||||
|
||||
func makeCoordinator() -> SearchBar.Coordinator {
|
||||
return Coordinator(text: $text)
|
||||
}
|
||||
|
||||
func makeUIView(context: UIViewRepresentableContext<SearchBar>) -> UISearchBar {
|
||||
let searchBar = UISearchBar(frame: .zero)
|
||||
searchBar.delegate = context.coordinator
|
||||
searchBar.autocapitalizationType = .none
|
||||
return searchBar
|
||||
}
|
||||
|
||||
func updateUIView(_ uiView: UISearchBar, context: UIViewRepresentableContext<SearchBar>) {
|
||||
uiView.text = text
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
func containsCaseInsensitive(_ string: String) -> Bool {
|
||||
return self.localizedCaseInsensitiveContains(string)
|
||||
}
|
||||
}
|
||||
|
||||
extension UIApplication {
|
||||
func endEditing() {
|
||||
sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
|
||||
}
|
||||
}
|
||||
struct SearchBar_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
SearchBar(text: .constant(""))
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
//
|
||||
// SearchViewController.h
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 9/4/20.
|
||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface SearchViewController : UITableViewController <UISearchBarDelegate, UITableViewDelegate, UITableViewDataSource>
|
||||
|
||||
@property (weak, nonatomic) IBOutlet UISearchBar *searchBar;
|
||||
@property (weak, nonatomic) IBOutlet UITableView *searchResults;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -1,105 +0,0 @@
|
||||
//
|
||||
// SearchViewController.m
|
||||
// MonsterCards
|
||||
//
|
||||
// Created by Tom Hicks on 9/4/20.
|
||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
||||
//
|
||||
|
||||
#import "SearchViewController.h"
|
||||
#import "MonsterViewController.h"
|
||||
#import "Monster.h"
|
||||
#import "AppDelegate.h"
|
||||
|
||||
@interface SearchViewController ()
|
||||
|
||||
@property NSArray* allMonsters;
|
||||
@property NSArray* foundMonsters;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SearchViewController {
|
||||
NSManagedObjectContext *_context;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
AppDelegate *appDelegate = (AppDelegate*)UIApplication.sharedApplication.delegate;
|
||||
_context = appDelegate.persistentContainer.viewContext;
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated {
|
||||
NSString *searchText = nil;
|
||||
if (self.searchBar != nil) {
|
||||
searchText = self.searchBar.text;
|
||||
}
|
||||
self.allMonsters = [_context executeFetchRequest:[Monster fetchRequest] error:nil];
|
||||
self.foundMonsters = [self filterAllMonstersWithText:searchText];
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
|
||||
#pragma mark - Navigation
|
||||
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
||||
if ([@"ShowMonsterDetail" isEqualToString:segue.identifier]) {
|
||||
NSIndexPath *indexPath = [self.searchResults indexPathForSelectedRow];
|
||||
if ([segue.destinationViewController isKindOfClass:[MonsterViewController class]]) {
|
||||
MonsterViewController *vc = (MonsterViewController*)segue.destinationViewController;
|
||||
vc.monster = [self.foundMonsters objectAtIndex:indexPath.row];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDataSource
|
||||
|
||||
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return [self.foundMonsters count];
|
||||
}
|
||||
|
||||
- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
|
||||
static NSString *simpleTableIdentifier = @"MonsterCell";
|
||||
|
||||
UITableViewCell *cell = [self.searchResults dequeueReusableCellWithIdentifier:simpleTableIdentifier];
|
||||
|
||||
if (cell == nil) {
|
||||
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
|
||||
}
|
||||
|
||||
Monster *monster = (Monster*)[self.foundMonsters objectAtIndex:indexPath.row];
|
||||
|
||||
cell.textLabel.text = monster.name;
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
#pragma mark - UISearchBarDelegate
|
||||
|
||||
- (NSArray*)filterAllMonstersWithText:(NSString *)searchText {
|
||||
if (searchText != nil && ![@"" isEqualToString:searchText]) {
|
||||
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id item, NSDictionary *bindings) {
|
||||
if (![item isKindOfClass:[Monster class]]) {
|
||||
return false;
|
||||
}
|
||||
Monster *monster = (Monster*)item;
|
||||
|
||||
if ([monster.name localizedCaseInsensitiveContainsString:searchText]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}];
|
||||
return [self.allMonsters filteredArrayUsingPredicate:predicate];
|
||||
} else {
|
||||
return self.allMonsters;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)searchBar:(UISearchBar *)searchBar
|
||||
textDidChange:(NSString *)searchText {
|
||||
|
||||
self.foundMonsters = [self filterAllMonstersWithText:searchText];
|
||||
|
||||
[self.tableView reloadData];
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user