diff --git a/Gulps WatchKit Extension/ExtensionDelegate.swift b/Gulps WatchKit Extension/ExtensionDelegate.swift index 5a8e1c4..001d266 100644 --- a/Gulps WatchKit Extension/ExtensionDelegate.swift +++ b/Gulps WatchKit Extension/ExtensionDelegate.swift @@ -57,6 +57,8 @@ class ExtensionDelegate: NSObject, WKExtensionDelegate, WCSessionDelegate { NSNotificationCenter.defaultCenter().postNotificationName(NotificationContextReceived, object: nil) self.reloadComplications() } + + // NOTE: The Apple Watch target does not yet support "Constants.Gulp.Custom.key" } func reloadComplications() { diff --git a/Gulps.xcodeproj/project.pbxproj b/Gulps.xcodeproj/project.pbxproj index 95c5fbb..4275fed 100644 --- a/Gulps.xcodeproj/project.pbxproj +++ b/Gulps.xcodeproj/project.pbxproj @@ -78,6 +78,7 @@ 65F55F2F1B5942A4008653F2 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 65F55F211B592D91008653F2 /* Localizable.strings */; }; 65F55F321B5F965F008653F2 /* drop.caf in Resources */ = {isa = PBXBuildFile; fileRef = 65F55F311B5D0A0E008653F2 /* drop.caf */; }; 800E72B73F54FA65CABC4BD6 /* Pods_Gulps.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A701E98A87809500CB34BF4 /* Pods_Gulps.framework */; }; + 9E879C4F1C5FA5CD00AFD0F4 /* CustomViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E879C4E1C5FA5CD00AFD0F4 /* CustomViewController.swift */; }; E3F5301FBFEDB00ED6B865BA /* Pods_GulpsToday.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD5FAC2A10E7AFB568C1D91E /* Pods_GulpsToday.framework */; }; /* End PBXBuildFile section */ @@ -227,11 +228,12 @@ 65F55F1F1B5904B8008653F2 /* FeedbackViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FeedbackViewController.swift; sourceTree = ""; }; 65F55F221B592D91008653F2 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = ""; }; 65F55F241B592DA2008653F2 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; - 65F55F261B5938BE008653F2 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Main.strings; sourceTree = ""; }; 65F55F281B593A0A008653F2 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Onboarding.strings; sourceTree = ""; }; 65F55F2A1B593C16008653F2 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Interface.strings; sourceTree = ""; }; 65F55F311B5D0A0E008653F2 /* drop.caf */ = {isa = PBXFileReference; lastKnownFileType = file; path = drop.caf; sourceTree = ""; }; 8339035B8C2DA7AA7C151C33 /* Pods-GulpsTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GulpsTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-GulpsTests/Pods-GulpsTests.release.xcconfig"; sourceTree = ""; }; + 9E879C4E1C5FA5CD00AFD0F4 /* CustomViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomViewController.swift; sourceTree = ""; }; + 9EF6A8D31C60CF4600C97FDE /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Main.strings; sourceTree = ""; }; BEC10A47E1AB990C16B7DEFC /* Pods_Gulps_WatchKit_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Gulps_WatchKit_App.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C5FBA99C443A944184AA9213 /* Pods-GulpsToday.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GulpsToday.release.xcconfig"; path = "Pods/Target Support Files/Pods-GulpsToday/Pods-GulpsToday.release.xcconfig"; sourceTree = ""; }; C916E6FEBE9F3B2034FA5E16 /* Pods-Gulps.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Gulps.release.xcconfig"; path = "Pods/Target Support Files/Pods-Gulps/Pods-Gulps.release.xcconfig"; sourceTree = ""; }; @@ -357,6 +359,7 @@ children = ( 6555BB691B0CB90A00CE436A /* Support */, 6537A8211AF24CC800EC16E9 /* DrinkViewController.swift */, + 9E879C4E1C5FA5CD00AFD0F4 /* CustomViewController.swift */, 6537A81F1AF24CC800EC16E9 /* CalendarViewController.swift */, 6537A8281AF24CC800EC16E9 /* SettingsViewController.swift */, 65F55F1F1B5904B8008653F2 /* FeedbackViewController.swift */, @@ -955,6 +958,7 @@ 65F44C211AF24B440093A3E9 /* AppDelegate.swift in Sources */, 6537A83A1AF24CC800EC16E9 /* NotificationViewController.swift in Sources */, 65C92DA71BB164D100E2DFE7 /* HealthKitHealper.swift in Sources */, + 9E879C4F1C5FA5CD00AFD0F4 /* CustomViewController.swift in Sources */, 650DCE651AF3CD4100E12764 /* Constants.swift in Sources */, 6537A8311AF24CC800EC16E9 /* Extensions.swift in Sources */, 6537A8361AF24CC800EC16E9 /* DrinkViewController.swift in Sources */, @@ -1047,7 +1051,7 @@ isa = PBXVariantGroup; children = ( 65F44C251AF24B440093A3E9 /* Base */, - 65F55F261B5938BE008653F2 /* it */, + 9EF6A8D31C60CF4600C97FDE /* it */, ); name = Main.storyboard; sourceTree = ""; diff --git a/Gulps/AppDelegate.swift b/Gulps/AppDelegate.swift index 0aef76c..9e8842a 100644 --- a/Gulps/AppDelegate.swift +++ b/Gulps/AppDelegate.swift @@ -117,6 +117,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, WCSessionDelegate { EntryHandler.sharedHandler.addGulp(NSUserDefaults.groupUserDefaults().doubleForKey(Constants.Gulp.Big.key())) } } + + // NOTE: We do not currently support a shortcut for the "Constants.Gulp.Custom.key" } func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]) { diff --git a/Gulps/Base.lproj/Main.storyboard b/Gulps/Base.lproj/Main.storyboard index a9ac1b9..6510dc3 100644 --- a/Gulps/Base.lproj/Main.storyboard +++ b/Gulps/Base.lproj/Main.storyboard @@ -1,9 +1,10 @@ - + - + + @@ -19,167 +20,309 @@ - - + + - + - - + + - - + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - + + + + + + @@ -187,6 +330,7 @@ + @@ -208,14 +352,12 @@ - @@ -278,21 +417,18 @@ By the way, we’d love some feedback! How do you feel about Gulps? - @@ -375,7 +509,6 @@ By the way, we’d love some feedback! How do you feel about Gulps? - @@ -461,7 +591,6 @@ By the way, we’d love some feedback! How do you feel about Gulps? - @@ -485,7 +614,6 @@ By the way, we’d love some feedback! How do you feel about Gulps? - @@ -511,7 +639,6 @@ By the way, we’d love some feedback! How do you feel about Gulps? - @@ -519,7 +646,6 @@ By the way, we’d love some feedback! How do you feel about Gulps? - @@ -527,42 +653,36 @@ By the way, we’d love some feedback! How do you feel about Gulps? - @@ -642,7 +761,6 @@ By the way, we’d love some feedback! How do you feel about Gulps? - @@ -661,7 +779,6 @@ By the way, we’d love some feedback! How do you feel about Gulps? - @@ -676,7 +793,6 @@ By the way, we’d love some feedback! How do you feel about Gulps? @@ -706,15 +819,13 @@ By the way, we’d love some feedback! How do you feel about Gulps? - @@ -726,14 +837,12 @@ By the way, we’d love some feedback! How do you feel about Gulps? - @@ -744,7 +853,6 @@ By the way, we’d love some feedback! How do you feel about Gulps? - @@ -755,7 +863,6 @@ By the way, we’d love some feedback! How do you feel about Gulps? - @@ -767,21 +874,18 @@ By the way, we’d love some feedback! How do you feel about Gulps? - @@ -792,7 +896,6 @@ By the way, we’d love some feedback! How do you feel about Gulps? - @@ -803,7 +906,6 @@ By the way, we’d love some feedback! How do you feel about Gulps? - @@ -815,21 +917,18 @@ By the way, we’d love some feedback! How do you feel about Gulps? - @@ -840,35 +939,61 @@ By the way, we’d love some feedback! How do you feel about Gulps? - + + + + + + + + + + + + + + + + + + + + + + + + + - + - - @@ -878,10 +1003,9 @@ By the way, we’d love some feedback! How do you feel about Gulps? - - + @@ -890,7 +1014,6 @@ By the way, we’d love some feedback! How do you feel about Gulps? - - + @@ -918,7 +1038,6 @@ By the way, we’d love some feedback! How do you feel about Gulps? - - + @@ -946,7 +1062,6 @@ By the way, we’d love some feedback! How do you feel about Gulps? - - + @@ -977,20 +1089,17 @@ By the way, we’d love some feedback! How do you feel about Gulps? - - @@ -1000,7 +1109,6 @@ By the way, we’d love some feedback! How do you feel about Gulps? - @@ -1012,6 +1120,7 @@ By the way, we’d love some feedback! How do you feel about Gulps? + @@ -1039,7 +1148,6 @@ By the way, we’d love some feedback! How do you feel about Gulps? - @@ -1058,7 +1166,6 @@ By the way, we’d love some feedback! How do you feel about Gulps? - @@ -1072,6 +1179,7 @@ By the way, we’d love some feedback! How do you feel about Gulps? + diff --git a/Gulps/Images.xcassets/Contents.json b/Gulps/Images.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Gulps/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Gulps/Images.xcassets/custom-icon.imageset/Contents.json b/Gulps/Images.xcassets/custom-icon.imageset/Contents.json new file mode 100644 index 0000000..265b3f8 --- /dev/null +++ b/Gulps/Images.xcassets/custom-icon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "custom-icon.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Gulps/Images.xcassets/custom-icon.imageset/custom-icon.pdf b/Gulps/Images.xcassets/custom-icon.imageset/custom-icon.pdf new file mode 100644 index 0000000..9bb61d9 Binary files /dev/null and b/Gulps/Images.xcassets/custom-icon.imageset/custom-icon.pdf differ diff --git a/Gulps/Info.plist b/Gulps/Info.plist index 8fd7963..fb7e259 100644 --- a/Gulps/Info.plist +++ b/Gulps/Info.plist @@ -73,6 +73,6 @@ UIInterfaceOrientationPortrait UIViewControllerBasedStatusBarAppearance - + diff --git a/Gulps/Support/Constants.swift b/Gulps/Support/Constants.swift index 59a0c3f..e276711 100644 --- a/Gulps/Support/Constants.swift +++ b/Gulps/Support/Constants.swift @@ -53,18 +53,30 @@ public enum Constants { } public enum Gulp: Int { - case Big, Small, Goal + case Big, Small, Custom, Goal public func key() -> String { switch self { case .Big: return "GULP_BIG" case .Small: return "GULP_SMALL" + case .Custom: + return "GULP_CUSTOM" case .Goal: return "PORTION_GOAL" } } } + + public enum CustomGulp: Int { + case On + public func key() -> String { + switch self { + case .On: + return "CUSTOM_GULP_ON" + } + } + } public enum Health: Int { case On diff --git a/Gulps/Support/Settings.swift b/Gulps/Support/Settings.swift index ce7501c..2383dc5 100644 --- a/Gulps/Support/Settings.swift +++ b/Gulps/Support/Settings.swift @@ -4,7 +4,7 @@ public class Settings { public class func registerDefaults() { let userDefaults = NSUserDefaults.groupUserDefaults() - // The defaults registered with registerDefaults are ignore by the Today Extension. :/ + // The defaults registered with registerDefaults are ignored by the Today Extension. if (!userDefaults.boolForKey("DEFAULTS_INSTALLED")) { userDefaults.setBool(true, forKey: "DEFAULTS_INSTALLED") userDefaults.setInteger(Constants.UnitsOfMeasure.Liters.rawValue, forKey: Constants.General.UnitOfMeasure.key()) diff --git a/Gulps/ViewControllers/CustomViewController.swift b/Gulps/ViewControllers/CustomViewController.swift new file mode 100644 index 0000000..ec92d21 --- /dev/null +++ b/Gulps/ViewControllers/CustomViewController.swift @@ -0,0 +1,102 @@ +// +// CustomViewController.swift +// Gulps +// +// Created by Ross Gibson on 01/02/2016. +// Copyright © 2016 Fancy Pixel. All rights reserved. +// + +import UIKit + +class CustomViewController: UIViewController { + + @IBOutlet weak var cancelButton: UIBarButtonItem! + @IBOutlet weak var doneButton: UIBarButtonItem! + @IBOutlet weak var customPortionText: UITextField! + @IBOutlet weak var unitOfMesureLabel: UILabel! + + var gulp: ((portion: String) -> ())? + + private let userDefaults = NSUserDefaults.groupUserDefaults() + + private let numberFormatter: NSNumberFormatter = { + let formatter = NSNumberFormatter() + formatter.numberStyle = .DecimalStyle + return formatter + }() + + // MARK: - Lifecycle + + override func viewWillAppear(animated: Bool) { + super.viewWillAppear(animated) + updateUI() + customPortionText.becomeFirstResponder() + } + + // MARK: - Actions + + @IBAction func cancelTapped(sender: UIBarButtonItem) { + dismissViewControllerAnimated(true, applyingGulp: false) + } + + @IBAction func doneTapped(sender: AnyObject) { + dismissViewControllerAnimated(true, applyingGulp: true) + } + + // MARK: - Private + + private func updateUI() { + let numberFormatter = NSNumberFormatter() + numberFormatter.numberStyle = .DecimalStyle + var suffix = "" + if let unit = Constants.UnitsOfMeasure(rawValue: userDefaults.integerForKey(Constants.General.UnitOfMeasure.key())) { + suffix = unit.suffixForUnitOfMeasure() + } + + unitOfMesureLabel.text = suffix + + customPortionText.text = numberFormatter.stringFromNumber(userDefaults.doubleForKey(Constants.Gulp.Custom.key())) + } + + func dismissViewControllerAnimated(animated: Bool, applyingGulp applyGulp: Bool) { + defer { + self.dismissViewControllerAnimated(animated, completion: { + if applyGulp { + self.gulp?(portion: Constants.Gulp.Custom.key()) + } + }) + } + + // Dismiss the keyboard before dismissing the view controller. + guard !customPortionText.isFirstResponder() else { + customPortionText.resignFirstResponder() + return + } + + } + +} + +extension CustomViewController : UITextFieldDelegate { + // MARK: - UITextFieldDelegate + + func textFieldDidEndEditing(textField: UITextField) { + if (textField == customPortionText) { + storeText(customPortionText, toKey: Constants.Gulp.Custom.key()) + } + } + + func storeText(textField: UITextField, toKey key: String) { + guard let text = textField.text else { + return + } + + let number = numberFormatter.numberFromString(text) as? Double + userDefaults.setDouble(number ?? 0.0, forKey: key) + userDefaults.synchronize() + } + + func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool { + return Globals.numericTextField(textField, shouldChangeCharactersInRange: range, replacementString: string) + } +} diff --git a/Gulps/ViewControllers/DrinkViewController.swift b/Gulps/ViewControllers/DrinkViewController.swift index 33cc558..752713a 100644 --- a/Gulps/ViewControllers/DrinkViewController.swift +++ b/Gulps/ViewControllers/DrinkViewController.swift @@ -10,11 +10,14 @@ public class DrinkViewController: UIViewController, UIAlertViewDelegate, UIViewC @IBOutlet public weak var percentageLabel: UICountingLabel! @IBOutlet public weak var addButton: UIButton! @IBOutlet public weak var smallButton: UIButton! + @IBOutlet public weak var customButton: UIButton! @IBOutlet public weak var largeButton: UIButton! @IBOutlet public weak var minusButton: UIButton! @IBOutlet weak var starButton: UIButton! @IBOutlet weak var meterContainerView: UIView! @IBOutlet weak var maskImage: UIImageView! + @IBOutlet weak var topView: UIView! + @IBOutlet weak var bottomView: UIView! public var userDefaults = NSUserDefaults.groupUserDefaults() public var progressMeter: BAFluidView? @@ -62,6 +65,14 @@ public class DrinkViewController: UIViewController, UIAlertViewDelegate, UIViewC public override func viewWillAppear(animated: Bool) { super.viewWillAppear(animated) + + if NSUserDefaults.groupUserDefaults().boolForKey(Constants.CustomGulp.On.key()) { + customButton.userInteractionEnabled = true + customButton.hidden = false + } else { + customButton.userInteractionEnabled = false + customButton.hidden = true + } view.layoutIfNeeded() @@ -94,6 +105,14 @@ public class DrinkViewController: UIViewController, UIAlertViewDelegate, UIViewC inView: view, fromFrame: CGRect(x: view.frame.width - 60, y: view.frame.height, width: 1, height: 1), direction: .Up, color: .destructiveColor()) } + + public override func viewDidDisappear(animated: Bool) { + super.viewDidDisappear(animated) + + if (expanded) { + contractAddButton() + } + } // MARK: - UI update @@ -116,6 +135,15 @@ public class DrinkViewController: UIViewController, UIAlertViewDelegate, UIViewC userDefaults.setBool(true, forKey: "FEEDBACK") userDefaults.synchronize() } + + if let customViewController = segue.destinationViewController as? CustomViewController { + customViewController.gulp = {[weak self] + (portion) in + if let weakSelf = self { + weakSelf.addGulp(portion) + } + } + } } // MARK: - Actions @@ -129,13 +157,8 @@ public class DrinkViewController: UIViewController, UIAlertViewDelegate, UIViewC } @IBAction public func selectionButtonAction(sender: UIButton) { - contractAddButton() - Globals.showPopTipOnceForKey("UNDO_HINT", userDefaults: userDefaults, - popTipText: NSLocalizedString("undo poptip", comment: ""), - inView: view, - fromFrame: minusButton.frame) let portion = smallButton == sender ? Constants.Gulp.Small.key() : Constants.Gulp.Big.key() - updateCurrentEntry(userDefaults.doubleForKey(portion)) + addGulp(portion) } @IBAction func removeGulpAction() { @@ -147,6 +170,21 @@ public class DrinkViewController: UIViewController, UIAlertViewDelegate, UIViewC _ = [yes, no].map { controller.addAction($0) } self.presentViewController(controller, animated: true) {} } + + // MARK: - Private + + private func addGulp(portion: String) { + if (expanded) { + contractAddButton() + } + + Globals.showPopTipOnceForKey("UNDO_HINT", userDefaults: userDefaults, + popTipText: NSLocalizedString("undo poptip", comment: ""), + inView: view, + fromFrame: minusButton.frame) + + updateCurrentEntry(userDefaults.doubleForKey(portion)) + } // MARK: - UIViewControllerTransitioningDelegate diff --git a/Gulps/ViewControllers/Onboarding/GulpsViewController.swift b/Gulps/ViewControllers/Onboarding/GulpsViewController.swift index fe627aa..51a58b7 100644 --- a/Gulps/ViewControllers/Onboarding/GulpsViewController.swift +++ b/Gulps/ViewControllers/Onboarding/GulpsViewController.swift @@ -45,6 +45,9 @@ class GulpsViewController: OnboardingViewController, UITextFieldDelegate { self.userDefaults.setDouble(small, forKey: Constants.Gulp.Small.key()) self.userDefaults.setDouble(big, forKey: Constants.Gulp.Big.key()) self.userDefaults.synchronize() + + // NOTE: The "Constants.Gulp.Custom.key" is set each time it's entered by the + // user, so it's not required in the onvoarding process. } func textFieldShouldReturn(textField: UITextField) -> Bool { diff --git a/Gulps/ViewControllers/SettingsViewController.swift b/Gulps/ViewControllers/SettingsViewController.swift index 4c17a77..eabd506 100644 --- a/Gulps/ViewControllers/SettingsViewController.swift +++ b/Gulps/ViewControllers/SettingsViewController.swift @@ -8,6 +8,7 @@ class SettingsViewController: UITableViewController, UIAlertViewDelegate, UIText @IBOutlet weak var smallPortionText: UITextField! @IBOutlet weak var largePortionText: UITextField! @IBOutlet weak var dailyGoalText: UITextField! + @IBOutlet weak var customGulpSwitch: UISwitch! @IBOutlet weak var notificationSwitch: UISwitch! @IBOutlet weak var healthSwitch: UISwitch! @IBOutlet weak var notificationFromLabel: UILabel! @@ -56,13 +57,15 @@ class SettingsViewController: UITableViewController, UIAlertViewDelegate, UIText largePortionText.text = numberFormatter.stringFromNumber(userDefaults.doubleForKey(Constants.Gulp.Big.key())) smallPortionText.text = numberFormatter.stringFromNumber(userDefaults.doubleForKey(Constants.Gulp.Small.key())) dailyGoalText.text = numberFormatter.stringFromNumber(userDefaults.doubleForKey(Constants.Gulp.Goal.key())) - - healthSwitch.on = userDefaults.boolForKey(Constants.Health.On.key()) + + customGulpSwitch.on = userDefaults.boolForKey(Constants.CustomGulp.On.key()) notificationSwitch.on = userDefaults.boolForKey(Constants.Notification.On.key()) notificationFromLabel.text = "\(userDefaults.integerForKey(Constants.Notification.From.key())):00" notificationToLabel.text = "\(userDefaults.integerForKey(Constants.Notification.To.key())):00" notificationIntervalLabel.text = "\(userDefaults.integerForKey(Constants.Notification.Interval.key())) " + NSLocalizedString("hours", comment: "") + + healthSwitch.on = userDefaults.boolForKey(Constants.Health.On.key()) } func updateNotificationPreferences() { @@ -147,6 +150,12 @@ class SettingsViewController: UITableViewController, UIAlertViewDelegate, UIText self.tableView.reloadData() updateNotificationPreferences() } + + @IBAction func customGulpAction(sender: UISwitch) { + userDefaults.setBool(sender.on, forKey: Constants.CustomGulp.On.key()) + userDefaults.synchronize() + self.tableView.reloadData() + } override func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 4 @@ -156,7 +165,7 @@ class SettingsViewController: UITableViewController, UIAlertViewDelegate, UIText if (section == 0) { return 1 } else if (section == 1) { - return 3 + return 4 } else if (section == 2) { if NSUserDefaults.groupUserDefaults().boolForKey(Constants.Notification.On.key()) { return 4 diff --git a/Gulps/ViewControllers/Support/DrinkViewController+Animations.swift b/Gulps/ViewControllers/Support/DrinkViewController+Animations.swift index f895ea2..f04e43e 100644 --- a/Gulps/ViewControllers/Support/DrinkViewController+Animations.swift +++ b/Gulps/ViewControllers/Support/DrinkViewController+Animations.swift @@ -1,9 +1,34 @@ import pop extension DrinkViewController { + + private var animationDistance: CGFloat { + get { + let maximumAnimationDistance: CGFloat = 100 + let padding: CGFloat = 16 + + var animationDistance = maximumAnimationDistance + + if NSUserDefaults.groupUserDefaults().boolForKey(Constants.CustomGulp.On.key()) { + // Calculate the animation distance allowing for the height of the custom button. + let startPosition = bottomView.bounds.height - addButton.center.y + let delta = bottomView.bounds.height - startPosition - padding - (customButton.bounds.height / 2) + animationDistance = delta > maximumAnimationDistance ? maximumAnimationDistance : delta + + // NOTE: It is possible that the animation distance may be less than the height of the button. This + // would cause the buttons to overlay each other, this would happen if the bottomView height is reduced. + + } + + return animationDistance + + // TODO: Prevent button over-lapping. + } + } func initAnimation() { smallButton.alpha = 0 + customButton.alpha = 0 largeButton.alpha = 0 starButton.transform = CGAffineTransformMakeScale(0.0001, 0.0001) } @@ -41,7 +66,7 @@ extension DrinkViewController { rotate.removedOnCompletion = true rotate.completionBlock = {(_, _) in self.addButton.userInteractionEnabled = true - _ = [self.smallButton, self.largeButton].map({$0.userInteractionEnabled = true}) + _ = [self.smallButton, self.customButton, self.largeButton].map({$0.userInteractionEnabled = true}) self.expanded = true } @@ -67,8 +92,8 @@ extension DrinkViewController { addButton.pop_addAnimation(color, forKey: "color") minusButton.pop_addAnimation(scaleMinus, forKey: "scaleMinus") - _ = [smallButton, largeButton].map({$0.alpha = 1}) - for button in [smallButton, largeButton] { + _ = [smallButton, customButton, largeButton].map({$0.alpha = 1}) + for button in [smallButton, customButton, largeButton] { let pop = POPSpringAnimation(propertyNamed: kPOPViewScaleXY) pop.fromValue = NSValue(CGPoint: CGPointMake(0.1, 0.1)) pop.toValue = NSValue(CGPoint: CGPointMake(1, 1)) @@ -80,18 +105,24 @@ extension DrinkViewController { let left = POPSpringAnimation(propertyNamed: kPOPLayerTranslationX) left.springBounciness = 5 left.fromValue = 0 - left.toValue = -100 + left.toValue = -animationDistance smallButton.layer.pop_addAnimation(left, forKey: "left") let right = POPSpringAnimation(propertyNamed: kPOPLayerTranslationX) right.springBounciness = 5 right.fromValue = 0 - right.toValue = 100 + right.toValue = animationDistance largeButton.layer.pop_addAnimation(right, forKey: "right") + + let top = POPSpringAnimation(propertyNamed: kPOPLayerTranslationY) + top.springBounciness = 5 + top.fromValue = 0 + top.toValue = -animationDistance + customButton.layer.pop_addAnimation(top, forKey: "top") } func contractAddButton() { - _ = [smallButton, largeButton].map({$0.userInteractionEnabled = false}) + _ = [smallButton, customButton, largeButton].map({$0.userInteractionEnabled = false}) addButton.userInteractionEnabled = false let rotate = POPSpringAnimation(propertyNamed: kPOPLayerRotation) @@ -124,7 +155,7 @@ extension DrinkViewController { addButton.pop_addAnimation(color, forKey: "color") minusButton.pop_addAnimation(scaleMinus, forKey: "scaleMinus") - for button in [smallButton, largeButton] { + for button in [smallButton, customButton, largeButton] { let pop = POPSpringAnimation(propertyNamed: kPOPViewScaleXY) pop.fromValue = NSValue(CGPoint: CGPointMake(1, 1)) pop.toValue = NSValue(CGPoint: CGPointMake(0.1, 0.1)) @@ -135,14 +166,20 @@ extension DrinkViewController { let left = POPBasicAnimation(propertyNamed: kPOPLayerTranslationX) left.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut) - left.fromValue = -100 + left.fromValue = -animationDistance left.toValue = 0 smallButton.layer.pop_addAnimation(left, forKey: "left") let right = POPBasicAnimation(propertyNamed: kPOPLayerTranslationX) right.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut) - right.fromValue = 100 + right.fromValue = animationDistance right.toValue = 0 largeButton.layer.pop_addAnimation(right, forKey: "right") + + let top = POPBasicAnimation(propertyNamed: kPOPLayerTranslationY) + top.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut) + top.fromValue = -animationDistance + top.toValue = 0 + customButton.layer.pop_addAnimation(top, forKey: "top") } } diff --git a/Gulps/it.lproj/Main.strings b/Gulps/it.lproj/Main.strings index 660963e..11c0592 100644 --- a/Gulps/it.lproj/Main.strings +++ b/Gulps/it.lproj/Main.strings @@ -1,17 +1,23 @@ -/* Class = "UITabBarItem"; title = "My progress"; ObjectID = "1AV-yg-enX"; */ +/* Class = "UITextField"; text = "0,2"; ObjectID = "0sb-ML-tka"; */ +"0sb-ML-tka.text" = "0,2"; + +/* Class = "UITabBarItem"; title = "Progressi"; ObjectID = "1AV-yg-enX"; */ "1AV-yg-enX.title" = "Progressi"; -/* Class = "UILabel"; text = "Liters drank over"; ObjectID = "4E2-NO-CiF"; */ +/* Class = "UILabel"; text = "Litri bevuti negli ultimi"; ObjectID = "4E2-NO-CiF"; */ "4E2-NO-CiF.text" = "Litri bevuti negli ultimi"; -/* Class = "UITabBarItem"; title = "Preferences"; ObjectID = "BBe-Kb-DPg"; */ +/* Class = "UILabel"; text = "Esporta dati in Salute"; ObjectID = "72o-fo-Kke"; */ +"72o-fo-Kke.text" = "Esporta dati in Salute"; + +/* Class = "UITabBarItem"; title = "Preferenze"; ObjectID = "BBe-Kb-DPg"; */ "BBe-Kb-DPg.title" = "Preferenze"; /* Class = "UILabel"; text = "Small gulp"; ObjectID = "Brt-M3-CC4"; */ "Brt-M3-CC4.text" = "Small gulp"; -/* Class = "UITableViewSection"; headerTitle = "Notifications"; ObjectID = "Cqk-U1-F6w"; */ +/* Class = "UITableViewSection"; headerTitle = "Notifiche"; ObjectID = "Cqk-U1-F6w"; */ "Cqk-U1-F6w.headerTitle" = "Notifiche"; /* Class = "UILabel"; text = "Oz"; ObjectID = "DT8-cB-ShB"; */ @@ -23,28 +29,34 @@ /* Class = "UITextField"; text = "0,2"; ObjectID = "HpM-IE-rg0"; */ "HpM-IE-rg0.text" = "0,2"; -/* Class = "UILabel"; text = "Liters"; ObjectID = "Jwx-tY-nJI"; */ +/* Class = "UILabel"; text = "Litri"; ObjectID = "Jwx-tY-nJI"; */ "Jwx-tY-nJI.text" = "Litri"; -/* Class = "UIButton"; normalTitle = "Nope"; ObjectID = "KcM-qN-18r"; */ +/* Class = "UIButton"; normalTitle = "No"; ObjectID = "KcM-qN-18r"; */ "KcM-qN-18r.normalTitle" = "No"; -/* Class = "UITableViewSection"; headerTitle = "Settings"; ObjectID = "M6x-da-DQK"; */ +/* Class = "UITableViewSection"; headerTitle = "Preferenze"; ObjectID = "M6x-da-DQK"; */ "M6x-da-DQK.headerTitle" = "Preferenze"; +/* Class = "UILabel"; text = "Gulp size"; ObjectID = "MVT-j7-QAE"; */ +"MVT-j7-QAE.text" = "Dimensioni gulp"; + /* Class = "UILabel"; text = "0%"; ObjectID = "Nja-Uo-jJw"; */ "Nja-Uo-jJw.text" = "0%"; -/* Class = "UILabel"; text = "From"; ObjectID = "Pnc-7E-Kee"; */ +/* Class = "UILabel"; text = "Da"; ObjectID = "Pnc-7E-Kee"; */ "Pnc-7E-Kee.text" = "Da"; -/* Class = "UIButton"; normalTitle = "Sure!"; ObjectID = "QXZ-Em-FFP"; */ +/* Class = "UINavigationItem"; title = "Custom Gulp"; ObjectID = "Q09-Pw-s4E"; */ +"Q09-Pw-s4E.title" = "Gulp personalizzato"; + +/* Class = "UIButton"; normalTitle = "Certo!"; ObjectID = "QXZ-Em-FFP"; */ "QXZ-Em-FFP.normalTitle" = "Certo!"; /* Class = "UILabel"; text = "200"; ObjectID = "R2u-Oc-VLI"; */ "R2u-Oc-VLI.text" = "200"; -/* Class = "UILabel"; text = "How about rating Gulps on the AppStore? We always appreciate the feedback!"; ObjectID = "R9l-6a-fbg"; */ +/* Class = "UILabel"; text = "Vuoi lasciare una recensione di Gulps nell'AppStore? Apprezzeremmo molto il tuo parere"; ObjectID = "R9l-6a-fbg"; */ "R9l-6a-fbg.text" = "Vuoi lasciare una recensione di Gulps nell'AppStore? Apprezzeremmo molto il tuo parere"; /* Class = "UILabel"; text = "Oz"; ObjectID = "RVh-z0-hYc"; */ @@ -53,34 +65,40 @@ /* Class = "UITextField"; text = "0,5"; ObjectID = "SBF-ug-rBd"; */ "SBF-ug-rBd.text" = "0,5"; -/* Class = "UITableViewSection"; headerTitle = "Portions"; ObjectID = "Uhu-cr-ZJf"; */ +/* Class = "UITableViewSection"; headerTitle = "Porzioni"; ObjectID = "Uhu-cr-ZJf"; */ "Uhu-cr-ZJf.headerTitle" = "Porzioni"; -/* Class = "UILabel"; text = "Remind me to drink"; ObjectID = "XIg-ci-rnk"; */ +/* Class = "UILabel"; text = "Oz"; ObjectID = "Vjp-66-WA9"; */ +"Vjp-66-WA9.text" = "Oz"; + +/* Class = "UILabel"; text = "Ricordami di bere"; ObjectID = "XIg-ci-rnk"; */ "XIg-ci-rnk.text" = "Ricordami di bere"; /* Class = "UILabel"; text = "8:00"; ObjectID = "aBp-Ni-lCc"; */ "aBp-Ni-lCc.text" = "8:00"; -/* Class = "UITabBarItem"; title = "Drink"; ObjectID = "acW-dT-cKf"; */ +/* Class = "UITabBarItem"; title = "Bevi"; ObjectID = "acW-dT-cKf"; */ "acW-dT-cKf.title" = "Bevi"; -/* Class = "UILabel"; text = "Yay!"; ObjectID = "ee7-af-1yT"; */ +/* Class = "UILabel"; text = "Grande!"; ObjectID = "ee7-af-1yT"; */ "ee7-af-1yT.text" = "Grande!"; /* Class = "UILabel"; text = "22:00"; ObjectID = "en4-wz-CaR"; */ "en4-wz-CaR.text" = "22:00"; -/* Class = "UILabel"; text = "To"; ObjectID = "f3c-OV-GHw"; */ +/* Class = "UILabel"; text = "A"; ObjectID = "f3c-OV-GHw"; */ "f3c-OV-GHw.text" = "A"; -/* Class = "UILabel"; text = "days"; ObjectID = "jpZ-Ht-zcx"; */ +/* Class = "UILabel"; text = "giorni"; ObjectID = "jpZ-Ht-zcx"; */ "jpZ-Ht-zcx.text" = "giorni"; -/* Class = "UIButton"; normalTitle = "Contact us"; ObjectID = "lX0-8d-2KC"; */ +/* Class = "UITableViewSection"; headerTitle = "Apple Salute"; ObjectID = "kL3-zV-Q1y"; */ +"kL3-zV-Q1y.headerTitle" = "Apple Salute"; + +/* Class = "UIButton"; normalTitle = "Contattaci"; ObjectID = "lX0-8d-2KC"; */ "lX0-8d-2KC.normalTitle" = "Contattaci"; -/* Class = "UILabel"; text = "2 hours"; ObjectID = "llL-4J-aC6"; */ +/* Class = "UILabel"; text = "2 ore"; ObjectID = "llL-4J-aC6"; */ "llL-4J-aC6.text" = "2 ore"; /* Class = "UILabel"; text = "200"; ObjectID = "lon-Jb-3Cm"; */ @@ -89,38 +107,35 @@ /* Class = "UILabel"; text = "0%"; ObjectID = "mEa-Xq-QB6"; */ "mEa-Xq-QB6.text" = "0%"; -/* Class = "UILabel"; text = "Unit of mesure"; ObjectID = "mud-m1-HKI"; */ +/* Class = "UILabel"; text = "Unità di misura"; ObjectID = "mud-m1-HKI"; */ "mud-m1-HKI.text" = "Unità di misura"; /* Class = "UILabel"; text = "Oz"; ObjectID = "nbd-CS-kU7"; */ "nbd-CS-kU7.text" = "Oz"; -/* Class = "UIButton"; normalTitle = "Close"; ObjectID = "nce-Ji-tce"; */ +/* Class = "UIButton"; normalTitle = "Chiudi"; ObjectID = "nce-Ji-tce"; */ "nce-Ji-tce.normalTitle" = "Chiudi"; -/* Class = "UILabel"; text = "Daily goal"; ObjectID = "pt1-x1-O82"; */ +/* Class = "UILabel"; text = "Obiettivo"; ObjectID = "pt1-x1-O82"; */ "pt1-x1-O82.text" = "Obiettivo"; -/* Class = "UILabel"; text = "Good job keeping healthy! \nBy the way, we’d love some feedback! How do you feel about Gulps? "; ObjectID = "q2X-D7-DBk"; */ +/* Class = "UILabel"; text = "Ti stai mantenendo in forma, ottimo! \nA proposito, ci piacerebbe avere un tuo parere! Cosa ne pensi di Gulps? "; ObjectID = "q2X-D7-DBk"; */ "q2X-D7-DBk.text" = "Ti stai mantenendo in forma, ottimo! \nA proposito, ci piacerebbe avere un tuo parere! Cosa ne pensi di Gulps? "; -/* Class = "UILabel"; text = "Sorry to hear that! Is there any way we can improve your experience with Gulps? Contact us, we'd love your feedback!"; ObjectID = "upP-xB-nVY"; */ +/* Class = "UILabel"; text = "Custom gulp"; ObjectID = "shg-KE-AQ9"; */ +"shg-KE-AQ9.text" = "Gulp personalizzato"; + +/* Class = "UILabel"; text = "Ci dispiace! Possiamo in qualche modo migliorare la tua esperienza con Gulps? Contattaci, apprezzeremmo molto un tuo parare!"; ObjectID = "upP-xB-nVY"; */ "upP-xB-nVY.text" = "Ci dispiace! Possiamo in qualche modo migliorare la tua esperienza con Gulps? Contattaci, apprezzeremmo molto un tuo parare!"; -/* Class = "UILabel"; text = "Every"; ObjectID = "w1F-Cx-bRx"; */ +/* Class = "UILabel"; text = "Ogni"; ObjectID = "w1F-Cx-bRx"; */ "w1F-Cx-bRx.text" = "Ogni"; -/* Class = "UIButton"; normalTitle = "Share"; ObjectID = "xk6-tX-JQH"; */ +/* Class = "UIButton"; normalTitle = "Condividi"; ObjectID = "xk6-tX-JQH"; */ "xk6-tX-JQH.normalTitle" = "Condividi"; /* Class = "UILabel"; text = "Big gulp"; ObjectID = "yCk-VT-Gki"; */ "yCk-VT-Gki.text" = "Big gulp"; -/* Class = "UILabel"; text = "Way to go!"; ObjectID = "zyj-Gr-Ph8"; */ +/* Class = "UILabel"; text = "Ottimo!"; ObjectID = "zyj-Gr-Ph8"; */ "zyj-Gr-Ph8.text" = "Ottimo!"; - -/* Class = "UITableViewSection"; headerTitle = "Apple Health"; ObjectID = "kL3-zV-Q1y"; */ -"kL3-zV-Q1y.headerTitle" = "Apple Salute"; - -/* Class = "UILabel"; text = "Export data to Health"; ObjectID = "72o-fo-Kke"; */ -"72o-fo-Kke.text" = "Esporta dati in Salute"; diff --git a/GulpsTests/DrinkViewControllerTests.swift b/GulpsTests/DrinkViewControllerTests.swift index fa25c23..e128100 100644 --- a/GulpsTests/DrinkViewControllerTests.swift +++ b/GulpsTests/DrinkViewControllerTests.swift @@ -19,6 +19,10 @@ class MockUserDefaults: NSUserDefaults { default: return 0 } + + // NOTE: The "Constants.Gulp.Custom.key" does not require a mock value. + // It is set by the user each time they enter a custom value, and it's + // not possible to set a custom value from anywhere else in the app. } override func boolForKey(defaultName: String) -> Bool { diff --git a/assets/gulps.sketch b/assets/gulps.sketch index c87f578..3fdcb2f 100644 Binary files a/assets/gulps.sketch and b/assets/gulps.sketch differ