From 1b5e22fa0d0a6332ce112ce4d60b56029edae0c3 Mon Sep 17 00:00:00 2001 From: Damian Mazurkiewicz Date: Sat, 31 Dec 2016 15:45:31 +0000 Subject: [PATCH] Add Rest Timer (#22) * Add Rest Timer * Align view * Add Settings * Enable/Disable cells * Add RestTime to settings * Use Rest Time from Settings * Add logic for settings * YouTube videos are disabled if the video is not available * Versioning --- BodyweightFitness.xcodeproj/project.pbxproj | 8 + BodyweightFitness/AppDelegate.swift | 16 ++ BodyweightFitness/Bodyweight Fitness.plist | 2 +- .../Controller/RestTimerViewController.swift | 156 +++++++++++ .../Controller/SettingsViewController.swift | 252 +++++++++++++++++- .../Controller/TimedViewController.swift | 12 +- .../Controller/WeightedViewController.swift | 10 +- .../Controller/WorkoutViewController.swift | 85 +++++- .../Service/PersistenceManager.swift | 41 +++ BodyweightFitness/UI/RestTimerView.xib | 160 +++++++++++ BodyweightFitness/UI/TimedView.xib | 16 +- BodyweightFitness/UI/WeightedView.xib | 15 +- 12 files changed, 742 insertions(+), 31 deletions(-) create mode 100644 BodyweightFitness/Controller/RestTimerViewController.swift create mode 100644 BodyweightFitness/UI/RestTimerView.xib diff --git a/BodyweightFitness.xcodeproj/project.pbxproj b/BodyweightFitness.xcodeproj/project.pbxproj index 3a2e656..319a541 100755 --- a/BodyweightFitness.xcodeproj/project.pbxproj +++ b/BodyweightFitness.xcodeproj/project.pbxproj @@ -42,6 +42,8 @@ 732CDDF11D84485D004C6607 /* bodyweight_fitness_one_leg_foot_supported_l_sit.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 732CDDEC1D84485D004C6607 /* bodyweight_fitness_one_leg_foot_supported_l_sit.mp4 */; }; 732CDDF21D84485D004C6607 /* bodyweight_fitness_tuck_l_sit.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 732CDDED1D84485D004C6607 /* bodyweight_fitness_tuck_l_sit.mp4 */; }; 732CDDF31D84485D004C6607 /* bodyweight_fitness_wrist_mobility.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 732CDDEE1D84485D004C6607 /* bodyweight_fitness_wrist_mobility.mp4 */; }; + 73374A8E1E167A6300EBC47B /* RestTimerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73374A8D1E167A6300EBC47B /* RestTimerViewController.swift */; }; + 73374A901E167AEF00EBC47B /* RestTimerView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 73374A8F1E167AEF00EBC47B /* RestTimerView.xib */; }; 734132E51E130FAE00BAEFD2 /* starting_stretching_flexibility_routine.json in Resources */ = {isa = PBXBuildFile; fileRef = 734132E41E130FAE00BAEFD2 /* starting_stretching_flexibility_routine.json */; }; 734132FA1E130FE900BAEFD2 /* starting_stretching_underarm_shoulder_stretch_intermediate.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 734132E71E130FE900BAEFD2 /* starting_stretching_underarm_shoulder_stretch_intermediate.mp4 */; }; 734132FB1E130FE900BAEFD2 /* starting_stretching_underarm_shoulder_stretch_beginner.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 734132E81E130FE900BAEFD2 /* starting_stretching_underarm_shoulder_stretch_beginner.mp4 */; }; @@ -259,6 +261,8 @@ 732CDDEC1D84485D004C6607 /* bodyweight_fitness_one_leg_foot_supported_l_sit.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = bodyweight_fitness_one_leg_foot_supported_l_sit.mp4; sourceTree = ""; }; 732CDDED1D84485D004C6607 /* bodyweight_fitness_tuck_l_sit.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = bodyweight_fitness_tuck_l_sit.mp4; sourceTree = ""; }; 732CDDEE1D84485D004C6607 /* bodyweight_fitness_wrist_mobility.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = bodyweight_fitness_wrist_mobility.mp4; sourceTree = ""; }; + 73374A8D1E167A6300EBC47B /* RestTimerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RestTimerViewController.swift; sourceTree = ""; }; + 73374A8F1E167AEF00EBC47B /* RestTimerView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = RestTimerView.xib; sourceTree = ""; }; 734132E41E130FAE00BAEFD2 /* starting_stretching_flexibility_routine.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = starting_stretching_flexibility_routine.json; sourceTree = ""; }; 734132E71E130FE900BAEFD2 /* starting_stretching_underarm_shoulder_stretch_intermediate.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = starting_stretching_underarm_shoulder_stretch_intermediate.mp4; sourceTree = ""; }; 734132E81E130FE900BAEFD2 /* starting_stretching_underarm_shoulder_stretch_beginner.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = starting_stretching_underarm_shoulder_stretch_beginner.mp4; sourceTree = ""; }; @@ -638,6 +642,7 @@ 732BA0231C5AE75900C1CDB5 /* ProgressPageViewController.xib */, 7329B68D1D0D84E4005F5F74 /* HomeView.xib */, 738B96CC1D763319009A8625 /* HomeBarView.xib */, + 73374A8F1E167AEF00EBC47B /* RestTimerView.xib */, 73BADE161CC3E2F9001ACEDC /* TimedView.xib */, 73BADE1C1CC3F98D001ACEDC /* WeightedView.xib */, 73BF3BA81CBE643200E0258D /* SettingsView.xib */, @@ -659,6 +664,7 @@ 7366D8DC1C4EEFD500819790 /* ProgressPageViewController.swift */, 7366D8D01C4EE9CC00819790 /* ProgressViewController.swift */, 7329B68B1D0D8476005F5F74 /* HomeViewController.swift */, + 73374A8D1E167A6300EBC47B /* RestTimerViewController.swift */, 73BADE181CC3E347001ACEDC /* TimedViewController.swift */, 73BADE1A1CC3F956001ACEDC /* WeightedViewController.swift */, E0F587381B52730000540104 /* SettingsViewController.swift */, @@ -960,6 +966,7 @@ 734132E51E130FAE00BAEFD2 /* starting_stretching_flexibility_routine.json in Resources */, 732BA0241C5AE75900C1CDB5 /* ProgressPageViewController.xib in Resources */, 738B96821D760F66009A8625 /* bodyweight_fitness_l_sit_pullup.mp4 in Resources */, + 73374A901E167AEF00EBC47B /* RestTimerView.xib in Resources */, 735BEA0A1CBD1F38003616C5 /* SideViewHeaderCell.xib in Resources */, 738B969D1D760F66009A8625 /* bodyweight_fitness_wall_pushup.mp4 in Resources */, 738B96A51D760F66009A8625 /* molding_mobility_circular_shrugs.mp4 in Resources */, @@ -1063,6 +1070,7 @@ E0F5872E1B5272C400540104 /* AppDelegate.swift in Sources */, 73AA28FB1CB7ACEB00DEED79 /* DashboardSingleItemCell.swift in Sources */, 73AA28EA1CABE69200DEED79 /* DashboardViewController.swift in Sources */, + 73374A8E1E167A6300EBC47B /* RestTimerViewController.swift in Sources */, 734B700B1C4BF7E900CFB6BD /* RepositorySection.swift in Sources */, 73AA28F11CB7ABC300DEED79 /* DashboardSectionCell.swift in Sources */, 7329B68C1D0D8476005F5F74 /* HomeViewController.swift in Sources */, diff --git a/BodyweightFitness/AppDelegate.swift b/BodyweightFitness/AppDelegate.swift index 2cd51d1..6371771 100755 --- a/BodyweightFitness/AppDelegate.swift +++ b/BodyweightFitness/AppDelegate.swift @@ -76,6 +76,22 @@ class AppDelegate: UIResponder, UIApplicationDelegate { if(defaults.objectForKey("playAudioWhenTimerStops") == nil) { defaults.setBool(true, forKey: "playAudioWhenTimerStops") } + + if(defaults.objectForKey("showRestTimer") == nil) { + defaults.setBool(true, forKey: "showRestTimer") + } + + if(defaults.objectForKey("showRestTimerAfterWarmup") == nil) { + defaults.setBool(false, forKey: "showRestTimerAfterWarmup") + } + + if(defaults.objectForKey("showRestTimerAfterBodylineDrills") == nil) { + defaults.setBool(true, forKey: "showRestTimerAfterBodylineDrills") + } + + if(defaults.objectForKey("showRestTimerAfterFlexibilityExercises") == nil) { + defaults.setBool(false, forKey: "showRestTimerAfterFlexibilityExercises") + } } func migrateSchemaIfNeeded() { diff --git a/BodyweightFitness/Bodyweight Fitness.plist b/BodyweightFitness/Bodyweight Fitness.plist index 37e9c16..6b64b0d 100755 --- a/BodyweightFitness/Bodyweight Fitness.plist +++ b/BodyweightFitness/Bodyweight Fitness.plist @@ -19,7 +19,7 @@ CFBundleSignature ???? CFBundleVersion - 149 + 150 Fabric APIKey diff --git a/BodyweightFitness/Controller/RestTimerViewController.swift b/BodyweightFitness/Controller/RestTimerViewController.swift new file mode 100644 index 0000000..810fb3c --- /dev/null +++ b/BodyweightFitness/Controller/RestTimerViewController.swift @@ -0,0 +1,156 @@ +import UIKit +import AVFoundation + +class RestTimerViewController: UIViewController, AVAudioPlayerDelegate { + @IBOutlet var timerMinutesButton: UIButton! + @IBOutlet var timerSecondsButton: UIButton! + + @IBOutlet var timerPlayButton: UIButton! + + @IBOutlet var previousButton: UIButton! + @IBOutlet var nextButton: UIButton! + + var rootViewController: WorkoutViewController? = nil + var current: Exercise = RoutineStream.sharedInstance.routine.getFirstExercise() + + var audioPlayer: AVAudioPlayer? + var timer = NSTimer() + var isPlaying = false + + var seconds = PersistenceManager.getRestTime() + var defaultSeconds = PersistenceManager.getRestTime() + + init() { + super.init(nibName: "RestTimerView", bundle: nil) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + } + + func changeExercise(currentExercise: Exercise) { + self.current = currentExercise + self.defaultSeconds = PersistenceManager.getRestTime() + + if let _ = self.current.previous { + self.previousButton.hidden = false + } else { + self.previousButton.hidden = true + } + + if let _ = self.current.next { + self.nextButton.hidden = false + } else { + self.nextButton.hidden = true + } + } + + func updateLabel() { + let (_, m, s) = secondsToHoursMinutesSeconds(seconds) + + timerMinutesButton.setTitle(printTimerValue(m), forState: UIControlState.Normal) + timerSecondsButton.setTitle(printTimerValue(s), forState: UIControlState.Normal) + } + + func printTimerValue(value: Int) -> String { + if(value > 9) { + return String(value) + } else { + return "0" + String(value) + } + } + + func secondsToHoursMinutesSeconds (seconds : Int) -> (Int, Int, Int) { + return (seconds / 3600, (seconds % 3600) / 60, (seconds % 3600) % 60) + } + + func stopTimer() { + isPlaying = false + + timer.invalidate() + } + + func startTimer() { + restartTimer(defaultSeconds) + + isPlaying = true + + timer = NSTimer.scheduledTimerWithTimeInterval( + 1, + target: self, + selector: #selector(updateTimer), + userInfo: nil, + repeats: true + ) + } + + func restartTimer(seconds: Int) { + stopTimer() + + self.seconds = seconds + + updateLabel() + } + + func updateTimer() { + seconds -= 1 + + if(seconds <= 0) { + self.rootViewController?.restTimerStopped() + + let defaults = NSUserDefaults.standardUserDefaults() + if(defaults.objectForKey("playAudioWhenTimerStops") != nil) { + let playAudioWhenTimerStops = defaults.boolForKey("playAudioWhenTimerStops") + if(playAudioWhenTimerStops) { + audioPlayerStart() + } + } else { + audioPlayerStart() + } + } + + updateLabel() + } + + func audioPlayerStart() { + let alertSound = NSURL(fileURLWithPath: NSBundle + .mainBundle() + .pathForResource("finished", ofType: "mp3")!) + + do { + try AVAudioSession.sharedInstance().setActive(true) + + audioPlayer = try AVAudioPlayer(contentsOfURL: alertSound, fileTypeHint: nil) + audioPlayer?.delegate = self + audioPlayer?.prepareToPlay() + audioPlayer?.play() + } catch { + print("AVAudioSession errors.") + } + } + + func audioPlayerDidFinishPlaying(player: AVAudioPlayer, successfully flag: Bool) { + do { + try AVAudioSession.sharedInstance().setActive(false, withOptions: + AVAudioSessionSetActiveOptions.NotifyOthersOnDeactivation) + } catch { + print("AVAudioSession errors.") + } + } + + @IBAction func stopButtonClicked(sender: AnyObject) { + self.rootViewController?.restTimerStopped() + } + + @IBAction func previousButtonClicked(sender: AnyObject) { + self.rootViewController?.previousButtonClicked(sender) + } + + @IBAction func nextButtonClicked(sender: AnyObject) { + self.rootViewController?.nextButtonClicked(sender) + } +} diff --git a/BodyweightFitness/Controller/SettingsViewController.swift b/BodyweightFitness/Controller/SettingsViewController.swift index e6cb488..7b4b0fe 100755 --- a/BodyweightFitness/Controller/SettingsViewController.swift +++ b/BodyweightFitness/Controller/SettingsViewController.swift @@ -66,6 +66,48 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi self.tabBarController?.title = "Settings" } + func showRestTimer(sender: UISwitch) { + if sender.on { + let defaults = NSUserDefaults.standardUserDefaults() + defaults.setBool(true, forKey: "showRestTimer") + } else { + let defaults = NSUserDefaults.standardUserDefaults() + defaults.setBool(false, forKey: "showRestTimer") + } + + self.tableView.reloadData() + } + + func showRestTimerAfterWarmup(sender: UISwitch) { + if sender.on { + let defaults = NSUserDefaults.standardUserDefaults() + defaults.setBool(true, forKey: "showRestTimerAfterWarmup") + } else { + let defaults = NSUserDefaults.standardUserDefaults() + defaults.setBool(false, forKey: "showRestTimerAfterWarmup") + } + } + + func showRestTimerAfterBodylineDrills(sender: UISwitch) { + if sender.on { + let defaults = NSUserDefaults.standardUserDefaults() + defaults.setBool(true, forKey: "showRestTimerAfterBodylineDrills") + } else { + let defaults = NSUserDefaults.standardUserDefaults() + defaults.setBool(false, forKey: "showRestTimerAfterBodylineDrills") + } + } + + func showRestTimerAfterFlexibilityExercises(sender: UISwitch) { + if sender.on { + let defaults = NSUserDefaults.standardUserDefaults() + defaults.setBool(true, forKey: "showRestTimerAfterFlexibilityExercises") + } else { + let defaults = NSUserDefaults.standardUserDefaults() + defaults.setBool(false, forKey: "showRestTimerAfterFlexibilityExercises") + } + } + func playAudioWhenTimerStops(sender: UISwitch) { if sender.on { let defaults = NSUserDefaults.standardUserDefaults() @@ -90,11 +132,14 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi } func numberOfSectionsInTableView(tableView: UITableView) -> Int { - return 6 + return 7 } func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - if (section == 4) { + if (section == 1) { + return 5 + } + if (section == 5) { return 2 } @@ -106,14 +151,16 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi case 0: return "General" case 1: - return "Weight Measurement" + return "Rest Timer" case 2: - return "Developed by" + return "Weight Measurement" case 3: - return "Credits" + return "Developed by" case 4: - return "About" + return "Credits" case 5: + return "About" + case 6: return "Version" default: return nil @@ -128,6 +175,10 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi return nil } + func restTimerEnabled() -> Bool { + return NSUserDefaults.standardUserDefaults().boolForKey("showRestTimer") + } + func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { if indexPath.section == 0 { let cell = tableView.dequeueReusableCellWithIdentifier("SettingsToggleCell", forIndexPath: indexPath) as! SettingsToggleCell @@ -151,6 +202,124 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi } if indexPath.section == 1 { + if indexPath.row == 0 { + let cell = tableView.dequeueReusableCellWithIdentifier("SettingsToggleCell", forIndexPath: indexPath) as! SettingsToggleCell + let toggle = UISwitch() + + cell.textLabel?.text = "Show rest timer" + cell.detailTextLabel?.text = "Shows rest timer after logging any exercise" + + let defaults = NSUserDefaults.standardUserDefaults() + if (defaults.objectForKey("showRestTimer") != nil) { + toggle.on = defaults.boolForKey("showRestTimer") + } + + toggle.addTarget(self, action: #selector(showRestTimer), forControlEvents: UIControlEvents.ValueChanged) + + cell.accessoryView = toggle + + return cell + } + + if indexPath.row == 1 { + let cell = tableView.dequeueReusableCellWithIdentifier("SettingsActionCell", forIndexPath: indexPath) as UITableViewCell! + + cell.textLabel?.text = "Default Rest Time" + + if (!restTimerEnabled()) { + cell.userInteractionEnabled = false + cell.contentView.alpha = 0.5 + } else { + cell.userInteractionEnabled = true + cell.contentView.alpha = 1.0 + } + + return cell + } + + if indexPath.row == 2 { + let cell = tableView.dequeueReusableCellWithIdentifier("SettingsToggleCell", forIndexPath: indexPath) as! SettingsToggleCell + let toggle = UISwitch() + + cell.textLabel?.text = "Show rest timer after Warmup" + cell.detailTextLabel?.text = "Bodyweight Fitness - Warmup" + + let defaults = NSUserDefaults.standardUserDefaults() + if (defaults.objectForKey("showRestTimerAfterWarmup") != nil) { + toggle.on = defaults.boolForKey("showRestTimerAfterWarmup") + } + + toggle.addTarget(self, action: #selector(showRestTimerAfterWarmup), forControlEvents: UIControlEvents.ValueChanged) + + cell.accessoryView = toggle + + if (!restTimerEnabled()) { + cell.userInteractionEnabled = false + cell.contentView.alpha = 0.5 + } else { + cell.userInteractionEnabled = true + cell.contentView.alpha = 1.0 + } + + return cell + } + + if indexPath.row == 3 { + let cell = tableView.dequeueReusableCellWithIdentifier("SettingsToggleCell", forIndexPath: indexPath) as! SettingsToggleCell + let toggle = UISwitch() + + cell.textLabel?.text = "Show rest timer after Bodyline Drills" + cell.detailTextLabel?.text = "Bodyweight Fitness - Bodyline Drills" + + let defaults = NSUserDefaults.standardUserDefaults() + if (defaults.objectForKey("showRestTimerAfterBodylineDrills") != nil) { + toggle.on = defaults.boolForKey("showRestTimerAfterBodylineDrills") + } + + toggle.addTarget(self, action: #selector(showRestTimerAfterBodylineDrills), forControlEvents: UIControlEvents.ValueChanged) + + cell.accessoryView = toggle + + if (!restTimerEnabled()) { + cell.userInteractionEnabled = false + cell.contentView.alpha = 0.5 + } else { + cell.userInteractionEnabled = true + cell.contentView.alpha = 1.0 + } + + return cell + } + + if indexPath.row == 4 { + let cell = tableView.dequeueReusableCellWithIdentifier("SettingsToggleCell", forIndexPath: indexPath) as! SettingsToggleCell + let toggle = UISwitch() + + cell.textLabel?.text = "Show rest timer after Flexibility Exercises" + cell.detailTextLabel?.text = "Starting Stretching and Molding Mobility" + + let defaults = NSUserDefaults.standardUserDefaults() + if (defaults.objectForKey("showRestTimerAfterFlexibilityExercises") != nil) { + toggle.on = defaults.boolForKey("showRestTimerAfterFlexibilityExercises") + } + + toggle.addTarget(self, action: #selector(showRestTimerAfterFlexibilityExercises), forControlEvents: UIControlEvents.ValueChanged) + + cell.accessoryView = toggle + + if (!restTimerEnabled()) { + cell.userInteractionEnabled = false + cell.contentView.alpha = 0.5 + } else { + cell.userInteractionEnabled = true + cell.contentView.alpha = 1.0 + } + + return cell + } + } + + if indexPath.section == 2 { let cell = tableView.dequeueReusableCellWithIdentifier("SettingsActionCell", forIndexPath: indexPath) as UITableViewCell! if PersistenceManager.getWeightUnit() == "lbs" { @@ -162,7 +331,7 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi return cell } - if indexPath.section == 2 { + if indexPath.section == 3 { let cell = tableView.dequeueReusableCellWithIdentifier("SettingsActionSubtitleCell", forIndexPath: indexPath) as UITableViewCell! cell.textLabel?.text = "Damian Mazurkiewicz" @@ -171,7 +340,7 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi return cell } - if indexPath.section == 3 { + if indexPath.section == 4 { if indexPath.row == 0 { let cell = tableView.dequeueReusableCellWithIdentifier("SettingsActionSubtitleCell", forIndexPath: indexPath) as UITableViewCell! @@ -182,7 +351,7 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi } } - if indexPath.section == 4 { + if indexPath.section == 5 { if indexPath.row == 0 { let cell = tableView.dequeueReusableCellWithIdentifier("SettingsActionCell", forIndexPath: indexPath) as UITableViewCell! @@ -215,7 +384,62 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi } func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { - if indexPath.section == 1 && indexPath.row == 0 { + if indexPath.section == 1 && indexPath.row == 1 { + let cell = tableView.dequeueReusableCellWithIdentifier("SettingsActionCell", forIndexPath: indexPath) as UITableViewCell! + + let alertController = UIAlertController( + title: nil, + message: nil, + preferredStyle: .ActionSheet) + + alertController.modalPresentationStyle = .Popover + + if let presenter = alertController.popoverPresentationController { + presenter.sourceView = cell; + presenter.sourceRect = cell.bounds; + } + + alertController.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)) + + alertController.addAction(UIAlertAction(title: "30 Seconds", style: .Default) { (action) in + PersistenceManager.setRestTime(30) + + self.tableView.deselectRowAtIndexPath(indexPath, animated: true) + self.tableView.reloadData() + }) + + alertController.addAction(UIAlertAction(title: "1 Minute", style: .Default) { (action) in + PersistenceManager.setRestTime(60) + + self.tableView.deselectRowAtIndexPath(indexPath, animated: true) + self.tableView.reloadData() + }) + + alertController.addAction(UIAlertAction(title: "1 Minute 30 Seconds", style: .Default) { (action) in + PersistenceManager.setRestTime(90) + + self.tableView.deselectRowAtIndexPath(indexPath, animated: true) + self.tableView.reloadData() + }) + + alertController.addAction(UIAlertAction(title: "2 Minutes", style: .Default) { (action) in + PersistenceManager.setRestTime(120) + + self.tableView.deselectRowAtIndexPath(indexPath, animated: true) + self.tableView.reloadData() + }) + + alertController.addAction(UIAlertAction(title: "2 Minutes 30 Seconds", style: .Default) { (action) in + PersistenceManager.setRestTime(150) + + self.tableView.deselectRowAtIndexPath(indexPath, animated: true) + self.tableView.reloadData() + }) + + self.presentViewController(alertController, animated: true, completion: nil) + } + + if indexPath.section == 2 && indexPath.row == 0 { let cell = tableView.dequeueReusableCellWithIdentifier("SettingsActionCell", forIndexPath: indexPath) as UITableViewCell! if PersistenceManager.getWeightUnit() == "lbs" { @@ -237,12 +461,14 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi } alertController.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)) + alertController.addAction(UIAlertAction(title: "Kilograms (kg)", style: .Default) { (action) in PersistenceManager.setWeightUnit("kg") self.tableView.deselectRowAtIndexPath(indexPath, animated: true) self.tableView.reloadData() }) + alertController.addAction(UIAlertAction(title: "Pounds (lbs)", style: .Default) { (action) in PersistenceManager.setWeightUnit("lbs") @@ -253,7 +479,7 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi self.presentViewController(alertController, animated: true, completion: nil) } - if indexPath.section == 2 { + if indexPath.section == 3 { if indexPath.row == 0 { if let requestUrl = NSURL(string: "https://www.github.com/mazurio") { UIApplication.sharedApplication().openURL(requestUrl) @@ -263,7 +489,7 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi } } - if indexPath.section == 3 { + if indexPath.section == 4 { if indexPath.row == 0 { if let requestUrl = NSURL(string: "http://dantuma.co.za") { UIApplication.sharedApplication().openURL(requestUrl) @@ -273,7 +499,7 @@ class SettingsViewController: UIViewController, UITableViewDataSource, UITableVi } } - if indexPath.section == 4 { + if indexPath.section == 5 { if indexPath.row == 0 { self.openStoreProductWithiTunesItemIdentifier("1018863605") diff --git a/BodyweightFitness/Controller/TimedViewController.swift b/BodyweightFitness/Controller/TimedViewController.swift index c40dfba..e347dd8 100644 --- a/BodyweightFitness/Controller/TimedViewController.swift +++ b/BodyweightFitness/Controller/TimedViewController.swift @@ -157,7 +157,8 @@ class TimedViewController: UIViewController, AVAudioPlayerDelegate { if (sets.count == 1 && sets[0].seconds == 0) { sets[0].seconds = loggedSeconds - showNotification(loggedSeconds) + self.showNotification(loggedSeconds) + self.showRestTimer() } else if (sets.count >= 1 && sets.count < 9) { let repositorySet = RepositorySet() @@ -169,7 +170,8 @@ class TimedViewController: UIViewController, AVAudioPlayerDelegate { repositoryRoutine.lastUpdatedTime = NSDate() - showNotification(loggedSeconds) + self.showNotification(loggedSeconds) + self.showRestTimer() } realm.add(repositoryRoutine, update: true) @@ -210,6 +212,10 @@ class TimedViewController: UIViewController, AVAudioPlayerDelegate { } } + func showRestTimer() { + self.rootViewController?.restTimerShouldStart() + } + func showNotification(seconds: Int) { let notification = CWStatusBarNotification() @@ -267,4 +273,4 @@ class TimedViewController: UIViewController, AVAudioPlayerDelegate { @IBAction func nextButtonClicked(sender: AnyObject) { self.rootViewController?.nextButtonClicked(sender) } -} \ No newline at end of file +} diff --git a/BodyweightFitness/Controller/WeightedViewController.swift b/BodyweightFitness/Controller/WeightedViewController.swift index 47f40d6..d26b2b7 100644 --- a/BodyweightFitness/Controller/WeightedViewController.swift +++ b/BodyweightFitness/Controller/WeightedViewController.swift @@ -65,6 +65,10 @@ class WeightedViewController: UIViewController { } } + func showRestTimer() { + self.rootViewController?.restTimerShouldStart() + } + func showNotification(set: Int, reps: Int) { let notification = CWStatusBarNotification() @@ -161,7 +165,8 @@ class WeightedViewController: UIViewController { if (sets.count == 1 && sets[0].reps == 0) { sets[0].reps = self.numberOfReps - showNotification(1, reps: self.numberOfReps) + self.showNotification(1, reps: self.numberOfReps) + self.showRestTimer() } else if (sets.count >= 1 && sets.count < 9) { let repositorySet = RepositorySet() @@ -177,6 +182,7 @@ class WeightedViewController: UIViewController { realm.add(repositoryRoutine, update: true) self.showNotification(sets.count, reps: self.numberOfReps) + self.showRestTimer() } RoutineStream.sharedInstance.setRepository() @@ -184,4 +190,4 @@ class WeightedViewController: UIViewController { } } } -} \ No newline at end of file +} diff --git a/BodyweightFitness/Controller/WorkoutViewController.swift b/BodyweightFitness/Controller/WorkoutViewController.swift index c40494d..096e9e8 100644 --- a/BodyweightFitness/Controller/WorkoutViewController.swift +++ b/BodyweightFitness/Controller/WorkoutViewController.swift @@ -13,9 +13,12 @@ class WorkoutViewController: UIViewController { @IBOutlet weak var middleViewHeightConstraint: NSLayoutConstraint! + let restTimerViewController: RestTimerViewController = RestTimerViewController() let timedViewController: TimedViewController = TimedViewController() let weightedViewController: WeightedViewController = WeightedViewController() + let userDefaults: UserDefaults = UserDefaults() + var current: Exercise = RoutineStream.sharedInstance.routine.getFirstExercise() init() { @@ -29,13 +32,22 @@ class WorkoutViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() + self.restTimerViewController.rootViewController = self self.timedViewController.rootViewController = self self.weightedViewController.rootViewController = self + + self.restTimerViewController.view.frame = self.topView.frame + self.restTimerViewController.willMoveToParentViewController(self) + self.addChildViewController(self.restTimerViewController) + self.topView.addSubview(self.restTimerViewController.view) + self.restTimerViewController.didMoveToParentViewController(self) + self.timedViewController.view.frame = self.topView.frame self.timedViewController.willMoveToParentViewController(self) self.addChildViewController(self.timedViewController) self.topView.addSubview(self.timedViewController.view) self.timedViewController.didMoveToParentViewController(self) + self.weightedViewController.view.frame = self.topView.frame self.weightedViewController.willMoveToParentViewController(self) self.addChildViewController(self.weightedViewController) @@ -132,9 +144,59 @@ class WorkoutViewController: UIViewController { self.navigationController?.presentViewController(logWorkoutController, animated: true, completion: nil) } + func restTimerShouldStart() { + if userDefaults.showRestTimer() { + let routineId = RoutineStream.sharedInstance.routine.routineId + + if let section = current.section { + if (section.sectionId == "section0") { + if userDefaults.showRestTimerAfterWarmup() { + showRestTimer() + } + } else if (section.sectionId == "section1") { + if userDefaults.showRestTimerAfterBodylineDrills() { + showRestTimer() + } + } else { + if (routineId != "routine0") { + if userDefaults.showRestTimerAfterFlexibilityExercises() { + showRestTimer() + } + } else { + showRestTimer() + } + } + } else { + showRestTimer() + } + } + } + + private func showRestTimer() { + self.restTimerViewController.startTimer() + self.restTimerViewController.view.hidden = false + + self.timedViewController.view.hidden = true + self.weightedViewController.view.hidden = true + } + + func restTimerStopped() { + self.restTimerViewController.stopTimer() + self.restTimerViewController.view.hidden = true + + if current.isTimed() { + self.timedViewController.view.hidden = false + self.weightedViewController.view.hidden = true + } else { + self.timedViewController.view.hidden = true + self.weightedViewController.view.hidden = false + } + } + internal func changeExercise(currentExercise: Exercise, updateTitle: Bool = true) { self.current = currentExercise + self.restTimerViewController.changeExercise(currentExercise) self.timedViewController.changeExercise(currentExercise) self.weightedViewController.changeExercise(currentExercise) @@ -150,12 +212,18 @@ class WorkoutViewController: UIViewController { } } - if current.isTimed() { - self.timedViewController.view.hidden = false + if self.restTimerViewController.isPlaying { + self.restTimerViewController.view.hidden = false + self.timedViewController.view.hidden = true self.weightedViewController.view.hidden = true } else { - self.timedViewController.view.hidden = true - self.weightedViewController.view.hidden = false + if current.isTimed() { + self.timedViewController.view.hidden = false + self.weightedViewController.view.hidden = true + } else { + self.timedViewController.view.hidden = true + self.weightedViewController.view.hidden = false + } } if (updateTitle) { @@ -233,13 +301,14 @@ class WorkoutViewController: UIViewController { } alertController.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)) - alertController.addAction(UIAlertAction(title: "Watch Full Video", style: .Default) { (action) in - + + if self.current.youTubeId != "" { + alertController.addAction(UIAlertAction(title: "Watch Full Video", style: .Default) { (action) in if let requestUrl = NSURL(string: "https://www.youtube.com/watch?v=" + self.current.youTubeId) { UIApplication.sharedApplication().openURL(requestUrl) } - - }) + }) + } alertController.addAction(UIAlertAction(title: "Today's Workout", style: .Default) { (action) in let backItem = UIBarButtonItem() diff --git a/BodyweightFitness/Service/PersistenceManager.swift b/BodyweightFitness/Service/PersistenceManager.swift index 8a2ca08..d407b96 100644 --- a/BodyweightFitness/Service/PersistenceManager.swift +++ b/BodyweightFitness/Service/PersistenceManager.swift @@ -4,6 +4,24 @@ public extension String { var NS: NSString { return (self as NSString) } } +class UserDefaults { + func showRestTimer() -> Bool { + return NSUserDefaults.standardUserDefaults().boolForKey("showRestTimer") + } + + func showRestTimerAfterWarmup() -> Bool { + return NSUserDefaults.standardUserDefaults().boolForKey("showRestTimerAfterWarmup") + } + + func showRestTimerAfterBodylineDrills() -> Bool { + return NSUserDefaults.standardUserDefaults().boolForKey("showRestTimerAfterBodylineDrills") + } + + func showRestTimerAfterFlexibilityExercises() -> Bool { + return NSUserDefaults.standardUserDefaults().boolForKey("showRestTimerAfterFlexibilityExercises") + } +} + class PersistenceManager { class func getWeightUnit() -> String { let fileManager = NSFileManager.defaultManager() @@ -28,6 +46,29 @@ class PersistenceManager { NSKeyedArchiver.archiveRootObject(weightUnit, toFile: dataFilePath) } + class func getRestTime() -> Int { + let fileManager = NSFileManager.defaultManager() + let directoryPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) + let documentsDirectory = directoryPath[0] + let dataFilePath = documentsDirectory.NS.stringByAppendingPathComponent("restTime.archive") + + if(fileManager.fileExistsAtPath(dataFilePath)) { + if let restTime = NSKeyedUnarchiver.unarchiveObjectWithFile(dataFilePath) as? Int { + return restTime + } + } + + return 60 + } + + class func setRestTime(restTime: Int) { + let directoryPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) + let documentsDirectory = directoryPath[0] + let dataFilePath = documentsDirectory.NS.stringByAppendingPathComponent("restTime.archive") + + NSKeyedArchiver.archiveRootObject(restTime, toFile: dataFilePath) + } + class func getNumberOfReps(id: String) -> Int { let fileManager = NSFileManager.defaultManager() let directoryPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) diff --git a/BodyweightFitness/UI/RestTimerView.xib b/BodyweightFitness/UI/RestTimerView.xib new file mode 100644 index 0000000..766471a --- /dev/null +++ b/BodyweightFitness/UI/RestTimerView.xib @@ -0,0 +1,160 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/BodyweightFitness/UI/TimedView.xib b/BodyweightFitness/UI/TimedView.xib index e66c8e1..26fdfee 100644 --- a/BodyweightFitness/UI/TimedView.xib +++ b/BodyweightFitness/UI/TimedView.xib @@ -1,7 +1,10 @@ - + + + + - + @@ -22,8 +25,10 @@ + + @@ -139,6 +150,7 @@