├── Capsmall.ttf
├── capture_it.ttf
├── Assets.xcassets
├── Contents.json
├── 8.imageset
│ ├── 8.png
│ ├── 8@2x.png
│ ├── 8@3x.png
│ └── Contents.json
├── BG.imageset
│ ├── Group.png
│ ├── Group@2x.png
│ ├── Group@3x.png
│ └── Contents.json
├── BGLogo.imageset
│ ├── BGLogo.png
│ ├── BGLogo@2x.png
│ ├── BGLogo@3x.png
│ └── Contents.json
├── Standard.imageset
│ ├── Shape.png
│ ├── Shape@2x.png
│ ├── Shape@3x.png
│ └── Contents.json
├── overlayLogo.imageset
│ ├── bg.png
│ ├── bg@2x.png
│ ├── bg@3x.png
│ └── Contents.json
├── CountDown.imageset
│ ├── Group.png
│ ├── Group@2x.png
│ ├── Group@3x.png
│ └── Contents.json
├── Group 2.imageset
│ ├── Group 2.png
│ └── Contents.json
├── Interval.imageset
│ ├── Group 2.png
│ ├── Group 2@2x.png
│ ├── Group 2@3x.png
│ └── Contents.json
├── LinkToShop.imageset
│ ├── Shape.png
│ ├── Shape@2x.png
│ ├── Shape@3x.png
│ └── Contents.json
├── backButton.imageset
│ ├── Shape.png
│ ├── Shape@2x.png
│ ├── Shape@3x.png
│ └── Contents.json
├── Questions.imageset
│ ├── Questions.png
│ ├── Questions@2x.png
│ ├── Questions@3x.png
│ └── Contents.json
├── Recording.imageset
│ ├── Recording.png
│ ├── Recording@2x.png
│ ├── Recording@3x.png
│ └── Contents.json
├── TileAddRep.imageset
│ ├── Group 2.png
│ ├── Group 2@2x.png
│ ├── Group 2@3x.png
│ └── Contents.json
├── AppIcon.appiconset
│ ├── AppIcon 5-6.png
│ ├── appIcon@2x.png
│ ├── AppIcon 5-6@2x.png
│ ├── App Icon 7-9@3x.png
│ ├── Spot Light 7-9@2x.png
│ ├── Spot Light 7-9@3x.png
│ ├── Spot Light Icon-1.png
│ ├── Spot Light Icon@2x.png
│ ├── Spot Light Icon@3x.png
│ ├── Spot Light Icon@2x-1.png
│ ├── Spot Light Icon@3x-1.png
│ └── Contents.json
├── FlipCamera.imageset
│ ├── FlipCamera.png
│ ├── FlipCamera@2x.png
│ ├── FlipCamera@3x.png
│ └── Contents.json
├── RecordDelay.imageset
│ ├── RecordDelay.png
│ ├── RecordDelay@2x.png
│ ├── RecordDelay@3x.png
│ └── Contents.json
├── Rectangle 7.imageset
│ ├── Rectangle 7.png
│ ├── Rectangle 7@2x.png
│ ├── Rectangle 7@3x.png
│ └── Contents.json
├── RecordButton.imageset
│ ├── RecordButton.png
│ ├── RecordButton@2x.png
│ ├── RecordButton@3x.png
│ └── Contents.json
├── TileAddRound.imageset
│ ├── TileAddRound.png
│ ├── TileAddRound@2x.png
│ ├── TileAddRound@3x.png
│ └── Contents.json
├── cancelButton.imageset
│ ├── Cancel button.png
│ ├── Cancel button@2x.png
│ ├── Cancel button@3x.png
│ └── Contents.json
└── HighVideoQuality.imageset
│ ├── HighVideoQuality.png
│ ├── HighVideoQuality@2x.png
│ ├── HighVideoQuality@3x.png
│ └── Contents.json
├── ufonts.com_folio-bold-condensed.ttf
├── Wodeo 2.xcodeproj
├── xcuserdata
│ └── GazLong.xcuserdatad
│ │ ├── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ ├── xcschememanagement.plist
│ │ └── Wodeo 2.xcscheme
└── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ └── GazLong.xcuserdatad
│ └── UserInterfaceState.xcuserstate
├── RoundedView.swift
├── BorderButton.swift
├── TimeTextLabel.swift
├── MainMenuCell.swift
├── WodeoProducts.swift
├── WodeoViewController.swift
├── RotatorView.swift
├── BallicticStashView.swift
├── CountDownWOD.swift
├── StandardWOD.swift
├── Wodeo 2
├── Info.plist
├── AppDelegate.swift
└── LaunchScreen.storyboard
├── WodeoModel.swift
├── InfoViewController.swift
├── IntervalWOD.swift
├── TabataResultsVC.swift
├── IntervalResultsVC.swift
├── StandardResultsVC.swift
├── CountDownResultsVC.swift
├── Spotlight.swift
├── UserSettings.swift
├── SpotlightTransitionController.swift
├── SettingsViewController.swift
├── TabataWOD.swift
├── SpotlightView.swift
├── TiledView.swift
├── CustomPhotoLibrary.swift
├── SpotlightViewController.swift
├── WodeoProductPage.swift
├── IntervalOverlay.swift
├── VideoMaker.swift
├── ResultsViewController.swift
├── StandardOverlay.swift
├── MainMenuTableViewController.swift
├── IAPHelper.swift
├── CountdownOverlay.swift
├── StopWatch.swift
├── TabataOverlay.swift
├── OverlayViewController.swift
├── IntervalSettingsVC.swift
└── TabataSettingsVC.swift
/Capsmall.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Capsmall.ttf
--------------------------------------------------------------------------------
/capture_it.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/capture_it.ttf
--------------------------------------------------------------------------------
/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Assets.xcassets/8.imageset/8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/8.imageset/8.png
--------------------------------------------------------------------------------
/Assets.xcassets/8.imageset/8@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/8.imageset/8@2x.png
--------------------------------------------------------------------------------
/Assets.xcassets/8.imageset/8@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/8.imageset/8@3x.png
--------------------------------------------------------------------------------
/ufonts.com_folio-bold-condensed.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/ufonts.com_folio-bold-condensed.ttf
--------------------------------------------------------------------------------
/Assets.xcassets/BG.imageset/Group.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/BG.imageset/Group.png
--------------------------------------------------------------------------------
/Assets.xcassets/BG.imageset/Group@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/BG.imageset/Group@2x.png
--------------------------------------------------------------------------------
/Assets.xcassets/BG.imageset/Group@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/BG.imageset/Group@3x.png
--------------------------------------------------------------------------------
/Assets.xcassets/BGLogo.imageset/BGLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/BGLogo.imageset/BGLogo.png
--------------------------------------------------------------------------------
/Assets.xcassets/Standard.imageset/Shape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/Standard.imageset/Shape.png
--------------------------------------------------------------------------------
/Assets.xcassets/overlayLogo.imageset/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/overlayLogo.imageset/bg.png
--------------------------------------------------------------------------------
/Assets.xcassets/BGLogo.imageset/BGLogo@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/BGLogo.imageset/BGLogo@2x.png
--------------------------------------------------------------------------------
/Assets.xcassets/BGLogo.imageset/BGLogo@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/BGLogo.imageset/BGLogo@3x.png
--------------------------------------------------------------------------------
/Assets.xcassets/CountDown.imageset/Group.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/CountDown.imageset/Group.png
--------------------------------------------------------------------------------
/Assets.xcassets/Group 2.imageset/Group 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/Group 2.imageset/Group 2.png
--------------------------------------------------------------------------------
/Assets.xcassets/Interval.imageset/Group 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/Interval.imageset/Group 2.png
--------------------------------------------------------------------------------
/Assets.xcassets/LinkToShop.imageset/Shape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/LinkToShop.imageset/Shape.png
--------------------------------------------------------------------------------
/Assets.xcassets/backButton.imageset/Shape.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/backButton.imageset/Shape.png
--------------------------------------------------------------------------------
/Assets.xcassets/CountDown.imageset/Group@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/CountDown.imageset/Group@2x.png
--------------------------------------------------------------------------------
/Assets.xcassets/CountDown.imageset/Group@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/CountDown.imageset/Group@3x.png
--------------------------------------------------------------------------------
/Assets.xcassets/Interval.imageset/Group 2@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/Interval.imageset/Group 2@2x.png
--------------------------------------------------------------------------------
/Assets.xcassets/Interval.imageset/Group 2@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/Interval.imageset/Group 2@3x.png
--------------------------------------------------------------------------------
/Assets.xcassets/LinkToShop.imageset/Shape@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/LinkToShop.imageset/Shape@2x.png
--------------------------------------------------------------------------------
/Assets.xcassets/LinkToShop.imageset/Shape@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/LinkToShop.imageset/Shape@3x.png
--------------------------------------------------------------------------------
/Assets.xcassets/Questions.imageset/Questions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/Questions.imageset/Questions.png
--------------------------------------------------------------------------------
/Assets.xcassets/Recording.imageset/Recording.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/Recording.imageset/Recording.png
--------------------------------------------------------------------------------
/Assets.xcassets/Standard.imageset/Shape@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/Standard.imageset/Shape@2x.png
--------------------------------------------------------------------------------
/Assets.xcassets/Standard.imageset/Shape@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/Standard.imageset/Shape@3x.png
--------------------------------------------------------------------------------
/Assets.xcassets/TileAddRep.imageset/Group 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/TileAddRep.imageset/Group 2.png
--------------------------------------------------------------------------------
/Assets.xcassets/backButton.imageset/Shape@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/backButton.imageset/Shape@2x.png
--------------------------------------------------------------------------------
/Assets.xcassets/backButton.imageset/Shape@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/backButton.imageset/Shape@3x.png
--------------------------------------------------------------------------------
/Assets.xcassets/overlayLogo.imageset/bg@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/overlayLogo.imageset/bg@2x.png
--------------------------------------------------------------------------------
/Assets.xcassets/overlayLogo.imageset/bg@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/overlayLogo.imageset/bg@3x.png
--------------------------------------------------------------------------------
/Assets.xcassets/AppIcon.appiconset/AppIcon 5-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/AppIcon.appiconset/AppIcon 5-6.png
--------------------------------------------------------------------------------
/Assets.xcassets/AppIcon.appiconset/appIcon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/AppIcon.appiconset/appIcon@2x.png
--------------------------------------------------------------------------------
/Assets.xcassets/FlipCamera.imageset/FlipCamera.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/FlipCamera.imageset/FlipCamera.png
--------------------------------------------------------------------------------
/Assets.xcassets/TileAddRep.imageset/Group 2@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/TileAddRep.imageset/Group 2@2x.png
--------------------------------------------------------------------------------
/Assets.xcassets/TileAddRep.imageset/Group 2@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/TileAddRep.imageset/Group 2@3x.png
--------------------------------------------------------------------------------
/Assets.xcassets/AppIcon.appiconset/AppIcon 5-6@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/AppIcon.appiconset/AppIcon 5-6@2x.png
--------------------------------------------------------------------------------
/Assets.xcassets/FlipCamera.imageset/FlipCamera@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/FlipCamera.imageset/FlipCamera@2x.png
--------------------------------------------------------------------------------
/Assets.xcassets/FlipCamera.imageset/FlipCamera@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/FlipCamera.imageset/FlipCamera@3x.png
--------------------------------------------------------------------------------
/Assets.xcassets/Questions.imageset/Questions@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/Questions.imageset/Questions@2x.png
--------------------------------------------------------------------------------
/Assets.xcassets/Questions.imageset/Questions@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/Questions.imageset/Questions@3x.png
--------------------------------------------------------------------------------
/Assets.xcassets/RecordDelay.imageset/RecordDelay.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/RecordDelay.imageset/RecordDelay.png
--------------------------------------------------------------------------------
/Assets.xcassets/Recording.imageset/Recording@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/Recording.imageset/Recording@2x.png
--------------------------------------------------------------------------------
/Assets.xcassets/Recording.imageset/Recording@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/Recording.imageset/Recording@3x.png
--------------------------------------------------------------------------------
/Assets.xcassets/Rectangle 7.imageset/Rectangle 7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/Rectangle 7.imageset/Rectangle 7.png
--------------------------------------------------------------------------------
/Assets.xcassets/AppIcon.appiconset/App Icon 7-9@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/AppIcon.appiconset/App Icon 7-9@3x.png
--------------------------------------------------------------------------------
/Assets.xcassets/RecordButton.imageset/RecordButton.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/RecordButton.imageset/RecordButton.png
--------------------------------------------------------------------------------
/Assets.xcassets/RecordDelay.imageset/RecordDelay@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/RecordDelay.imageset/RecordDelay@2x.png
--------------------------------------------------------------------------------
/Assets.xcassets/RecordDelay.imageset/RecordDelay@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/RecordDelay.imageset/RecordDelay@3x.png
--------------------------------------------------------------------------------
/Assets.xcassets/Rectangle 7.imageset/Rectangle 7@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/Rectangle 7.imageset/Rectangle 7@2x.png
--------------------------------------------------------------------------------
/Assets.xcassets/Rectangle 7.imageset/Rectangle 7@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/Rectangle 7.imageset/Rectangle 7@3x.png
--------------------------------------------------------------------------------
/Assets.xcassets/TileAddRound.imageset/TileAddRound.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/TileAddRound.imageset/TileAddRound.png
--------------------------------------------------------------------------------
/Assets.xcassets/cancelButton.imageset/Cancel button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/cancelButton.imageset/Cancel button.png
--------------------------------------------------------------------------------
/Assets.xcassets/AppIcon.appiconset/Spot Light 7-9@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/AppIcon.appiconset/Spot Light 7-9@2x.png
--------------------------------------------------------------------------------
/Assets.xcassets/AppIcon.appiconset/Spot Light 7-9@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/AppIcon.appiconset/Spot Light 7-9@3x.png
--------------------------------------------------------------------------------
/Assets.xcassets/AppIcon.appiconset/Spot Light Icon-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/AppIcon.appiconset/Spot Light Icon-1.png
--------------------------------------------------------------------------------
/Assets.xcassets/AppIcon.appiconset/Spot Light Icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/AppIcon.appiconset/Spot Light Icon@2x.png
--------------------------------------------------------------------------------
/Assets.xcassets/AppIcon.appiconset/Spot Light Icon@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/AppIcon.appiconset/Spot Light Icon@3x.png
--------------------------------------------------------------------------------
/Assets.xcassets/RecordButton.imageset/RecordButton@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/RecordButton.imageset/RecordButton@2x.png
--------------------------------------------------------------------------------
/Assets.xcassets/RecordButton.imageset/RecordButton@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/RecordButton.imageset/RecordButton@3x.png
--------------------------------------------------------------------------------
/Assets.xcassets/TileAddRound.imageset/TileAddRound@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/TileAddRound.imageset/TileAddRound@2x.png
--------------------------------------------------------------------------------
/Assets.xcassets/TileAddRound.imageset/TileAddRound@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/TileAddRound.imageset/TileAddRound@3x.png
--------------------------------------------------------------------------------
/Assets.xcassets/cancelButton.imageset/Cancel button@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/cancelButton.imageset/Cancel button@2x.png
--------------------------------------------------------------------------------
/Assets.xcassets/cancelButton.imageset/Cancel button@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/cancelButton.imageset/Cancel button@3x.png
--------------------------------------------------------------------------------
/Assets.xcassets/AppIcon.appiconset/Spot Light Icon@2x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/AppIcon.appiconset/Spot Light Icon@2x-1.png
--------------------------------------------------------------------------------
/Assets.xcassets/AppIcon.appiconset/Spot Light Icon@3x-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/AppIcon.appiconset/Spot Light Icon@3x-1.png
--------------------------------------------------------------------------------
/Assets.xcassets/HighVideoQuality.imageset/HighVideoQuality.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/HighVideoQuality.imageset/HighVideoQuality.png
--------------------------------------------------------------------------------
/Assets.xcassets/HighVideoQuality.imageset/HighVideoQuality@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/HighVideoQuality.imageset/HighVideoQuality@2x.png
--------------------------------------------------------------------------------
/Assets.xcassets/HighVideoQuality.imageset/HighVideoQuality@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Assets.xcassets/HighVideoQuality.imageset/HighVideoQuality@3x.png
--------------------------------------------------------------------------------
/Wodeo 2.xcodeproj/xcuserdata/GazLong.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/Wodeo 2.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Wodeo 2.xcodeproj/project.xcworkspace/xcuserdata/GazLong.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrk-9/iOS-app_Wodeo-2/HEAD/Wodeo 2.xcodeproj/project.xcworkspace/xcuserdata/GazLong.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Assets.xcassets/Group 2.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Group 2.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Assets.xcassets/8.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "8.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "8@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "8@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Assets.xcassets/BG.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Group.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Group@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Group@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Assets.xcassets/overlayLogo.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "bg.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "bg@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "bg@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Assets.xcassets/BGLogo.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "BGLogo.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "BGLogo@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "BGLogo@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Assets.xcassets/CountDown.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Group.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Group@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Group@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Assets.xcassets/LinkToShop.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Shape.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Shape@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Shape@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Assets.xcassets/Standard.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Shape.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Shape@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Shape@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Assets.xcassets/backButton.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Shape.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Shape@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Shape@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Assets.xcassets/Interval.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Group 2.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Group 2@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Group 2@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Assets.xcassets/TileAddRep.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Group 2.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Group 2@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Group 2@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Assets.xcassets/Questions.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Questions.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Questions@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Questions@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Assets.xcassets/Recording.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Recording.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Recording@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Recording@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Assets.xcassets/FlipCamera.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "FlipCamera.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "FlipCamera@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "FlipCamera@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Assets.xcassets/RecordDelay.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "RecordDelay.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "RecordDelay@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "RecordDelay@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Assets.xcassets/Rectangle 7.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Rectangle 7.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Rectangle 7@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Rectangle 7@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Assets.xcassets/RecordButton.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "RecordButton.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "RecordButton@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "RecordButton@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Assets.xcassets/TileAddRound.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "TileAddRound.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "TileAddRound@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "TileAddRound@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Assets.xcassets/cancelButton.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "Cancel button.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "Cancel button@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "Cancel button@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Assets.xcassets/HighVideoQuality.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "HighVideoQuality.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "HighVideoQuality@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "HighVideoQuality@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/RoundedView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RoundedView.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 15/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class RoundedView: UIView {
12 |
13 | override init(frame: CGRect) {
14 | super.init(frame: frame)
15 | setUp()
16 | }
17 |
18 | required init?(coder aDecoder: NSCoder) {
19 | super.init(coder: aDecoder)
20 | setUp()
21 | }
22 |
23 | func setUp(){
24 | layer.cornerRadius = 4.0
25 | layer.masksToBounds = true
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Wodeo 2.xcodeproj/xcuserdata/GazLong.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Wodeo 2.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 9C1F63521C655F41008954A0
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/BorderButton.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BorderButton.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 29/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class BorderButton: UIButton {
12 |
13 | override init(frame: CGRect) {
14 | super.init(frame: frame)
15 | commonInit()
16 | }
17 |
18 | required init?(coder aDecoder: NSCoder) {
19 | super.init(coder: aDecoder)
20 | commonInit()
21 | }
22 |
23 |
24 | func commonInit(){
25 |
26 | layer.borderColor = UIColor(red: 240/255.0, green: 178/255.0, blue: 71/255.0, alpha: 1.0).CGColor
27 | layer.borderWidth = 1.5
28 | layer.cornerRadius = 5.0
29 | layer.masksToBounds = true
30 | }
31 |
32 |
33 | }
--------------------------------------------------------------------------------
/TimeTextLabel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TimeTextLabel.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 19/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class TimeTextLabel: CATextLayer {
12 |
13 | override init(layer: AnyObject) {
14 | super.init(layer: layer)
15 | }
16 |
17 | init(fontSize:CGFloat) {
18 | super.init()
19 | font = "Folio-BoldCondensed"
20 | self.fontSize = fontSize
21 | alignmentMode = kCAAlignmentLeft
22 | foregroundColor = UIColor(red: 243/255.0, green: 80/255.0, blue: 47/255.0, alpha: 1.0).CGColor
23 | backgroundColor = UIColor.clearColor().CGColor
24 | }
25 |
26 | required init?(coder aDecoder: NSCoder) {
27 | super.init(coder: aDecoder)
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/MainMenuCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MainMenuCell.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 15/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class MainMenuCell: UITableViewCell {
12 |
13 | @IBOutlet weak var cellImage:UIImageView!
14 | @IBOutlet weak var title:UILabel!
15 | @IBOutlet weak var subTitle:UILabel!
16 | @IBOutlet weak var fadedBackgrondView:UIView!
17 |
18 |
19 | let backGroundView = UIView()
20 |
21 |
22 | override func awakeFromNib() {
23 | super.awakeFromNib()
24 | fadedBackgrondView.layer.cornerRadius = 15.0
25 | fadedBackgrondView.layer.masksToBounds = true
26 |
27 |
28 | let bgv = UIView(frame:bounds)
29 | bgv.layer.cornerRadius = 15.0
30 | bgv.backgroundColor = UIColor(red: 204/255.0, green: 204/255.0, blue: 204/255.0, alpha: 0.4)
31 |
32 | selectedBackgroundView = bgv
33 |
34 | }
35 |
36 | required init?(coder aDecoder: NSCoder) {
37 | super.init(coder: aDecoder)
38 |
39 |
40 |
41 |
42 | }
43 |
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/WodeoProducts.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WodeoProducts.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 28/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // Use enum as a simple namespace. (It has no cases so you can't instantiate it.)
12 | public enum WodeoProducts {
13 |
14 | /// TODO: Change this to whatever you set on iTunes connect
15 | private static let Prefix = "co.uk.ballisticstash.wodeo2."
16 |
17 | /// MARK: - Supported Product Identifiers
18 | public static let moreVideo = Prefix + "exvideo"
19 |
20 |
21 | // All of the products assembled into a set of product identifiers.
22 | private static let productIdentifiers: Set = [WodeoProducts.moreVideo]
23 |
24 |
25 | /// Static instance of IAPHelper that for rage products.
26 | public static let store = IAPHelper(productIdentifiers: WodeoProducts.productIdentifiers)
27 | }
28 |
29 | /// Return the resourcename for the product identifier.
30 | func resourceNameForProductIdentifier(productIdentifier: String) -> String? {
31 | return productIdentifier.componentsSeparatedByString(".").last
32 | }
33 |
--------------------------------------------------------------------------------
/WodeoViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WodeoViewController.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 16/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 |
12 | class WodeoViewController: UIViewController {
13 |
14 | override func viewDidLoad() {
15 | super.viewDidLoad()
16 |
17 |
18 |
19 |
20 | }
21 |
22 | override func didReceiveMemoryWarning() {
23 | super.didReceiveMemoryWarning()
24 | // Dispose of any resources that can be recreated.
25 | }
26 |
27 | func orientationChanged() {
28 |
29 | }
30 |
31 | func currentOrientation() -> UIDeviceOrientation {
32 |
33 | switch UIDevice.currentDevice().orientation {
34 | case .LandscapeLeft:
35 | return .LandscapeRight
36 | case .LandscapeRight:
37 | return .LandscapeLeft
38 | case .PortraitUpsideDown:
39 | return .PortraitUpsideDown
40 | default:
41 | return .Portrait
42 | }
43 | }
44 |
45 | override func prefersStatusBarHidden() -> Bool {
46 | return true
47 | }
48 |
49 | override func shouldAutorotate() -> Bool {
50 | return false
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/RotatorView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RotatorView.swift
3 | // Video Testing
4 | //
5 | // Created by Gareth Long on 12/02/2016.
6 | // Copyright © 2016 gazlongapps. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class RotatorView: UIView {
12 |
13 | func rotateViewsToMatchOrientation(orientation:UIDeviceOrientation){
14 |
15 | let animationDuration = 0.5
16 | var rotation:CGFloat = 0.0
17 |
18 | switch orientation {
19 |
20 | case.LandscapeLeft:rotation = CGFloat(-M_PI_2)
21 | case.LandscapeRight:rotation = CGFloat(M_PI_2)
22 | case.Portrait: rotation = 0.0
23 | case.PortraitUpsideDown:rotation = CGFloat(2 * M_PI_2)
24 | default: rotation = 0.0
25 | }
26 |
27 |
28 | for childView in self.subviews {
29 |
30 |
31 | if !childView.isKindOfClass(UILabel) {
32 | UIView.animateWithDuration(animationDuration, delay: 0.0, usingSpringWithDamping:1.0, initialSpringVelocity: 2.0, options:.TransitionNone, animations:{
33 |
34 | childView.transform = CGAffineTransformMakeRotation(rotation)
35 |
36 | }, completion:nil)
37 | }
38 |
39 |
40 | }
41 |
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/BallicticStashView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BallicticStashView.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 15/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | protocol BallicticStashDelegate {
12 | func ballisticStachDidReturn()
13 | }
14 |
15 | class BallicticStashView: UIViewController,UIWebViewDelegate {
16 |
17 | @IBOutlet weak var webView: UIWebView!
18 |
19 | var delegate:BallicticStashDelegate?
20 |
21 | override func viewDidLoad() {
22 | super.viewDidLoad()
23 |
24 | let url = NSURL(string:"http://www.ballisticstash.co.uk/?v=79cba1185463")
25 |
26 | let request = NSURLRequest(URL:url!)
27 |
28 | webView.loadRequest(request)
29 |
30 |
31 |
32 | // Do any additional setup after loading the view.
33 | }
34 | @IBAction func backButtonPressed(sender: AnyObject) {
35 | delegate!.ballisticStachDidReturn()
36 | }
37 |
38 | func webViewDidFinishLoad(webView: UIWebView) {
39 |
40 | UIView.animateWithDuration(0.6, animations: {
41 | webView.alpha = 1.0
42 | })
43 |
44 | }
45 |
46 |
47 |
48 | override func didReceiveMemoryWarning() {
49 | super.didReceiveMemoryWarning()
50 | // Dispose of any resources that can be recreated.
51 | }
52 |
53 |
54 | /*
55 | // MARK: - Navigation
56 |
57 | // In a storyboard-based application, you will often want to do a little preparation before navigation
58 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
59 | // Get the new view controller using segue.destinationViewController.
60 | // Pass the selected object to the new view controller.
61 | }
62 | */
63 |
64 | override func prefersStatusBarHidden() -> Bool {
65 | return true
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/CountDownWOD.swift:
--------------------------------------------------------------------------------
1 |
2 |
3 | class CountDownWOD:WOD , StopWatchDelegate{
4 |
5 | var stopWatch:StopWatch
6 | var rounds = [Round]()
7 | var totalWorkOutSeconds = TimeInterval()
8 | var countDownFromTime:Int = 0
9 |
10 |
11 | override init(with name: String) {
12 | stopWatch = StopWatch(type: StopWatchType.CountDown)
13 | super.init(with: name)
14 |
15 |
16 | }
17 |
18 | func startWorkOut() {
19 |
20 | stopWatch.delegate = self
21 | stopWatch.start()
22 | addRound()
23 |
24 | }
25 |
26 | func stopWorkOut(){
27 | totalWorkOutSeconds = stopWatch.currentDuration
28 | stopWatch.stop()
29 |
30 | }
31 |
32 |
33 |
34 | func addRound(){
35 | rounds.append(Round(startTime:stopWatch.currentDuration, roundNumber: rounds.count + 1))
36 | }
37 |
38 | func addRep(){
39 | //Get last round reps
40 | let cRound = rounds.last
41 | let timeStamp = stopWatch.currentDuration
42 |
43 | cRound!.reps.append(Rep(startTime:timeStamp, repNumber:cRound!.reps.count + 1))
44 | }
45 |
46 | private func totalReps() -> Int {
47 |
48 | var repCount = 0
49 | for round in rounds {
50 | repCount += round.reps.count
51 | }
52 | return repCount
53 |
54 | }
55 |
56 | func setCountDownTime(time:Int){
57 | stopWatch.countDownFromSeconds = time
58 | }
59 |
60 | //Stop watch delegate
61 | func stopWatchDidUpdateWithTimeInterval(t: TimeInterval) {
62 | delegate.wodStopWatchDidUpdateToValue(t.intervalString(false))
63 |
64 | }
65 |
66 | func stopWatchDidCountDown(t: TimeInterval) {
67 | delegate.workOutDidEnd()
68 | }
69 |
70 | func stopWatchInLastThreeSeconds(t: TimeInterval) {
71 |
72 | }
73 |
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/StandardWOD.swift:
--------------------------------------------------------------------------------
1 |
2 |
3 | class StandardWOD: WOD,StopWatchDelegate{
4 |
5 |
6 | //Stop watch to time it
7 | var stopWatch:StopWatch
8 | var rounds = [Round]()
9 | var totalWorkOutSeconds = TimeInterval()
10 |
11 |
12 | override init(with name: String) {
13 | stopWatch = StopWatch(type: StopWatchType.CountUp)
14 | super.init(with: name)
15 | }
16 |
17 |
18 | //Public functions
19 | func startWorkOut() {
20 | stopWatch.delegate = self
21 | stopWatch.start()
22 | addRound()
23 | }
24 |
25 | func stopWorkOut(){
26 | totalWorkOutSeconds = stopWatch.currentDuration
27 | stopWatch.stop()
28 |
29 | }
30 |
31 | //Work out data
32 | func addRound(){
33 | let cDuration = stopWatch.currentDuration
34 |
35 | if let round = rounds.last {
36 | round.endTime = cDuration
37 | }
38 |
39 | rounds.append(Round(startTime:cDuration, roundNumber: rounds.count + 1))
40 | }
41 |
42 | func addRep(){
43 | //Get last round reps
44 | let cRound = rounds.last
45 | let timeStamp = stopWatch.currentDuration
46 |
47 | cRound!.reps.append(Rep(startTime:timeStamp, repNumber:cRound!.reps.count + 1))
48 | }
49 |
50 | private func totalReps() -> Int {
51 |
52 | var repCount = 0
53 | for round in rounds {
54 | repCount += round.reps.count
55 | }
56 | return repCount
57 |
58 | }
59 |
60 | //Stop watch delegate
61 | func stopWatchDidUpdateWithTimeInterval(t: TimeInterval) {
62 | workOutTime = t.secondsAsDouble!
63 | delegate.wodStopWatchDidUpdateToValue(t.intervalString(false))
64 |
65 | }
66 |
67 | func stopWatchDidCountDown(t: TimeInterval) {
68 |
69 | }
70 |
71 | func stopWatchInLastThreeSeconds(t: TimeInterval) {
72 |
73 | }
74 |
75 |
76 | }
77 |
78 |
--------------------------------------------------------------------------------
/Wodeo 2/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.1
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 2
23 | LSRequiresIPhoneOS
24 |
25 | NSAppTransportSecurity
26 |
27 | NSExceptionDomains
28 |
29 | www.ballisticstash.co.uk
30 |
31 | NSExceptionAllowsInsecureHTTPLoads
32 |
33 | NSExceptionMinimumTLSVersion
34 | TLSv1.1
35 | NSIncludesSubdomains
36 |
37 |
38 |
39 |
40 | UIAppFonts
41 |
42 | capture_it.ttf
43 | Capsmall.ttf
44 |
45 | UILaunchStoryboardName
46 | LaunchScreen
47 | UIMainStoryboardFile
48 | Main
49 | UIRequiredDeviceCapabilities
50 |
51 | armv7
52 |
53 | UIStatusBarHidden
54 |
55 | UISupportedInterfaceOrientations
56 |
57 | UIInterfaceOrientationPortrait
58 |
59 | UISupportedInterfaceOrientations~ipad
60 |
61 | UIInterfaceOrientationPortrait
62 | UIInterfaceOrientationPortraitUpsideDown
63 | UIInterfaceOrientationLandscapeLeft
64 | UIInterfaceOrientationLandscapeRight
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/Wodeo 2/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 05/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
18 | // Override point for customization after application launch.
19 |
20 | /*
21 | for family: String in UIFont.familyNames()
22 | {
23 | print("\(family)")
24 | for names: String in UIFont.fontNamesForFamilyName(family)
25 | {
26 | print("== \(names)")
27 | }
28 | }
29 | */
30 |
31 |
32 | return true
33 |
34 | }
35 |
36 | func applicationWillResignActive(application: UIApplication) {
37 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
38 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
39 | }
40 |
41 | func applicationDidEnterBackground(application: UIApplication) {
42 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
43 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
44 | }
45 |
46 | func applicationWillEnterForeground(application: UIApplication) {
47 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
48 | }
49 |
50 | func applicationDidBecomeActive(application: UIApplication) {
51 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
52 | }
53 |
54 | func applicationWillTerminate(application: UIApplication) {
55 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
56 | }
57 |
58 |
59 |
60 | }
61 |
62 |
--------------------------------------------------------------------------------
/WodeoModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WodeoModel.swift
3 | // timerTesting
4 | //
5 | // Created by Gareth Long on 13/02/2016.
6 | // Copyright © 2016 gazlongapps. All rights reserved.
7 | //
8 |
9 | /*
10 | To be subclassed for each type of work out.
11 |
12 | */
13 |
14 |
15 |
16 | import Foundation
17 |
18 | protocol WODDelegate {
19 | func wodStopWatchDidUpdateToValue(timeValue:String)
20 | func workOutDidEnd()
21 | }
22 |
23 | class Rep {
24 |
25 | var startTime:TimeInterval
26 | var endTime:TimeInterval?
27 | let repNumber:Int!
28 |
29 | init(startTime:TimeInterval,repNumber:Int){
30 | self.startTime = startTime
31 | self.repNumber = repNumber
32 | }
33 |
34 | }
35 |
36 | class Round {
37 |
38 | var startTime:TimeInterval!
39 | var endTime:TimeInterval?
40 | var reps = [Rep]()
41 | var roundNumber:Int = 0
42 |
43 |
44 | init(startTime:TimeInterval,roundNumber:Int){
45 | self.startTime = startTime
46 | self.roundNumber = roundNumber
47 | }
48 |
49 | //func title() -> String {
50 |
51 | // }
52 |
53 | func titleForRepAtIndex(index:Int) -> String {
54 | let repStartTime = reps[index].startTime
55 | var repEndTime = TimeInterval(secondsAsDouble:0.0)
56 |
57 | if index == reps.count - 1 {
58 | //Its the last rep so use the round end time
59 | if let et = endTime{
60 | repEndTime = et
61 | }else{
62 | repEndTime = TimeInterval(secondsAsDouble:0.0)
63 | }
64 | }else{
65 | repEndTime = reps[index + 1].startTime
66 | }
67 |
68 | return "Rep: \(index + 1) (\(repStartTime.intervalString(true)) - \(repEndTime.intervalString(true)))"
69 |
70 | }
71 | }
72 |
73 | class PresetRound: Round {
74 |
75 | var presetDuration:Int!
76 | var loopNumber:Int!
77 |
78 | init(roundNumber:Int,presetDuration:Int,loopNumber:Int){
79 | super.init(startTime:TimeInterval(secondsAsDouble: 0.0), roundNumber:roundNumber)
80 | self.presetDuration = presetDuration
81 | self.loopNumber = loopNumber
82 | }
83 | }
84 |
85 |
86 | class WOD {
87 |
88 | var totalVideoSecondsAllowed:Double = UserSettings.sharedInstance.getPurchasedVideo() ? 100000.0:300.0
89 |
90 | //Public variables
91 | var name:String
92 | var startDate:NSDate
93 | var countDownTime:Int = 0
94 | var delegate:WODDelegate!
95 | var workOutTime:Double = 0.0
96 |
97 | init(with name:String) {
98 | self.name = name
99 | self.startDate = NSDate()
100 |
101 | }
102 |
103 | }
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/InfoViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // InfoViewController.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 16/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class InfoViewController: SpotlightViewController {
12 |
13 | @IBOutlet var annotationViews: [UIView]!
14 | @IBOutlet var rotatorView:RotatorView!
15 |
16 | var stepIndex: Int = 0
17 |
18 | override func viewDidLoad() {
19 | super.viewDidLoad()
20 | NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(InfoViewController.orientationChanged), name: UIDeviceOrientationDidChangeNotification, object: nil)
21 | delegate = self
22 | }
23 |
24 | func next(labelAnimated: Bool) {
25 | updateAnnotationView(labelAnimated)
26 |
27 | let screenSize = UIScreen.mainScreen().bounds.size
28 | switch stepIndex {
29 | case 0:
30 | spotlightView.appear(Spotlight.Oval(center: CGPointMake(screenSize.width - 34,30), diameter: 60))
31 | case 1:
32 | spotlightView.move(Spotlight.Oval(center: CGPointMake(32,28), diameter: 60))
33 | case 2:
34 | spotlightView.move(Spotlight.Oval(center: CGPointMake(screenSize.width - 45,screenSize.height - 50), diameter: 60))
35 | case 3:
36 | dismissViewControllerAnimated(true, completion: nil)
37 | default:
38 | break
39 | }
40 |
41 | stepIndex += 1
42 | }
43 |
44 | func updateAnnotationView(animated: Bool) {
45 | annotationViews.enumerate().forEach { index, view in
46 | UIView .animateWithDuration(animated ? 0.25 : 0) {
47 | view.alpha = index == self.stepIndex ? 1 : 0
48 | }
49 | }
50 | }
51 |
52 |
53 | func orientationChanged() {
54 | self.rotatorView.rotateViewsToMatchOrientation(currentOrientation())
55 | }
56 |
57 | func currentOrientation() -> UIDeviceOrientation {
58 |
59 | switch UIDevice.currentDevice().orientation {
60 | case .LandscapeLeft:
61 | return .LandscapeRight
62 | case .LandscapeRight:
63 | return .LandscapeLeft
64 | case .PortraitUpsideDown:
65 | return .PortraitUpsideDown
66 | default:
67 | return .Portrait
68 | }
69 | }
70 |
71 | override func prefersStatusBarHidden() -> Bool {
72 | return true
73 | }
74 |
75 | }
76 |
77 | extension InfoViewController: SpotlightViewControllerDelegate {
78 | func spotlightViewControllerWillPresent(viewController: SpotlightViewController, animated: Bool) {
79 | next(false)
80 | }
81 |
82 | func spotlightViewControllerTapped(viewController: SpotlightViewController, isInsideSpotlight: Bool) {
83 | next(true)
84 | }
85 |
86 | func spotlightViewControllerWillDismiss(viewController: SpotlightViewController, animated: Bool) {
87 | spotlightView.disappear()
88 | }
89 | }
90 |
91 |
--------------------------------------------------------------------------------
/IntervalWOD.swift:
--------------------------------------------------------------------------------
1 |
2 | protocol IntervalWOD_Delegate {
3 | func intervalWODDidMoveToRoundWithName(name:String)
4 | }
5 |
6 | class IntervalWOD:WOD,StopWatchDelegate {
7 |
8 |
9 | var rounds = [PresetRound]()
10 | var stopWatch:StopWatch!
11 | var repStopWatch:StopWatch!
12 | var roundCounter:Int = 0
13 | var loopCounter:Int = 1
14 | var loops:Int = 1
15 |
16 | var intervalDelegate:IntervalWOD_Delegate?
17 |
18 | override init(with name: String) {
19 | stopWatch = StopWatch(type: StopWatchType.CountDown)
20 | repStopWatch = StopWatch(type: StopWatchType.CountUp)
21 | super.init(with: name)
22 | }
23 |
24 | func startWorkOut(){
25 | repStopWatch.start()
26 | stopWatch.delegate = self
27 | nextInterval()
28 | }
29 |
30 | func stopWorkOut() {
31 | repStopWatch.stop()
32 | stopWatch.stop()
33 | }
34 |
35 | func addRep() -> String {
36 | //Get current round
37 | let round = rounds[roundCounter - 1]
38 | round.reps.append(Rep(startTime: repStopWatch.currentDuration, repNumber: round.reps.count + 1))
39 |
40 | return "Rep: \(round.reps.count)"
41 | }
42 |
43 | private func nextInterval(){
44 |
45 | stopWatch.stop()
46 |
47 |
48 | if roundCounter < rounds.count {
49 |
50 | if let it = intervalDelegate {
51 | it.intervalWODDidMoveToRoundWithName("Loop \(rounds[roundCounter].loopNumber) Round \(roundCounter + 1) of \(rounds.count)")
52 | }
53 |
54 |
55 | stopWatch.countDownFromSeconds = rounds[roundCounter].presetDuration
56 | roundCounter += 1
57 | stopWatch.start()
58 | }else{
59 | delegate.workOutDidEnd()
60 | }
61 | }
62 |
63 | func totalWorkOutSeconds() -> Int {
64 | var retTotal = 0
65 | for r in rounds {
66 | retTotal += r.presetDuration
67 | }
68 |
69 | return retTotal
70 | }
71 |
72 | //Stop watch delegate
73 | func stopWatchDidCountDown(t: TimeInterval) {
74 |
75 | nextInterval()
76 | }
77 |
78 | func stopWatchDidUpdateWithTimeInterval(t: TimeInterval) {
79 | delegate.wodStopWatchDidUpdateToValue(t.intervalString(false))
80 | }
81 |
82 | func stringForRound(index:Int,andRep:Int) -> String {
83 |
84 | let loopNumber = rounds[index].loopNumber
85 | let maxNumberOfLoops = rounds.last!.loopNumber
86 |
87 | let roundNumber = rounds[index].roundNumber
88 | let maxNumberOfRounds = rounds.count
89 |
90 | let repNumber = andRep + 1
91 | let maxReps = rounds[index].reps.count
92 |
93 | return "Loop \(loopNumber) of \(maxNumberOfLoops) | Round \(roundNumber) of \(maxNumberOfRounds) | Rep \(repNumber) of \(maxReps)"
94 |
95 |
96 |
97 |
98 | }
99 |
100 |
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/TabataResultsVC.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TabataResultsVC.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 27/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import AVFoundation
11 |
12 |
13 | class TabataResultsVC: ResultsViewController {
14 |
15 | var tabataWOD:TabataWOD!
16 |
17 |
18 | override func viewDidLoad() {
19 | super.viewDidLoad()
20 |
21 | }
22 |
23 | override func viewWillAppear(animated: Bool) {
24 |
25 | workOutName.text = tabataWOD.name
26 |
27 | let formater = NSDateFormatter()
28 | formater.dateStyle = .FullStyle
29 | workOutDate.text = formater.stringFromDate(tabataWOD.startDate)
30 |
31 | }
32 |
33 | //Table view dataSource Methods
34 | override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
35 | return tabataWOD.rounds.count
36 | }
37 |
38 | func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
39 | return "Round \(section + 1)"
40 | }
41 |
42 | override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
43 | return tabataWOD.rounds[section].reps.count
44 | }
45 |
46 | override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
47 |
48 | let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
49 |
50 | cell.textLabel!.text = tabataWOD.rounds[indexPath.section].titleForRepAtIndex(indexPath.row)
51 |
52 | return cell
53 | }
54 |
55 | func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
56 | return 50.0
57 | }
58 |
59 | func tableView(tableView: UITableView, shouldHighlightRowAtIndexPath indexPath: NSIndexPath) -> Bool {
60 | return false
61 |
62 | }
63 |
64 | func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
65 | let titleLabel = UILabel(frame:CGRectMake(0.0,0.0,300.0,70.0))
66 | titleLabel.font = UIFont(name:"Folio-BoldCondensed", size: 30.0)
67 | titleLabel.text = self.tableView(tableView, titleForHeaderInSection: section)
68 | titleLabel.textColor = UIColor(red: 240/255.0, green: 240/255.0, blue: 240/255.0, alpha: 1.0)
69 |
70 | let contView = UIView(frame: titleLabel.frame)
71 | contView.addSubview(titleLabel)
72 |
73 | return contView
74 | }
75 |
76 | override func applyVideoEffectsToComposition(composition: AVMutableVideoComposition, size: CGSize) {
77 |
78 | super.applyVideoEffectsToComposition(composition, size: size)
79 |
80 | overlayLayer.addSublayer(layerGenerator.timerView(size, forSeconds:tabataWOD.totalWorkOutSeconds(), withDelay: tabataWOD.countDownTime,countUp: false))
81 |
82 |
83 | overlayLayer.addSublayer(layerGenerator.tabataWorkOutInfo(size, workOut: tabataWOD, withDelay: tabataWOD.countDownTime))
84 |
85 |
86 |
87 |
88 | parentLayer.addSublayer(overlayLayer)
89 | composition.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videoLayer, inLayer: parentLayer)
90 |
91 |
92 |
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/IntervalResultsVC.swift:
--------------------------------------------------------------------------------
1 | //
2 | // IntervalResultsVC.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 26/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import AVFoundation
11 |
12 |
13 | class IntervalResultsVC: ResultsViewController {
14 |
15 | var intervalWOD:IntervalWOD!
16 |
17 |
18 | override func viewDidLoad() {
19 | super.viewDidLoad()
20 |
21 | }
22 |
23 | override func viewWillAppear(animated: Bool) {
24 |
25 | workOutName.text = intervalWOD.name
26 |
27 | let formater = NSDateFormatter()
28 | formater.dateStyle = .FullStyle
29 | workOutDate.text = formater.stringFromDate(intervalWOD.startDate)
30 |
31 | }
32 |
33 | //Table view dataSource Methods
34 | override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
35 | return intervalWOD.rounds.count
36 | }
37 |
38 | func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
39 | return "Round \(section + 1)"
40 | }
41 |
42 | override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
43 | return intervalWOD.rounds[section].reps.count
44 | }
45 |
46 | override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
47 |
48 | let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
49 |
50 | cell.textLabel!.text = intervalWOD.rounds[indexPath.section].titleForRepAtIndex(indexPath.row)
51 |
52 | return cell
53 | }
54 |
55 | func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
56 | return 50.0
57 | }
58 |
59 | func tableView(tableView: UITableView, shouldHighlightRowAtIndexPath indexPath: NSIndexPath) -> Bool {
60 | return false
61 |
62 | }
63 |
64 | func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
65 | let titleLabel = UILabel(frame:CGRectMake(0.0,0.0,300.0,70.0))
66 | titleLabel.font = UIFont(name:"Folio-BoldCondensed", size: 30.0)
67 | titleLabel.text = self.tableView(tableView, titleForHeaderInSection: section)
68 | titleLabel.textColor = UIColor(red: 240/255.0, green: 240/255.0, blue: 240/255.0, alpha: 1.0)
69 |
70 | let contView = UIView(frame: titleLabel.frame)
71 | contView.addSubview(titleLabel)
72 |
73 | return contView
74 | }
75 |
76 | override func applyVideoEffectsToComposition(composition: AVMutableVideoComposition, size: CGSize) {
77 |
78 | super.applyVideoEffectsToComposition(composition, size: size)
79 |
80 | overlayLayer.addSublayer(layerGenerator.timerView(size, forSeconds:intervalWOD.totalWorkOutSeconds(), withDelay: intervalWOD.countDownTime,countUp: false))
81 |
82 |
83 | overlayLayer.addSublayer(layerGenerator.intervalWorkOutInfo(size, workOut: intervalWOD, withDelay: intervalWOD.countDownTime))
84 |
85 |
86 |
87 |
88 | parentLayer.addSublayer(overlayLayer)
89 | composition.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videoLayer, inLayer: parentLayer)
90 |
91 |
92 |
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/StandardResultsVC.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StandardResultsVC.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 19/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import AVFoundation
11 |
12 | class StandardResultsVC: ResultsViewController {
13 |
14 | var standardWorkout:StandardWOD!
15 |
16 |
17 | override func viewDidLoad() {
18 | super.viewDidLoad()
19 |
20 | }
21 |
22 | override func viewWillAppear(animated: Bool) {
23 |
24 | workOutName.text = standardWorkout.name
25 |
26 | let formater = NSDateFormatter()
27 | formater.dateStyle = .FullStyle
28 | workOutDate.text = formater.stringFromDate(standardWorkout.startDate)
29 |
30 | }
31 |
32 | //Table view dataSource Methods
33 | override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
34 | return standardWorkout.rounds.count
35 | }
36 |
37 | func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
38 | return "Round \(section + 1)"
39 | }
40 |
41 | override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
42 | return standardWorkout.rounds[section].reps.count
43 | }
44 |
45 | override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
46 |
47 | let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
48 |
49 | cell.textLabel!.text = standardWorkout.rounds[indexPath.section].titleForRepAtIndex(indexPath.row)
50 |
51 | return cell
52 | }
53 |
54 | func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
55 | return 50.0
56 | }
57 |
58 | func tableView(tableView: UITableView, shouldHighlightRowAtIndexPath indexPath: NSIndexPath) -> Bool {
59 | return false
60 | }
61 |
62 |
63 | func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
64 | let titleLabel = UILabel(frame:CGRectMake(0.0,0.0,300.0,70.0))
65 | titleLabel.font = UIFont(name:"Folio-BoldCondensed", size: 30.0)
66 | titleLabel.text = self.tableView(tableView, titleForHeaderInSection: section)
67 | titleLabel.textColor = UIColor(red: 240/255.0, green: 240/255.0, blue: 240/255.0, alpha: 1.0)
68 |
69 | let contView = UIView(frame: titleLabel.frame)
70 | contView.addSubview(titleLabel)
71 |
72 | return contView
73 | }
74 |
75 | override func applyVideoEffectsToComposition(composition: AVMutableVideoComposition, size: CGSize) {
76 |
77 | super.applyVideoEffectsToComposition(composition, size: size)
78 |
79 | overlayLayer.addSublayer(layerGenerator.timerView(size, forSeconds:Int(standardWorkout.totalWorkOutSeconds.secondsAsDouble!), withDelay: standardWorkout.countDownTime,countUp: true))
80 |
81 | overlayLayer.addSublayer(layerGenerator.standardWorkOutInfo(size, workOut:standardWorkout,withDelay:standardWorkout.countDownTime))
82 |
83 | parentLayer.addSublayer(overlayLayer)
84 | composition.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videoLayer, inLayer: parentLayer)
85 |
86 |
87 |
88 | }
89 |
90 | }
91 |
--------------------------------------------------------------------------------
/CountDownResultsVC.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CountDownResultsVC.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 22/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import AVFoundation
11 |
12 |
13 | class CountDownResultsVC: ResultsViewController {
14 |
15 | var countDownWorkout:CountDownWOD!
16 |
17 |
18 | override func viewDidLoad() {
19 | super.viewDidLoad()
20 |
21 | }
22 |
23 | override func viewWillAppear(animated: Bool) {
24 |
25 | workOutName.text = countDownWorkout.name
26 |
27 | let formater = NSDateFormatter()
28 | formater.dateStyle = .FullStyle
29 | workOutDate.text = formater.stringFromDate(countDownWorkout.startDate)
30 |
31 | }
32 |
33 | //Table view dataSource Methods
34 | override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
35 | return countDownWorkout.rounds.count
36 | }
37 |
38 | func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
39 | return "Round \(section + 1)"
40 | }
41 |
42 | override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
43 | return countDownWorkout.rounds[section].reps.count
44 | }
45 |
46 | override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
47 |
48 | let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
49 |
50 | cell.textLabel!.text = countDownWorkout.rounds[indexPath.section].titleForRepAtIndex(indexPath.row)
51 |
52 | return cell
53 | }
54 |
55 | func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
56 | return 50.0
57 | }
58 |
59 | func tableView(tableView: UITableView, shouldHighlightRowAtIndexPath indexPath: NSIndexPath) -> Bool {
60 | return false
61 | }
62 |
63 | func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
64 | let titleLabel = UILabel(frame:CGRectMake(0.0,0.0,300.0,70.0))
65 | titleLabel.font = UIFont(name:"Folio-BoldCondensed", size: 30.0)
66 | titleLabel.text = self.tableView(tableView, titleForHeaderInSection: section)
67 | titleLabel.textColor = UIColor(red: 240/255.0, green: 240/255.0, blue: 240/255.0, alpha: 1.0)
68 |
69 | let contView = UIView(frame: titleLabel.frame)
70 | contView.addSubview(titleLabel)
71 |
72 | return contView
73 | }
74 |
75 | override func applyVideoEffectsToComposition(composition: AVMutableVideoComposition, size: CGSize) {
76 |
77 | super.applyVideoEffectsToComposition(composition, size: size)
78 |
79 | overlayLayer.addSublayer(layerGenerator.timerView(size, forSeconds:Int(countDownWorkout.totalWorkOutSeconds.secondsAsDouble!), withDelay: countDownWorkout.countDownTime,countUp: false))
80 |
81 | overlayLayer.addSublayer(layerGenerator.countDownWorkOutInfo(size, workOut:countDownWorkout, withDelay:countDownWorkout.countDownTime))
82 |
83 |
84 | parentLayer.addSublayer(overlayLayer)
85 | composition.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayer: videoLayer, inLayer: parentLayer)
86 |
87 |
88 |
89 | }
90 |
91 | }
92 |
--------------------------------------------------------------------------------
/Spotlight.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Spotlight.swift
3 | // Gecco
4 | //
5 | // Created by yukiasai on 2016/01/17.
6 | // Copyright (c) 2016 yukiasai. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | public protocol SpotlightType {
12 | var frame: CGRect { get }
13 | var center: CGPoint { get }
14 | var path: UIBezierPath { get }
15 | var infinitesmalPath: UIBezierPath { get }
16 | }
17 |
18 | public extension SpotlightType {
19 | var center: CGPoint {
20 | return CGPoint(x: frame.midX, y: frame.midY)
21 | }
22 |
23 | var infinitesmalPath: UIBezierPath {
24 | return UIBezierPath(roundedRect: CGRect(origin: center, size: CGSizeZero), cornerRadius: 0)
25 | }
26 | }
27 |
28 | public class Spotlight {
29 | public class Oval: SpotlightType {
30 | public var frame: CGRect
31 | public init(frame: CGRect) {
32 | self.frame = frame
33 | }
34 |
35 | public convenience init(center: CGPoint, diameter: CGFloat) {
36 | let frame = CGRectMake(center.x - diameter / 2, center.y - diameter / 2, diameter, diameter)
37 | self.init(frame: frame)
38 | }
39 |
40 | public convenience init(view: UIView, margin: CGFloat) {
41 | let origin = view.superview!.convertPoint(view.frame.origin, toCoordinateSpace: view.window!.screen.fixedCoordinateSpace)
42 | let center = CGPoint(x: origin.x + view.bounds.width / 2, y: origin.y + view.bounds.height / 2)
43 | let diameter = max(view.bounds.width, view.bounds.height) + margin * 2
44 | self.init(center: center, diameter: diameter)
45 | }
46 |
47 | public var path: UIBezierPath {
48 | return UIBezierPath(roundedRect: frame, cornerRadius: frame.width / 2)
49 | }
50 | }
51 |
52 | public class Rect: SpotlightType {
53 | public var frame: CGRect
54 | public init(frame: CGRect) {
55 | self.frame = frame
56 | }
57 |
58 | public init(center: CGPoint, size: CGSize) {
59 | let frame = CGRectMake(center.x - size.width / 2, center.y - size.height / 2, size.width, size.height)
60 | self.frame = frame
61 | }
62 |
63 | public init(view: UIView, margin: CGFloat) {
64 | let viewOrigin = view.superview!.convertPoint(view.frame.origin, toCoordinateSpace: view.window!.screen.fixedCoordinateSpace)
65 | let origin = CGPoint(x: viewOrigin.x - margin, y: viewOrigin.y - margin)
66 | let size = CGSize(width: view.bounds.width + margin * 2, height: view.bounds.height + margin * 2)
67 | self.frame = CGRect(origin: origin, size: size)
68 | }
69 |
70 | public var path: UIBezierPath {
71 | return UIBezierPath(roundedRect: frame, cornerRadius: 0)
72 | }
73 | }
74 |
75 | public class RoundedRect: Rect {
76 | public var cornerRadius: CGFloat
77 | public init(center: CGPoint, size: CGSize, cornerRadius: CGFloat) {
78 | self.cornerRadius = cornerRadius
79 | super.init(center: center, size: size)
80 | }
81 |
82 | public init(view: UIView, margin: CGFloat, cornerRadius: CGFloat) {
83 | self.cornerRadius = cornerRadius
84 | super.init(view: view, margin: margin)
85 | }
86 |
87 | public override var path: UIBezierPath {
88 | return UIBezierPath(roundedRect: frame, cornerRadius: cornerRadius)
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/Wodeo 2.xcodeproj/xcuserdata/GazLong.xcuserdatad/xcschemes/Wodeo 2.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/UserSettings.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UserSettings.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 27/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 |
12 |
13 |
14 | class UserSettings:NSObject {
15 |
16 | let UD_VideoDelay = "VideoDelay"
17 | let UD_VideoQuality = "VideoQuality"
18 | let UD_CameraFlip = "CameraFlip"
19 | let UD_StandardName = "StandardName"
20 | let UD_JudgeName = "JudgeName"
21 |
22 | //Products
23 | let UD_PurchasedVideo = "PerchacedVideo"
24 | let UD_userVideoMessageOptOut = "UserVideoMessageOptOut"
25 | let UD_lastTimeUserWasInformed = "LastTimeUserWasInformed"
26 |
27 | static let sharedInstance = UserSettings()
28 | let ud = NSUserDefaults.standardUserDefaults()
29 |
30 |
31 | //Video quality
32 | func saveVideoQuality(quality:VideoQuality){
33 | ud.setObject(quality.rawValue, forKey: UD_VideoQuality)
34 | }
35 |
36 | func getVideoQuality() -> VideoQuality{
37 |
38 | if let r = ud.objectForKey(UD_VideoQuality){
39 | if let q = VideoQuality(rawValue:r as! String){
40 | return q
41 | }else{
42 | return VideoQuality.High
43 | }
44 | }else{
45 | return VideoQuality.High
46 | }
47 |
48 |
49 | }
50 |
51 | //Delay Settings
52 | func saveDelay(delay:Int){
53 | ud.setInteger(delay, forKey: UD_VideoDelay)
54 | }
55 |
56 | func getDelay() -> Int {
57 |
58 | let r = ud.integerForKey(UD_VideoDelay)
59 | if r > 0{
60 | return r
61 | }else{
62 | return 5
63 | }
64 | }
65 |
66 | //CameraFlip
67 | func saveCameraFlip(flip:CameraDevice){
68 | ud.setObject(flip.rawValue, forKey: UD_CameraFlip)
69 | }
70 |
71 | func getCameraFlip() -> CameraDevice {
72 | if let s = ud.objectForKey(UD_CameraFlip){
73 | if let r = CameraDevice(rawValue:s as! String) {
74 | return r
75 | }else{
76 | return CameraDevice.Front
77 | }
78 | }else{
79 | return CameraDevice.Front
80 | }
81 | }
82 |
83 | //Standard Name
84 | func saveStandardName(name:String){
85 | ud.setObject(name, forKey: UD_StandardName)
86 | }
87 |
88 | func getStandardName() -> String {
89 | if let r = ud.objectForKey(UD_StandardName) {
90 | return r as! String
91 | }else{
92 | return ""
93 | }
94 | }
95 |
96 | //Judge name
97 | func saveJudgeName(name:String){
98 | ud.setObject(name, forKey: UD_JudgeName)
99 | }
100 |
101 | func getJudgeName() -> String {
102 | if let r = ud.objectForKey(UD_JudgeName){
103 | return r as! String
104 | }else{
105 | return ""
106 | }
107 | }
108 |
109 |
110 | //Purchaced Video
111 | func savePurchasedVideo(){
112 | ud.setBool(true, forKey: UD_PurchasedVideo)
113 | }
114 |
115 | func getPurchasedVideo() -> Bool {
116 | return ud.boolForKey(UD_PurchasedVideo)
117 | }
118 |
119 | func saveUserVideoMessageOptOut(){
120 | ud.setBool(true, forKey:UD_userVideoMessageOptOut)
121 | let dateValue = NSTimeIntervalSince1970
122 | ud.setDouble(dateValue, forKey: UD_lastTimeUserWasInformed)
123 | }
124 |
125 |
126 | func getUserVideoMessageOptOut() -> Bool{
127 | let now = NSTimeIntervalSince1970
128 | if now - ud.doubleForKey(UD_lastTimeUserWasInformed) > 2592000{
129 | return false
130 | }else{
131 | return true
132 | }
133 | }
134 |
135 |
136 | }
137 |
138 |
139 |
--------------------------------------------------------------------------------
/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "29x29",
5 | "idiom" : "iphone",
6 | "filename" : "Spot Light Icon-1.png",
7 | "scale" : "1x"
8 | },
9 | {
10 | "size" : "29x29",
11 | "idiom" : "iphone",
12 | "filename" : "Spot Light Icon@2x-1.png",
13 | "scale" : "2x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Spot Light Icon@3x-1.png",
19 | "scale" : "3x"
20 | },
21 | {
22 | "size" : "40x40",
23 | "idiom" : "iphone",
24 | "filename" : "Spot Light 7-9@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "40x40",
29 | "idiom" : "iphone",
30 | "filename" : "Spot Light 7-9@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "57x57",
35 | "idiom" : "iphone",
36 | "filename" : "AppIcon 5-6.png",
37 | "scale" : "1x"
38 | },
39 | {
40 | "size" : "57x57",
41 | "idiom" : "iphone",
42 | "filename" : "AppIcon 5-6@2x.png",
43 | "scale" : "2x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "appIcon@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "App Icon 7-9@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "idiom" : "ipad",
59 | "size" : "29x29",
60 | "scale" : "1x"
61 | },
62 | {
63 | "idiom" : "ipad",
64 | "size" : "29x29",
65 | "scale" : "2x"
66 | },
67 | {
68 | "idiom" : "ipad",
69 | "size" : "40x40",
70 | "scale" : "1x"
71 | },
72 | {
73 | "idiom" : "ipad",
74 | "size" : "40x40",
75 | "scale" : "2x"
76 | },
77 | {
78 | "idiom" : "ipad",
79 | "size" : "50x50",
80 | "scale" : "1x"
81 | },
82 | {
83 | "idiom" : "ipad",
84 | "size" : "50x50",
85 | "scale" : "2x"
86 | },
87 | {
88 | "idiom" : "ipad",
89 | "size" : "72x72",
90 | "scale" : "1x"
91 | },
92 | {
93 | "idiom" : "ipad",
94 | "size" : "72x72",
95 | "scale" : "2x"
96 | },
97 | {
98 | "idiom" : "ipad",
99 | "size" : "76x76",
100 | "scale" : "1x"
101 | },
102 | {
103 | "idiom" : "ipad",
104 | "size" : "76x76",
105 | "scale" : "2x"
106 | },
107 | {
108 | "idiom" : "ipad",
109 | "size" : "83.5x83.5",
110 | "scale" : "2x"
111 | },
112 | {
113 | "size" : "24x24",
114 | "idiom" : "watch",
115 | "scale" : "2x",
116 | "role" : "notificationCenter",
117 | "subtype" : "38mm"
118 | },
119 | {
120 | "size" : "27.5x27.5",
121 | "idiom" : "watch",
122 | "scale" : "2x",
123 | "role" : "notificationCenter",
124 | "subtype" : "42mm"
125 | },
126 | {
127 | "size" : "29x29",
128 | "idiom" : "watch",
129 | "filename" : "Spot Light Icon@2x.png",
130 | "role" : "companionSettings",
131 | "scale" : "2x"
132 | },
133 | {
134 | "size" : "29x29",
135 | "idiom" : "watch",
136 | "filename" : "Spot Light Icon@3x.png",
137 | "role" : "companionSettings",
138 | "scale" : "3x"
139 | },
140 | {
141 | "size" : "40x40",
142 | "idiom" : "watch",
143 | "scale" : "2x",
144 | "role" : "appLauncher",
145 | "subtype" : "38mm"
146 | },
147 | {
148 | "size" : "86x86",
149 | "idiom" : "watch",
150 | "scale" : "2x",
151 | "role" : "quickLook",
152 | "subtype" : "38mm"
153 | },
154 | {
155 | "size" : "98x98",
156 | "idiom" : "watch",
157 | "scale" : "2x",
158 | "role" : "quickLook",
159 | "subtype" : "42mm"
160 | }
161 | ],
162 | "info" : {
163 | "version" : 1,
164 | "author" : "xcode"
165 | }
166 | }
--------------------------------------------------------------------------------
/SpotlightTransitionController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SpotlightTransitionController.swift
3 | // Gecco
4 | //
5 | // Created by yukiasai on 2016/01/19.
6 | // Copyright (c) 2016 yukiasai. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | protocol SpotlightTransitionControllerDelegate: class {
12 | func spotlightTransitionWillPresent(controller: SpotlightTransitionController, transitionContext: UIViewControllerContextTransitioning)
13 | func spotlightTransitionWillDismiss(controller: SpotlightTransitionController, transitionContext: UIViewControllerContextTransitioning)
14 | }
15 |
16 | class SpotlightTransitionController: NSObject, UIViewControllerAnimatedTransitioning {
17 | var isPresent = false
18 |
19 | var delegate: SpotlightTransitionControllerDelegate?
20 |
21 | func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
22 | return 0.25
23 | }
24 |
25 | func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
26 | if isPresent {
27 | animateTransitionForPresent(transitionContext)
28 | } else {
29 | animateTransitionForDismiss(transitionContext)
30 | }
31 | }
32 |
33 | private func animateTransitionForPresent(transitionContext: UIViewControllerContextTransitioning) {
34 | guard let source = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey),
35 | let destination = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey) else {
36 | fatalError()
37 | }
38 | transitionContext.containerView()?.insertSubview(destination.view, aboveSubview: source.view)
39 |
40 | destination.view.alpha = 0
41 |
42 | source.viewWillDisappear(true)
43 | destination.viewWillAppear(true)
44 |
45 | let duration = transitionDuration(transitionContext)
46 | CATransaction.begin()
47 | CATransaction.setCompletionBlock {
48 | transitionContext.completeTransition(true)
49 | }
50 | { // In transation
51 | UIView.animateWithDuration(duration, delay: 0, options: .CurveEaseInOut,
52 | animations: {
53 | destination.view.alpha = 1.0
54 | },
55 | completion: { _ in
56 | destination.viewDidAppear(true)
57 | source.viewDidDisappear(true)
58 | }
59 | )
60 | delegate?.spotlightTransitionWillPresent(self, transitionContext: transitionContext)
61 | }()
62 | CATransaction.commit()
63 | }
64 |
65 | private func animateTransitionForDismiss(transitionContext: UIViewControllerContextTransitioning) {
66 | guard let source = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey),
67 | let destination = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey) else {
68 | fatalError()
69 | }
70 |
71 | source.viewWillDisappear(true)
72 | destination.viewWillAppear(true)
73 |
74 | let duration = transitionDuration(transitionContext)
75 |
76 | CATransaction.begin()
77 | CATransaction.setCompletionBlock {
78 | transitionContext.completeTransition(true)
79 | }
80 | { // In transation
81 | UIView.animateWithDuration(duration, delay: 0, options: .CurveEaseInOut,
82 | animations: {
83 | source.view.alpha = 0.0
84 | },
85 | completion: { _ in
86 | destination.viewDidAppear(true)
87 | source.viewDidDisappear(true)
88 | }
89 | )
90 | delegate?.spotlightTransitionWillDismiss(self, transitionContext: transitionContext)
91 | }()
92 | CATransaction.commit()
93 | }
94 |
95 | func animationEnded(transitionCompleted: Bool) {
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/Wodeo 2/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/SettingsViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SettingsViewController.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 21/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class SettingsViewController: UIViewController,ViewCanceling {
12 |
13 | var wod:WOD!
14 | var delegate:ViewCanceling!
15 |
16 | @IBOutlet weak var minuteLabel:UILabel!
17 | @IBOutlet weak var secondLabel:UILabel!
18 | @IBOutlet weak var minuteStepper:UIStepper!
19 | @IBOutlet weak var secondStepper:UIStepper!
20 |
21 | var minStepperPV:Double = 0.0
22 | var secStepperPV:Double = 0.0
23 |
24 |
25 | @IBAction func minuteStepperChanged(sender:UIStepper){
26 |
27 |
28 | if shouldWarnUser(){
29 | if sender.value > minStepperPV {
30 | sender.value -= 1
31 | }
32 | warnUser()
33 | }else{
34 | minuteLabel.text = "\(Int(minuteStepper.value))"
35 | }
36 | }
37 |
38 | @IBAction func secondStepperChanged(sender:UIStepper){
39 | if shouldWarnUser(){
40 | if sender.value > secStepperPV{
41 | sender.value -= 1
42 | }
43 | warnUser()
44 | }else{
45 | secondLabel.text = "\(Int(secondStepper.value))"
46 | }
47 | }
48 |
49 | func shouldWarnUser() -> Bool{
50 |
51 | let total = (minuteStepper.value * 60) + (secondStepper.value)
52 |
53 | if !UserSettings.sharedInstance.getPurchasedVideo() && total > wod.totalVideoSecondsAllowed {
54 | return true
55 | }else{
56 | return false
57 | }
58 | }
59 |
60 | func warnUser(){
61 | let infoView = UIAlertController(title: "5 Minute Limit", message: "If you like our app, unlock more minutes and help support future great features, with the 'Unlimited Video' purchase. (It's as cheap as they would allow us!)", preferredStyle: UIAlertControllerStyle.Alert)
62 | infoView.view.tintColor = UIColor(red: 240/255.0, green: 178/255.0, blue: 71/255.0, alpha: 1.0)
63 |
64 | let ok = UIAlertAction(title:"Lets Go!", style: UIAlertActionStyle.Default, handler:{
65 | (alert:UIAlertAction!) -> Void in
66 |
67 | let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("WodeoStore") as! WodeoProductPage
68 | viewController.delegate = self
69 | self.presentViewController(viewController, animated: true, completion: nil)
70 |
71 |
72 | })
73 |
74 | let no = UIAlertAction(title: "No thanks..", style: UIAlertActionStyle.Cancel, handler:{ (alert:UIAlertAction!) -> Void in
75 | })
76 |
77 | infoView.addAction(ok)
78 | infoView.addAction(no)
79 |
80 | presentViewController(infoView, animated:true, completion:nil)
81 | }
82 |
83 | @IBAction func done(sender:UIButton) {
84 | performSegueWithIdentifier("goToCamera", sender: nil)
85 | }
86 |
87 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
88 |
89 | if segue.identifier == "goToCamera" {
90 | let vc = segue.destinationViewController as! VideoViewController
91 | let cWOD = wod as! CountDownWOD
92 | cWOD.setCountDownTime(countDownTime())
93 | vc.workout = cWOD
94 | vc.delegate = self
95 | }
96 |
97 | }
98 |
99 | @IBAction func cancel(sender:UIButton) {
100 | delegate.viewDidCancel()
101 | }
102 |
103 | func countDownTime() -> Int {
104 | return Int((minuteStepper.value * 60) + (secondStepper.value))
105 | }
106 |
107 | override func viewDidLoad() {
108 | super.viewDidLoad()
109 |
110 | // Do any additional setup after loading the view.
111 | }
112 |
113 | override func didReceiveMemoryWarning() {
114 | super.didReceiveMemoryWarning()
115 | // Dispose of any resources that can be recreated.
116 | }
117 |
118 | override func prefersStatusBarHidden() -> Bool {
119 | return true
120 | }
121 |
122 | func viewDidCancel() {
123 | dismissViewControllerAnimated(true, completion: nil)
124 | }
125 |
126 | }
127 |
--------------------------------------------------------------------------------
/TabataWOD.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TabataWOD.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 27/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | protocol TabataWOD_Delegate {
12 | func tabataWODDidMoveToRoundWithName(name:String)
13 | func tabataWODDidMoveToRestPeriod()
14 | func tabataWODDidMoveOutOfRestPeriod()
15 | }
16 |
17 | class TabataWOD:WOD,StopWatchDelegate {
18 |
19 |
20 | var rounds = [PresetRound]()
21 | var stopWatch:StopWatch!
22 | var repStopWatch:StopWatch!
23 | var roundCounter:Int = 0
24 | var loopCounter:Int = 1
25 | var loops:Int = 1
26 | var restPeriodBetweenRounds:Int = 0
27 | var isResting = false
28 |
29 | var tabataDelegate:TabataWOD_Delegate?
30 |
31 | override init(with name: String) {
32 | stopWatch = StopWatch(type: StopWatchType.CountDown)
33 | repStopWatch = StopWatch(type: StopWatchType.CountUp)
34 | super.init(with: name)
35 | }
36 |
37 | func startWorkOut(){
38 | repStopWatch.start()
39 | stopWatch.delegate = self
40 | nextInterval()
41 | }
42 |
43 | func stopWorkOut() {
44 | repStopWatch.stop()
45 | stopWatch.stop()
46 | }
47 |
48 | func addRep() -> String {
49 | //Get current round
50 | let round = rounds[roundCounter - 1]
51 | round.reps.append(Rep(startTime: repStopWatch.currentDuration, repNumber: round.reps.count + 1))
52 |
53 | return "Rep: \(round.reps.count)"
54 | }
55 |
56 |
57 | private func nextInterval(){
58 |
59 | stopWatch.stop()
60 |
61 | if restPeriodBetweenRounds > 0 && !isResting && roundCounter > 0 && roundCounter < rounds.count {
62 | isResting = true
63 | stopWatch.countDownFromSeconds = restPeriodBetweenRounds
64 | stopWatch.start()
65 |
66 | if let td = tabataDelegate {
67 | td.tabataWODDidMoveToRestPeriod()
68 | }else{
69 | print("Delagate not set")
70 | }
71 | return
72 | }
73 |
74 | if roundCounter < rounds.count {
75 |
76 | isResting = false
77 |
78 | if let it = tabataDelegate {
79 | it.tabataWODDidMoveToRoundWithName("Loop \(rounds[roundCounter].loopNumber) Round \(roundCounter + 1) of \(rounds.count)")
80 | }
81 |
82 |
83 | stopWatch.countDownFromSeconds = rounds[roundCounter].presetDuration
84 | roundCounter += 1
85 |
86 | if let td = tabataDelegate {
87 | td.tabataWODDidMoveOutOfRestPeriod()
88 | }else{
89 | print("Delegate not set")
90 | }
91 |
92 | stopWatch.start()
93 |
94 | }else{
95 | delegate.workOutDidEnd()
96 | }
97 | }
98 |
99 | func totalWorkOutSeconds() -> Int {
100 | var retTotal = 0
101 | for r in rounds {
102 | retTotal += r.presetDuration
103 | }
104 |
105 | //Add on the rest period
106 | let restPeriod = (rounds.count - 1) * restPeriodBetweenRounds
107 | retTotal += restPeriod
108 |
109 | return retTotal
110 | }
111 |
112 | //Stop watch delegate
113 | func stopWatchDidCountDown(t: TimeInterval) {
114 |
115 | nextInterval()
116 | }
117 |
118 | func stopWatchDidUpdateWithTimeInterval(t: TimeInterval) {
119 | delegate.wodStopWatchDidUpdateToValue(t.intervalString(false))
120 | }
121 |
122 | func stringForRound(index:Int,andRep:Int) -> String {
123 |
124 | let loopNumber = rounds[index].loopNumber
125 | let maxNumberOfLoops = rounds.last!.loopNumber
126 |
127 | let roundNumber = rounds[index].roundNumber
128 | let maxNumberOfRounds = rounds.count
129 |
130 | let repNumber = andRep + 1
131 | let maxReps = rounds[index].reps.count
132 |
133 | return "Loop \(loopNumber) of \(maxNumberOfLoops) | Round \(roundNumber) of \(maxNumberOfRounds) | Rep \(repNumber) of \(maxReps)"
134 |
135 |
136 |
137 |
138 | }
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 | }
--------------------------------------------------------------------------------
/SpotlightView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SpotlightView.swift
3 | // Gecco
4 | //
5 | // Created by yukiasai on 2016/01/16.
6 | // Copyright (c) 2016 yukiasai. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | public class SpotlightView: UIView {
12 | public static let defaultAnimateDuration: NSTimeInterval = 0.25
13 |
14 | private lazy var maskLayer: CAShapeLayer = {
15 | let layer = CAShapeLayer()
16 | layer.fillRule = kCAFillRuleEvenOdd
17 | layer.fillColor = UIColor.blackColor().CGColor
18 | return layer
19 | }()
20 |
21 | var spotlight: SpotlightType?
22 |
23 | public override init(frame: CGRect) {
24 | super.init(frame: frame)
25 | commonInit()
26 | }
27 |
28 | public required init?(coder aDecoder: NSCoder) {
29 | super.init(coder: aDecoder)
30 | commonInit()
31 | }
32 |
33 | private func commonInit() {
34 | layer.mask = maskLayer
35 | }
36 |
37 | public override func layoutSubviews() {
38 | super.layoutSubviews()
39 |
40 | maskLayer.frame = frame
41 | }
42 |
43 | public func appear(spotlight: SpotlightType, duration: NSTimeInterval = SpotlightView.defaultAnimateDuration) {
44 | maskLayer.addAnimation(appearAnimation(duration, spotlight: spotlight), forKey: nil)
45 | self.spotlight = spotlight
46 | }
47 |
48 | public func disappear(duration: NSTimeInterval = SpotlightView.defaultAnimateDuration) {
49 | maskLayer.addAnimation(disappearAnimation(duration), forKey: nil)
50 | }
51 |
52 | public func move(toSpotlight: SpotlightType, duration: NSTimeInterval = SpotlightView.defaultAnimateDuration, moveType: SpotlightMoveType = .Direct) {
53 | switch moveType {
54 | case .Direct:
55 | moveDirect(toSpotlight, duration: duration)
56 | case .Disappear:
57 | moveDisappear(toSpotlight, duration: duration)
58 | }
59 | }
60 | }
61 |
62 | extension SpotlightView {
63 | private func moveDirect(toSpotlight: SpotlightType, duration: NSTimeInterval = SpotlightView.defaultAnimateDuration) {
64 | maskLayer.addAnimation(moveAnimation(duration, toSpotlight: toSpotlight), forKey: nil)
65 | spotlight = toSpotlight
66 | }
67 |
68 | private func moveDisappear(toSpotlight: SpotlightType, duration: NSTimeInterval = SpotlightView.defaultAnimateDuration) {
69 | CATransaction.begin()
70 | CATransaction.setCompletionBlock {
71 | self.appear(toSpotlight, duration: duration)
72 | self.spotlight = toSpotlight
73 | }
74 | disappear(duration)
75 | CATransaction.commit()
76 | }
77 |
78 | private func maskPath(path: UIBezierPath) -> UIBezierPath {
79 | return [path].reduce(UIBezierPath(rect: frame)) {
80 | $0.appendPath($1)
81 | return $0
82 | }
83 | }
84 |
85 | private func appearAnimation(duration: NSTimeInterval, spotlight: SpotlightType) -> CAAnimation {
86 | let beginPath = maskPath(spotlight.infinitesmalPath)
87 | let endPath = maskPath(spotlight.path)
88 | return pathAnimation(duration, beginPath:beginPath, endPath: endPath)
89 | }
90 |
91 | private func disappearAnimation(duration: NSTimeInterval) -> CAAnimation {
92 | let endPath = maskPath(spotlight!.infinitesmalPath)
93 | return pathAnimation(duration, beginPath:nil, endPath: endPath)
94 | }
95 |
96 | private func moveAnimation(duration: NSTimeInterval, toSpotlight: SpotlightType) -> CAAnimation {
97 | let endPath = maskPath(toSpotlight.path)
98 | return pathAnimation(duration, beginPath:nil, endPath: endPath)
99 | }
100 |
101 | private func pathAnimation(duration: NSTimeInterval, beginPath: UIBezierPath?, endPath: UIBezierPath) -> CAAnimation {
102 | let animation = CABasicAnimation(keyPath: "path")
103 | animation.duration = duration
104 | animation.timingFunction = CAMediaTimingFunction(controlPoints: 0.66, 0, 0.33, 1)
105 | if let path = beginPath {
106 | animation.fromValue = path.CGPath
107 | }
108 | animation.toValue = endPath.CGPath
109 | animation.removedOnCompletion = false
110 | animation.fillMode = kCAFillModeForwards
111 | return animation
112 | }
113 | }
114 |
115 | public enum SpotlightMoveType {
116 | case Direct
117 | case Disappear
118 | }
--------------------------------------------------------------------------------
/TiledView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TiledView.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 26/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @IBDesignable
12 |
13 | class TiledView: UIView {
14 |
15 |
16 | var backgroundLayer:CALayer!
17 | var topL:CATextLayer!
18 | var leftL:CATextLayer!
19 | var rightL:CATextLayer!
20 | var bottomL:CATextLayer!
21 |
22 | var top:Bool = false {
23 | didSet{
24 | topL.hidden = top ? false:true
25 | bottomL.hidden = top ? true:false
26 | }
27 | }
28 |
29 | var instruction:String = "" {
30 | didSet{
31 | topL.string = instruction
32 | leftL.string = instruction
33 | rightL.string = instruction
34 | bottomL.string = instruction
35 | }
36 | }
37 |
38 |
39 |
40 | override init(frame: CGRect) {
41 | super.init(frame: frame)
42 |
43 | backgroundLayer = CALayer()
44 | backgroundLayer.frame = frame
45 | backgroundLayer.cornerRadius = 20.0
46 | backgroundLayer.masksToBounds = true
47 | backgroundLayer.backgroundColor = UIColor(red: 54/255.0, green: 54/255.0, blue: 54/255.0, alpha: 0.2).CGColor
48 | backgroundLayer.borderWidth = 1.0
49 | backgroundLayer.borderColor = UIColor(red: 243/255.0, green: 80/255.0, blue: 47/255.0, alpha:0.4).CGColor
50 | layer.addSublayer(backgroundLayer)
51 |
52 | topL = CATextLayer()
53 | leftL = CATextLayer()
54 | rightL = CATextLayer()
55 | bottomL = CATextLayer()
56 |
57 | topL.alignmentMode = kCAAlignmentCenter
58 | leftL.alignmentMode = kCAAlignmentCenter
59 | rightL.alignmentMode = kCAAlignmentCenter
60 | bottomL.alignmentMode = kCAAlignmentCenter
61 |
62 | let op:Float = 0.5
63 | topL.opacity = op
64 | leftL.opacity = op
65 | rightL.opacity = op
66 | bottomL.opacity = op
67 |
68 | topL.font = "Folio-BoldCondensed"
69 | leftL.font = "Folio-BoldCondensed"
70 | rightL.font = "Folio-BoldCondensed"
71 | bottomL.font = "Folio-BoldCondensed"
72 |
73 | let fontSize:CGFloat = 25.0
74 | topL.fontSize = fontSize
75 | leftL.fontSize = fontSize
76 | rightL.fontSize = fontSize
77 | bottomL.fontSize = fontSize
78 |
79 |
80 |
81 | topL.foregroundColor = UIColor.yellowColor().CGColor
82 | leftL.foregroundColor = UIColor.yellowColor().CGColor
83 | rightL.foregroundColor = UIColor.yellowColor().CGColor
84 | bottomL.foregroundColor = UIColor.yellowColor().CGColor
85 |
86 |
87 | leftL.transform = CATransform3DRotate(leftL.transform, CGFloat(-M_PI_2), 0.0, 0.0, 1.0)
88 | rightL.transform = CATransform3DRotate(rightL.transform, CGFloat(M_PI_2), 0.0, 0.0, 1.0)
89 |
90 | topL.backgroundColor = UIColor.clearColor().CGColor
91 | leftL.backgroundColor = UIColor.clearColor().CGColor
92 | rightL.backgroundColor = UIColor.clearColor().CGColor
93 | bottomL.backgroundColor = UIColor.clearColor().CGColor
94 |
95 |
96 | layer.addSublayer(topL)
97 | layer.addSublayer(bottomL)
98 | layer.addSublayer(leftL)
99 | layer.addSublayer(rightL)
100 |
101 |
102 |
103 | }
104 |
105 | override func layoutSubviews() {
106 | super.layoutSubviews()
107 | backgroundLayer.frame = bounds
108 |
109 |
110 | let lh:CGFloat = 50.0
111 | let vm = bounds.size.width / 2.0
112 | let lo:CGFloat = lh / 2.0
113 |
114 | topL.bounds = CGRectMake(0.0, 0.0, bounds.size.width, lh)
115 | leftL.bounds = CGRectMake(0.0, 0.0, bounds.size.width, lh)
116 | rightL.bounds = CGRectMake(0.0, 0.0, bounds.size.width, lh)
117 | bottomL.bounds = CGRectMake(0.0, 0.0, bounds.size.width, lh)
118 |
119 | topL.position = CGPoint(x: vm, y:lo)
120 | leftL.position = CGPoint(x:lo, y: bounds.size.height / 2.0)
121 | rightL.position = CGPoint(x: bounds.size.width - lo, y: bounds.size.height / 2.0)
122 | bottomL.position = CGPoint(x:vm, y:bounds.size.height - lo)
123 |
124 |
125 | }
126 |
127 | required init?(coder aDecoder: NSCoder) {
128 | super.init(coder: aDecoder)
129 | }
130 |
131 |
132 | }
133 |
--------------------------------------------------------------------------------
/CustomPhotoLibrary.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CustomPhotoLibrary.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 20/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 | import UIKit
12 | import AVFoundation
13 | import AssetsLibrary
14 | import Photos
15 |
16 |
17 |
18 | class CustomPhotoAlbum: NSObject {
19 |
20 | static let albumName = "Wodeo 2"
21 |
22 | static let sharedInstance = CustomPhotoAlbum()
23 |
24 |
25 | var assetCollection: PHAssetCollection!
26 | override init() {
27 | super.init()
28 |
29 | if let assetCollection = fetchAssetCollectionForAlbum() {
30 | self.assetCollection = assetCollection
31 | return
32 | }
33 |
34 | if PHPhotoLibrary.authorizationStatus() != PHAuthorizationStatus.Authorized {
35 | PHPhotoLibrary.requestAuthorization({ (status: PHAuthorizationStatus) -> Void in
36 | status
37 | })
38 | }
39 |
40 | if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.Authorized {
41 | self.createAlbum()
42 | } else {
43 | PHPhotoLibrary.requestAuthorization(requestAuthorizationHandler)
44 | }
45 | }
46 |
47 | func requestAuthorizationHandler(status: PHAuthorizationStatus) {
48 | if PHPhotoLibrary.authorizationStatus() == PHAuthorizationStatus.Authorized {
49 | // ideally this ensures the creation of the photo album even if authorization wasn't prompted till after init was done
50 | print("trying again to create the album")
51 | self.createAlbum()
52 | } else {
53 | print("should really prompt the user to let them know it's failed")
54 | }
55 | }
56 |
57 | func createAlbum() {
58 | PHPhotoLibrary.sharedPhotoLibrary().performChanges({
59 | PHAssetCollectionChangeRequest.creationRequestForAssetCollectionWithTitle(CustomPhotoAlbum.albumName) // create an asset collection with the album name
60 | }) { success, error in
61 | if success {
62 | self.assetCollection = self.fetchAssetCollectionForAlbum()
63 | } else {
64 | print("error \(error)")
65 | }
66 | }
67 | }
68 |
69 | func fetchAssetCollectionForAlbum() -> PHAssetCollection! {
70 | let fetchOptions = PHFetchOptions()
71 | fetchOptions.predicate = NSPredicate(format: "title = %@", CustomPhotoAlbum.albumName)
72 | let collection = PHAssetCollection.fetchAssetCollectionsWithType(.Album, subtype: .Any, options: fetchOptions)
73 |
74 | if let _: AnyObject = collection.firstObject {
75 | return collection.firstObject as! PHAssetCollection
76 | }
77 | return nil
78 | }
79 |
80 | func saveImage(image: UIImage, metadata: NSDictionary) {
81 | if assetCollection == nil {
82 | return // if there was an error upstream, skip the save
83 | }
84 |
85 | PHPhotoLibrary.sharedPhotoLibrary().performChanges({
86 | let assetChangeRequest = PHAssetChangeRequest.creationRequestForAssetFromImage(image)
87 | let assetPlaceHolder = assetChangeRequest.placeholderForCreatedAsset
88 | assetChangeRequest.creationDate = (metadata["DatePhotoTaken"] as! NSDate)
89 | let albumChangeRequest = PHAssetCollectionChangeRequest(forAssetCollection: self.assetCollection)
90 | albumChangeRequest!.addAssets([assetPlaceHolder!])
91 | }, completionHandler: nil)
92 | }
93 |
94 | func saveVideo(videoURL:NSURL, metadata: NSDictionary){
95 | if assetCollection == nil {
96 | return
97 | }
98 |
99 | PHPhotoLibrary.sharedPhotoLibrary().performChanges({
100 | let assetChangeRequest = PHAssetChangeRequest.creationRequestForAssetFromVideoAtFileURL(videoURL)
101 | let assetPlaceHolder = assetChangeRequest!.placeholderForCreatedAsset
102 | assetChangeRequest!.creationDate = (metadata["DateVideoTaken"] as! NSDate)
103 | let albumChangeRequest = PHAssetCollectionChangeRequest(forAssetCollection: self.assetCollection)
104 | albumChangeRequest!.addAssets([assetPlaceHolder!])
105 | }, completionHandler:{(finished:Bool,error:NSError?) in
106 |
107 | if let err = error {
108 | print("\(err.localizedDescription)")
109 | }
110 |
111 | })
112 | }
113 |
114 | }
115 |
--------------------------------------------------------------------------------
/SpotlightViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SpotlightViewController.swift
3 | // Gecco
4 | //
5 | // Created by yukiasai on 2016/01/15.
6 | // Copyright (c) 2016 yukiasai. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @objc public protocol SpotlightViewControllerDelegate: class {
12 | optional func spotlightViewControllerWillPresent(viewController: SpotlightViewController, animated: Bool)
13 | optional func spotlightViewControllerWillDismiss(viewController: SpotlightViewController, animated: Bool)
14 | optional func spotlightViewControllerTapped(viewController: SpotlightViewController, isInsideSpotlight: Bool)
15 | }
16 |
17 | public class SpotlightViewController: UIViewController {
18 |
19 | public var delegate: SpotlightViewControllerDelegate?
20 |
21 | private lazy var transitionController: SpotlightTransitionController = {
22 | let controller = SpotlightTransitionController()
23 | controller.delegate = self
24 | return controller
25 | }()
26 |
27 | public let spotlightView = SpotlightView()
28 | public let contentView = UIView()
29 |
30 | public var alpha: CGFloat = 0.5
31 |
32 | override public init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
33 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
34 | commonInit()
35 | }
36 |
37 | required public init?(coder aDecoder: NSCoder) {
38 | super.init(coder: aDecoder)
39 | commonInit()
40 | }
41 |
42 | private func commonInit() {
43 | modalPresentationStyle = .OverCurrentContext
44 | transitioningDelegate = self
45 | }
46 |
47 | public override func viewDidLoad() {
48 | super.viewDidLoad()
49 |
50 | setupSpotlightView(alpha)
51 | setupContentView()
52 | setupTapGesture()
53 |
54 | view.backgroundColor = UIColor.clearColor()
55 | }
56 |
57 | public override func viewDidAppear(animated: Bool) {
58 | super.viewDidAppear(animated)
59 | }
60 |
61 | private func setupSpotlightView(alpha: CGFloat) {
62 | spotlightView.frame = view.bounds
63 | spotlightView.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: alpha)
64 | spotlightView.userInteractionEnabled = false
65 | view.insertSubview(spotlightView, atIndex: 0)
66 | view.addConstraints([NSLayoutAttribute.Top, .Bottom, .Left, .Right].map {
67 | NSLayoutConstraint(item: view, attribute: $0, relatedBy: .Equal, toItem: spotlightView, attribute: $0, multiplier: 1, constant: 0)
68 | })
69 | }
70 |
71 | private func setupContentView() {
72 | contentView.frame = view.bounds
73 | contentView.backgroundColor = UIColor.clearColor()
74 | view.addSubview(contentView)
75 | view.addConstraints([NSLayoutAttribute.Top, .Bottom, .Left, .Right].map {
76 | NSLayoutConstraint(item: view, attribute: $0, relatedBy: .Equal, toItem: contentView, attribute: $0, multiplier: 1, constant: 0)
77 | })
78 | }
79 |
80 | private func setupTapGesture() {
81 | let gesture = UITapGestureRecognizer(target: self, action: #selector(SpotlightViewController.viewTapped(_:)));
82 | view.addGestureRecognizer(gesture)
83 | }
84 | }
85 |
86 | extension SpotlightViewController {
87 | func viewTapped(gesture: UITapGestureRecognizer) {
88 | let touchPoint = gesture.locationInView(spotlightView)
89 | let isInside = spotlightView.spotlight?.frame.contains(touchPoint) ?? false
90 | delegate?.spotlightViewControllerTapped?(self, isInsideSpotlight: isInside)
91 | }
92 | }
93 |
94 | extension SpotlightViewController: SpotlightTransitionControllerDelegate {
95 | func spotlightTransitionWillPresent(controller: SpotlightTransitionController, transitionContext: UIViewControllerContextTransitioning) {
96 | delegate?.spotlightViewControllerWillPresent?(self, animated: transitionContext.isAnimated())
97 | }
98 |
99 | func spotlightTransitionWillDismiss(controller: SpotlightTransitionController, transitionContext: UIViewControllerContextTransitioning) {
100 | delegate?.spotlightViewControllerWillDismiss?(self, animated: transitionContext.isAnimated())
101 | }
102 | }
103 |
104 | extension SpotlightViewController: UIViewControllerTransitioningDelegate {
105 | public func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
106 | transitionController.isPresent = true
107 | return transitionController
108 | }
109 |
110 | public func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
111 | transitionController.isPresent = false
112 | return transitionController
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/WodeoProductPage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WodeoProductPage.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 28/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import StoreKit
11 |
12 | class WodeoProductPage: UIViewController {
13 |
14 |
15 | var delegate:ViewCanceling?
16 |
17 | @IBOutlet weak var tableView:UITableView!
18 | var products = [SKProduct]()
19 | lazy var refreshControl: UIRefreshControl = {
20 | let refreshControl = UIRefreshControl()
21 | refreshControl.tintColor = UIColor.whiteColor()
22 | refreshControl.addTarget(self, action:#selector(WodeoProductPage.reload), forControlEvents: UIControlEvents.ValueChanged)
23 |
24 | return refreshControl
25 | }()
26 |
27 |
28 | deinit {
29 | NSNotificationCenter.defaultCenter().removeObserver(self)
30 | }
31 |
32 |
33 |
34 | // priceFormatter is used to show proper, localized currency
35 | lazy var priceFormatter: NSNumberFormatter = {
36 | let pf = NSNumberFormatter()
37 | pf.formatterBehavior = .Behavior10_4
38 | pf.numberStyle = .CurrencyStyle
39 | return pf
40 | }()
41 |
42 | override func viewDidLoad() {
43 | super.viewDidLoad()
44 |
45 | self.tableView.addSubview(self.refreshControl)
46 | reload()
47 | refreshControl.beginRefreshing()
48 |
49 | // Subscribe to a notification that fires when a product is purchased.
50 | NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(WodeoProductPage.productPurchased(_:)), name: IAPHelperProductPurchasedNotification, object: nil)
51 |
52 | }
53 |
54 | // Fetch the products from iTunes connect, redisplay the table on successful completion
55 | func reload() {
56 | products = []
57 | tableView.reloadData()
58 | WodeoProducts.store.requestProductsWithCompletionHandler { success, products in
59 | if success {
60 | self.products = products
61 | self.tableView.reloadData()
62 | }
63 | self.refreshControl.endRefreshing()
64 | }
65 | }
66 |
67 | // Restore purchases to this device.
68 | @IBAction func restoreTapped(sender: AnyObject) {
69 | WodeoProducts.store.restoreCompletedTransactions()
70 | }
71 |
72 | // Purchase the product
73 | func buyButtonTapped(button: UIButton) {
74 | let product = products[button.tag]
75 | WodeoProducts.store.purchaseProduct(product)
76 | }
77 |
78 | // When a product is purchased, this notification fires, redraw the correct row
79 | func productPurchased(notification: NSNotification) {
80 | let productIdentifier = notification.object as! String
81 | UserSettings.sharedInstance.savePurchasedVideo()
82 | for (index, product) in products.enumerate() {
83 |
84 |
85 |
86 | if product.productIdentifier == productIdentifier {
87 | tableView.reloadRowsAtIndexPaths([NSIndexPath(forRow: index, inSection: 0)], withRowAnimation: .Fade)
88 | break
89 | }
90 | }
91 | }
92 |
93 |
94 | //Admin
95 | override func prefersStatusBarHidden() -> Bool {
96 | return true
97 | }
98 |
99 | @IBAction func didCancel(sender:UIButton){
100 | if let d = delegate{
101 | d.viewDidCancel()
102 | }
103 | }
104 |
105 | }
106 |
107 | extension WodeoProductPage:UITableViewDelegate {
108 |
109 | }
110 |
111 |
112 | extension WodeoProductPage:UITableViewDataSource {
113 |
114 | func numberOfSectionsInTableView(tableView: UITableView) -> Int {
115 | return 1
116 | }
117 |
118 | func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
119 | return products.count
120 | }
121 |
122 | func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
123 | let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
124 |
125 | let product = products[indexPath.row]
126 | cell.textLabel?.text = product.localizedTitle
127 |
128 | if WodeoProducts.store.isProductPurchased(product.productIdentifier) {
129 | cell.accessoryType = .Checkmark
130 | cell.accessoryView = nil
131 | cell.detailTextLabel?.text = ""
132 | }
133 | else if IAPHelper.canMakePayments() {
134 | priceFormatter.locale = product.priceLocale
135 | //cell.detailTextLabel?.text = priceFormatter.stringFromNumber(product.price)
136 |
137 | let button = BorderButton(frame: CGRect(x: 0, y: 0, width: 100.0, height: 37))
138 | button.setTitleColor(view.tintColor, forState: .Normal)
139 | button.setTitle("\(priceFormatter.stringFromNumber(product.price)!)", forState: .Normal)
140 | button.tag = indexPath.row
141 | button.addTarget(self, action: #selector(WodeoProductPage.buyButtonTapped(_:)), forControlEvents: .TouchUpInside)
142 | cell.accessoryType = .None
143 | cell.accessoryView = button
144 | } else {
145 | cell.accessoryType = .None
146 | cell.accessoryView = nil
147 | cell.detailTextLabel?.text = "Not Available"
148 | }
149 | return cell }
150 |
151 | }
152 |
--------------------------------------------------------------------------------
/IntervalOverlay.swift:
--------------------------------------------------------------------------------
1 | //
2 | // IntervalOverlay.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 22/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class IntervalOverlay: OverlayViewController ,WODDelegate,IntervalWOD_Delegate,ResultsControllerDelegate {
12 |
13 | var intervalWOD:IntervalWOD!
14 | @IBOutlet weak var mainRotatorView:RotatorView!
15 | var repCountLabel = TimeTextLabel(fontSize:30.0)
16 | var roundCountLabel = TimeTextLabel(fontSize: 30.0)
17 | //layout vars
18 | let counterLH:CGFloat = 35.0
19 |
20 |
21 |
22 | //View lifecycle
23 | override func viewDidLoad() {
24 | super.viewDidLoad()
25 |
26 |
27 |
28 |
29 | print("Interval layout is loading")
30 |
31 | intervalWOD = workOut as! IntervalWOD
32 | intervalWOD.delegate = self
33 | intervalWOD.intervalDelegate = self
34 | }
35 |
36 | override func viewDidDisappear(animated: Bool) {
37 | super.viewDidDisappear(animated)
38 |
39 | }
40 |
41 |
42 | //Stop watch delegate
43 | override func stopWatchDidCountDown(t: TimeInterval) {
44 | super.stopWatchDidCountDown(t)
45 | intervalWOD.startWorkOut()
46 | addButtonUI()
47 |
48 | }
49 |
50 |
51 | //Create and layout the UI
52 | override func createUI() {
53 | super.createUI()
54 |
55 | }
56 |
57 | func addButtonUI(){
58 |
59 | let addRepButton = TiledView()
60 | addRepButton.frame = CGRectMake(0.0, 0.0, view.frame.size.width, view.frame.size.height)
61 | addRepButton.instruction = "Add Repp"
62 | addRepButton.top = true
63 | addRepButton.addGestureRecognizer(UITapGestureRecognizer(target: self, action:#selector(IntervalOverlay.addRep(_:))))
64 | mainRotatorView.addSubview(addRepButton)
65 |
66 | UIView.animateWithDuration(0.5, animations:{
67 | self.mainRotatorView.alpha = 1.0
68 | })
69 | }
70 |
71 |
72 | override func layOutForPortrait() {
73 | super.layOutForPortrait()
74 | repCountLabel.frame = CGRectMake(2.0, 0.0, fw,counterLH)
75 | roundCountLabel.frame = CGRectMake(2.0, counterLH,fw, counterLH)
76 |
77 |
78 | view.layer.addSublayer(repCountLabel)
79 | view.layer.addSublayer(roundCountLabel)
80 | }
81 |
82 | override func layOutForLandscapeLeft() {
83 | super.layOutForLandscapeLeft()
84 |
85 | let labelWidth = (fh/2) - 5.0
86 |
87 | repCountLabel.frame = CGRectMake(0.0, 0.0,labelWidth, counterLH)
88 | repCountLabel.alignmentMode = kCAAlignmentRight
89 | repCountLabel.position = CGPointMake(counterLH / 2,labelWidth/2)
90 | repCountLabel.transform = CATransform3DRotate(repCountLabel.transform, CGFloat(-M_PI_2), 0.0, 0.0, 1.0)
91 |
92 | roundCountLabel.frame = CGRectMake(0.0, 0.0,labelWidth, counterLH)
93 | roundCountLabel.position = CGPointMake(counterLH / 2,(fh - (labelWidth / 2)) - 5)
94 | roundCountLabel.transform = CATransform3DRotate(roundCountLabel.transform, CGFloat(-M_PI_2), 0.0, 0.0, 1.0)
95 |
96 | view.layer.addSublayer(repCountLabel)
97 | view.layer.addSublayer(roundCountLabel)
98 |
99 |
100 |
101 | }
102 |
103 | override func layOutForLandscapeRight() {
104 | super.layOutForLandscapeRight()
105 |
106 | let labelWidth = (fh/2) - 5.0
107 |
108 | repCountLabel.frame = CGRectMake(0.0, 0.0,labelWidth, counterLH)
109 | repCountLabel.alignmentMode = kCAAlignmentRight
110 | repCountLabel.position = CGPointMake(fw - (counterLH / 2),(fh - (labelWidth / 2)) - 5)
111 | repCountLabel.transform = CATransform3DRotate(repCountLabel.transform, CGFloat(M_PI_2), 0.0, 0.0, 1.0)
112 |
113 | roundCountLabel.frame = CGRectMake(0.0, 0.0,labelWidth, counterLH)
114 | roundCountLabel.position = CGPointMake(fw - (counterLH / 2),labelWidth/2)
115 | roundCountLabel.transform = CATransform3DRotate(roundCountLabel.transform, CGFloat(M_PI_2), 0.0, 0.0, 1.0)
116 |
117 | view.layer.addSublayer(repCountLabel)
118 | view.layer.addSublayer(roundCountLabel)
119 |
120 |
121 | mainRotatorView.transform = CGAffineTransformMakeRotation(CGFloat(M_PI_2 * 2))
122 |
123 |
124 | }
125 |
126 |
127 |
128 | //Wod delegate
129 | func wodStopWatchDidUpdateToValue(timeValue: String) {
130 | countDownLabel.string = timeValue
131 | }
132 |
133 | func workOutDidEnd() {
134 | intervalWOD.stopWorkOut()
135 | videoManager!.stopRecordingVideo { (videoURL, error) -> Void in
136 | }
137 | }
138 |
139 | //Interval WOD delegate
140 | func intervalWODDidMoveToRoundWithName(name: String) {
141 | repCountLabel.string = ""
142 | roundCountLabel.string = name
143 | }
144 |
145 | //Buttons
146 | func addRep(sender:UITapGestureRecognizer){
147 | if sender.state == .Ended{
148 | repCountLabel.string = intervalWOD.addRep()
149 | }
150 |
151 | }
152 |
153 | override func cancelButtonPressed(){
154 | super.cancelButtonPressed()
155 | intervalWOD.stopWorkOut()
156 |
157 | }
158 |
159 | override func cameraManagerDidGenerateVideoAsset(url: NSURL) {
160 |
161 | super.cameraManagerDidGenerateVideoAsset(url)
162 | //Add overlay and set it as the video manager
163 | let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("IntervalResults") as! IntervalResultsVC
164 | presentViewController(viewController, animated: true, completion: nil)
165 | viewController.intervalWOD = intervalWOD
166 | viewController.videoToMakeURL = url
167 | viewController.delegate = self
168 | }
169 |
170 | func resultsDidCancel() {
171 | dismissViewControllerAnimated(true, completion:nil)
172 | }
173 |
174 | override func saveButtonPressed(){
175 | countDownStopWatch.stop()
176 | let cWorkOut = workOut as! IntervalWOD
177 | cWorkOut.stopWorkOut()
178 | videoManager!.stopRecordingVideo { (videoURL, error) -> Void in
179 |
180 | }
181 | }
182 |
183 |
184 | }
185 |
--------------------------------------------------------------------------------
/VideoMaker.swift:
--------------------------------------------------------------------------------
1 | //
2 | // VideoMaker.swift
3 | // Video Testing
4 | //
5 | // Created by Gareth Long on 06/02/2016.
6 | // Copyright © 2016 gazlongapps. All rights reserved.
7 | //
8 |
9 | import AVFoundation
10 | import AssetsLibrary
11 | import UIKit
12 | import Photos
13 |
14 |
15 | class VideoMaker: UIViewController {
16 |
17 | var videoToMakeURL:NSURL?
18 | var videoToMake:AVAsset?
19 | var exporter:AVAssetExportSession!
20 |
21 | func videoOutput() {
22 |
23 | videoToMake = AVAsset(URL: videoToMakeURL!)
24 |
25 | if (videoToMake == nil) {
26 |
27 | return
28 | }
29 |
30 | //This holds the different tracks of the video like audio and the layers
31 | let mixComposition = AVMutableComposition()
32 |
33 | let videoTrack = mixComposition.addMutableTrackWithMediaType(AVMediaTypeVideo, preferredTrackID: Int32(kCMPersistentTrackID_Invalid))
34 |
35 | do{
36 | try videoTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero,videoToMake!.duration), ofTrack: videoToMake!.tracksWithMediaType(AVMediaTypeVideo)[0], atTime: kCMTimeZero)
37 | }catch let error as NSError{
38 | print("Error inserting time range on video track \(error.localizedDescription)")
39 | return
40 | }catch{
41 | print("An unknown error occured")
42 | }
43 |
44 |
45 | //Add the audio
46 | let audioTrack1 = mixComposition.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: Int32(kCMPersistentTrackID_Invalid))
47 |
48 | do{
49 | try audioTrack1.insertTimeRange(CMTimeRangeMake(kCMTimeZero,videoToMake!.duration), ofTrack: videoToMake!.tracksWithMediaType(AVMediaTypeAudio)[0], atTime: kCMTimeZero)
50 | }catch let error as NSError{
51 | print("Error inserting time range on video track \(error.localizedDescription)")
52 | return
53 | }catch{
54 | print("An unknown error occured")
55 | }
56 |
57 |
58 | //Make the instructions for the other layers
59 | let mainInstrucation = AVMutableVideoCompositionInstruction()
60 | mainInstrucation.timeRange = CMTimeRangeMake(kCMTimeZero, videoToMake!.duration)
61 |
62 | //Create the layer instructions
63 | let videoLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videoTrack)
64 | let videoAssetTrack = videoToMake!.tracksWithMediaType(AVMediaTypeVideo)[0]
65 |
66 |
67 | let assetInfo = orientationFromTransform(videoAssetTrack.preferredTransform)
68 | // sort size it in respect to the video orientation.
69 |
70 | videoLayerInstruction.setTransform(videoAssetTrack.preferredTransform, atTime: kCMTimeZero)
71 | videoLayerInstruction.setOpacity(0.0, atTime:videoToMake!.duration)
72 |
73 | //Add the instructions
74 | mainInstrucation.layerInstructions = [videoLayerInstruction]
75 | let mainCompositionInst = AVMutableVideoComposition()
76 |
77 | var naturalSize:CGSize
78 | if assetInfo.isPortrait {
79 | naturalSize = CGSizeMake(videoAssetTrack.naturalSize.height, videoAssetTrack.naturalSize.width);
80 | }else{
81 | naturalSize = videoAssetTrack.naturalSize
82 | }
83 |
84 | let renderWidth = naturalSize.width
85 | let renderHeight = naturalSize.height
86 |
87 | mainCompositionInst.renderSize = CGSizeMake(renderWidth, renderHeight)
88 | mainCompositionInst.instructions = [mainInstrucation]
89 | mainCompositionInst.frameDuration = CMTimeMake(1, 30);
90 |
91 |
92 |
93 |
94 | //So now the main composition has been created add the video affects
95 | applyVideoEffectsToComposition(mainCompositionInst, size: naturalSize)
96 |
97 | let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory,.UserDomainMask, true)
98 | let documentsDirectory = paths[0]
99 | let random = Int(arc4random_uniform(1000))
100 | let url = NSURL(fileURLWithPath:documentsDirectory).URLByAppendingPathComponent("FinalVideo\(random).mov")
101 |
102 | //Create the exporter
103 | exporter = AVAssetExportSession(asset: mixComposition, presetName:AVAssetExportPresetHighestQuality)
104 |
105 | exporter.outputURL = url
106 | exporter.outputFileType = AVFileTypeMPEG4
107 | exporter.shouldOptimizeForNetworkUse = true
108 | exporter.videoComposition = mainCompositionInst
109 |
110 |
111 | //Perform the export
112 | exporter!.exportAsynchronouslyWithCompletionHandler() {
113 | dispatch_async(dispatch_get_main_queue(), { () -> Void in
114 | self.exportDidFinish(self.exporter!)
115 | })
116 | }
117 | }
118 |
119 | func orientationFromTransform(transform: CGAffineTransform) -> (orientation: UIImageOrientation, isPortrait: Bool) {
120 | var assetOrientation = UIImageOrientation.Up
121 | var isPortrait = false
122 | if transform.a == 0 && transform.b == 1.0 && transform.c == -1.0 && transform.d == 0 {
123 | assetOrientation = .Right
124 | isPortrait = true
125 | } else if transform.a == 0 && transform.b == -1.0 && transform.c == 1.0 && transform.d == 0 {
126 | assetOrientation = .Left
127 | isPortrait = true
128 | } else if transform.a == 1.0 && transform.b == 0 && transform.c == 0 && transform.d == 1.0 {
129 | assetOrientation = .Up
130 | } else if transform.a == -1.0 && transform.b == 0 && transform.c == 0 && transform.d == -1.0 {
131 | assetOrientation = .Down
132 | }
133 | return (assetOrientation, isPortrait)
134 | }
135 |
136 |
137 | func exportDidFinish(session: AVAssetExportSession) {
138 | switch session.status {
139 | case AVAssetExportSessionStatus.Failed:
140 | print("Failed \(session.error)")
141 | case AVAssetExportSessionStatus.Cancelled:
142 | print("Cancelled \(session.error)")
143 | default:
144 | print("complete")
145 | print("\(session.outputURL)")
146 | saveFileAtURLToPhotoLibrary(session.outputURL!)
147 | }
148 |
149 | }
150 |
151 | func saveFileAtURLToPhotoLibrary(url:NSURL){
152 | let lib = CustomPhotoAlbum()
153 | lib.saveVideo(url,metadata: ["DateVideoTaken":NSDate()])
154 | }
155 |
156 | func applyVideoEffectsToComposition(composition: AVMutableVideoComposition, size: CGSize) {
157 |
158 |
159 |
160 |
161 |
162 | }
163 |
164 |
165 | }
--------------------------------------------------------------------------------
/ResultsViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ResultsViewController.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 18/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import AVFoundation
11 |
12 | protocol ResultsControllerDelegate {
13 | func resultsDidCancel()
14 | }
15 |
16 | class ResultsViewController: VideoMaker,UITableViewDelegate,UITableViewDataSource,UITextFieldDelegate {
17 |
18 |
19 |
20 | //View outlets
21 | @IBOutlet weak var workOutDate: UILabel!
22 | @IBOutlet weak var workOutName: UILabel!
23 | @IBOutlet weak var carriedOutByTextfield: UITextField!
24 | @IBOutlet weak var judgedByTextField:UITextField!
25 | @IBOutlet weak var tableView: UITableView!
26 |
27 | @IBOutlet weak var progressLabel: UILabel!
28 | @IBOutlet weak var progressView: UIProgressView!
29 |
30 |
31 | var overlayLayer:CALayer!
32 | var parentLayer:CALayer!
33 | var videoLayer:CALayer!
34 | var layerGenerator = VideoOverlayGenerator()
35 |
36 | var delegate:ResultsControllerDelegate?
37 |
38 | override func viewDidLoad() {
39 | super.viewDidLoad()
40 |
41 | if UserSettings.sharedInstance.getStandardName() != "" {
42 | carriedOutByTextfield.text = UserSettings.sharedInstance.getStandardName()
43 | }
44 |
45 | if UserSettings.sharedInstance.getJudgeName() != "" {
46 | judgedByTextField.text = UserSettings.sharedInstance.getJudgeName()
47 | }
48 | }
49 |
50 |
51 | //Textfield delegate
52 | func textFieldShouldReturn(textField: UITextField) -> Bool {
53 | textField.resignFirstResponder()
54 | return true
55 | }
56 |
57 | //Table view dataSource Methods
58 | func numberOfSectionsInTableView(tableView: UITableView) -> Int {
59 | return 1
60 | }
61 |
62 | func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
63 | return 1
64 | }
65 |
66 | func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
67 |
68 | let cell = tableView.dequeueReusableCellWithIdentifier("menuCell", forIndexPath: indexPath)
69 |
70 |
71 | return cell
72 | }
73 |
74 |
75 |
76 | //Button actions
77 | @IBAction func cancelButton(sender: AnyObject) {
78 |
79 | let optionMenu = UIAlertController(title:"Are you sure, the video will be lost?", message: "", preferredStyle:UIAlertControllerStyle.ActionSheet)
80 | let harmful = UIAlertAction(title: "Delete", style: UIAlertActionStyle.Destructive, handler:{
81 | (alert:UIAlertAction!) -> Void in
82 | self.delegate!.resultsDidCancel()
83 | })
84 | let cancel = UIAlertAction(title:"Cancel", style: UIAlertActionStyle.Cancel, handler: nil)
85 |
86 | optionMenu.addAction(harmful)
87 | optionMenu.addAction(cancel)
88 |
89 | presentViewController(optionMenu, animated: true, completion: nil)
90 |
91 |
92 | }
93 | @IBAction func saveVideo(sender: AnyObject) {
94 |
95 | if carriedOutByTextfield.text != "" {
96 | UserSettings.sharedInstance.saveStandardName(carriedOutByTextfield.text!)
97 | }
98 |
99 | if judgedByTextField.text != "" {
100 | UserSettings.sharedInstance.saveJudgeName(judgedByTextField.text!)
101 | }
102 |
103 |
104 | let infoView = UIAlertController(title: "Create video", message: "Your video will be saved to your video library in a folder called 'Wodeo 2'. This may take some time depending on video length and quality.", preferredStyle: UIAlertControllerStyle.Alert)
105 |
106 | let ok = UIAlertAction(title:"Lets Go!", style: UIAlertActionStyle.Default, handler:{
107 |
108 | (alert:UIAlertAction!) -> Void in
109 |
110 | self.showWorkingsOut()
111 | self.videoOutput()
112 | })
113 |
114 | infoView.addAction(ok)
115 |
116 | presentViewController(infoView, animated:true, completion:nil)
117 |
118 |
119 | }
120 |
121 | func showWorkingsOut(){
122 | workOutName.hidden = true
123 | workOutDate.hidden = true
124 | progressLabel.hidden = false
125 | progressView.hidden = false
126 |
127 | _ = NSTimer.scheduledTimerWithTimeInterval(0.1, target: self, selector: #selector(ResultsViewController.progressTimerUpdated(_:)), userInfo: nil, repeats:true)
128 |
129 |
130 |
131 | }
132 |
133 | func progressTimerUpdated(timer:NSTimer){
134 | progressView.progress = exporter.progress
135 |
136 | let frmt = NSNumberFormatter()
137 | frmt.numberStyle = NSNumberFormatterStyle.PercentStyle
138 | let currentPercentage = frmt.stringFromNumber(NSNumber(float:exporter.progress))
139 | progressLabel.text = "Creating your video: \(currentPercentage!)"
140 |
141 | if exporter.progress == 1.0 {
142 | timer.invalidate()
143 | progressLabel.text = "Done!"
144 | delegate!.resultsDidCancel()
145 | }
146 |
147 |
148 | }
149 |
150 | //Admin
151 | override func prefersStatusBarHidden() -> Bool {
152 | return true
153 | }
154 |
155 | override func applyVideoEffectsToComposition(composition: AVMutableVideoComposition, size: CGSize) {
156 |
157 | parentLayer = CALayer()
158 | parentLayer.frame = CGRectMake(0.0, 0.0, size.width, size.height)
159 | videoLayer = CALayer()
160 | videoLayer.frame = CGRectMake(0.0, 0.0, size.width, size.height)
161 | parentLayer.addSublayer(videoLayer)
162 | overlayLayer = CALayer()
163 | overlayLayer.frame = CGRectMake(0, 0, size.width, size.height)
164 | overlayLayer.masksToBounds = true
165 |
166 |
167 | overlayLayer.addSublayer(layerGenerator.logo(size))
168 | overlayLayer.addSublayer(layerGenerator.workOutAndDate(size, withText:"\(workOutName.text!) \(workOutDate.text!)"))
169 |
170 | if carriedOutByTextfield.text != "" {
171 | overlayLayer.addSublayer(layerGenerator.carriedOutBy(size, withText:"Carried out by: \(carriedOutByTextfield.text!)"))
172 | }
173 |
174 | if judgedByTextField.text != "" {
175 | overlayLayer.addSublayer(layerGenerator.judgedBy(size, withText:"Judged by: \(judgedByTextField.text!)"))
176 | }
177 |
178 | overlayLayer.addSublayer(layerGenerator.mainBackingLayer(size))
179 |
180 |
181 | }
182 |
183 | func stringForWorkOurDetails() -> String {
184 | return "OverRide"
185 | }
186 |
187 |
188 |
189 | }
190 |
--------------------------------------------------------------------------------
/StandardOverlay.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StandardOverlay.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 16/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import AVFoundation
11 |
12 | class StandardOverlay: OverlayViewController,WODDelegate,ResultsControllerDelegate {
13 |
14 |
15 | //Down cast the work out to the current work out
16 | var standardWorkOut:StandardWOD!
17 |
18 | @IBOutlet weak var topView:RotatorView!
19 | @IBOutlet weak var bottomView:RotatorView!
20 |
21 | //Views specific to Standard overlay
22 | var repCountLabel = TimeTextLabel(fontSize: 30.0)
23 | var roundCountLabel = TimeTextLabel(fontSize: 30.0)
24 |
25 | //layout vars
26 | let counterLH:CGFloat = 35.0
27 |
28 |
29 | override func viewDidLoad() {
30 | super.viewDidLoad()
31 |
32 | standardWorkOut = workOut as! StandardWOD
33 | standardWorkOut.delegate = self
34 | }
35 |
36 | // override func viewDidAppear(animated: Bool) {
37 | // super.viewDidAppear(animated)
38 | // view.sendSubviewToBack(testView)
39 | // }
40 |
41 |
42 | override func layOutForPortrait() {
43 | super.layOutForPortrait()
44 | repCountLabel.frame = CGRectMake(fw * 0.6, 0.0, fw * 0.4,counterLH)
45 | roundCountLabel.frame = CGRectMake(fw * 0.6, counterLH, fw * 0.6, counterLH)
46 |
47 | view.layer.addSublayer(repCountLabel)
48 | view.layer.addSublayer(roundCountLabel)
49 | }
50 |
51 | override func layOutForLandscapeLeft() {
52 | super.layOutForLandscapeLeft()
53 |
54 | repCountLabel.frame = CGRectMake(0.0, 0.0, fw * 0.4, counterLH)
55 | repCountLabel.position = CGPointMake(counterLH / 2,fh / 2)
56 | repCountLabel.transform = CATransform3DRotate(repCountLabel.transform, CGFloat(-M_PI_2), 0.0, 0.0, 1.0)
57 |
58 | roundCountLabel.frame = CGRectMake(0.0, 0.0, fw * 0.4, counterLH)
59 | roundCountLabel.position = CGPointMake(counterLH / 2,fw * 0.4)
60 | roundCountLabel.transform = CATransform3DRotate(roundCountLabel.transform, CGFloat(-M_PI_2), 0.0, 0.0, 1.0)
61 |
62 |
63 | view.layer.addSublayer(repCountLabel)
64 | view.layer.addSublayer(roundCountLabel)
65 |
66 |
67 | }
68 |
69 | override func layOutForLandscapeRight() {
70 | super.layOutForLandscapeRight()
71 |
72 | repCountLabel.frame = CGRectMake(0.0, 0.0, fw * 0.4, counterLH)
73 | repCountLabel.position = CGPointMake(fw - (counterLH / 2),fh / 2)
74 | repCountLabel.transform = CATransform3DRotate(repCountLabel.transform, CGFloat(M_PI_2), 0.0, 0.0, 1.0)
75 |
76 | roundCountLabel.frame = CGRectMake(0.0, 0.0, fw * 0.4, counterLH)
77 | roundCountLabel.position = CGPointMake(fw - (counterLH / 2),fh * 0.8)
78 | roundCountLabel.transform = CATransform3DRotate(roundCountLabel.transform, CGFloat(M_PI_2), 0.0, 0.0, 1.0)
79 |
80 |
81 | view.layer.addSublayer(repCountLabel)
82 | view.layer.addSublayer(roundCountLabel)
83 |
84 | topView.transform = CGAffineTransformMakeRotation(CGFloat(M_PI_2 * 2))
85 | bottomView.transform = CGAffineTransformMakeRotation(CGFloat(M_PI_2 * 2))
86 |
87 | }
88 |
89 | override func cancelButtonPressed(){
90 | super.cancelButtonPressed()
91 | standardWorkOut.stopWorkOut()
92 | }
93 |
94 |
95 | override func stopWatchDidCountDown(t: TimeInterval) {
96 | super.stopWatchDidCountDown(t)
97 | //the count down timer has stopped so add the count up timer
98 | standardWorkOut.startWorkOut()
99 | addButtonUI()
100 | }
101 |
102 | func addButtonUI(){
103 |
104 | let buffer:CGFloat = 20.0
105 |
106 | let addRepButton = TiledView(frame: CGRectMake(0.0, 0.0, topView.frame.size.width - buffer, topView.frame.size.height - buffer))
107 | addRepButton.center = CGPointMake(topView.frame.size.width / 2, topView.frame.size.height / 2)
108 | addRepButton.instruction = "Add Rep"
109 | addRepButton.top = true
110 | addRepButton.addGestureRecognizer(UITapGestureRecognizer(target: self, action:#selector(StandardOverlay.addRep(_:))))
111 | topView.addSubview(addRepButton)
112 |
113 | let addRoundButton = TiledView(frame:CGRectMake(0.0, 0.0, bottomView.frame.size.width - buffer, bottomView.frame.size.height - buffer))
114 | addRoundButton.top = false
115 | addRoundButton.frame = CGRectMake(0.0, 0.0, bottomView.frame.size.width - buffer, bottomView.frame.size.height - buffer)
116 | addRoundButton.center = CGPointMake(bottomView.frame.size.width / 2, bottomView.frame.size.height / 2)
117 | addRoundButton.instruction = "Add Round"
118 | addRoundButton.addGestureRecognizer(UITapGestureRecognizer(target: self, action:#selector(StandardOverlay.addRound(_:))))
119 | bottomView.addSubview(addRoundButton)
120 |
121 |
122 |
123 | UIView.animateWithDuration(0.5, animations:{
124 | self.topView.alpha = 1.0
125 | self.bottomView.alpha = 1.0
126 | })
127 | }
128 |
129 |
130 | func addRep(sender:UITapGestureRecognizer){
131 | standardWorkOut.addRep()
132 | repCountLabel.string = "Reps:\(standardWorkOut.rounds.last!.reps.count)"
133 | }
134 |
135 | func addRound(sender:UIGestureRecognizer){
136 | standardWorkOut.addRound()
137 | roundCountLabel.string = "Rounds:\(standardWorkOut.rounds.count)"
138 | repCountLabel.string = "Reps:\(standardWorkOut.rounds.last!.reps.count)"
139 | }
140 |
141 | //Wod delegate methods
142 | func wodStopWatchDidUpdateToValue(timeValue: String) {
143 | if standardWorkOut.workOutTime > workOut!.totalVideoSecondsAllowed{
144 | countDownStopWatch.stop()
145 | let cWorkOut = workOut as! StandardWOD
146 | cWorkOut.stopWorkOut()
147 | videoManager!.stopRecordingVideo { (videoURL, error) -> Void in
148 |
149 | }
150 |
151 | }
152 | countDownLabel.string = timeValue
153 | }
154 |
155 | override func cameraManagerDidGenerateVideoAsset(url: NSURL) {
156 |
157 | super.cameraManagerDidGenerateVideoAsset(url)
158 | //Add overlay and set it as the video manager
159 | let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("resultsScreen") as! StandardResultsVC
160 | presentViewController(viewController, animated: true, completion: nil)
161 | viewController.standardWorkout = standardWorkOut
162 | viewController.videoToMakeURL = url
163 | viewController.delegate = self
164 | }
165 |
166 | func resultsDidCancel() {
167 | dismissViewControllerAnimated(false, completion: {
168 |
169 | self.delegate!.overlayDidCancel()
170 |
171 | })
172 | }
173 |
174 | func workOutDidEnd() {
175 | print("Work out ended")
176 | }
177 |
178 |
179 | }
180 |
181 |
182 |
183 |
184 |
--------------------------------------------------------------------------------
/MainMenuTableViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MainMenuTableViewController.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 15/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | protocol ViewCanceling {
12 | func viewDidCancel()
13 | }
14 |
15 | class MainMenuTableViewController: UIViewController ,UITableViewDataSource,UITableViewDelegate,BallicticStashDelegate,ViewCanceling {
16 |
17 | @IBOutlet weak var tableView:UITableView!
18 |
19 | var menuItems = [(String,String,String)]()
20 |
21 | var wodToSend:WOD?
22 |
23 | override func viewDidLoad() {
24 | super.viewDidLoad()
25 |
26 | // Uncomment the following line to preserve selection between presentations
27 | // self.clearsSelectionOnViewWillAppear = false
28 | //tableView.backgroundView = UIImageView(image: UIImage(named: "bg"))
29 | tableView.contentInset = UIEdgeInsetsMake(9.0, 0.0, 0.0, 0.0)
30 |
31 | menuItems.append(("Standard","STANDARD","FOR TIME"))
32 | menuItems.append(("CountDown","COUNTDOWN","AMRAP"))
33 | menuItems.append(("Interval","INTERVAL",""))
34 | menuItems.append(("8","TABATA",""))
35 |
36 |
37 |
38 | }
39 |
40 | func ballisticStachDidReturn() {
41 | dismissViewControllerAnimated(true, completion: nil)
42 | }
43 |
44 | override func didReceiveMemoryWarning() {
45 | super.didReceiveMemoryWarning()
46 | // Dispose of any resources that can be recreated.
47 | }
48 |
49 | // MARK: - Table view data source
50 |
51 | func numberOfSectionsInTableView(tableView: UITableView) -> Int {
52 | // #warning Incomplete implementation, return the number of sections
53 | return 1
54 | }
55 |
56 | func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
57 | // #warning Incomplete implementation, return the number of rows
58 | return menuItems.count
59 | }
60 |
61 |
62 | func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
63 |
64 | let cell = tableView.dequeueReusableCellWithIdentifier("menuCell", forIndexPath: indexPath) as! MainMenuCell
65 |
66 | cell.title.text = menuItems[indexPath.row].1
67 | cell.subTitle.text = menuItems[indexPath.row].2
68 | cell.cellImage.image = UIImage(named: menuItems[indexPath.row].0)
69 |
70 | return cell
71 | }
72 |
73 |
74 |
75 | func tableView(tableView: UITableView, shouldHighlightRowAtIndexPath indexPath: NSIndexPath) -> Bool {
76 | return true
77 | }
78 |
79 | //MARK: - Table view delegate
80 | func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
81 | tableView.deselectRowAtIndexPath(indexPath, animated: false)
82 |
83 | switch indexPath.row {
84 | case 0:
85 | wodToSend = StandardWOD(with: "STANDARD")
86 | performSegueWithIdentifier("videoViewcontroller", sender: nil)
87 |
88 | case 1:
89 | wodToSend = CountDownWOD(with:"AMRAP")
90 | performSegueWithIdentifier("countDownSettings", sender: nil)
91 |
92 | case 2:
93 | wodToSend = IntervalWOD(with:"INTERVAL")
94 | performSegueWithIdentifier("interval", sender: nil)
95 |
96 | case 3:
97 | wodToSend = TabataWOD(with: "TABATA")
98 | performSegueWithIdentifier("tabata", sender: nil)
99 | default:print("No cell selected")
100 |
101 | }
102 | }
103 |
104 | // MARK: - Navigation
105 |
106 | override func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject?) -> Bool {
107 | switch identifier {
108 | case "videoViewcontroller":
109 | if !UserSettings.sharedInstance.getUserVideoMessageOptOut(){
110 | let infoView = UIAlertController(title: "5 Minute Limit", message: "If you like our app, unlock more minutes and help support future great features, with the 'Unlimited Video' purchase. (It's as cheap as they would allow us!)", preferredStyle: UIAlertControllerStyle.Alert)
111 | infoView.view.tintColor = UIColor(red: 240/255.0, green: 178/255.0, blue: 71/255.0, alpha: 1.0)
112 |
113 | let ok = UIAlertAction(title:"Lets Go!", style: UIAlertActionStyle.Default, handler:{
114 | (alert:UIAlertAction!) -> Void in
115 | self.performSegueWithIdentifier("wodeoProducts", sender: nil)
116 | })
117 |
118 | let no = UIAlertAction(title: "No thanks..", style: UIAlertActionStyle.Cancel, handler:{ (alert:UIAlertAction!) -> Void in
119 | UserSettings.sharedInstance.saveUserVideoMessageOptOut()
120 | self.performSegueWithIdentifier("videoViewcontroller", sender: nil)
121 | })
122 |
123 | infoView.addAction(ok)
124 | infoView.addAction(no)
125 |
126 | presentViewController(infoView, animated:true, completion:nil)
127 | return false
128 | }else{
129 | return true
130 | }
131 |
132 | default:
133 | return true
134 | }
135 | }
136 |
137 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
138 | super.prepareForSegue(segue, sender: sender)
139 |
140 | if !shouldPerformSegueWithIdentifier(segue.identifier!, sender: sender){
141 | return
142 | }
143 |
144 | switch segue.identifier! {
145 | case "wodeoProducts" :print("Going to products")
146 | let vc = segue.destinationViewController as! WodeoProductPage
147 | vc.delegate = self
148 |
149 | case "videoViewcontroller":
150 |
151 | let vc = segue.destinationViewController as! VideoViewController
152 | vc.workout = wodToSend
153 | vc.delegate = self
154 |
155 | case "countDownSettings" :
156 | let vc = segue.destinationViewController as! SettingsViewController
157 | vc.wod = wodToSend
158 | vc.delegate = self
159 |
160 | case "interval" :
161 | let vc = segue.destinationViewController as! IntervalSettingsVC
162 | vc.wod = wodToSend
163 | vc.delegate = self
164 |
165 | case "tabata" :
166 | let vc = segue.destinationViewController as! TabataSettingsVC
167 | vc.wod = wodToSend
168 | vc.delegate = self
169 |
170 | default:print("Dont know what was picked")
171 | }
172 |
173 |
174 | }
175 |
176 | //View cancelling delegate
177 | func viewDidCancel() {
178 | dismissViewControllerAnimated(true, completion:nil)
179 | }
180 |
181 |
182 | override func prefersStatusBarHidden() -> Bool {
183 | return true
184 | }
185 |
186 | override func shouldAutorotate() -> Bool {
187 | return false
188 | }
189 |
190 | }
191 |
--------------------------------------------------------------------------------
/IAPHelper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // IAPHelper.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 28/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import StoreKit
10 |
11 | /// Notification that is generated when a product is purchased.
12 | public let IAPHelperProductPurchasedNotification = "IAPHelperProductPurchasedNotification"
13 |
14 | /// Product identifiers are unique strings registered on the app store.
15 | public typealias ProductIdentifier = String
16 |
17 | /// Completion handler called when products are fetched.
18 | public typealias RequestProductsCompletionHandler = (success: Bool, products: [SKProduct]) -> ()
19 |
20 |
21 | /// A Helper class for In-App-Purchases, it can fetch products, tell you if a product has been purchased,
22 | /// purchase products, and restore purchases. Uses NSUserDefaults to cache if a product has been purchased.
23 | public class IAPHelper : NSObject {
24 |
25 | /// MARK: - Private Properties
26 |
27 | // Used to keep track of the possible products and which ones have been purchased.
28 | private let productIdentifiers: Set
29 | private var purchasedProductIdentifiers = Set()
30 |
31 | // Used by SKProductsRequestDelegate
32 | private var productsRequest: SKProductsRequest?
33 | private var completionHandler: RequestProductsCompletionHandler?
34 |
35 | /// MARK: - User facing API
36 |
37 | /// Initialize the helper. Pass in the set of ProductIdentifiers supported by the app.
38 | public init(productIdentifiers: Set) {
39 | self.productIdentifiers = productIdentifiers
40 | super.init()
41 | SKPaymentQueue.defaultQueue().addTransactionObserver(self)
42 |
43 |
44 | for productIdentifier in productIdentifiers {
45 | let purchased = NSUserDefaults.standardUserDefaults().boolForKey(productIdentifier)
46 | if purchased {
47 | purchasedProductIdentifiers.insert(productIdentifier)
48 | print("Previously purchased: \(productIdentifier)")
49 | } else {
50 | print("Not purchased: \(productIdentifier)")
51 | }
52 | }
53 | }
54 |
55 | public class func canMakePayments() -> Bool {
56 | return SKPaymentQueue.canMakePayments()
57 | }
58 |
59 |
60 | /// Gets the list of SKProducts from the Apple server calls the handler with the list of products.
61 | public func requestProductsWithCompletionHandler(handler: RequestProductsCompletionHandler) {
62 | completionHandler = handler
63 | productsRequest = SKProductsRequest(productIdentifiers: productIdentifiers)
64 | productsRequest?.delegate = self
65 | productsRequest?.start()
66 | }
67 |
68 | /// Initiates purchase of a product.
69 | public func purchaseProduct(product: SKProduct) {
70 | print("Buying \(product.productIdentifier)...")
71 | let payment = SKPayment(product: product)
72 | SKPaymentQueue.defaultQueue().addPayment(payment)
73 | }
74 |
75 | /// Given the product identifier, returns true if that product has been purchased.
76 | public func isProductPurchased(productIdentifier: ProductIdentifier) -> Bool {
77 | return purchasedProductIdentifiers.contains(productIdentifier)
78 | }
79 |
80 | /// If the state of whether purchases have been made is lost (e.g. the
81 | /// user deletes and reinstalls the app) this will recover the purchases.
82 | public func restoreCompletedTransactions() {
83 | SKPaymentQueue.defaultQueue().restoreCompletedTransactions()
84 | }
85 | }
86 |
87 | // MARK: - SKProductsRequestDelegate
88 |
89 | extension IAPHelper: SKProductsRequestDelegate {
90 | public func productsRequest(request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) {
91 | print("Loaded list of products...")
92 | let products = response.products
93 | completionHandler?(success: true, products: products)
94 | clearRequest()
95 |
96 | // debug printing
97 | for p in products {
98 | print("Found product: \(p.productIdentifier) \(p.localizedTitle) \(p.price.floatValue)")
99 | }
100 | }
101 |
102 | public func request(request: SKRequest, didFailWithError error: NSError) {
103 | print("Failed to load list of products.")
104 | print("Error: \(error)")
105 | clearRequest()
106 | }
107 |
108 | private func clearRequest() {
109 | productsRequest = nil
110 | completionHandler = nil
111 | }
112 | }
113 |
114 | extension IAPHelper: SKPaymentTransactionObserver {
115 | /// This is a function called by the payment queue, not to be called directly.
116 | /// For each transaction act accordingly, save in the purchased cache, issue notifications,
117 | /// mark the transaction as complete.
118 | public func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
119 | for transaction in transactions {
120 | switch (transaction.transactionState) {
121 | case .Purchased:
122 | completeTransaction(transaction)
123 | break
124 | case .Failed:
125 | failedTransaction(transaction)
126 | break
127 | case .Restored:
128 | restoreTransaction(transaction)
129 | break
130 | case .Deferred:
131 | break
132 | case .Purchasing:
133 | break
134 | }
135 | }
136 |
137 | }
138 |
139 | private func completeTransaction(transaction: SKPaymentTransaction) {
140 | print("completeTransaction...")
141 | provideContentForProductIdentifier(transaction.payment.productIdentifier)
142 | SKPaymentQueue.defaultQueue().finishTransaction(transaction)
143 | }
144 |
145 | private func restoreTransaction(transaction: SKPaymentTransaction) {
146 | let productIdentifier = transaction.originalTransaction!.payment.productIdentifier
147 | print("restoreTransaction... \(productIdentifier)")
148 | provideContentForProductIdentifier(productIdentifier)
149 | SKPaymentQueue.defaultQueue().finishTransaction(transaction)
150 | }
151 |
152 | // Helper: Saves the fact that the product has been purchased and posts a notification.
153 | private func provideContentForProductIdentifier(productIdentifier: String) {
154 | purchasedProductIdentifiers.insert(productIdentifier)
155 | NSUserDefaults.standardUserDefaults().setBool(true, forKey: productIdentifier)
156 | NSUserDefaults.standardUserDefaults().synchronize()
157 | NSNotificationCenter.defaultCenter().postNotificationName(IAPHelperProductPurchasedNotification, object: productIdentifier)
158 | }
159 |
160 | private func failedTransaction(transaction: SKPaymentTransaction) {
161 | print("failedTransaction...")
162 | if transaction.error!.code != SKErrorCode.PaymentCancelled.rawValue {
163 | print("Transaction error: \(transaction.error!.localizedDescription)")
164 | }
165 | SKPaymentQueue.defaultQueue().finishTransaction(transaction)
166 | }
167 | }
168 |
169 |
--------------------------------------------------------------------------------
/CountdownOverlay.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CountdownOverlay.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 21/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class CountdownOverlay: OverlayViewController ,WODDelegate , ResultsControllerDelegate{
12 |
13 | //Downcast the work out to the current workout
14 | var countDownWorkOut:CountDownWOD!
15 |
16 |
17 |
18 | @IBOutlet weak var topView:RotatorView!
19 | @IBOutlet weak var bottomView:RotatorView!
20 |
21 | //Views specific to Standard overlay
22 | var repCountLabel = TimeTextLabel(fontSize: 30.0)
23 | var roundCountLabel = TimeTextLabel(fontSize: 30.0)
24 |
25 | //layout vars
26 | let counterLH:CGFloat = 35.0
27 |
28 |
29 | override func viewDidLoad() {
30 | super.viewDidLoad()
31 |
32 | countDownWorkOut = workOut as! CountDownWOD
33 | countDownWorkOut.delegate = self
34 | }
35 |
36 | // override func viewDidAppear(animated: Bool) {
37 | // super.viewDidAppear(animated)
38 | // view.sendSubviewToBack(testView)
39 | // }
40 |
41 | override func layOutForPortrait() {
42 | super.layOutForPortrait()
43 | repCountLabel.frame = CGRectMake(2.0, 0.0, fw,counterLH)
44 | roundCountLabel.frame = CGRectMake(2.0, counterLH,fw, counterLH)
45 |
46 |
47 | view.layer.addSublayer(repCountLabel)
48 | view.layer.addSublayer(roundCountLabel) }
49 |
50 | override func layOutForLandscapeLeft() {
51 | super.layOutForLandscapeLeft()
52 |
53 | let labelWidth = (fh/2) - 5.0
54 |
55 | repCountLabel.frame = CGRectMake(0.0, 0.0,labelWidth, counterLH)
56 | repCountLabel.alignmentMode = kCAAlignmentRight
57 | repCountLabel.position = CGPointMake(counterLH / 2,labelWidth/2)
58 | repCountLabel.transform = CATransform3DRotate(repCountLabel.transform, CGFloat(-M_PI_2), 0.0, 0.0, 1.0)
59 |
60 | roundCountLabel.frame = CGRectMake(0.0, 0.0,labelWidth, counterLH)
61 | roundCountLabel.position = CGPointMake(counterLH / 2,(fh - (labelWidth / 2)) - 5)
62 | roundCountLabel.transform = CATransform3DRotate(roundCountLabel.transform, CGFloat(-M_PI_2), 0.0, 0.0, 1.0)
63 |
64 | view.layer.addSublayer(repCountLabel)
65 | view.layer.addSublayer(roundCountLabel)
66 |
67 |
68 |
69 | }
70 |
71 | override func layOutForLandscapeRight() {
72 | super.layOutForLandscapeRight()
73 |
74 | let labelWidth = (fh/2) - 5.0
75 |
76 | repCountLabel.frame = CGRectMake(0.0, 0.0,labelWidth, counterLH)
77 | repCountLabel.alignmentMode = kCAAlignmentRight
78 | repCountLabel.position = CGPointMake(fw - (counterLH / 2),(fh - (labelWidth / 2)) - 5)
79 | repCountLabel.transform = CATransform3DRotate(repCountLabel.transform, CGFloat(M_PI_2), 0.0, 0.0, 1.0)
80 |
81 | roundCountLabel.frame = CGRectMake(0.0, 0.0,labelWidth, counterLH)
82 | roundCountLabel.position = CGPointMake(fw - (counterLH / 2),labelWidth/2)
83 | roundCountLabel.transform = CATransform3DRotate(roundCountLabel.transform, CGFloat(M_PI_2), 0.0, 0.0, 1.0)
84 |
85 | view.layer.addSublayer(repCountLabel)
86 | view.layer.addSublayer(roundCountLabel)
87 |
88 |
89 | topView.transform = CGAffineTransformMakeRotation(CGFloat(M_PI_2 * 2))
90 | bottomView.transform = CGAffineTransformMakeRotation(CGFloat(M_PI_2 * 2))
91 |
92 |
93 | }
94 |
95 | override func cancelButtonPressed(){
96 | super.cancelButtonPressed()
97 | countDownWorkOut.stopWorkOut()
98 | }
99 |
100 |
101 | override func stopWatchDidCountDown(t: TimeInterval) {
102 | super.stopWatchDidCountDown(t)
103 | //the count down timer has stopped so add the count up timer
104 | countDownWorkOut.startWorkOut()
105 | addButtonUI()
106 | }
107 |
108 | func addButtonUI(){
109 |
110 | let buffer:CGFloat = 20.0
111 |
112 | let addRepButton = TiledView()
113 | addRepButton.frame = CGRectMake(0.0, 0.0, topView.frame.size.width - buffer, topView.frame.size.height - buffer)
114 | addRepButton.center = CGPointMake(topView.frame.size.width / 2, topView.frame.size.height / 2)
115 | addRepButton.instruction = "Add Rep"
116 | addRepButton.top = true
117 | addRepButton.addGestureRecognizer(UITapGestureRecognizer(target: self, action:#selector(CountdownOverlay.addRep(_:))))
118 | topView.addSubview(addRepButton)
119 |
120 | let addRoundButton = TiledView()
121 | addRoundButton.frame = CGRectMake(0.0, 0.0, bottomView.frame.size.width - buffer, bottomView.frame.size.height - buffer)
122 | addRoundButton.center = CGPointMake(bottomView.frame.size.width / 2, bottomView.frame.size.height / 2)
123 | addRoundButton.instruction = "Add Round"
124 | addRoundButton.top = false
125 | addRoundButton.addGestureRecognizer(UITapGestureRecognizer(target: self, action:#selector(CountdownOverlay.addRound(_:))))
126 | bottomView.addSubview(addRoundButton)
127 |
128 |
129 |
130 |
131 | UIView.animateWithDuration(0.5, animations:{
132 | self.topView.alpha = 1.0
133 | self.bottomView.alpha = 1.0
134 | })
135 | }
136 |
137 |
138 | func addRep(sender:UITapGestureRecognizer){
139 | countDownWorkOut.addRep()
140 | repCountLabel.string = "Reps:\(countDownWorkOut.rounds.last!.reps.count)"
141 | }
142 |
143 | func addRound(sender:UITapGestureRecognizer){
144 | countDownWorkOut.addRound()
145 | roundCountLabel.string = "Rounds:\(countDownWorkOut.rounds.count)"
146 | repCountLabel.string = "Reps:\(countDownWorkOut.rounds.last!.reps.count)"
147 | }
148 |
149 | //Wod delegate methods
150 | func wodStopWatchDidUpdateToValue(timeValue: String) {
151 | countDownLabel.string = (timeValue)
152 | }
153 |
154 | func workOutDidEnd() {
155 | countDownStopWatch.stop()
156 | let cWorkOut = workOut as! CountDownWOD
157 | cWorkOut.stopWorkOut()
158 | videoManager!.stopRecordingVideo { (videoURL, error) -> Void in
159 |
160 | }
161 |
162 | }
163 |
164 | override func cameraManagerDidGenerateVideoAsset(url: NSURL) {
165 |
166 | super.cameraManagerDidGenerateVideoAsset(url)
167 | //Add overlay and set it as the video manager
168 | let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("countDownResults") as! CountDownResultsVC
169 | presentViewController(viewController, animated: true, completion: nil)
170 | viewController.countDownWorkout = countDownWorkOut
171 | viewController.videoToMakeURL = url
172 | viewController.delegate = self
173 | }
174 |
175 | override func saveButtonPressed(){
176 | countDownStopWatch.stop()
177 | let cWorkOut = workOut as! CountDownWOD
178 | cWorkOut.stopWorkOut()
179 | videoManager!.stopRecordingVideo { (videoURL, error) -> Void in
180 |
181 | }
182 | }
183 |
184 |
185 | func resultsDidCancel() {
186 | dismissViewControllerAnimated(false, completion: {
187 |
188 | self.delegate!.overlayDidCancel()
189 |
190 | })
191 | }
192 |
193 |
194 | }
195 |
196 |
197 |
198 |
199 |
--------------------------------------------------------------------------------
/StopWatch.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TimeTest.swift
3 | // timerTesting
4 | //
5 | // Created by Gareth Long on 13/02/2016.
6 | // Copyright © 2016 gazlongapps. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import AVFoundation
11 |
12 |
13 | extension Int {
14 |
15 | func timesThrough() -> String{
16 | if self == 1 {
17 | return "1 time through"
18 | }else{
19 | return "\(self) times through"
20 | }
21 | }
22 |
23 | }
24 |
25 | extension Double {
26 |
27 | /// Rounds the double to decimal places value
28 | func stringForTimeInterval(withMicroSeconds:Bool) -> String {
29 |
30 | var retString = ""
31 | let startTime = self
32 | let baseTime = NSDate(timeIntervalSince1970: startTime)
33 | let dateFormatter = NSDateFormatter()
34 |
35 |
36 | if withMicroSeconds {
37 |
38 | dateFormatter.dateFormat = "HH:mm:ss.SS"
39 | dateFormatter.timeZone = NSTimeZone(forSecondsFromGMT:0)
40 | //Remove the blank components
41 | let slipDownString = dateFormatter.stringFromDate(baseTime).componentsSeparatedByString(":")
42 |
43 | for part in slipDownString {
44 | if part != "00" {
45 | retString += part
46 | if part.rangeOfString(".") == nil {
47 | retString += ":"
48 | }
49 | }
50 | }
51 |
52 | }else{
53 |
54 | dateFormatter.dateFormat = "HH:mm:ss"
55 | dateFormatter.timeZone = NSTimeZone(forSecondsFromGMT:0)
56 |
57 | //Remove the blank components
58 | var slipDownString = dateFormatter.stringFromDate(baseTime).componentsSeparatedByString(":")
59 |
60 | //trim out the initial zero's
61 |
62 |
63 |
64 |
65 |
66 | //mark leading zeros for removal
67 | for i in 0.. String {
109 | if let secs = secondsAsDouble {
110 | return secs.stringForTimeInterval(withMilliseconds)
111 | }else {
112 | return "Time Interval Set"
113 | }
114 | }
115 |
116 | }
117 |
118 | enum StopWatchType {
119 | case CountUp
120 | case CountDown
121 | }
122 |
123 | protocol StopWatchDelegate {
124 |
125 | func stopWatchDidUpdateWithTimeInterval(t:TimeInterval)
126 | func stopWatchDidCountDown(t:TimeInterval)
127 | }
128 |
129 |
130 | class StopWatch:NSObject {
131 |
132 | var startTime:CFAbsoluteTime!
133 | var timer:NSTimer?
134 | let type:StopWatchType
135 | var timerStarted = false
136 | var countDownFromSeconds:Int?
137 | var delegate:StopWatchDelegate!
138 | var inLastThreeSeconds = false
139 | var speechSynth = AVSpeechSynthesizer()
140 | //let myUtterence = AVSpeechUtterance(string:"3......2......1")
141 |
142 | var currentDuration:TimeInterval {
143 | if timerStarted {
144 | let currentTime = CFAbsoluteTimeGetCurrent()
145 | return TimeInterval(secondsAsDouble:currentTime - startTime!)
146 | }else{
147 | return TimeInterval(secondsAsDouble: 0.0)
148 | }
149 |
150 | }
151 |
152 | init(type:StopWatchType){
153 | self.type = type
154 | }
155 |
156 | func stop(){
157 |
158 | if speechSynth.speaking {
159 |
160 | speechSynth.stopSpeakingAtBoundary(.Immediate)
161 | }
162 |
163 | if let t = timer {
164 | t.invalidate()
165 | }
166 | }
167 |
168 | func start(){
169 | inLastThreeSeconds = false
170 | startTime = CFAbsoluteTimeGetCurrent()
171 | timer = NSTimer.scheduledTimerWithTimeInterval(0.01, target: self, selector:#selector(StopWatch.update), userInfo: nil, repeats: true)
172 | timerStarted = true
173 | }
174 |
175 | func update(){
176 | switch type {
177 | case .CountUp:updateCurrentDuration()
178 | case .CountDown:updateTimeRemaining()
179 | }
180 | }
181 |
182 |
183 | func delay(seconds seconds: Double, completion:()->()) {
184 | let popTime = dispatch_time(DISPATCH_TIME_NOW, Int64( Double(NSEC_PER_SEC) * seconds ))
185 |
186 | dispatch_after(popTime, dispatch_get_main_queue()) {
187 | completion()
188 | }
189 | }
190 |
191 | var speaking:Bool = false
192 | func countDown() {
193 | speaking = true
194 | let priority = QOS_CLASS_USER_INITIATED
195 | dispatch_async(dispatch_get_global_queue(priority, 0)) {
196 |
197 |
198 | let utterence = AVSpeechUtterance(string:"3")
199 | self.speechSynth.speakUtterance(utterence)
200 |
201 | self.delay(seconds: 1, completion: {
202 | let utterence = AVSpeechUtterance(string:"2")
203 | self.speechSynth.speakUtterance(utterence)
204 | })
205 |
206 | self.delay(seconds: 2, completion: {
207 | let utterence = AVSpeechUtterance(string:"1")
208 | self.speechSynth.speakUtterance(utterence)
209 |
210 | })
211 |
212 |
213 | }
214 | }
215 |
216 | func updateTimeRemaining() {
217 | if timerStarted {
218 | let currentTime = CFAbsoluteTimeGetCurrent()
219 |
220 | if let cdt = countDownFromSeconds {
221 | let ct = Double(cdt) - (currentTime - startTime!)
222 |
223 | if ct <= 0 {
224 | self.speaking = false
225 | delegate.stopWatchDidCountDown(TimeInterval(secondsAsDouble:0.0))
226 | }else{
227 |
228 | if ct <= 4 && !speaking {
229 | countDown()
230 | }
231 |
232 | delegate.stopWatchDidUpdateWithTimeInterval(TimeInterval(secondsAsDouble:ct))
233 | }
234 |
235 | }else{
236 | print("Count down time not set")
237 | }
238 | }else{
239 | print("Timer not started")
240 | }
241 |
242 | }
243 | func updateCurrentDuration() {
244 | if timerStarted {
245 | let currentTime = CFAbsoluteTimeGetCurrent()
246 | if delegate != nil {
247 | delegate.stopWatchDidUpdateWithTimeInterval(TimeInterval(secondsAsDouble:currentTime - startTime!))
248 | }
249 | }else{
250 | print("Timer not started")
251 | }
252 | }
253 |
254 | }
--------------------------------------------------------------------------------
/TabataOverlay.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TabataOverlay.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 27/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class TabataOverlay: OverlayViewController ,WODDelegate,TabataWOD_Delegate,ResultsControllerDelegate {
12 |
13 | var tabataWOD:TabataWOD!
14 | @IBOutlet weak var mainRotatorView:RotatorView!
15 | var repCountLabel = TimeTextLabel(fontSize:30.0)
16 | var roundCountLabel = TimeTextLabel(fontSize: 30.0)
17 | var restingLabel = TimeTextLabel(fontSize: 100.0)
18 | //layout vars
19 | let counterLH:CGFloat = 35.0
20 |
21 | var isResting = false
22 |
23 |
24 |
25 | //View lifecycle
26 | override func viewDidLoad() {
27 | super.viewDidLoad()
28 |
29 |
30 |
31 |
32 | print("Interval layout is loading")
33 |
34 | tabataWOD = workOut as! TabataWOD
35 | tabataWOD.delegate = self
36 | tabataWOD.tabataDelegate = self
37 | }
38 |
39 | override func viewDidDisappear(animated: Bool) {
40 | super.viewDidDisappear(animated)
41 |
42 | }
43 |
44 |
45 | //Stop watch delegate
46 | override func stopWatchDidCountDown(t: TimeInterval) {
47 | super.stopWatchDidCountDown(t)
48 | tabataWOD.startWorkOut()
49 | addButtonUI()
50 |
51 | }
52 |
53 |
54 | //Create and layout the UI
55 | override func createUI() {
56 | super.createUI()
57 |
58 | }
59 |
60 | func addButtonUI(){
61 |
62 | let addRepButton = TiledView()
63 | addRepButton.frame = CGRectMake(0.0, 0.0, view.frame.size.width, view.frame.size.height)
64 | addRepButton.instruction = "Add Rep"
65 | addRepButton.top = true
66 | addRepButton.addGestureRecognizer(UITapGestureRecognizer(target: self, action:#selector(TabataOverlay.addRep(_:))))
67 | mainRotatorView.addSubview(addRepButton)
68 |
69 | restingLabel.string = "WORK"
70 |
71 | UIView.animateWithDuration(0.5, animations:{
72 | self.mainRotatorView.alpha = 1.0
73 | })
74 | }
75 |
76 |
77 | override func layOutForPortrait() {
78 | super.layOutForPortrait()
79 | repCountLabel.frame = CGRectMake(2.0, 0.0, fw,counterLH)
80 | roundCountLabel.frame = CGRectMake(2.0, counterLH,fw, counterLH)
81 | restingLabel.frame = CGRectMake(0.0, fh - 200.0, fw, 200.0)
82 | restingLabel.alignmentMode = kCAAlignmentCenter
83 |
84 |
85 | view.layer.addSublayer(restingLabel)
86 | view.layer.addSublayer(repCountLabel)
87 | view.layer.addSublayer(roundCountLabel)
88 | }
89 |
90 | override func layOutForLandscapeLeft() {
91 | super.layOutForLandscapeLeft()
92 |
93 | let labelWidth = (fh/2) - 5.0
94 |
95 | repCountLabel.frame = CGRectMake(0.0, 0.0,labelWidth, counterLH)
96 | repCountLabel.alignmentMode = kCAAlignmentRight
97 | repCountLabel.position = CGPointMake(counterLH / 2,labelWidth/2)
98 | repCountLabel.transform = CATransform3DRotate(repCountLabel.transform, CGFloat(-M_PI_2), 0.0, 0.0, 1.0)
99 |
100 | roundCountLabel.frame = CGRectMake(0.0, 0.0,labelWidth, counterLH)
101 | roundCountLabel.position = CGPointMake(counterLH / 2,(fh - (labelWidth / 2)) - 5)
102 | roundCountLabel.transform = CATransform3DRotate(roundCountLabel.transform, CGFloat(-M_PI_2), 0.0, 0.0, 1.0)
103 |
104 | restingLabel.frame = CGRectMake(0.0,0.0, fw, 200.0)
105 | restingLabel.position = CGPointMake(fw - 100.0, fh/2)
106 | restingLabel.alignmentMode = kCAAlignmentCenter
107 | restingLabel.transform = CATransform3DRotate(restingLabel.transform, CGFloat(-M_PI_2), 0.0, 0.0, 1.0)
108 |
109 | view.layer.addSublayer(restingLabel)
110 |
111 | view.layer.addSublayer(repCountLabel)
112 | view.layer.addSublayer(roundCountLabel)
113 |
114 |
115 |
116 | }
117 |
118 | override func layOutForLandscapeRight() {
119 | super.layOutForLandscapeRight()
120 |
121 | let labelWidth = (fh/2) - 5.0
122 |
123 | repCountLabel.frame = CGRectMake(0.0, 0.0,labelWidth, counterLH)
124 | repCountLabel.alignmentMode = kCAAlignmentRight
125 | repCountLabel.position = CGPointMake(fw - (counterLH / 2),(fh - (labelWidth / 2)) - 5)
126 | repCountLabel.transform = CATransform3DRotate(repCountLabel.transform, CGFloat(M_PI_2), 0.0, 0.0, 1.0)
127 |
128 | roundCountLabel.frame = CGRectMake(0.0, 0.0,labelWidth, counterLH)
129 | roundCountLabel.position = CGPointMake(fw - (counterLH / 2),labelWidth/2)
130 | roundCountLabel.transform = CATransform3DRotate(roundCountLabel.transform, CGFloat(M_PI_2), 0.0, 0.0, 1.0)
131 |
132 | restingLabel.frame = CGRectMake(0.0,0.0, fw, 200.0)
133 | restingLabel.position = CGPointMake(100.0, fh/2)
134 | restingLabel.alignmentMode = kCAAlignmentCenter
135 | restingLabel.transform = CATransform3DRotate(restingLabel.transform, CGFloat(M_PI_2), 0.0, 0.0, 1.0)
136 |
137 | view.layer.addSublayer(restingLabel)
138 |
139 |
140 | view.layer.addSublayer(repCountLabel)
141 | view.layer.addSublayer(roundCountLabel)
142 |
143 |
144 | mainRotatorView.transform = CGAffineTransformMakeRotation(CGFloat(M_PI_2 * 2))
145 |
146 |
147 | }
148 |
149 |
150 |
151 | //Wod delegate
152 | func wodStopWatchDidUpdateToValue(timeValue: String) {
153 | countDownLabel.string = timeValue
154 | }
155 |
156 | func workOutDidEnd() {
157 | tabataWOD.stopWorkOut()
158 | videoManager!.stopRecordingVideo { (videoURL, error) -> Void in
159 | }
160 | }
161 |
162 | //Tabata WOD delegate
163 | func tabataWODDidMoveToRoundWithName(name: String) {
164 | repCountLabel.string = ""
165 | roundCountLabel.string = name
166 | }
167 |
168 | func tabataWODDidMoveToRestPeriod() {
169 | isResting = true
170 | restingLabel.string = "REST"
171 | restingLabel.foregroundColor = UIColor.redColor().CGColor
172 |
173 | }
174 |
175 | func tabataWODDidMoveOutOfRestPeriod() {
176 | isResting = false
177 | restingLabel.string = "WORK"
178 | restingLabel.foregroundColor = UIColor.greenColor().CGColor
179 | }
180 |
181 |
182 |
183 |
184 |
185 | //Buttons
186 | func addRep(sender:UITapGestureRecognizer){
187 |
188 | if !isResting {
189 | if sender.state == .Ended{
190 | repCountLabel.string = tabataWOD.addRep()
191 | }
192 | }
193 |
194 | }
195 |
196 | override func cancelButtonPressed(){
197 | super.cancelButtonPressed()
198 | tabataWOD.stopWorkOut()
199 |
200 | }
201 |
202 | override func cameraManagerDidGenerateVideoAsset(url: NSURL) {
203 |
204 | super.cameraManagerDidGenerateVideoAsset(url)
205 | //Add overlay and set it as the video manager
206 | let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("TabataResultsVC") as! TabataResultsVC
207 | presentViewController(viewController, animated: true, completion: nil)
208 | viewController.tabataWOD = tabataWOD
209 | viewController.videoToMakeURL = url
210 | viewController.delegate = self
211 | }
212 |
213 | func resultsDidCancel() {
214 | dismissViewControllerAnimated(true, completion:nil)
215 | }
216 |
217 | override func saveButtonPressed(){
218 | countDownStopWatch.stop()
219 | let cWorkOut = workOut as! TabataWOD
220 | cWorkOut.stopWorkOut()
221 | videoManager!.stopRecordingVideo { (videoURL, error) -> Void in
222 |
223 | }
224 | }
225 |
226 | }
227 |
--------------------------------------------------------------------------------
/OverlayViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // OverlayViewController.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 16/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import AVFoundation
11 |
12 | protocol OverlayDelegate {
13 |
14 | func overlayDidCancel()
15 | func overlayDidSave()
16 |
17 | }
18 |
19 | class OverlayViewController: UIViewController,StopWatchDelegate,CameraManagerDelegate {
20 |
21 |
22 | var videoManager:CameraManager?
23 |
24 | //Layout variables
25 | let sbw:CGFloat = 60.0
26 | let sbh:CGFloat = 60.0
27 | let slh:CGFloat = 70.0
28 | var fw:CGFloat {
29 | return view.frame.size.width
30 | }
31 | var fh:CGFloat {
32 | return view.frame.size.height
33 | }
34 |
35 | //Overlay settings
36 | var delegate:OverlayDelegate?
37 | var workOut:WOD?
38 | var videoSettings:VideoSettings!
39 | var hasCountDown:Bool = false
40 |
41 | //Overlay views
42 | var cancelButton:UIButton!
43 | var saveButton:UIButton!
44 | var countDownLabel = TimeTextLabel(fontSize: 30.0)
45 | //var testView:UIImageView!
46 |
47 | var countDownStopWatch = StopWatch(type:StopWatchType.CountDown)
48 |
49 |
50 |
51 |
52 | // Lay out functions
53 | func layOutForPortrait() {
54 |
55 | // testView.frame = view.bounds
56 |
57 | // view.addSubview(testView)
58 |
59 |
60 | saveButton.frame = CGRectMake(fw - sbw,fh-sbh,sbw,sbh)
61 | cancelButton.frame = CGRectMake(0.0, fh-sbh, sbw, sbh)
62 |
63 | countDownLabel.frame = CGRectMake(0.0, 0.0, fw,fw * 0.5)
64 | countDownLabel.position = CGPointMake(fw / 2,fh / 2)
65 |
66 | view.addSubview(saveButton)
67 | view.addSubview(cancelButton)
68 | view.layer.addSublayer(countDownLabel)
69 |
70 | addPulsing()
71 | }
72 |
73 | func layOutForLandscapeLeft(){
74 |
75 | saveButton.frame = CGRectMake(fw - sbw,0.0,sbw,sbh)
76 | cancelButton.frame = CGRectMake(fw - sbw,fh - sbh, sbw, sbh)
77 |
78 | countDownLabel.frame = CGRectMake(0.0, 0.0, fw,fw * 0.5)
79 | countDownLabel.position = CGPointMake(fw / 2,fh / 2)
80 | countDownLabel.transform = CATransform3DRotate(countDownLabel.transform, CGFloat(-M_PI_2), 0.0, 0.0, 1.0)
81 |
82 | view.addSubview(saveButton)
83 | view.addSubview(cancelButton)
84 | view.layer.addSublayer(countDownLabel)
85 |
86 | addPulsing()
87 | }
88 |
89 | func layOutForLandscapeRight(){
90 |
91 | saveButton.frame = CGRectMake(0.0,fh - sbh,sbw,sbh)
92 | cancelButton.frame = CGRectMake(0.0,0.0, sbw, sbh)
93 |
94 | countDownLabel.frame = CGRectMake(0.0, 0.0,fw,fw * 0.5)
95 | countDownLabel.position = CGPointMake(fw / 2,fh / 2)
96 | countDownLabel.transform = CATransform3DRotate(countDownLabel.transform, CGFloat(M_PI_2), 0.0, 0.0, 1.0)
97 |
98 | view.addSubview(saveButton)
99 | view.addSubview(cancelButton)
100 | view.layer.addSublayer(countDownLabel)
101 |
102 | addPulsing()
103 |
104 | }
105 |
106 | func createUI(){
107 |
108 |
109 | // testView = UIImageView(image: UIImage(named: "bg2.png"))
110 |
111 | saveButton = UIButton(type:.Custom)
112 | saveButton.setTitle("", forState: .Normal)
113 | saveButton.setImage(UIImage(named:"Recording")!, forState: .Normal)
114 | saveButton.addTarget(self, action: #selector(OverlayViewController.saveButtonPressed), forControlEvents:.TouchUpInside)
115 |
116 | cancelButton = UIButton(type:.Custom)
117 | cancelButton.setTitle("", forState: .Normal)
118 | cancelButton.setImage(UIImage(named:"cancelButton")!, forState: .Normal)
119 | cancelButton.addTarget(self, action: #selector(OverlayViewController.cancelButtonPressed), forControlEvents:.TouchUpInside)
120 |
121 | countDownLabel = TimeTextLabel(fontSize: 30.0)
122 | countDownLabel.fontSize = fw * 0.25
123 | countDownLabel.alignmentMode = kCAAlignmentCenter
124 |
125 | }
126 |
127 |
128 | func initiateCountDownTimer(){
129 | countDownStopWatch.delegate = self
130 | countDownStopWatch.countDownFromSeconds = videoSettings.videoDelay
131 | countDownStopWatch.start()
132 | }
133 |
134 |
135 | //Stop watch timer delegate methods for countdown timer
136 | func stopWatchDidCountDown(t: TimeInterval) {
137 | countDownStopWatch.stop()
138 | }
139 |
140 | func stopWatchDidUpdateWithTimeInterval(t: TimeInterval) {
141 | countDownLabel.string = t.intervalString(false)
142 | }
143 |
144 |
145 | //Button methods
146 |
147 | func saveButtonPressed(){
148 | countDownStopWatch.stop()
149 | let cWorkOut = workOut as! StandardWOD
150 | cWorkOut.stopWorkOut()
151 | videoManager!.stopRecordingVideo { (videoURL, error) -> Void in
152 |
153 | }
154 | }
155 |
156 | func cameraManagerDidGenerateVideoAsset(url: NSURL) {
157 | print("Got URL of imgae : \(url.absoluteString)")
158 | }
159 |
160 | func cancelButtonPressed(){
161 |
162 | countDownStopWatch.stop()
163 | self.delegate!.overlayDidCancel()
164 | }
165 |
166 |
167 | //Initialization
168 | override func viewDidAppear(animated: Bool) {
169 | super.viewDidAppear(animated)
170 |
171 | //Lay out depending on the orientation of the screen
172 | switch videoSettings.videoOrientation! {
173 | case .Portrait:layOutForPortrait()
174 | case .LandscapeLeft:layOutForLandscapeLeft()
175 | case .LandscapeRight:layOutForLandscapeRight()
176 | default: print("Orientation not set when moving from video to over lay")
177 | }
178 |
179 | initiateCountDownTimer()
180 | }
181 |
182 | override func viewDidLoad() {
183 | super.viewDidLoad()
184 | hasCountDown = videoSettings.videoDelay > 0
185 | //Create all the buttons ready to be layed out later
186 | createUI()
187 | }
188 | override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
189 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
190 | commonInit()
191 | }
192 |
193 | required init?(coder aDecoder: NSCoder) {
194 | super.init(coder: aDecoder)
195 | commonInit()
196 | }
197 |
198 | private func commonInit() {
199 | modalPresentationStyle = .OverCurrentContext
200 | }
201 |
202 |
203 | //View animtions
204 | func addPulsing() {
205 |
206 | let alphaChange = CAKeyframeAnimation(keyPath:"opacity")
207 | alphaChange.duration = 2.0
208 | alphaChange.values = [1.0,0.2,1.0]
209 | alphaChange.keyTimes = [0.0,0.4,0.8]
210 | alphaChange.removedOnCompletion = false
211 | alphaChange.repeatCount = HUGE
212 | alphaChange.calculationMode = kCAAnimationPaced
213 |
214 | let pulsing = CAKeyframeAnimation(keyPath: "transform.scale")
215 |
216 | pulsing.duration = 2.0
217 |
218 | pulsing.values = [1.0,0.8,1.0]
219 | pulsing.keyTimes = [0.0,0.4,0.8]
220 |
221 | pulsing.removedOnCompletion = false
222 | pulsing.repeatCount = HUGE
223 | pulsing.calculationMode = kCAAnimationPaced
224 |
225 | //set background color
226 | saveButton.tintColor = UIColor.redColor()
227 | let layer = saveButton.layer
228 | layer.addAnimation(pulsing, forKey: "pulsing")
229 | layer.addAnimation(alphaChange, forKey: "alpha")
230 | }
231 |
232 |
233 | //Adimin
234 | override func shouldAutorotate() -> Bool {
235 | return false
236 | }
237 |
238 | override func prefersStatusBarHidden() -> Bool {
239 | return true
240 | }
241 |
242 |
243 | override func didReceiveMemoryWarning() {
244 | super.didReceiveMemoryWarning()
245 | // Dispose of any resources that can be recreated.
246 | }
247 |
248 |
249 |
250 |
251 |
252 | }
253 |
--------------------------------------------------------------------------------
/IntervalSettingsVC.swift:
--------------------------------------------------------------------------------
1 | //
2 | // IntervalSettingsVC.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 22/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 |
12 |
13 | class IntervalSettingsVC: UIViewController,UITableViewDataSource,UITableViewDelegate,ViewCanceling {
14 |
15 | var wod:WOD!
16 | var delegate:ViewCanceling?
17 |
18 | var rounds = [PresetRound]()
19 |
20 | @IBOutlet weak var loopLabel:UILabel!
21 | @IBOutlet weak var roundLabel:UILabel!
22 | @IBOutlet weak var loopStepper:UIStepper!
23 | @IBOutlet weak var roundStepper:UIStepper!
24 | @IBOutlet weak var tableView:UITableView!
25 |
26 |
27 | var loopStepperPV:Double = 1.0
28 | var roundStepperPV:Double = 1.0
29 |
30 | override func viewDidLoad() {
31 | super.viewDidLoad()
32 |
33 | let round = PresetRound(roundNumber: 1, presetDuration: 30,loopNumber:1)
34 | rounds.append(round)
35 |
36 | // Do any additional setup after loading the view.
37 | }
38 |
39 | override func didReceiveMemoryWarning() {
40 | super.didReceiveMemoryWarning()
41 | // Dispose of any resources that can be recreated.
42 | }
43 |
44 |
45 | @IBAction func loopStepperChanged(sender:UIStepper){
46 | if shouldWarnUser(){
47 | if sender.value > loopStepperPV{
48 | sender.value -= 1
49 | }
50 | warnUser()
51 | }else{
52 | loopLabel.text = Int(sender.value).timesThrough()
53 | }
54 |
55 | }
56 |
57 | func warnUser(){
58 | let infoView = UIAlertController(title: "5 Minute Limit", message: "If you like our app, unlock more minutes and help support future great features, with the 'Unlimited Video' purchase. (It's as cheap as they would allow us!)", preferredStyle: UIAlertControllerStyle.Alert)
59 | infoView.view.tintColor = UIColor(red: 240/255.0, green: 178/255.0, blue: 71/255.0, alpha: 1.0)
60 |
61 | let ok = UIAlertAction(title:"Lets Go!", style: UIAlertActionStyle.Default, handler:{
62 | (alert:UIAlertAction!) -> Void in
63 |
64 | let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("WodeoStore") as! WodeoProductPage
65 | viewController.delegate = self
66 | self.presentViewController(viewController, animated: true, completion: nil)
67 |
68 |
69 | })
70 |
71 | let no = UIAlertAction(title: "No thanks..", style: UIAlertActionStyle.Cancel, handler:{ (alert:UIAlertAction!) -> Void in
72 | })
73 |
74 | infoView.addAction(ok)
75 | infoView.addAction(no)
76 |
77 | presentViewController(infoView, animated:true, completion:nil)
78 | }
79 |
80 | @IBAction func roundStepperChanged(sender:UIStepper){
81 |
82 | if sender.value < roundStepperPV {
83 | roundLabel.text = "\(Int(sender.value))"
84 |
85 | if Int(sender.value) == rounds.count {
86 | return
87 | }
88 |
89 | if Int(sender.value) <= rounds.count {
90 | rounds.removeLast()
91 | }else{
92 | let newRound = PresetRound(roundNumber:rounds.count + 1, presetDuration: 30,loopNumber:1)
93 | rounds.append(newRound)
94 | }
95 |
96 | tableView.reloadData()
97 | roundStepperPV = sender.value
98 | return
99 |
100 | }
101 |
102 |
103 | if shouldWarnUser(){
104 | if sender.value > roundStepperPV {
105 | sender.value -= 1
106 | }
107 | warnUser()
108 | }else{
109 |
110 | roundLabel.text = "\(Int(sender.value))"
111 |
112 | if Int(sender.value) == rounds.count {
113 | return
114 | }
115 |
116 | if Int(sender.value) <= rounds.count {
117 | rounds.removeLast()
118 | }else{
119 | let newRound = PresetRound(roundNumber:rounds.count + 1, presetDuration: 30,loopNumber:1)
120 | rounds.append(newRound)
121 | }
122 |
123 | tableView.reloadData()
124 | roundStepperPV = sender.value
125 | }
126 | }
127 |
128 | @IBAction func cancel(sender:AnyObject){
129 | if let del = delegate {
130 | del.viewDidCancel()
131 | }else{
132 | print("Delegate not set")
133 | }
134 | }
135 |
136 | @IBAction func go(sender:AnyObject){
137 |
138 |
139 |
140 | performSegueWithIdentifier("goToVideo", sender: nil)
141 |
142 | }
143 |
144 |
145 | //UITableView Delegate
146 | func tableView(tableView: UITableView, shouldHighlightRowAtIndexPath indexPath: NSIndexPath) -> Bool {
147 | return false
148 | }
149 |
150 |
151 | func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
152 |
153 | let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
154 |
155 | let stepper = UIStepper(frame:CGRectMake(cell.frame.width - 102,(cell.frame.height / 2) - 14.5,94,29))
156 | stepper.maximumValue = 10000
157 | stepper.minimumValue = 1
158 | stepper.wraps = false
159 | stepper.addTarget(self, action:#selector(IntervalSettingsVC.cellStepperChanged(_:)), forControlEvents:.ValueChanged)
160 | stepper.tag = indexPath.row
161 | stepper.tintColor = UIColor(red: 240/255.0, green: 178/255.0, blue: 71/255.0, alpha: 1.0)
162 |
163 |
164 | cell.addSubview(stepper)
165 |
166 | cell.textLabel!.text = "Round \(rounds[indexPath.row].roundNumber)"
167 |
168 | let roundDuration = rounds[indexPath.row].presetDuration
169 |
170 | stepper.value = Double(roundDuration)
171 |
172 | if roundDuration <= 59 {
173 | cell.detailTextLabel!.text = "\(Double(rounds[indexPath.row].presetDuration).stringForTimeInterval(false)) seconds"
174 | }else{
175 | cell.detailTextLabel!.text = "\(Double(rounds[indexPath.row].presetDuration).stringForTimeInterval(false))"
176 | }
177 |
178 | return cell
179 |
180 | }
181 |
182 | func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
183 | return rounds.count
184 | }
185 |
186 | func numberOfSectionsInTableView(tableView: UITableView) -> Int {
187 | return 1
188 | }
189 |
190 | func cellStepperChanged(sender:UIStepper) {
191 | rounds[sender.tag].presetDuration = Int(sender.value)
192 | tableView.reloadData()
193 | }
194 |
195 | func shouldWarnUser() -> Bool {
196 | var totalRoundSeconds = 0
197 | for r in rounds {
198 | totalRoundSeconds += r.presetDuration
199 | }
200 |
201 | return (Double(totalRoundSeconds) * loopStepper.value) > wod.totalVideoSecondsAllowed ? true : false
202 | }
203 |
204 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
205 |
206 | if segue.identifier == "goToVideo" {
207 | let vc = segue.destinationViewController as! VideoViewController
208 | vc.delegate = self
209 | let intWOD = wod as! IntervalWOD
210 |
211 |
212 | intWOD.rounds = gererateRounds()
213 | intWOD.loops = Int(loopStepper.value)
214 | vc.workout = intWOD
215 |
216 |
217 |
218 | var counter = 1
219 | for r in intWOD.rounds {
220 | print("Round \(counter) Loop = \(r.loopNumber)")
221 | counter += 1
222 | }
223 |
224 | }
225 | }
226 |
227 | func gererateRounds() -> [PresetRound] {
228 |
229 | var loopCounter = 0
230 | var roundCounter = 0
231 | var proccessedRounds = [PresetRound]()
232 |
233 |
234 | while loopCounter < Int(loopStepper.value) {
235 | for r in rounds {
236 | proccessedRounds.append(PresetRound(roundNumber: r.roundNumber, presetDuration:r.presetDuration, loopNumber: loopCounter + 1))
237 | roundCounter += 1
238 | }
239 | loopCounter += 1
240 | }
241 |
242 | return proccessedRounds
243 |
244 | }
245 |
246 | override func prefersStatusBarHidden() -> Bool {
247 | return true
248 | }
249 |
250 | func viewDidCancel() {
251 | dismissViewControllerAnimated(true, completion: nil)
252 | }
253 |
254 | }
255 |
--------------------------------------------------------------------------------
/TabataSettingsVC.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TabataSettingsVC.swift
3 | // Wodeo 2
4 | //
5 | // Created by Gareth Long on 27/02/2016.
6 | // Copyright © 2016 Elliott Brown. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class TabataSettingsVC: UIViewController,UITableViewDataSource,UITableViewDelegate,ViewCanceling {
12 |
13 | var wod:WOD!
14 | var delegate:ViewCanceling?
15 |
16 | var rounds = [PresetRound]()
17 |
18 | @IBOutlet weak var loopLabel:UILabel!
19 | @IBOutlet weak var roundLabel:UILabel!
20 | @IBOutlet weak var loopStepper:UIStepper!
21 | @IBOutlet weak var roundStepper:UIStepper!
22 | @IBOutlet weak var restLabel:UILabel!
23 | @IBOutlet weak var restStepper:UIStepper!
24 | @IBOutlet weak var tableView:UITableView!
25 |
26 |
27 | var restStepperPV = 0.0
28 | var loopStepperPV = 0.0
29 | var roundStepperPV = 0.0
30 |
31 |
32 | override func viewDidLoad() {
33 | super.viewDidLoad()
34 |
35 | var counter = 0
36 | while counter < 8 {
37 | let round = PresetRound(roundNumber:counter + 1, presetDuration: 20,loopNumber:1)
38 | rounds.append(round)
39 | counter += 1
40 | }
41 |
42 | restStepper.value = 10
43 | roundStepper.value = 8
44 |
45 | // Do any additional setup after loading the view.
46 | }
47 |
48 | override func didReceiveMemoryWarning() {
49 | super.didReceiveMemoryWarning()
50 | // Dispose of any resources that can be recreated.
51 | }
52 |
53 |
54 | @IBAction func restStepperChanged(sender:UIStepper){
55 |
56 | if sender.value < restStepperPV{
57 | restLabel.text = "\(Int(sender.value))"
58 | restStepperPV = sender.value
59 | return
60 | }
61 |
62 | if shouldWarnUser(){
63 | if sender.value > restStepperPV {
64 | sender.value -= 1
65 | restStepperPV = sender.value
66 | }
67 | warnUser()
68 | }else{
69 | restLabel.text = "\(Int(sender.value))"
70 | restStepperPV = sender.value
71 | }
72 |
73 |
74 | }
75 |
76 |
77 | @IBAction func loopStepperChanged(sender:UIStepper){
78 |
79 | if sender.value < loopStepperPV{
80 | loopLabel.text = Int(sender.value).timesThrough()
81 | loopStepperPV = sender.value
82 | return
83 | }
84 |
85 | if shouldWarnUser(){
86 | if sender.value > loopStepperPV {
87 | sender.value -= 1
88 | }
89 | warnUser()
90 | }else{
91 | loopLabel.text = Int(sender.value).timesThrough()
92 | loopStepperPV = sender.value
93 | }
94 |
95 | }
96 |
97 | @IBAction func roundStepperChanged(sender:UIStepper){
98 |
99 | if sender.value < roundStepperPV{
100 | roundLabel.text = "\(Int(sender.value))"
101 |
102 | if Int(sender.value) == rounds.count {
103 | return
104 | }
105 |
106 | if Int(sender.value) <= rounds.count {
107 | rounds.removeLast()
108 | }else{
109 | let newRound = PresetRound(roundNumber:rounds.count + 1, presetDuration: 20,loopNumber:1)
110 | rounds.append(newRound)
111 | }
112 |
113 | tableView.reloadData()
114 | roundStepperPV = sender.value
115 | return
116 | }
117 |
118 | if shouldWarnUser(){
119 | if sender.value > roundStepperPV {
120 | sender.value -= 1
121 | }
122 | warnUser()
123 | }else{
124 |
125 | roundLabel.text = "\(Int(sender.value))"
126 |
127 | if Int(sender.value) == rounds.count {
128 | return
129 | }
130 |
131 | if Int(sender.value) <= rounds.count {
132 | rounds.removeLast()
133 | }else{
134 | let newRound = PresetRound(roundNumber:rounds.count + 1, presetDuration: 20,loopNumber:1)
135 | rounds.append(newRound)
136 | }
137 |
138 | tableView.reloadData()
139 | roundStepperPV = sender.value
140 | }
141 | }
142 |
143 | func shouldWarnUser() -> Bool {
144 | var totalSecondsInRounds = 0
145 | for r in rounds {
146 | totalSecondsInRounds += r.presetDuration
147 | }
148 |
149 | let totalRest = restStepper.value * (Double(rounds.count) - 1)
150 |
151 | let total = Double(totalRest + Double(totalSecondsInRounds)) * loopStepper.value
152 |
153 | return total > wod.totalVideoSecondsAllowed ? true : false
154 |
155 | }
156 |
157 | func warnUser(){
158 | let infoView = UIAlertController(title: "5 Minute Limit", message: "If you like our app, unlock more minutes and help support future great features, with the 'Unlimited Video' purchase. (It's as cheap as they would allow us!)", preferredStyle: UIAlertControllerStyle.Alert)
159 | infoView.view.tintColor = UIColor(red: 240/255.0, green: 178/255.0, blue: 71/255.0, alpha: 1.0)
160 |
161 | let ok = UIAlertAction(title:"Lets Go!", style: UIAlertActionStyle.Default, handler:{
162 | (alert:UIAlertAction!) -> Void in
163 |
164 | let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("WodeoStore") as! WodeoProductPage
165 | viewController.delegate = self
166 | self.presentViewController(viewController, animated: true, completion: nil)
167 |
168 |
169 | })
170 |
171 | let no = UIAlertAction(title: "No thanks..", style: UIAlertActionStyle.Cancel, handler:{ (alert:UIAlertAction!) -> Void in
172 | })
173 |
174 | infoView.addAction(ok)
175 | infoView.addAction(no)
176 |
177 | presentViewController(infoView, animated:true, completion:nil)
178 | }
179 |
180 |
181 | @IBAction func cancel(sender:AnyObject){
182 | if let del = delegate {
183 | del.viewDidCancel()
184 | }else{
185 | print("Delegate not set")
186 | }
187 | }
188 |
189 | @IBAction func go(sender:AnyObject){
190 |
191 |
192 |
193 | performSegueWithIdentifier("goToVideo", sender: nil)
194 |
195 | }
196 |
197 |
198 | //UITableView Delegate
199 |
200 | func tableView(tableView: UITableView, shouldHighlightRowAtIndexPath indexPath: NSIndexPath) -> Bool {
201 | return false
202 | }
203 |
204 | func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
205 |
206 | let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
207 |
208 | let stepper = UIStepper(frame:CGRectMake(cell.frame.width - 102,(cell.frame.height / 2) - 14.5,94,29))
209 | stepper.maximumValue = 10000
210 | stepper.minimumValue = 1
211 | stepper.wraps = false
212 | stepper.addTarget(self, action:#selector(TabataSettingsVC.cellStepperChanged(_:)), forControlEvents:.ValueChanged)
213 | stepper.tag = indexPath.row
214 | stepper.tintColor = UIColor(red: 240/255.0, green: 178/255.0, blue: 71/255.0, alpha: 1.0)
215 |
216 |
217 | cell.addSubview(stepper)
218 |
219 | cell.textLabel!.text = "Round \(rounds[indexPath.row].roundNumber)"
220 |
221 | let roundDuration = rounds[indexPath.row].presetDuration
222 |
223 | stepper.value = Double(roundDuration)
224 |
225 | if roundDuration <= 59 {
226 | cell.detailTextLabel!.text = "\(Double(rounds[indexPath.row].presetDuration).stringForTimeInterval(false)) seconds"
227 | }else{
228 | cell.detailTextLabel!.text = "\(Double(rounds[indexPath.row].presetDuration).stringForTimeInterval(false))"
229 | }
230 |
231 | return cell
232 |
233 | }
234 |
235 | func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
236 | return rounds.count
237 | }
238 |
239 | func numberOfSectionsInTableView(tableView: UITableView) -> Int {
240 | return 1
241 | }
242 |
243 | func cellStepperChanged(sender:UIStepper) {
244 | rounds[sender.tag].presetDuration = Int(sender.value)
245 | tableView.reloadData()
246 | }
247 |
248 |
249 | override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
250 |
251 | if segue.identifier == "goToVideo" {
252 | let vc = segue.destinationViewController as! VideoViewController
253 | vc.delegate = self
254 | let intWOD = wod as! TabataWOD
255 |
256 |
257 | intWOD.rounds = gererateRounds()
258 | intWOD.loops = Int(loopStepper.value)
259 | intWOD.restPeriodBetweenRounds = Int(restStepper.value)
260 | vc.workout = intWOD
261 |
262 |
263 |
264 | var counter = 1
265 | for r in intWOD.rounds {
266 | print("Round \(counter) Loop = \(r.loopNumber)")
267 | counter += 1
268 | }
269 |
270 | }
271 | }
272 |
273 | func gererateRounds() -> [PresetRound] {
274 |
275 | var loopCounter = 0
276 | var roundCounter = 0
277 | var proccessedRounds = [PresetRound]()
278 |
279 |
280 | while loopCounter < Int(loopStepper.value) {
281 | for r in rounds {
282 | proccessedRounds.append(PresetRound(roundNumber: r.roundNumber, presetDuration:r.presetDuration, loopNumber: loopCounter + 1))
283 | roundCounter += 1
284 | }
285 | loopCounter += 1
286 | }
287 |
288 | return proccessedRounds
289 |
290 | }
291 |
292 | override func prefersStatusBarHidden() -> Bool {
293 | return true
294 | }
295 |
296 | func viewDidCancel() {
297 | dismissViewControllerAnimated(true, completion: nil)
298 | }
299 |
300 | }
301 |
--------------------------------------------------------------------------------