diff --git a/APCAppCore/APCAppCore.xcodeproj/project.pbxproj b/APCAppCore/APCAppCore.xcodeproj/project.pbxproj index 7c8ae0ab..52339235 100644 --- a/APCAppCore/APCAppCore.xcodeproj/project.pbxproj +++ b/APCAppCore/APCAppCore.xcodeproj/project.pbxproj @@ -722,6 +722,8 @@ FB4E468C1C7E6E0000CADC1F /* APCNavigationFooter.m in Sources */ = {isa = PBXBuildFile; fileRef = FB4E468A1C7E6E0000CADC1F /* APCNavigationFooter.m */; }; FBAE3D5E1C878064003CEC4A /* APCOptionalStepViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = FBAE3D5C1C878064003CEC4A /* APCOptionalStepViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; FBAE3D5F1C878064003CEC4A /* APCOptionalStepViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FBAE3D5D1C878064003CEC4A /* APCOptionalStepViewController.m */; }; + FF00961B1D05F23D006258B5 /* APCPickerItemQuantity.h in Headers */ = {isa = PBXBuildFile; fileRef = FF0096191D05F23D006258B5 /* APCPickerItemQuantity.h */; }; + FF00961C1D05F23D006258B5 /* APCPickerItemQuantity.m in Sources */ = {isa = PBXBuildFile; fileRef = FF00961A1D05F23D006258B5 /* APCPickerItemQuantity.m */; }; FF0B4A961C29D98C00224EF7 /* APCScene.h in Headers */ = {isa = PBXBuildFile; fileRef = FF0B4A941C29D98C00224EF7 /* APCScene.h */; settings = {ATTRIBUTES = (Public, ); }; }; FF0B4A971C29D98C00224EF7 /* APCScene.m in Sources */ = {isa = PBXBuildFile; fileRef = FF0B4A951C29D98C00224EF7 /* APCScene.m */; }; FF44E50A1C1A3BB200F07DA9 /* BridgeSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FF44E5091C1A3BB200F07DA9 /* BridgeSDK.framework */; }; @@ -1483,6 +1485,8 @@ FB4E468A1C7E6E0000CADC1F /* APCNavigationFooter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = APCNavigationFooter.m; sourceTree = ""; }; FBAE3D5C1C878064003CEC4A /* APCOptionalStepViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APCOptionalStepViewController.h; sourceTree = ""; }; FBAE3D5D1C878064003CEC4A /* APCOptionalStepViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = APCOptionalStepViewController.m; sourceTree = ""; }; + FF0096191D05F23D006258B5 /* APCPickerItemQuantity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APCPickerItemQuantity.h; sourceTree = ""; }; + FF00961A1D05F23D006258B5 /* APCPickerItemQuantity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = APCPickerItemQuantity.m; sourceTree = ""; }; FF0B4A941C29D98C00224EF7 /* APCScene.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APCScene.h; sourceTree = ""; }; FF0B4A951C29D98C00224EF7 /* APCScene.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = APCScene.m; sourceTree = ""; }; FF44E5091C1A3BB200F07DA9 /* BridgeSDK.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = BridgeSDK.framework; path = "../../../../../Library/Developer/Xcode/DerivedData/Parkinson-dphbgsbdqdzhcbeketqhdiecbfyz/Build/Products/Debug-iphoneos/BridgeSDK.framework"; sourceTree = ""; }; @@ -2739,6 +2743,8 @@ 5B04329E1A318AA2000DC9ED /* APCSignInTask.m */, F5F129411A2F78490015982C /* APCTableViewItem.h */, F5F129421A2F78490015982C /* APCTableViewItem.m */, + FF0096191D05F23D006258B5 /* APCPickerItemQuantity.h */, + FF00961A1D05F23D006258B5 /* APCPickerItemQuantity.m */, ); path = Model; sourceTree = ""; @@ -3261,6 +3267,7 @@ 5B9B36A71A95D9C900389F42 /* APCActivitiesBasicTableViewCell.h in Headers */, 3650C65B1AA29BB50075C935 /* APCCatastrophicErrorViewController.h in Headers */, 7B751D571B0A61A600E77BD2 /* NSDictionary+APCStringify.h in Headers */, + FF00961B1D05F23D006258B5 /* APCPickerItemQuantity.h in Headers */, F5B9480C1A73272C0034C522 /* APCScheduleExpressionTokenizer.h in Headers */, F5F12A851A2F78490015982C /* APCOnboarding.h in Headers */, 369E27F31A96B7A200D35DFA /* APCMedicationLozenge.h in Headers */, @@ -3665,6 +3672,7 @@ F5F12AE11A2F78490015982C /* APCSwitchTableViewCell.m in Sources */, F5B946351A7309A20034C522 /* ZZError.m in Sources */, 5BE106F11A9EE2D900CA5A07 /* APCDashboardGraphTableViewCell.m in Sources */, + FF00961C1D05F23D006258B5 /* APCPickerItemQuantity.m in Sources */, CFFDEDF31A95734000B25581 /* APCMedicationSummaryTableViewCell.m in Sources */, 7B8DBEC51AA1871D007B4026 /* APCVerticalThinLineView.m in Sources */, 5B0432A01A318AA2000DC9ED /* APCSignInTask.m in Sources */, diff --git a/APCAppCore/APCAppCore/DataSubstrate/Model/APCUser+UserData.h b/APCAppCore/APCAppCore/DataSubstrate/Model/APCUser+UserData.h index af8b7ee7..7ee6e9e6 100644 --- a/APCAppCore/APCAppCore/DataSubstrate/Model/APCUser+UserData.h +++ b/APCAppCore/APCAppCore/DataSubstrate/Model/APCUser+UserData.h @@ -33,6 +33,8 @@ #import "APCUser.h" +@class APCPickerItemQuantity; + @interface APCUser (UserData) /* Biologcal Sex */ @@ -58,9 +60,8 @@ + (NSArray *) medications; /* Height */ -+ (NSArray *) heights; - -+ (double)heightInInchesForSelectedIndices:(NSArray *)selectedIndices; ++ (NSArray *) heights __attribute__((deprecated("Please use -localizedHeightPickerDataAndSelectedIndices:"))); ++ (double)heightInInchesForSelectedIndices:(NSArray *)selectedIndices __attribute__((deprecated("Please use -setHeightForPickerData:selectedIndices:"))); + (double)heightInInches:(HKQuantity *)height; @@ -71,4 +72,8 @@ + (double)weightInKilograms:(HKQuantity *)weight; +- (NSArray *)localizedHeightPickerDataAndSelectedIndices:(NSArray **)selectedIndices; +- (void)setHeightForPickerData:(NSArray *)pickerData selectedIndices:(NSArray *)selectedIndices; + + @end diff --git a/APCAppCore/APCAppCore/DataSubstrate/Model/APCUser+UserData.m b/APCAppCore/APCAppCore/DataSubstrate/Model/APCUser+UserData.m index cbb868a2..070be817 100644 --- a/APCAppCore/APCAppCore/DataSubstrate/Model/APCUser+UserData.m +++ b/APCAppCore/APCAppCore/DataSubstrate/Model/APCUser+UserData.m @@ -33,6 +33,7 @@ #import "APCUser+UserData.h" #import "APCLocalization.h" +#import "APCPickerItemQuantity.h" @implementation APCUser (UserData) @@ -127,6 +128,91 @@ + (NSArray *) medications { /******* Height *******/ + +- (NSArray *)localizedHeightPickerDataAndSelectedIndices:(NSArray **)selectedIndices +{ + // Find the appropriate localized unit + NSLengthFormatterUnit formatterUnit; + NSLengthFormatter *formatter = [[NSLengthFormatter alloc] init]; + formatter.unitStyle = NSFormattingUnitStyleMedium; + formatter.forPersonHeightUse = YES; + [formatter unitStringFromMeters:2.0 usedUnit:&formatterUnit]; + + // Convert the formatter unit to a HKUnit (default to centimeter) + NSArray *hkUnits = @[[HKUnit meterUnitWithMetricPrefix:HKMetricPrefixCenti]]; + NSArray *formatterUnits = @[@(NSLengthFormatterUnitCentimeter)]; + NSArray *maxValues = @[@(floor((8 * 12 + 11) * 2.54))]; + NSArray *builderUnits = hkUnits; + + switch (formatterUnit) { + case NSLengthFormatterUnitInch: + case NSLengthFormatterUnitFoot: + case NSLengthFormatterUnitYard: + hkUnits = @[[HKUnit footUnit], [HKUnit inchUnit]]; + formatterUnits = @[@(NSLengthFormatterUnitFoot), @(NSLengthFormatterUnitInch)]; + maxValues = @[@(8), @(11)]; + builderUnits = hkUnits; + formatter.unitStyle = NSFormattingUnitStyleShort; + break; + + case NSLengthFormatterUnitCentimeter: + break; + + default: + hkUnits = @[[HKUnit meterUnit]]; + formatterUnits = @[@(NSLengthFormatterUnitMeter)]; + break; + } + + NSMutableArray *heights = [NSMutableArray new]; + NSMutableArray *currentIndices = self.height != nil ? [NSMutableArray new] : nil; + + for (NSUInteger ii=0; ii < maxValues.count; ii++) { + + NSMutableArray *columnData = [NSMutableArray new]; + NSUInteger maxValue = [maxValues[ii] unsignedIntegerValue]; + HKUnit *builderUnit = builderUnits[ii]; + double height = [self.height doubleValueForUnit:builderUnit]; + for (NSUInteger jj=0; jj < ii; jj++) { + HKQuantity *unitQuantity = [HKQuantity quantityWithUnit:builderUnits[jj] doubleValue:1.0]; + height -= [currentIndices[jj] doubleValue] * [unitQuantity doubleValueForUnit:builderUnit]; + } + BOOL isLast = (ii == maxValues.count - 1); + NSUInteger current = MIN(maxValue, isLast ? round(height) : floor(height)); + [currentIndices addObject:@(current)]; + + for (NSUInteger nn=0; nn <= maxValue; nn++) { + HKQuantity *quantity = [HKQuantity quantityWithUnit:builderUnit doubleValue:(double)nn]; + double value = [quantity doubleValueForUnit:hkUnits[ii]]; + NSString *text = [formatter stringFromValue:value unit:[formatterUnits[ii] integerValue]]; + APCPickerItemQuantity *pickerData = [[APCPickerItemQuantity alloc] initWithQuantity:quantity text:text]; + [columnData addObject:pickerData]; + } + + [heights addObject:[columnData copy]]; + } + + if (currentIndices != nil && selectedIndices != nil) { + *selectedIndices = [currentIndices copy]; + } + + return [heights copy]; +} + +- (void)setHeightForPickerData:(NSArray *)pickerData selectedIndices:(NSArray *)selectedIndices { + double value = 0; + HKUnit *unit = [HKUnit meterUnit]; + for (NSInteger ii=selectedIndices.count - 1; ii >= 0; ii--) { + NSUInteger idx = [selectedIndices[ii] unsignedIntegerValue]; + NSArray *columnData = pickerData[ii]; + APCPickerItemQuantity *item = columnData[idx]; + value += [item.quantity doubleValueForUnit:unit]; + } + if (value > 0) { + self.height = [HKQuantity quantityWithUnit:unit doubleValue:value]; + } +} + + (NSArray *) heights { return @[ @[@"0'", @"1'", @"2'", @"3'", @"4'", @"5'", @"6'", @"7'", @"8'"], diff --git a/APCAppCore/APCAppCore/UI/Model/APCPickerItemQuantity.h b/APCAppCore/APCAppCore/UI/Model/APCPickerItemQuantity.h new file mode 100644 index 00000000..2c63d39a --- /dev/null +++ b/APCAppCore/APCAppCore/UI/Model/APCPickerItemQuantity.h @@ -0,0 +1,49 @@ +// +// APCPickerItemQuantity.h +// APCAppCore +// +// Copyright © 2016 Sage Bionetworks. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder(s) nor the names of any contributors +// may be used to endorse or promote products derived from this software without +// specific prior written permission. No license is granted to the trademarks of +// the copyright holders even if such marks are included in this software. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface APCPickerItemQuantity : NSObject + +@property (nonatomic, readonly) HKQuantity *quantity; +@property (nonatomic, readonly) NSString *text; + +- (instancetype)initWithQuantity:(HKQuantity *)quantity text:(NSString *)text NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/APCAppCore/APCAppCore/UI/Model/APCPickerItemQuantity.m b/APCAppCore/APCAppCore/UI/Model/APCPickerItemQuantity.m new file mode 100644 index 00000000..cb932bd5 --- /dev/null +++ b/APCAppCore/APCAppCore/UI/Model/APCPickerItemQuantity.m @@ -0,0 +1,63 @@ +// +// APCPickerItemQuantity.m +// APCAppCore +// +// Copyright © 2016 Sage Bionetworks. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// 3. Neither the name of the copyright holder(s) nor the names of any contributors +// may be used to endorse or promote products derived from this software without +// specific prior written permission. No license is granted to the trademarks of +// the copyright holders even if such marks are included in this software. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + + +#import "APCPickerItemQuantity.h" + +@implementation APCPickerItemQuantity + +- (instancetype)initWithQuantity:(HKQuantity *)quantity text:(NSString *)text { + if (self = [super init]) { + _quantity = quantity; + _text = text; + } + return self; +} + +- (NSString *)description { + return self.text; +} + +- (id)copyWithZone:(NSZone *)zone { + return [[[self class] allocWithZone:zone] initWithQuantity:self.quantity text:self.text]; +} + +- (NSUInteger)hash { + return self.quantity.hash; +} + +- (BOOL)isEqual:(id)object { + return [object isMemberOfClass:[self class]] && [self.quantity isEqual:[object quantity]]; +} + +@end diff --git a/APCAppCore/APCAppCore/UI/Model/APCTableViewItem.h b/APCAppCore/APCAppCore/UI/Model/APCTableViewItem.h index 68bf5c6c..9dff7e76 100644 --- a/APCAppCore/APCAppCore/UI/Model/APCTableViewItem.h +++ b/APCAppCore/APCAppCore/UI/Model/APCTableViewItem.h @@ -85,6 +85,10 @@ @property (nonatomic, copy) NSString *value; +@property (nonatomic, copy) HKUnit *unit; + +- (HKQuantity *)quantity; + @end diff --git a/APCAppCore/APCAppCore/UI/Model/APCTableViewItem.m b/APCAppCore/APCAppCore/UI/Model/APCTableViewItem.m index da1c7d91..fa267811 100644 --- a/APCAppCore/APCAppCore/UI/Model/APCTableViewItem.m +++ b/APCAppCore/APCAppCore/UI/Model/APCTableViewItem.m @@ -101,7 +101,7 @@ - (NSString *) stringValue { NSInteger selectedRowInComponent = [self.selectedRowIndices[i] integerValue]; - [string appendString:component[selectedRowInComponent]]; + [string appendString:[component[selectedRowInComponent] description]]; if (i < (self.pickerData.count - 1)) { [string appendString:@" "]; diff --git a/APCAppCore/APCAppCore/UI/Onboarding/SignUp/APCSignUpMedicalInfoViewController.m b/APCAppCore/APCAppCore/UI/Onboarding/SignUp/APCSignUpMedicalInfoViewController.m index 5d776e52..39661834 100644 --- a/APCAppCore/APCAppCore/UI/Onboarding/SignUp/APCSignUpMedicalInfoViewController.m +++ b/APCAppCore/APCAppCore/UI/Onboarding/SignUp/APCSignUpMedicalInfoViewController.m @@ -176,72 +176,6 @@ - (NSArray *)prepareContent { } break; - case kAPCUserInfoItemTypeHeight: - { - APCTableViewCustomPickerItem *field = [APCTableViewCustomPickerItem new]; - field.caption = NSLocalizedStringWithDefaultValue(@"Height", @"APCAppCore", APCBundle(), @"Height", @""); - field.reuseIdentifier = kAPCDefaultTableViewCellIdentifier; - field.selectionStyle = UITableViewCellSelectionStyleGray; - field.detailDiscloserStyle = YES; - field.textAlignnment = NSTextAlignmentRight; - field.pickerData = [APCUser heights]; - - NSInteger indexOfMyHeightInFeet = 0; - NSInteger indexOfMyHeightInInches = 0; - - if (self.user.height) { - double heightInInches = round([APCUser heightInInches:self.user.height]); - - NSString *feet = [NSString stringWithFormat:@"%d'", (int)heightInInches/12]; - NSString *inches = [NSString stringWithFormat:@"%d''", (int)heightInInches%12]; - - NSArray *allPossibleHeightsInFeet = field.pickerData [0]; - NSArray *allPossibleHeightsInInches = field.pickerData [1]; - - //107 inches i.e. 8'11" is the max. height. - if (heightInInches <= 107) { - indexOfMyHeightInFeet = [allPossibleHeightsInFeet indexOfObject: feet]; - indexOfMyHeightInInches = [allPossibleHeightsInInches indexOfObject: inches]; - } else { - indexOfMyHeightInFeet = allPossibleHeightsInFeet.count-1; - indexOfMyHeightInInches = allPossibleHeightsInInches.count-1; - } - - } - - if (indexOfMyHeightInFeet && indexOfMyHeightInInches) { - field.selectedRowIndices = @[ @(indexOfMyHeightInFeet), @(indexOfMyHeightInInches) ]; - } - - APCTableViewRow *row = [APCTableViewRow new]; - row.item = field; - row.itemType = kAPCUserInfoItemTypeHeight; - [rowItems addObject:row]; - } - break; - - case kAPCUserInfoItemTypeWeight: - { - APCTableViewTextFieldItem *field = [APCTableViewTextFieldItem new]; - field.caption = NSLocalizedStringWithDefaultValue(@"Weight", @"APCAppCore", APCBundle(), @"Weight", @""); - field.placeholder = NSLocalizedStringWithDefaultValue(@"add weight (lb)", @"APCAppCore", APCBundle(), @"add weight (lb)", @""); - field.style = UITableViewCellStyleValue1; - field.reuseIdentifier = kAPCTextFieldTableViewCellIdentifier; - field.regularExpression = kAPCMedicalInfoItemWeightRegEx; - field.keyboardType = UIKeyboardTypeDecimalPad; - field.textAlignnment = NSTextAlignmentRight; - - if (self.user.weight) { - field.value = [NSString stringWithFormat:@"%.0f", [APCUser weightInPounds:self.user.weight]]; - } - - APCTableViewRow *row = [APCTableViewRow new]; - row.item = field; - row.itemType = kAPCUserInfoItemTypeWeight; - [rowItems addObject:row]; - } - break; - case kAPCUserInfoItemTypeWakeUpTime: { APCTableViewDatePickerItem *field = [APCTableViewDatePickerItem new]; @@ -301,7 +235,12 @@ - (NSArray *)prepareContent { } break; - default: + default: { + APCTableViewRow *row = [self createTableViewRowForItemType:itemType user:self.user]; + if (row != nil) { + [rowItems addObject:row]; + } + } break; } } @@ -351,44 +290,8 @@ - (void)loadProfileValuesInModel { self.user.medications = [(APCTableViewCustomPickerItem *)item stringValue]; break; - case kAPCUserInfoItemTypeHeight: - { - double height = [APCUser heightInInchesForSelectedIndices:[(APCTableViewCustomPickerItem *)item selectedRowIndices]]; - - if (height > 0) { - HKUnit *inchUnit = [HKUnit inchUnit]; - HKQuantity *heightQuantity = [HKQuantity quantityWithUnit:inchUnit doubleValue:height]; - - self.user.height = heightQuantity; - } - } - - break; - - case kAPCUserInfoItemTypeWeight: - { - double weight = [[(APCTableViewTextFieldItem *)item value] floatValue]; - - if (weight > 0) { - HKUnit *poundUnit = [HKUnit poundUnit]; - HKQuantity *weightQuantity = [HKQuantity quantityWithUnit:poundUnit doubleValue:weight]; - - self.user.weight = weightQuantity; - } - } - break; - - case kAPCUserInfoItemTypeSleepTime: - self.user.sleepTime = [(APCTableViewDatePickerItem *)item date]; - break; - - case kAPCUserInfoItemTypeWakeUpTime: - self.user.wakeUpTime = [(APCTableViewDatePickerItem *)item date]; - break; - default: - //#warning ASSERT_MESSAGE Require - NSAssert(itemType <= kAPCUserInfoItemTypeWakeUpTime, @"ASSER_MESSAGE"); + [self updateUser:self.user forItem:item itemType:itemType]; break; } } diff --git a/APCAppCore/APCAppCore/UI/TabBarControllers/Profile/APCProfileViewController.m b/APCAppCore/APCAppCore/UI/TabBarControllers/Profile/APCProfileViewController.m index 177f3d80..bf05fc83 100644 --- a/APCAppCore/APCAppCore/UI/TabBarControllers/Profile/APCProfileViewController.m +++ b/APCAppCore/APCAppCore/UI/TabBarControllers/Profile/APCProfileViewController.m @@ -623,78 +623,6 @@ - (NSArray *)prepareContent } break; - case kAPCUserInfoItemTypeHeight: - { - - APCTableViewCustomPickerItem *field = [APCTableViewCustomPickerItem new]; - field.caption = NSLocalizedStringWithDefaultValue(@"Height", @"APCAppCore", APCBundle(), @"Height", @""); - field.reuseIdentifier = kAPCDefaultTableViewCellIdentifier; - field.detailDiscloserStyle = YES; - field.textAlignnment = NSTextAlignmentRight; - field.pickerData = [APCUser heights]; - field.selectionStyle = self.isEditing ? UITableViewCellSelectionStyleGray : UITableViewCellSelectionStyleNone; - field.editable = NO; - - NSInteger defaultIndexOfMyHeightInFeet = 5; - NSInteger defaultIndexOfMyHeightInInches = 0; - - double usersHeight = [APCUser heightInInches:self.user.height]; - - if (usersHeight) { - double heightInInches = round(usersHeight); - NSString *feet = [NSString stringWithFormat:@"%i'", (int)heightInInches/12]; - NSString *inches = [NSString stringWithFormat:@"%i''", (int)heightInInches%12]; - - NSArray *allPossibleHeightsInFeet = field.pickerData [0]; - NSArray *allPossibleHeightsInInches = field.pickerData [1]; - - NSInteger indexOfMyHeightInFeet = [allPossibleHeightsInFeet indexOfObject: feet]; - NSInteger indexOfMyHeightInInches = [allPossibleHeightsInInches indexOfObject: inches]; - - if (indexOfMyHeightInFeet == NSNotFound) { - indexOfMyHeightInFeet = defaultIndexOfMyHeightInFeet; - } - - if (indexOfMyHeightInInches == NSNotFound) { - indexOfMyHeightInInches = defaultIndexOfMyHeightInInches; - } - - field.selectedRowIndices = @[ @(indexOfMyHeightInFeet), @(indexOfMyHeightInInches) ]; - - } - - APCTableViewRow *row = [APCTableViewRow new]; - row.item = field; - row.itemType = kAPCUserInfoItemTypeHeight; - [rowItems addObject:row]; - } - break; - - case kAPCUserInfoItemTypeWeight: - { - APCTableViewTextFieldItem *field = [APCTableViewTextFieldItem new]; - field.caption = NSLocalizedStringWithDefaultValue(@"Weight", @"APCAppCore", APCBundle(), @"Weight", @""); - field.placeholder = NSLocalizedStringWithDefaultValue(@"add weight (lb)", @"APCAppCore", APCBundle(), @"add weight (lb)", @""); - field.regularExpression = kAPCMedicalInfoItemWeightRegEx; - - double userWeight = [APCUser weightInPounds:self.user.weight]; - - if (userWeight) { - field.value = [NSString stringWithFormat:@"%.0f", userWeight]; - } - - field.keyboardType = UIKeyboardTypeDecimalPad; - field.textAlignnment = NSTextAlignmentRight; - field.reuseIdentifier = kAPCTextFieldTableViewCellIdentifier; - field.selectionStyle = self.isEditing ? UITableViewCellSelectionStyleGray : UITableViewCellSelectionStyleNone; - - APCTableViewRow *row = [APCTableViewRow new]; - row.item = field; - row.itemType = kAPCUserInfoItemTypeWeight; - [rowItems addObject:row]; - } - break; - case kAPCUserInfoItemTypeWakeUpTime: { APCTableViewDatePickerItem *field = [APCTableViewDatePickerItem new]; @@ -756,7 +684,13 @@ - (NSArray *)prepareContent } break; - default: + default:{ + APCTableViewRow *row = [self createTableViewRowForItemType:itemType user:self.user]; + if (row != nil) { + [rowItems addObject:row]; + } + } + break; } } @@ -1463,36 +1397,6 @@ - (void)loadProfileValuesInModel case kAPCUserInfoItemTypeMedication: self.user.medications = [(APCTableViewCustomPickerItem *)item stringValue]; break; - - case kAPCUserInfoItemTypeHeight: - { - APCTableViewCustomPickerItem *heightPicker = (APCTableViewCustomPickerItem *)item; - double height = [APCUser heightInInchesForSelectedIndices:heightPicker.selectedRowIndices]; - - HKUnit *inchUnit = [HKUnit inchUnit]; - HKQuantity *heightQuantity = [HKQuantity quantityWithUnit:inchUnit doubleValue:height]; - - self.user.height = heightQuantity; - } - break; - - case kAPCUserInfoItemTypeWeight: - { - double weight = [[(APCTableViewTextFieldItem *)item value] floatValue]; - HKUnit *poundUnit = [HKUnit poundUnit]; - HKQuantity *weightQuantity = [HKQuantity quantityWithUnit:poundUnit doubleValue:weight]; - - self.user.weight = weightQuantity; - } - break; - - case kAPCUserInfoItemTypeSleepTime: - self.user.sleepTime = [(APCTableViewDatePickerItem *)item date]; - break; - - case kAPCUserInfoItemTypeWakeUpTime: - self.user.wakeUpTime = [(APCTableViewDatePickerItem *)item date]; - break; case kAPCUserInfoItemTypeDataGroups: [self.dataGroupsManager setSurveyAnswerWithItem:item]; @@ -1537,6 +1441,7 @@ - (void)loadProfileValuesInModel break; default: + [self updateUser:self.user forItem:item itemType:itemType]; break; } } @@ -1760,6 +1665,12 @@ - (void)presentSettingsAlert:(NSError *)error [self.navigationController presentViewController:alertContorller animated:YES completion:nil]; } +- (void)beginEditing { + if (!self.isEditing) { + [self editFields:self.rightBarButton]; + } +} + - (IBAction)editFields:(UIBarButtonItem *)sender { if (self.isEditing) { diff --git a/APCAppCore/APCAppCore/UI/TableViewCells/APCPickerTableViewCell.m b/APCAppCore/APCAppCore/UI/TableViewCells/APCPickerTableViewCell.m index b0369156..9851cb85 100644 --- a/APCAppCore/APCAppCore/UI/TableViewCells/APCPickerTableViewCell.m +++ b/APCAppCore/APCAppCore/UI/TableViewCells/APCPickerTableViewCell.m @@ -113,7 +113,7 @@ - (NSString *) pickerView: (UIPickerView *) __unused pickerView titleForRow: (NSInteger) row forComponent: (NSInteger) component { - return self.pickerValues[component][row]; + return [self.pickerValues[component][row] description]; } #pragma mark - UIPickerViewDelegate Methods diff --git a/APCAppCore/APCAppCore/UI/ViewControllers/APCUserInfoViewController.h b/APCAppCore/APCAppCore/UI/ViewControllers/APCUserInfoViewController.h index 8980000b..75903164 100644 --- a/APCAppCore/APCAppCore/UI/ViewControllers/APCUserInfoViewController.h +++ b/APCAppCore/APCAppCore/UI/ViewControllers/APCUserInfoViewController.h @@ -77,4 +77,8 @@ - (APCTableViewItemType)itemTypeForIndexPath:(NSIndexPath *)indexPath; +- (APCTableViewRow *)createTableViewRowForItemType:(APCUserInfoItemType)itemType user:(APCUser *)user; + +- (void)updateUser:(APCUser *)user forItem:(APCTableViewItem *)item itemType:(APCUserInfoItemType)itemType; + @end diff --git a/APCAppCore/APCAppCore/UI/ViewControllers/APCUserInfoViewController.m b/APCAppCore/APCAppCore/UI/ViewControllers/APCUserInfoViewController.m index c3691ddf..fa0131a0 100644 --- a/APCAppCore/APCAppCore/UI/ViewControllers/APCUserInfoViewController.m +++ b/APCAppCore/APCAppCore/UI/ViewControllers/APCUserInfoViewController.m @@ -36,6 +36,7 @@ #import "NSDate+Helper.h" #import "UIColor+APCAppearance.h" #import "UIFont+APCAppearance.h" +#import "APCLocalization.h" static CGFloat const kPickerCellHeight = 164.0f; @@ -74,6 +75,10 @@ - (void)setupBasicCellAppearance:(UITableViewCell *)cell [cell.textLabel setFont:[UIFont appRegularFontWithSize:17.0f]]; } +- (void)beginEditing { + // DO nothing. Overrideable by subclass. +} + #pragma mark - UITableViewDataSource methods - (NSInteger) numberOfSectionsInTableView: (UITableView *) __unused tableView @@ -436,7 +441,11 @@ - (void)handlePickerForIndexPath:(NSIndexPath *)indexPath { if (self.isPickerShowing && (self.pickerIndexPath.row - 1 == indexPath.row) && (indexPath.section == self.pickerIndexPath.section)) { [self hidePickerCell]; - } else{ + } + else { + // Ensure that the view controller is editing before selecting index path + [self beginEditing]; + NSIndexPath *selectedIndexpath = [self actualSelectedIndexPath:indexPath]; if (self.isPickerShowing) { @@ -507,4 +516,130 @@ - (APCTableViewItemType)itemTypeForIndexPath:(NSIndexPath *)indexPath return itemRow.itemType; } +- (APCTableViewRow *)createTableViewRowForItemType:(APCUserInfoItemType)itemType user:(APCUser *)user { + switch (itemType) { + + case kAPCUserInfoItemTypeHeight: { + + APCTableViewCustomPickerItem *field = [APCTableViewCustomPickerItem new]; + field.caption = NSLocalizedStringWithDefaultValue(@"Height", @"APCAppCore", APCBundle(), @"Height", @""); + field.reuseIdentifier = kAPCDefaultTableViewCellIdentifier; + field.selectionStyle = UITableViewCellSelectionStyleGray; + field.detailDiscloserStyle = YES; + field.textAlignnment = NSTextAlignmentRight; + + NSArray *selectedIndices; + field.pickerData = [user localizedHeightPickerDataAndSelectedIndices:&selectedIndices]; + if (selectedIndices) { + field.selectedRowIndices = selectedIndices; + } + + APCTableViewRow *row = [APCTableViewRow new]; + row.item = field; + row.itemType = kAPCUserInfoItemTypeHeight; + return row; + } + + case kAPCUserInfoItemTypeWeight: + { + APCTableViewTextFieldItem *field = [APCTableViewTextFieldItem new]; + field.caption = NSLocalizedStringWithDefaultValue(@"Weight", @"APCAppCore", APCBundle(), @"Weight", @""); + field.style = UITableViewCellStyleValue1; + field.reuseIdentifier = kAPCTextFieldTableViewCellIdentifier; + field.regularExpression = kAPCMedicalInfoItemWeightRegEx; + field.keyboardType = UIKeyboardTypeDecimalPad; + field.textAlignnment = NSTextAlignmentRight; + + // Find the appropriate localized unit + NSMassFormatterUnit formatterUnit; + NSMassFormatter *formatter = [[NSMassFormatter alloc] init]; + formatter.unitStyle = NSFormattingUnitStyleMedium; + formatter.forPersonMassUse = YES; + NSString *desiredUnit = [formatter unitStringFromKilograms:0 usedUnit:&formatterUnit]; + + // Convert the formatter unit to a HKUnit + switch (formatterUnit) { + case NSMassFormatterUnitOunce: + field.unit = [HKUnit ounceUnit]; break; + case NSMassFormatterUnitPound: + field.unit = [HKUnit poundUnit]; + formatter.numberFormatter.maximumFractionDigits = 0; + break; + case NSMassFormatterUnitStone: + field.unit = [HKUnit stoneUnit]; break; + case NSMassFormatterUnitGram: + field.unit = [HKUnit gramUnit]; break; + case NSMassFormatterUnitKilogram: + field.unit = [HKUnit gramUnitWithMetricPrefix:HKMetricPrefixKilo]; + formatter.numberFormatter.maximumFractionDigits = 1; + break; + } + + if (user.weight) { + field.value = [formatter stringFromKilograms:[user.weight doubleValueForUnit:[HKUnit gramUnitWithMetricPrefix:HKMetricPrefixKilo]]]; + } + + NSString *placeholderFormat = NSLocalizedStringWithDefaultValue(@"add weight (%@)", @"APCAppCore", APCBundle(), @"add weight (%@)", @""); + field.placeholder = [NSString stringWithFormat:placeholderFormat, desiredUnit]; + + APCTableViewRow *row = [APCTableViewRow new]; + row.item = field; + row.itemType = kAPCUserInfoItemTypeWeight; + return row; + } + + default: { + return nil; + } + } +} + +- (void)updateUser:(APCUser *)user forItem:(APCTableViewItem *)item itemType:(APCUserInfoItemType)itemType { + + switch (itemType) { + + case kAPCUserInfoItemTypeHeight: + { + APCTableViewCustomPickerItem *pickerItem = (APCTableViewCustomPickerItem *)item; + [user setHeightForPickerData:pickerItem.pickerData selectedIndices:pickerItem.selectedRowIndices]; + } + break; + + case kAPCUserInfoItemTypeWeight: + { + APCTableViewTextFieldItem *textItem = (APCTableViewTextFieldItem *)item; + NSMutableCharacterSet *numberSet = [[NSCharacterSet decimalDigitCharacterSet] mutableCopy]; + [numberSet addCharactersInString:@".,"]; + NSRange range = [textItem.value rangeOfCharacterFromSet:numberSet options:NSBackwardsSearch]; + if (range.location != NSNotFound) { + NSString *textValue = [textItem.value substringToIndex:range.location+1]; + NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; + double weight = [[formatter numberFromString:textValue] doubleValue]; + HKUnit *unit = textItem.unit; + if (range.location+1 < textItem.value.length) { + HKUnit *returnedUnit = [HKUnit unitFromString:[textItem.value substringFromIndex:range.location+1 ]]; + if ((returnedUnit != nil) && ![returnedUnit isEqual:unit] && + [[HKQuantity quantityWithUnit:unit doubleValue:1] isCompatibleWithUnit:returnedUnit]) { + unit = returnedUnit; + } + } + user.weight = [HKQuantity quantityWithUnit:unit doubleValue:weight]; + } + } + break; + + case kAPCUserInfoItemTypeSleepTime: + user.sleepTime = [(APCTableViewDatePickerItem *)item date]; + break; + + case kAPCUserInfoItemTypeWakeUpTime: + user.wakeUpTime = [(APCTableViewDatePickerItem *)item date]; + break; + + default: + break; + } + +} + @end