Convertes to Swift and SwiftUI

This commit is contained in:
2021-01-16 01:12:49 -08:00
parent 3d54342687
commit f6ef6a7f3d
459 changed files with 2301 additions and 23910 deletions

View 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)
}
}

View File

@@ -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

View File

@@ -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

View 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)
}
}

View 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)
}
}

View File

@@ -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

View File

@@ -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

View 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)
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 */

View File

@@ -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";

View File

@@ -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 */

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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)
}
}

View File

@@ -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

View File

@@ -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

View 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))
}
}

View 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))
}
}

View 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))
}
}

View 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"))
}
}

View 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)")
}
}
}

View File

@@ -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

View File

@@ -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

View 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)
}
}

View 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(""))
}
}

View File

@@ -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

View File

@@ -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