├── WorkoutApp ├── SupportingFiles │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── common │ │ │ ├── Contents.json │ │ │ ├── add_button.imageset │ │ │ │ ├── add_button.pdf │ │ │ │ └── Contents.json │ │ │ └── down_arrow.imageset │ │ │ │ ├── down_arrow.pdf │ │ │ │ └── Contents.json │ │ ├── overview │ │ │ ├── Contents.json │ │ │ ├── checkmark_done.imageset │ │ │ │ ├── Checked.pdf │ │ │ │ └── Contents.json │ │ │ ├── checkmark_not_done.imageset │ │ │ │ ├── Oval.pdf │ │ │ │ └── Contents.json │ │ │ └── right_arrow.imageset │ │ │ │ ├── Right Arrow.pdf │ │ │ │ └── Contents.json │ │ ├── stats │ │ │ ├── Contents.json │ │ │ ├── stats_averagePace.imageset │ │ │ │ ├── Speed Icon.pdf │ │ │ │ └── Contents.json │ │ │ ├── stats_totalSteps.imageset │ │ │ │ ├── Steps Icon.pdf │ │ │ │ └── Contents.json │ │ │ ├── stats_heartRate.imageset │ │ │ │ ├── Heart Beat Icon.pdf │ │ │ │ └── Contents.json │ │ │ └── stats_totalDistance.imageset │ │ │ │ ├── Maps Icon.pdf │ │ │ │ └── Contents.json │ │ ├── tabBar │ │ │ ├── Contents.json │ │ │ ├── overview_tab.imageset │ │ │ │ ├── overview_tab.pdf │ │ │ │ └── Contents.json │ │ │ ├── progress_tab.imageset │ │ │ │ ├── progress_tab.pdf │ │ │ │ └── Contents.json │ │ │ ├── session_tab.imageset │ │ │ │ ├── session_tab.pdf │ │ │ │ └── Contents.json │ │ │ └── settings_tab.imageset │ │ │ │ ├── settings_tab.pdf │ │ │ │ └── Contents.json │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ └── Base.lproj │ │ └── LaunchScreen.storyboard ├── Application │ ├── AppDelegate.swift │ └── SceneDelegate.swift ├── Controllers │ ├── Settings │ │ └── SettingsController.swift │ ├── Base │ │ ├── Views │ │ │ ├── WABaseView.swift │ │ │ ├── WABarsView │ │ │ │ ├── WABarsView.swift │ │ │ │ └── Views │ │ │ │ │ └── WABarView.swift │ │ │ ├── WAChartsView │ │ │ │ ├── Views │ │ │ │ │ ├── XAxisView.swift │ │ │ │ │ ├── YAxisView.swift │ │ │ │ │ └── ChartView.swift │ │ │ │ └── WAChartsView.swift │ │ │ ├── WAButton.swift │ │ │ └── WABaseInfoView.swift │ │ └── WABaseController.swift │ ├── Navigation │ │ ├── NavBarController.swift │ │ └── TabBarController.swift │ ├── Session │ │ ├── Views │ │ │ ├── Steps │ │ │ │ └── StepsView.swift │ │ │ ├── Stats │ │ │ │ ├── StatsView.swift │ │ │ │ └── Views │ │ │ │ │ └── StatsItemView.swift │ │ │ └── Timer │ │ │ │ ├── PercentView.swift │ │ │ │ ├── ProgressView.swift │ │ │ │ └── TimerView.swift │ │ └── SessionController.swift │ ├── Progress │ │ ├── Views │ │ │ ├── DailyPerformance │ │ │ │ └── DailyPerformanceView.swift │ │ │ └── MonthlyPerformnaceView │ │ │ │ └── MonthlyPerformanceView.swift │ │ └── ProgressController.swift │ └── Overview │ │ ├── Views │ │ ├── NavBar │ │ │ ├── WeekView │ │ │ │ ├── WeekView.swift │ │ │ │ └── WeekdayView.swift │ │ │ └── OverviewNavBar.swift │ │ └── CollectionView │ │ │ └── Views │ │ │ ├── SectionHeaderView.swift │ │ │ └── TrainingCellView.swift │ │ └── OverviewController.swift └── Helpers │ ├── Extensions │ ├── Date + ext.swift │ ├── UIColor + ext.swift │ └── UIView + ext.swift │ └── Resouces.swift └── WorkoutApp.xcodeproj ├── project.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── xcuserdata └── viktor.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist └── project.pbxproj /WorkoutApp/SupportingFiles/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/common/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/overview/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/stats/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/tabBar/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /WorkoutApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/common/add_button.imageset/add_button.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VAmpersand/WorkoutApp/HEAD/WorkoutApp/SupportingFiles/Assets.xcassets/common/add_button.imageset/add_button.pdf -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/common/down_arrow.imageset/down_arrow.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VAmpersand/WorkoutApp/HEAD/WorkoutApp/SupportingFiles/Assets.xcassets/common/down_arrow.imageset/down_arrow.pdf -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/overview/checkmark_done.imageset/Checked.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VAmpersand/WorkoutApp/HEAD/WorkoutApp/SupportingFiles/Assets.xcassets/overview/checkmark_done.imageset/Checked.pdf -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/overview/checkmark_not_done.imageset/Oval.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VAmpersand/WorkoutApp/HEAD/WorkoutApp/SupportingFiles/Assets.xcassets/overview/checkmark_not_done.imageset/Oval.pdf -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/overview/right_arrow.imageset/Right Arrow.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VAmpersand/WorkoutApp/HEAD/WorkoutApp/SupportingFiles/Assets.xcassets/overview/right_arrow.imageset/Right Arrow.pdf -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/tabBar/overview_tab.imageset/overview_tab.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VAmpersand/WorkoutApp/HEAD/WorkoutApp/SupportingFiles/Assets.xcassets/tabBar/overview_tab.imageset/overview_tab.pdf -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/tabBar/progress_tab.imageset/progress_tab.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VAmpersand/WorkoutApp/HEAD/WorkoutApp/SupportingFiles/Assets.xcassets/tabBar/progress_tab.imageset/progress_tab.pdf -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/tabBar/session_tab.imageset/session_tab.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VAmpersand/WorkoutApp/HEAD/WorkoutApp/SupportingFiles/Assets.xcassets/tabBar/session_tab.imageset/session_tab.pdf -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/tabBar/settings_tab.imageset/settings_tab.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VAmpersand/WorkoutApp/HEAD/WorkoutApp/SupportingFiles/Assets.xcassets/tabBar/settings_tab.imageset/settings_tab.pdf -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/stats/stats_averagePace.imageset/Speed Icon.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VAmpersand/WorkoutApp/HEAD/WorkoutApp/SupportingFiles/Assets.xcassets/stats/stats_averagePace.imageset/Speed Icon.pdf -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/stats/stats_totalSteps.imageset/Steps Icon.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VAmpersand/WorkoutApp/HEAD/WorkoutApp/SupportingFiles/Assets.xcassets/stats/stats_totalSteps.imageset/Steps Icon.pdf -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/stats/stats_heartRate.imageset/Heart Beat Icon.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VAmpersand/WorkoutApp/HEAD/WorkoutApp/SupportingFiles/Assets.xcassets/stats/stats_heartRate.imageset/Heart Beat Icon.pdf -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/stats/stats_totalDistance.imageset/Maps Icon.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/VAmpersand/WorkoutApp/HEAD/WorkoutApp/SupportingFiles/Assets.xcassets/stats/stats_totalDistance.imageset/Maps Icon.pdf -------------------------------------------------------------------------------- /WorkoutApp.xcodeproj/xcuserdata/viktor.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/common/add_button.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "add_button.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/common/down_arrow.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "down_arrow.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/overview/checkmark_done.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Checked.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/overview/checkmark_not_done.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Oval.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/overview/right_arrow.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Right Arrow.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/tabBar/overview_tab.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "overview_tab.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/tabBar/progress_tab.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "progress_tab.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/tabBar/session_tab.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "session_tab.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/tabBar/settings_tab.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "settings_tab.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/stats/stats_averagePace.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Speed Icon.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/stats/stats_heartRate.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Heart Beat Icon.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/stats/stats_totalDistance.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Maps Icon.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/stats/stats_totalSteps.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Steps Icon.pdf", 5 | "idiom" : "universal" 6 | } 7 | ], 8 | "info" : { 9 | "author" : "xcode", 10 | "version" : 1 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /WorkoutApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /WorkoutApp.xcodeproj/xcuserdata/viktor.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | WorkoutApp.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /WorkoutApp/Application/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 23.05.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | func application( 14 | _ application: UIApplication, 15 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 16 | ) -> Bool { 17 | return true 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Settings/SettingsController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsController.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 23.05.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | final class SettingsController: WABaseController { 11 | 12 | override func viewDidLoad() { 13 | super.viewDidLoad() 14 | 15 | title = R.Strings.NavBar.settings 16 | } 17 | } 18 | 19 | extension SettingsController { 20 | override func setupViews() { 21 | super.setupViews() 22 | 23 | } 24 | 25 | override func constraintViews() { 26 | super.constraintViews() 27 | 28 | } 29 | 30 | override func configureAppearance() { 31 | super.configureAppearance() 32 | 33 | title = R.Strings.NavBar.settings 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Base/Views/WABaseView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WABaseView.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 26.05.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | class WABaseView: UIView { 11 | override init(frame: CGRect) { 12 | super.init(frame: frame) 13 | 14 | setupViews() 15 | constaintViews() 16 | configureAppearance() 17 | } 18 | 19 | required init?(coder: NSCoder) { 20 | super.init(coder: coder) 21 | 22 | setupViews() 23 | constaintViews() 24 | configureAppearance() 25 | } 26 | } 27 | 28 | @objc extension WABaseView { 29 | func setupViews() {} 30 | func constaintViews() {} 31 | 32 | func configureAppearance() { 33 | backgroundColor = .white 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Navigation/NavBarController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavBarController.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 23.05.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | final class NavBarController: UINavigationController { 11 | 12 | override func viewDidLoad() { 13 | super.viewDidLoad() 14 | 15 | configureAppearance() 16 | } 17 | 18 | private func configureAppearance() { 19 | view.backgroundColor = .white 20 | navigationBar.isTranslucent = false 21 | navigationBar.standardAppearance.titleTextAttributes = [ 22 | .foregroundColor: R.Colors.titleGray, 23 | .font: R.Fonts.helvelticaRegular(with: 17) 24 | ] 25 | 26 | navigationBar.addBottomBorder(with: R.Colors.separator, height: 1) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /WorkoutApp/Application/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 23.05.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | func scene(_ scene: UIScene, 15 | willConnectTo session: UISceneSession, 16 | options connectionOptions: UIScene.ConnectionOptions) { 17 | 18 | let tabBarController = TabBarController() 19 | 20 | guard let windowScene = (scene as? UIWindowScene) else { return } 21 | window = UIWindow(frame: windowScene.coordinateSpace.bounds) 22 | window?.windowScene = windowScene 23 | window?.rootViewController = tabBarController 24 | window?.makeKeyAndVisible() 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | UIApplicationSceneManifest 6 | 7 | UIApplicationSupportsMultipleScenes 8 | 9 | UISceneConfigurations 10 | 11 | UIWindowSceneSessionRoleApplication 12 | 13 | 14 | UISceneConfigurationName 15 | Default Configuration 16 | UISceneDelegateClassName 17 | $(PRODUCT_MODULE_NAME).SceneDelegate 18 | 19 | 20 | 21 | 22 | UIStatusBarStyle 23 | UIStatusBarStyleDefault 24 | 25 | 26 | -------------------------------------------------------------------------------- /WorkoutApp/Helpers/Extensions/Date + ext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Date + ext.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 28.05.2022. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Date { 11 | static var calendar: Calendar = { 12 | var calendar = Calendar(identifier: .gregorian) 13 | calendar.firstWeekday = 2 14 | return calendar 15 | }() 16 | 17 | var startOfWeek: Date { 18 | let components = Date.calendar.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self) 19 | guard let firstDay = Date.calendar.date(from: components) else { return self } 20 | return Date.calendar.date(byAdding: .day, value: 0, to: firstDay) ?? self 21 | } 22 | 23 | func agoForward(to days: Int) -> Date { 24 | return Date.calendar.date(byAdding: .day, value: days, to: self) ?? self 25 | } 26 | 27 | func stripTime() -> Date { 28 | let components = Date.calendar.dateComponents([.year, .month, .day], from: self) 29 | return Date.calendar.date(from: components) ?? self 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /WorkoutApp/Helpers/Extensions/UIColor + ext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor + ext.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 23.05.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIColor { 11 | convenience init(hexString: String) { 12 | let hex = hexString.trimmingCharacters(in: CharacterSet.alphanumerics.inverted) 13 | var int = UInt64() 14 | Scanner(string: hex).scanHexInt64(&int) 15 | let a, r, g, b: UInt64 16 | switch hex.count { 17 | case 3: // RGB (12-bit) 18 | (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17) 19 | case 6: // RGB (24-bit) 20 | (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF) 21 | case 8: // ARGB (32-bit) 22 | (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF) 23 | default: 24 | (a, r, g, b) = (255, 0, 0, 0) 25 | } 26 | self.init(red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: CGFloat(a) / 255) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Session/Views/Steps/StepsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StepsView.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 08.10.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | final class StepsView: WABaseInfoView { 11 | 12 | private let barsView = WABarsView() 13 | 14 | func configure(with itmes: [WABarView.Data]) { 15 | barsView.configure(with: itmes) 16 | } 17 | } 18 | 19 | extension StepsView { 20 | override func setupViews() { 21 | super.setupViews() 22 | 23 | setupView(barsView) 24 | } 25 | 26 | override func constaintViews() { 27 | super.constaintViews() 28 | 29 | NSLayoutConstraint.activate([ 30 | barsView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 15), 31 | barsView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10), 32 | barsView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10), 33 | barsView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -15), 34 | ]) 35 | } 36 | 37 | override func configureAppearance() { 38 | super.configureAppearance() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Progress/Views/DailyPerformance/DailyPerformanceView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DailyPerformanceView.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 07.12.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | final class DailyPerformanceView: WABaseInfoView { 11 | 12 | private let barsView = WABarsView() 13 | 14 | func configure(with itmes: [WABarView.Data]) { 15 | barsView.configure(with: itmes) 16 | } 17 | } 18 | 19 | extension DailyPerformanceView { 20 | override func setupViews() { 21 | super.setupViews() 22 | 23 | setupView(barsView) 24 | } 25 | 26 | override func constaintViews() { 27 | super.constaintViews() 28 | 29 | NSLayoutConstraint.activate([ 30 | barsView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 15), 31 | barsView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10), 32 | barsView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10), 33 | barsView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -15), 34 | ]) 35 | } 36 | 37 | override func configureAppearance() { 38 | super.configureAppearance() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Progress/Views/MonthlyPerformnaceView/MonthlyPerformanceView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MonthlyPerformanceView.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 07.12.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | final class MonthlyPerformanceView: WABaseInfoView { 11 | 12 | private let chartsView = WAChartsView() 13 | 14 | func configure(with itmes: [WAChartsView.Data], topChartOffset: Int) { 15 | chartsView.configure(with: itmes, topChartOffset: topChartOffset) 16 | } 17 | } 18 | 19 | extension MonthlyPerformanceView { 20 | override func setupViews() { 21 | super.setupViews() 22 | 23 | setupView(chartsView) 24 | } 25 | 26 | override func constaintViews() { 27 | super.constaintViews() 28 | 29 | NSLayoutConstraint.activate([ 30 | chartsView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 15), 31 | chartsView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 15), 32 | chartsView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -15), 33 | chartsView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -15), 34 | ]) 35 | } 36 | 37 | override func configureAppearance() { 38 | super.configureAppearance() 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Base/Views/WABarsView/WABarsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WABarsView.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 08.10.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | final class WABarsView: WABaseView { 11 | 12 | private let stackView: UIStackView = { 13 | let view = UIStackView() 14 | view.distribution = .fillEqually 15 | return view 16 | }() 17 | 18 | func configure(with data: [WABarView.Data]) { 19 | data.forEach { 20 | let barView = WABarView(data: $0) 21 | stackView.addArrangedSubview(barView) 22 | } 23 | } 24 | } 25 | 26 | extension WABarsView { 27 | override func setupViews() { 28 | super.setupViews() 29 | 30 | setupView(stackView) 31 | } 32 | 33 | override func constaintViews() { 34 | super.constaintViews() 35 | 36 | NSLayoutConstraint.activate([ 37 | stackView.leadingAnchor.constraint(equalTo: leadingAnchor), 38 | stackView.topAnchor.constraint(equalTo: topAnchor), 39 | stackView.trailingAnchor.constraint(equalTo: trailingAnchor), 40 | stackView.bottomAnchor.constraint(equalTo: bottomAnchor), 41 | ]) 42 | } 43 | 44 | override func configureAppearance() { 45 | super.configureAppearance() 46 | 47 | backgroundColor = .clear 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Session/Views/Stats/StatsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StatsView.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 08.10.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | final class StatsView: WABaseInfoView { 11 | 12 | private let stackView: UIStackView = { 13 | let view = UIStackView() 14 | view.axis = .vertical 15 | view.spacing = 15 16 | return view 17 | }() 18 | 19 | func configure(with itmes: [StatsItem]) { 20 | itmes.forEach { 21 | let itmeView = StatsItemView() 22 | itmeView.configure(with: $0) 23 | stackView.addArrangedSubview(itmeView) 24 | } 25 | } 26 | } 27 | 28 | extension StatsView { 29 | override func setupViews() { 30 | super.setupViews() 31 | 32 | setupView(stackView) 33 | } 34 | 35 | override func constaintViews() { 36 | super.constaintViews() 37 | 38 | NSLayoutConstraint.activate([ 39 | stackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 15), 40 | stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 15), 41 | stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -15), 42 | stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -15), 43 | ]) 44 | } 45 | 46 | override func configureAppearance() { 47 | super.configureAppearance() 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Overview/Views/NavBar/WeekView/WeekView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WeekView.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 28.05.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | final class WeekView: WABaseView { 11 | 12 | private let stackView: UIStackView = { 13 | let stackView = UIStackView() 14 | stackView.spacing = 7 15 | stackView.distribution = .fillEqually 16 | return stackView 17 | }() 18 | } 19 | 20 | extension WeekView { 21 | override func setupViews() { 22 | super.setupViews() 23 | 24 | setupView(stackView) 25 | } 26 | 27 | override func constaintViews() { 28 | super.constaintViews() 29 | 30 | NSLayoutConstraint.activate([ 31 | stackView.topAnchor.constraint(equalTo: topAnchor), 32 | stackView.leadingAnchor.constraint(equalTo: leadingAnchor), 33 | stackView.trailingAnchor.constraint(equalTo: trailingAnchor), 34 | stackView.bottomAnchor.constraint(equalTo: bottomAnchor), 35 | ]) 36 | } 37 | 38 | override func configureAppearance() { 39 | super.configureAppearance() 40 | 41 | var weekdays = Date.calendar.shortStandaloneWeekdaySymbols 42 | 43 | if Date.calendar.firstWeekday == 2 { 44 | let sun = weekdays.remove(at: 0) 45 | weekdays.append(sun) 46 | } 47 | 48 | weekdays.enumerated().forEach { index, name in 49 | let view = WeekdayView() 50 | view.configure(with: index, and: name) 51 | stackView.addArrangedSubview(view) 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Overview/Views/CollectionView/Views/SectionHeaderView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SectionHeaderView.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 10.02.2023. 6 | // 7 | 8 | import UIKit 9 | 10 | final class SectionHeaderView: UICollectionReusableView { 11 | static let id = "SectionHeaderView" 12 | 13 | private let title: UILabel = { 14 | let lable = UILabel() 15 | lable.font = R.Fonts.helvelticaRegular(with: 13) 16 | lable.textColor = R.Colors.inactive 17 | lable.textAlignment = .center 18 | return lable 19 | }() 20 | 21 | override init(frame: CGRect) { 22 | super.init(frame: frame) 23 | 24 | setupViews() 25 | constaintViews() 26 | configureAppearance() 27 | } 28 | 29 | required init?(coder: NSCoder) { 30 | super.init(frame: .zero) 31 | 32 | setupViews() 33 | constaintViews() 34 | configureAppearance() 35 | } 36 | 37 | func configure(with date: Date) { 38 | let dateFormatter = DateFormatter() 39 | dateFormatter.dateFormat = "EEEE, MMMM dd" 40 | 41 | self.title.text = dateFormatter.string(from: date).uppercased() 42 | } 43 | } 44 | 45 | private extension SectionHeaderView { 46 | func setupViews() { 47 | setupView(title) 48 | } 49 | 50 | func constaintViews() { 51 | NSLayoutConstraint.activate([ 52 | title.centerXAnchor.constraint(equalTo: centerXAnchor), 53 | title.centerYAnchor.constraint(equalTo: centerYAnchor) 54 | ]) 55 | } 56 | 57 | func configureAppearance() {} 58 | } 59 | 60 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Base/Views/WAChartsView/Views/XAxisView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // YAxisView.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 07.12.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | final class XAxisView: WABaseView { 11 | 12 | private let stackView: UIStackView = { 13 | let view = UIStackView() 14 | view.distribution = .equalSpacing 15 | return view 16 | }() 17 | 18 | func configure(with data: [WAChartsView.Data]) { 19 | stackView.arrangedSubviews.forEach { 20 | $0.removeFromSuperview() 21 | } 22 | 23 | data.forEach { 24 | let lable = UILabel() 25 | lable.font = R.Fonts.helvelticaRegular(with: 9) 26 | lable.textColor = R.Colors.inactive 27 | lable.textAlignment = .center 28 | lable.text = $0.title.uppercased() 29 | 30 | stackView.addArrangedSubview(lable) 31 | } 32 | } 33 | } 34 | 35 | extension XAxisView { 36 | override func setupViews() { 37 | super.setupViews() 38 | 39 | setupView(stackView) 40 | } 41 | 42 | override func constaintViews() { 43 | super.constaintViews() 44 | 45 | NSLayoutConstraint.activate([ 46 | stackView.leadingAnchor.constraint(equalTo: leadingAnchor), 47 | stackView.topAnchor.constraint(equalTo: topAnchor), 48 | stackView.trailingAnchor.constraint(equalTo: trailingAnchor), 49 | stackView.bottomAnchor.constraint(equalTo: bottomAnchor) 50 | ]) 51 | } 52 | 53 | override func configureAppearance() { 54 | super.configureAppearance() 55 | 56 | backgroundColor = .clear 57 | } 58 | } 59 | 60 | 61 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Base/Views/WAChartsView/Views/YAxisView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // XAxisView.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 07.12.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | final class YAxisView: WABaseView { 11 | 12 | private let stackView: UIStackView = { 13 | let view = UIStackView() 14 | view.axis = .vertical 15 | view.distribution = .equalSpacing 16 | return view 17 | }() 18 | 19 | func configure(with data: [WAChartsView.Data]) { 20 | stackView.arrangedSubviews.forEach { 21 | $0.removeFromSuperview() 22 | } 23 | 24 | (0...9).reversed().forEach { 25 | let lable = UILabel() 26 | lable.font = R.Fonts.helvelticaRegular(with: 9) 27 | lable.textColor = R.Colors.inactive 28 | lable.textAlignment = .right 29 | lable.text = "\($0 * 10)" // TODO: - Remake to colculated interval 30 | 31 | stackView.addArrangedSubview(lable) 32 | } 33 | } 34 | } 35 | 36 | extension YAxisView { 37 | override func setupViews() { 38 | super.setupViews() 39 | 40 | setupView(stackView) 41 | } 42 | 43 | override func constaintViews() { 44 | super.constaintViews() 45 | 46 | NSLayoutConstraint.activate([ 47 | stackView.leadingAnchor.constraint(equalTo: leadingAnchor), 48 | stackView.topAnchor.constraint(equalTo: topAnchor), 49 | stackView.trailingAnchor.constraint(equalTo: trailingAnchor), 50 | stackView.bottomAnchor.constraint(equalTo: bottomAnchor) 51 | ]) 52 | } 53 | 54 | override func configureAppearance() { 55 | super.configureAppearance() 56 | 57 | backgroundColor = .clear 58 | } 59 | } 60 | 61 | 62 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Base/WABaseController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WABaseController.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 23.05.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | enum NavBarPosition { 11 | case left 12 | case right 13 | } 14 | 15 | class WABaseController: UIViewController { 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | 20 | setupViews() 21 | constraintViews() 22 | configureAppearance() 23 | } 24 | } 25 | 26 | @objc extension WABaseController { 27 | 28 | func setupViews() {} 29 | func constraintViews() {} 30 | 31 | func configureAppearance() { 32 | view.backgroundColor = R.Colors.background 33 | } 34 | 35 | func navBarLeftButtonHandler() { 36 | print("NavBar left button tapped") 37 | } 38 | 39 | func navBarRightButtonHandler() { 40 | print("NavBar right button tapped") 41 | } 42 | } 43 | 44 | extension WABaseController { 45 | func addNavBarButton(at position: NavBarPosition, with title: String) { 46 | let button = UIButton(type: .system) 47 | button.setTitle(title, for: .normal) 48 | button.setTitleColor(R.Colors.active, for: .normal) 49 | button.setTitleColor(R.Colors.inactive, for: .disabled) 50 | button.titleLabel?.font = R.Fonts.helvelticaRegular(with: 17) 51 | 52 | switch position { 53 | case .left: 54 | button.addTarget(self, action: #selector(navBarLeftButtonHandler), for: .touchUpInside) 55 | navigationItem.leftBarButtonItem = UIBarButtonItem(customView: button) 56 | case .right: 57 | button.addTarget(self, action: #selector(navBarRightButtonHandler), for: .touchUpInside) 58 | navigationItem.rightBarButtonItem = UIBarButtonItem(customView: button) 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Session/Views/Timer/PercentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PercentView.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 05.09.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | extension TimerView { 11 | final class PercentView: WABaseView { 12 | 13 | private let stackView: UIStackView = { 14 | let view = UIStackView() 15 | view.axis = .vertical 16 | view.distribution = .fillProportionally 17 | view.spacing = 5 18 | return view 19 | }() 20 | 21 | private let percentLabel: UILabel = { 22 | let label = UILabel() 23 | label.font = R.Fonts.helvelticaRegular(with: 23) 24 | label.textColor = R.Colors.titleGray 25 | label.textAlignment = .center 26 | return label 27 | }() 28 | 29 | private let subtitleLabel: UILabel = { 30 | let label = UILabel() 31 | label.font = R.Fonts.helvelticaRegular(with: 10) 32 | label.textColor = R.Colors.inactive 33 | label.textAlignment = .center 34 | return label 35 | }() 36 | 37 | override func setupViews() { 38 | super.setupViews() 39 | 40 | setupView(stackView) 41 | stackView.addArrangedSubview(percentLabel) 42 | stackView.addArrangedSubview(subtitleLabel) 43 | } 44 | 45 | override func constaintViews() { 46 | super.constaintViews() 47 | 48 | NSLayoutConstraint.activate([ 49 | stackView.leadingAnchor.constraint(equalTo: leadingAnchor), 50 | stackView.topAnchor.constraint(equalTo: topAnchor), 51 | stackView.trailingAnchor.constraint(equalTo: trailingAnchor), 52 | stackView.bottomAnchor.constraint(equalTo: bottomAnchor) 53 | ]) 54 | } 55 | 56 | func configure(with title: String, andValue value: Int) { 57 | subtitleLabel.text = title 58 | percentLabel.text = "\(value)%" 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Navigation/TabBarController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TabBarController.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 23.05.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | enum Tabs: Int, CaseIterable { 11 | case overview 12 | case session 13 | case progress 14 | case settings 15 | } 16 | 17 | final class TabBarController: UITabBarController { 18 | 19 | override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { 20 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 21 | 22 | configureAppearance() 23 | } 24 | 25 | required init?(coder: NSCoder) { 26 | super.init(coder: coder) 27 | 28 | configureAppearance() 29 | } 30 | 31 | func switchTo(tab: Tabs) { 32 | selectedIndex = tab.rawValue 33 | } 34 | 35 | private func configureAppearance() { 36 | tabBar.tintColor = R.Colors.active 37 | tabBar.barTintColor = R.Colors.inactive 38 | tabBar.backgroundColor = .white 39 | tabBar.layer.borderColor = R.Colors.separator.cgColor 40 | tabBar.layer.borderWidth = 1 41 | tabBar.layer.masksToBounds = true 42 | 43 | let controllers: [NavBarController] = Tabs.allCases.map { tab in 44 | let controller = NavBarController(rootViewController: getController(for: tab)) 45 | controller.tabBarItem = UITabBarItem(title: R.Strings.TabBar.title(for: tab), 46 | image: R.Images.TabBar.icon(for: tab), 47 | tag: tab.rawValue) 48 | return controller 49 | } 50 | 51 | setViewControllers(controllers, animated: false) 52 | } 53 | 54 | private func getController(for tab: Tabs) -> WABaseController { 55 | switch tab { 56 | case .overview: return OverviewController() 57 | case .session: return SessionController() 58 | case .progress: return ProgressController() 59 | case .settings: return SettingsController() 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /WorkoutApp/Helpers/Extensions/UIView + ext.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView + ext.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 23.05.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | extension UIView { 11 | func addBottomBorder(with color: UIColor, height: CGFloat) { 12 | let separator = UIView() 13 | separator.backgroundColor = color 14 | separator.autoresizingMask = [.flexibleWidth, .flexibleHeight] 15 | separator.frame = CGRect(x: 0, 16 | y: frame.height - height, 17 | width: frame.width, 18 | height: height) 19 | addSubview(separator) 20 | } 21 | 22 | func makeSystem(_ button: UIButton) { 23 | button.addTarget(self, action: #selector(handleIn), for: [ 24 | .touchDown, 25 | .touchDragInside 26 | ]) 27 | 28 | button.addTarget(self, action: #selector(handleOut), for: [ 29 | .touchDragOutside, 30 | .touchUpInside, 31 | .touchUpOutside, 32 | .touchDragExit, 33 | .touchCancel 34 | ]) 35 | } 36 | 37 | @objc func handleIn() { 38 | UIView.animate(withDuration: 0.15) { self.alpha = 0.55 } 39 | } 40 | 41 | @objc func handleOut() { 42 | UIView.animate(withDuration: 0.15) { self.alpha = 1 } 43 | } 44 | 45 | @objc func setupView(_ view: UIView) { 46 | addSubview(view) 47 | view.translatesAutoresizingMaskIntoConstraints = false 48 | } 49 | 50 | func roundCorners(_ corners: UIRectCorner, radius: CGFloat) { 51 | let path = UIBezierPath(roundedRect: bounds, 52 | byRoundingCorners: corners, 53 | cornerRadii: CGSize(width: radius, height: radius)) 54 | 55 | let borderLayer = CAShapeLayer() 56 | borderLayer.frame = bounds 57 | borderLayer.path = path.cgPath 58 | borderLayer.strokeColor = R.Colors.separator.cgColor 59 | borderLayer.fillColor = UIColor.clear.cgColor 60 | borderLayer.lineWidth = 1 61 | layer.addSublayer(borderLayer) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Base/Views/WAChartsView/WAChartsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WAChartsView.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 07.12.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | extension WAChartsView { 11 | struct Data { 12 | let value: Int 13 | let title: String 14 | } 15 | } 16 | 17 | final class WAChartsView: WABaseView { 18 | 19 | private let yAxisView = YAxisView() 20 | private let xAxisView = XAxisView() 21 | private let chartView = ChartView() 22 | 23 | func configure(with data: [WAChartsView.Data], topChartOffset: Int = 10) { 24 | yAxisView.configure(with: data) 25 | xAxisView.configure(with: data) 26 | chartView.configure(with: data, topChartOffset: topChartOffset) 27 | } 28 | } 29 | 30 | extension WAChartsView { 31 | override func setupViews() { 32 | super.setupViews() 33 | 34 | setupView(yAxisView) 35 | setupView(xAxisView) 36 | setupView(chartView) 37 | } 38 | 39 | override func constaintViews() { 40 | super.constaintViews() 41 | 42 | NSLayoutConstraint.activate([ 43 | yAxisView.leadingAnchor.constraint(equalTo: leadingAnchor), 44 | yAxisView.topAnchor.constraint(equalTo: topAnchor), 45 | yAxisView.bottomAnchor.constraint(equalTo: xAxisView.topAnchor, constant: -12), 46 | 47 | xAxisView.leadingAnchor.constraint(equalTo: yAxisView.trailingAnchor, constant: 8), 48 | xAxisView.bottomAnchor.constraint(equalTo: bottomAnchor), 49 | xAxisView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: 4), 50 | 51 | chartView.leadingAnchor.constraint(equalTo: yAxisView.trailingAnchor, constant: 16), 52 | chartView.topAnchor.constraint(equalTo: topAnchor, constant: 4), 53 | chartView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -4), 54 | chartView.bottomAnchor.constraint(equalTo: xAxisView.topAnchor, constant: -16), 55 | ]) 56 | } 57 | 58 | override func configureAppearance() { 59 | super.configureAppearance() 60 | 61 | backgroundColor = .clear 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /WorkoutApp/SupportingFiles/Base.lproj/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 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Overview/Views/NavBar/WeekView/WeekdayView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WeekdayView.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 28.05.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | extension WeekView { 11 | final class WeekdayView: WABaseView { 12 | 13 | private let nameLabel: UILabel = { 14 | let lable = UILabel() 15 | lable.font = R.Fonts.helvelticaRegular(with: 9) 16 | lable.textAlignment = .center 17 | return lable 18 | }() 19 | 20 | private let dateLabel: UILabel = { 21 | let lable = UILabel() 22 | lable.font = R.Fonts.helvelticaRegular(with: 15) 23 | lable.textAlignment = .center 24 | return lable 25 | }() 26 | 27 | private let stackView: UIStackView = { 28 | let view = UIStackView() 29 | view.spacing = 3 30 | view.axis = .vertical 31 | return view 32 | }() 33 | 34 | func configure(with index: Int, and name: String) { 35 | let startOfWeek = Date().startOfWeek 36 | let currenrDay = startOfWeek.agoForward(to: index) 37 | let day = Date.calendar.component(.day, from: currenrDay) 38 | 39 | let isToday = currenrDay.stripTime() == Date().stripTime() 40 | 41 | backgroundColor = isToday ? R.Colors.active : R.Colors.background 42 | 43 | nameLabel.text = name.uppercased() 44 | nameLabel.textColor = isToday ? .white : R.Colors.inactive 45 | 46 | dateLabel.text = "\(day)" 47 | dateLabel.textColor = isToday ? .white : R.Colors.inactive 48 | } 49 | } 50 | } 51 | 52 | 53 | extension WeekView.WeekdayView { 54 | override func setupViews() { 55 | super.setupViews() 56 | 57 | setupView(stackView) 58 | 59 | stackView.addArrangedSubview(nameLabel) 60 | stackView.addArrangedSubview(dateLabel) 61 | } 62 | 63 | override func constaintViews() { 64 | super.constaintViews() 65 | 66 | NSLayoutConstraint.activate([ 67 | stackView.centerXAnchor.constraint(equalTo: centerXAnchor), 68 | stackView.centerYAnchor.constraint(equalTo: centerYAnchor) 69 | ]) 70 | } 71 | 72 | override func configureAppearance() { 73 | super.configureAppearance() 74 | 75 | layer.cornerRadius = 5 76 | layer.masksToBounds = true 77 | } 78 | } 79 | 80 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Base/Views/WABarsView/Views/WABarView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WABarView.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 08.10.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | extension WABarView { 11 | struct Data { 12 | let value: String 13 | let heightMultiplier: Double 14 | let title: String 15 | } 16 | } 17 | 18 | final class WABarView: WABaseView { 19 | 20 | private let heightMultiplier: Double 21 | 22 | private let valueLabel: UILabel = { 23 | let label = UILabel() 24 | label.font = R.Fonts.helvelticaRegular(with: 13) 25 | label.textColor = R.Colors.active 26 | return label 27 | }() 28 | 29 | private let barView: UIView = { 30 | let view = UIView() 31 | view.backgroundColor = R.Colors.active 32 | view.layer.cornerRadius = 2.5 33 | return view 34 | }() 35 | 36 | private let titleLabel: UILabel = { 37 | let label = UILabel() 38 | label.font = R.Fonts.helvelticaRegular(with: 9) 39 | label.textColor = R.Colors.inactive 40 | return label 41 | }() 42 | 43 | init(data: Data) { 44 | self.heightMultiplier = data.heightMultiplier 45 | super.init(frame: .zero) 46 | 47 | valueLabel.text = data.value 48 | titleLabel.text = data.title.uppercased() 49 | } 50 | 51 | required init?(coder: NSCoder) { 52 | self.heightMultiplier = 0 53 | super.init(frame: .zero) 54 | } 55 | } 56 | 57 | extension WABarView { 58 | override func setupViews() { 59 | super.setupViews() 60 | 61 | setupView(valueLabel) 62 | setupView(barView) 63 | setupView(titleLabel) 64 | } 65 | 66 | override func constaintViews() { 67 | super.constaintViews() 68 | 69 | NSLayoutConstraint.activate([ 70 | valueLabel.centerXAnchor.constraint(equalTo: centerXAnchor), 71 | valueLabel.heightAnchor.constraint(equalToConstant: 10), 72 | 73 | barView.topAnchor.constraint(equalTo: valueLabel.bottomAnchor, constant: 7), 74 | barView.centerXAnchor.constraint(equalTo: centerXAnchor), 75 | barView.widthAnchor.constraint(equalToConstant: 17), 76 | barView.heightAnchor.constraint(equalTo: heightAnchor, multiplier: heightMultiplier * 0.8), 77 | 78 | titleLabel.topAnchor.constraint(equalTo: barView.bottomAnchor, constant: 10), 79 | titleLabel.centerXAnchor.constraint(equalTo: centerXAnchor), 80 | titleLabel.bottomAnchor.constraint(equalTo: bottomAnchor), 81 | titleLabel.heightAnchor.constraint(equalToConstant: 10) 82 | ]) 83 | } 84 | 85 | override func configureAppearance() { 86 | super.configureAppearance() 87 | 88 | backgroundColor = .clear 89 | } 90 | } 91 | 92 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Base/Views/WAButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WAButton.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 24.05.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | public enum WAButtonType { 11 | case primary 12 | case secondary 13 | } 14 | 15 | final class WAButton: UIButton { 16 | 17 | private var type: WAButtonType = .primary 18 | 19 | private let lable: UILabel = { 20 | let lable = UILabel() 21 | lable.textAlignment = .center 22 | return lable 23 | }() 24 | 25 | private let iconView: UIImageView = { 26 | let view = UIImageView() 27 | view.image = R.Images.Common.downArrow?.withRenderingMode(.alwaysTemplate) 28 | return view 29 | }() 30 | 31 | init(with type: WAButtonType) { 32 | super.init(frame: .zero) 33 | self.type = type 34 | 35 | setupViews() 36 | constaintViews() 37 | configureAppearance() 38 | } 39 | 40 | required init?(coder: NSCoder) { 41 | super.init(frame: .zero) 42 | 43 | setupViews() 44 | constaintViews() 45 | configureAppearance() 46 | } 47 | 48 | func setTitle(_ title: String?) { 49 | lable.text = title 50 | } 51 | } 52 | 53 | private extension WAButton { 54 | 55 | func setupViews() { 56 | setupView(lable) 57 | setupView(iconView) 58 | } 59 | 60 | func constaintViews() { 61 | var horisontalOffset: CGFloat { 62 | switch type { 63 | case .primary: return 0 64 | case .secondary: return 10 65 | } 66 | } 67 | 68 | NSLayoutConstraint.activate([ 69 | iconView.centerYAnchor.constraint(equalTo: centerYAnchor), 70 | iconView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -horisontalOffset), 71 | iconView.heightAnchor.constraint(equalToConstant: 5), 72 | iconView.widthAnchor.constraint(equalToConstant: 10), 73 | 74 | lable.centerYAnchor.constraint(equalTo: centerYAnchor), 75 | lable.trailingAnchor.constraint(equalTo: iconView.leadingAnchor, constant: -10), 76 | lable.leadingAnchor.constraint(equalTo: leadingAnchor, constant: horisontalOffset * 2) 77 | ]) 78 | } 79 | 80 | func configureAppearance() { 81 | switch type { 82 | case .primary: 83 | lable.textColor = R.Colors.inactive 84 | lable.font = R.Fonts.helvelticaRegular(with: 13) 85 | iconView.tintColor = R.Colors.inactive 86 | 87 | case .secondary: 88 | backgroundColor = R.Colors.secondary 89 | layer.cornerRadius = 14 90 | lable.textColor = R.Colors.active 91 | lable.font = R.Fonts.helvelticaRegular(with: 15) 92 | iconView.tintColor = R.Colors.active 93 | } 94 | 95 | makeSystem(self) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Base/Views/WABaseInfoView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WABaseInfoView.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 04.06.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | class WABaseInfoView: WABaseView { 11 | 12 | private let titleLabel: UILabel = { 13 | let label = UILabel() 14 | label.font = R.Fonts.helvelticaRegular(with: 13) 15 | label.textColor = R.Colors.inactive 16 | return label 17 | }() 18 | 19 | private let button = WAButton(with: .primary) 20 | 21 | let contentView: UIView = { 22 | let view = UIView() 23 | view.backgroundColor = .white 24 | view.layer.borderColor = R.Colors.separator.cgColor 25 | view.layer.borderWidth = 1 26 | view.layer.cornerRadius = 5 27 | return view 28 | }() 29 | 30 | init(with title: String? = nil, buttonTitle: String? = nil) { 31 | titleLabel.text = title?.uppercased() 32 | titleLabel.textAlignment = buttonTitle == nil ? .center : .left 33 | 34 | button.setTitle(buttonTitle?.uppercased()) 35 | button.isHidden = buttonTitle == nil ? true : false 36 | 37 | super.init(frame: .zero) 38 | } 39 | 40 | required init?(coder: NSCoder) { 41 | super.init(frame: .zero) 42 | } 43 | 44 | func addButtonTarget(target: Any?, action: Selector) { 45 | button.addTarget(action, action: action, for: .touchUpInside) 46 | } 47 | } 48 | 49 | extension WABaseInfoView { 50 | override func setupViews() { 51 | super.setupViews() 52 | 53 | setupView(titleLabel) 54 | setupView(button) 55 | setupView(contentView) 56 | } 57 | 58 | override func constaintViews() { 59 | super.constaintViews() 60 | 61 | let contentTopAnchor: NSLayoutAnchor = titleLabel.text == nil ? topAnchor : titleLabel.bottomAnchor 62 | let contentTopOffset: CGFloat = titleLabel.text == nil ? 0 : 10 63 | 64 | NSLayoutConstraint.activate([ 65 | titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor), 66 | titleLabel.topAnchor.constraint(equalTo: topAnchor), 67 | titleLabel.trailingAnchor.constraint(equalTo: trailingAnchor), 68 | 69 | button.trailingAnchor.constraint(equalTo: trailingAnchor), 70 | button.centerYAnchor.constraint(equalTo: titleLabel.centerYAnchor), 71 | button.heightAnchor.constraint(equalToConstant: 28), 72 | 73 | contentView.topAnchor.constraint(equalTo: contentTopAnchor, constant: contentTopOffset), 74 | contentView.leadingAnchor.constraint(equalTo: leadingAnchor), 75 | contentView.trailingAnchor.constraint(equalTo: trailingAnchor), 76 | contentView.bottomAnchor.constraint(equalTo: bottomAnchor) 77 | ]) 78 | } 79 | 80 | override func configureAppearance() { 81 | super.configureAppearance() 82 | 83 | backgroundColor = .clear 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Overview/Views/NavBar/OverviewNavBar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OverviewNavBar.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 26.05.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | final class OverviewNavBar: WABaseView { 11 | 12 | private let titleLabel: UILabel = { 13 | let lable = UILabel() 14 | lable.text = R.Strings.NavBar.overview 15 | lable.textColor = R.Colors.titleGray 16 | lable.font = R.Fonts.helvelticaRegular(with: 22) 17 | return lable 18 | }() 19 | 20 | private let allWorkoutsButton: WAButton = { 21 | let button = WAButton(with: .secondary) 22 | button.setTitle(R.Strings.Overview.allWorcoutsButton) 23 | return button 24 | }() 25 | 26 | private let addButton: UIButton = { 27 | let button = UIButton() 28 | button.setImage(R.Images.Common.add, for: .normal) 29 | return button 30 | }() 31 | 32 | private let weekView = WeekView() 33 | 34 | override func layoutSubviews() { 35 | super.layoutSubviews() 36 | 37 | addBottomBorder(with: R.Colors.separator, height: 1) 38 | } 39 | 40 | func addAllWorkoutsAction(_ action: Selector, with target: Any?) { 41 | allWorkoutsButton.addTarget(target, action: action, for: .touchUpInside) 42 | } 43 | 44 | func addAdditingAction(_ action: Selector, with target: Any?) { 45 | addButton.addTarget(target, action: action, for: .touchUpInside) 46 | } 47 | } 48 | 49 | extension OverviewNavBar { 50 | override func setupViews() { 51 | super.setupViews() 52 | 53 | setupView(titleLabel) 54 | setupView(allWorkoutsButton) 55 | setupView(addButton) 56 | setupView(weekView) 57 | } 58 | 59 | override func constaintViews() { 60 | super.constaintViews() 61 | 62 | NSLayoutConstraint.activate([ 63 | addButton.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor, constant: 8), 64 | addButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -15), 65 | addButton.heightAnchor.constraint(equalToConstant: 28), 66 | addButton.widthAnchor.constraint(equalToConstant: 28), 67 | 68 | allWorkoutsButton.topAnchor.constraint(equalTo: addButton.topAnchor), 69 | allWorkoutsButton.trailingAnchor.constraint(equalTo: addButton.leadingAnchor, constant: -15), 70 | allWorkoutsButton.heightAnchor.constraint(equalToConstant: 28), 71 | 72 | titleLabel.centerYAnchor.constraint(equalTo: allWorkoutsButton.centerYAnchor), 73 | titleLabel.trailingAnchor.constraint(equalTo: allWorkoutsButton.leadingAnchor), 74 | titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 15), 75 | 76 | weekView.topAnchor.constraint(equalTo: addButton.bottomAnchor, constant: 15), 77 | weekView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 15), 78 | weekView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -15), 79 | weekView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -15), 80 | weekView.heightAnchor.constraint(equalToConstant: 47) 81 | ]) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Session/Views/Stats/Views/StatsItemView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StatsItemView.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 08.10.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | enum StatsItem { 11 | case averagePace(value: String) 12 | case heartRate(value: String) 13 | case totalDistance(value: String) 14 | case totalSteps(value: String) 15 | 16 | var data: StatsItemView.ItemData { 17 | switch self { 18 | case .averagePace(let value): 19 | return .init(image: R.Images.Session.Stats.averagePace, 20 | value: value + " / km", 21 | title: R.Strings.Session.averagePace) 22 | case .heartRate(let value): 23 | return .init(image: R.Images.Session.Stats.heartRate, 24 | value: value + " bpm", 25 | title: R.Strings.Session.heartRate) 26 | case .totalDistance(let value): 27 | return .init(image: R.Images.Session.Stats.totalDistance, 28 | value: value + " km", 29 | title: R.Strings.Session.totalDistance) 30 | case .totalSteps(let value): 31 | return .init(image: R.Images.Session.Stats.totalSteps, 32 | value: value, 33 | title: R.Strings.Session.totalSteps) 34 | } 35 | } 36 | } 37 | 38 | final class StatsItemView: WABaseView { 39 | struct ItemData { 40 | let image: UIImage? 41 | let value: String 42 | let title: String 43 | } 44 | 45 | private let imageView = UIImageView() 46 | 47 | private let valueLabel: UILabel = { 48 | let label = UILabel() 49 | label.font = R.Fonts.helvelticaRegular(with: 17) 50 | label.textColor = R.Colors.titleGray 51 | return label 52 | }() 53 | 54 | private let titleLabel: UILabel = { 55 | let label = UILabel() 56 | label.font = R.Fonts.helvelticaRegular(with: 10) 57 | label.textColor = R.Colors.inactive 58 | return label 59 | }() 60 | 61 | private let stackView: UIStackView = { 62 | let view = UIStackView() 63 | view.axis = .vertical 64 | return view 65 | }() 66 | 67 | func configure(with item: StatsItem) { 68 | imageView.image = item.data.image 69 | valueLabel.text = item.data.value 70 | titleLabel.text = item.data.title.uppercased() 71 | } 72 | } 73 | 74 | extension StatsItemView { 75 | override func setupViews() { 76 | super.setupViews() 77 | 78 | setupView(imageView) 79 | setupView(stackView) 80 | stackView.addArrangedSubview(valueLabel) 81 | stackView.addArrangedSubview(titleLabel) 82 | } 83 | 84 | override func constaintViews() { 85 | super.constaintViews() 86 | 87 | NSLayoutConstraint.activate([ 88 | imageView.centerYAnchor.constraint(equalTo: centerYAnchor), 89 | imageView.leadingAnchor.constraint(equalTo: leadingAnchor), 90 | imageView.widthAnchor.constraint(equalToConstant: 23), 91 | 92 | stackView.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 15), 93 | stackView.topAnchor.constraint(equalTo: topAnchor), 94 | stackView.bottomAnchor.constraint(equalTo: bottomAnchor), 95 | stackView.trailingAnchor.constraint(equalTo: trailingAnchor) 96 | ]) 97 | } 98 | 99 | override func configureAppearance() { 100 | super.configureAppearance() 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Overview/Views/CollectionView/Views/TrainingCellView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TrainingCellView.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 10.02.2023. 6 | // 7 | 8 | import UIKit 9 | 10 | enum CellRoundedType { 11 | case top, bottom, all, notRounded 12 | } 13 | 14 | final class TrainingCellView: UICollectionViewCell { 15 | static let id = "TrainingCellView" 16 | 17 | private let checkmarkView = UIImageView(image: R.Images.Overview.checkmarkNotDone) 18 | 19 | private let stackView: UIStackView = { 20 | let view = UIStackView() 21 | view.axis = .vertical 22 | view.spacing = 3 23 | return view 24 | }() 25 | 26 | private let title: UILabel = { 27 | let lable = UILabel() 28 | lable.font = R.Fonts.helvelticaRegular(with: 17) 29 | lable.textColor = R.Colors.titleGray 30 | return lable 31 | }() 32 | 33 | private let subtitle: UILabel = { 34 | let lable = UILabel() 35 | lable.font = R.Fonts.helvelticaRegular(with: 13) 36 | lable.textColor = R.Colors.inactive 37 | return lable 38 | }() 39 | 40 | private let rightArrowView = UIImageView(image: R.Images.Overview.rightArrow) 41 | 42 | override init(frame: CGRect) { 43 | super.init(frame: frame) 44 | 45 | setupViews() 46 | constaintViews() 47 | configureAppearance() 48 | } 49 | 50 | required init?(coder: NSCoder) { 51 | super.init(frame: .zero) 52 | 53 | setupViews() 54 | constaintViews() 55 | configureAppearance() 56 | } 57 | 58 | func configure(with title: String, subtitle: String, isDone: Bool, roundedType: CellRoundedType) { 59 | self.title.text = title 60 | self.subtitle.text = subtitle 61 | 62 | checkmarkView.image = isDone ? R.Images.Overview.checkmarkDone : R.Images.Overview.checkmarkNotDone 63 | 64 | switch roundedType { 65 | case .all: self.roundCorners([.allCorners], radius: 5) 66 | case .bottom: self.roundCorners([.bottomLeft, .bottomRight], radius: 5) 67 | case .top: self.roundCorners([.topLeft, .topRight], radius: 5) 68 | case .notRounded: self.roundCorners([.allCorners], radius: 0) 69 | } 70 | } 71 | } 72 | 73 | private extension TrainingCellView { 74 | func setupViews() { 75 | setupView(checkmarkView) 76 | setupView(stackView) 77 | stackView.addArrangedSubview(title) 78 | stackView.addArrangedSubview(subtitle) 79 | setupView(rightArrowView) 80 | } 81 | 82 | func constaintViews() { 83 | NSLayoutConstraint.activate([ 84 | checkmarkView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 15), 85 | checkmarkView.centerYAnchor.constraint(equalTo: centerYAnchor), 86 | checkmarkView.heightAnchor.constraint(equalToConstant: 28), 87 | checkmarkView.widthAnchor.constraint(equalTo: checkmarkView.heightAnchor), 88 | 89 | stackView.leadingAnchor.constraint(equalTo: checkmarkView.trailingAnchor, constant: 15), 90 | stackView.centerYAnchor.constraint(equalTo: centerYAnchor), 91 | stackView.trailingAnchor.constraint(equalTo: rightArrowView.leadingAnchor, constant: -15), 92 | 93 | rightArrowView.centerYAnchor.constraint(equalTo: centerYAnchor), 94 | rightArrowView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -15), 95 | rightArrowView.heightAnchor.constraint(equalToConstant: 12), 96 | rightArrowView.widthAnchor.constraint(equalToConstant: 7), 97 | ]) 98 | } 99 | 100 | func configureAppearance() { 101 | backgroundColor = .white 102 | } 103 | } 104 | 105 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Session/SessionController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SessionController.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 23.05.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | final class SessionController: WABaseController { 11 | private let timerView = TimerView() 12 | private let statsView = StatsView(with: R.Strings.Session.workoutStats) 13 | private let stepsView = StepsView(with: R.Strings.Session.stepsCounter) 14 | 15 | private let timerDuration = 5.0 16 | 17 | override func navBarLeftButtonHandler() { 18 | if timerView.state == .isStopped { 19 | timerView.startTimer() 20 | } else { 21 | timerView.pauseTimer() 22 | } 23 | 24 | timerView.state = timerView.state == .isRuning ? .isStopped : .isRuning 25 | addNavBarButton( 26 | at: .left, 27 | with: timerView.state == .isRuning 28 | ? R.Strings.Session.navBarPause : R.Strings.Session.navBarStart 29 | ) 30 | } 31 | 32 | override func navBarRightButtonHandler() { 33 | timerView.stopTimer() 34 | timerView.state = .isStopped 35 | 36 | addNavBarButton(at: .left, with: R.Strings.Session.navBarStart) 37 | } 38 | } 39 | 40 | extension SessionController { 41 | override func setupViews() { 42 | super.setupViews() 43 | 44 | view.setupView(timerView) 45 | view.setupView(statsView) 46 | view.setupView(stepsView) 47 | } 48 | 49 | override func constraintViews() { 50 | super.constraintViews() 51 | 52 | NSLayoutConstraint.activate([ 53 | timerView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 15), 54 | timerView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 15), 55 | timerView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -15), 56 | 57 | statsView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 15), 58 | statsView.topAnchor.constraint(equalTo: timerView.bottomAnchor, constant: 10), 59 | statsView.trailingAnchor.constraint(equalTo: view.centerXAnchor, constant: -7.5), 60 | 61 | stepsView.leadingAnchor.constraint(equalTo: view.centerXAnchor, constant: 7.5), 62 | stepsView.topAnchor.constraint(equalTo: statsView.topAnchor), 63 | stepsView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -15), 64 | stepsView.heightAnchor.constraint(equalTo: statsView.heightAnchor), 65 | ]) 66 | } 67 | 68 | override func configureAppearance() { 69 | super.configureAppearance() 70 | 71 | title = R.Strings.NavBar.session 72 | navigationController?.tabBarItem.title = R.Strings.TabBar.title(for: .session) 73 | 74 | addNavBarButton(at: .left, with: R.Strings.Session.navBarStart) 75 | addNavBarButton(at: .right, with: R.Strings.Session.navBarFinish) 76 | 77 | timerView.configure(with: timerDuration, progress: 0) 78 | timerView.callBack = { [weak self] in 79 | self?.navBarRightButtonHandler() 80 | } 81 | 82 | statsView.configure(with: [.heartRate(value: "155"), 83 | .averagePace(value: "8'20''"), 84 | .totalSteps(value: "7,682"), 85 | .totalDistance(value: "8.25")]) 86 | 87 | stepsView.configure(with: [.init(value: "8k", heightMultiplier: 1, title: "2/14"), 88 | .init(value: "7k", heightMultiplier: 0.8, title: "2/15"), 89 | .init(value: "5k", heightMultiplier: 0.6, title: "2/16"), 90 | .init(value: "6k", heightMultiplier: 0.3, title: "2/17")]) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Progress/ProgressController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProgressController.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 23.05.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | final class ProgressController: WABaseController { 11 | 12 | private let dailyPerformanceView = DailyPerformanceView(with: R.Strings.Progress.dailyPerformance, 13 | buttonTitle: R.Strings.Progress.last7Days) 14 | private let monthlyPerformanceView = MonthlyPerformanceView(with: R.Strings.Progress.monthlyPerformance, 15 | buttonTitle: R.Strings.Progress.last10Months) 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | } 20 | } 21 | 22 | extension ProgressController { 23 | override func setupViews() { 24 | super.setupViews() 25 | 26 | view.setupView(dailyPerformanceView) 27 | view.setupView(monthlyPerformanceView) 28 | } 29 | 30 | override func constraintViews() { 31 | super.constraintViews() 32 | 33 | NSLayoutConstraint.activate([ 34 | dailyPerformanceView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 15), 35 | dailyPerformanceView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 15), 36 | dailyPerformanceView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -15), 37 | dailyPerformanceView.heightAnchor.constraint(equalTo: dailyPerformanceView.widthAnchor, multiplier: 0.68), 38 | 39 | monthlyPerformanceView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 15), 40 | monthlyPerformanceView.topAnchor.constraint(equalTo: dailyPerformanceView.bottomAnchor, constant: 15), 41 | monthlyPerformanceView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -15), 42 | monthlyPerformanceView.heightAnchor.constraint(equalTo: monthlyPerformanceView.widthAnchor, 43 | multiplier: 1.06), 44 | ]) 45 | 46 | } 47 | 48 | override func configureAppearance() { 49 | super.configureAppearance() 50 | 51 | title = R.Strings.NavBar.progress 52 | navigationController?.tabBarItem.title = R.Strings.TabBar.title(for: .progress) 53 | 54 | addNavBarButton(at: .left, with: R.Strings.Progress.navBarLeft) 55 | addNavBarButton(at: .right, with: R.Strings.Progress.navBarRight) 56 | 57 | dailyPerformanceView.configure(with: [.init(value: "1", heightMultiplier: 0.2, title: "Mon"), 58 | .init(value: "2", heightMultiplier: 0.4, title: "Teu"), 59 | .init(value: "3", heightMultiplier: 0.6, title: "Wen"), 60 | .init(value: "4", heightMultiplier: 0.8, title: "Thu"), 61 | .init(value: "5", heightMultiplier: 1, title: "Fri"), 62 | .init(value: "3", heightMultiplier: 0.6, title: "Sat"), 63 | .init(value: "2", heightMultiplier: 0.4, title: "Sun")]) 64 | 65 | monthlyPerformanceView.configure(with: [.init(value: 45, title: "Mar"), 66 | .init(value: 55, title: "Apr"), 67 | .init(value: 60, title: "May"), 68 | .init(value: 65, title: "Jun"), 69 | .init(value: 70, title: "Jul"), 70 | .init(value: 80, title: "Aug"), 71 | .init(value: 65, title: "Sep"), 72 | .init(value: 45, title: "Oct"), 73 | .init(value: 30, title: "Nov"), 74 | .init(value: 15, title: "Dec")], 75 | topChartOffset: 10) 76 | } 77 | } 78 | 79 | -------------------------------------------------------------------------------- /WorkoutApp/Helpers/Resouces.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Resouces.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 23.05.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | enum R { 11 | enum Colors { 12 | static let active = UIColor(hexString: "#437BFE") 13 | static let inactive = UIColor(hexString: "#929DA5") 14 | 15 | static let background = UIColor(hexString: "#F8F9F9") 16 | static let separator = UIColor(hexString: "#E8ECEF") 17 | static let secondary = UIColor(hexString: "#F0F3FF") 18 | 19 | static let titleGray = UIColor(hexString: "#545C77") 20 | } 21 | 22 | enum Strings { 23 | enum TabBar { 24 | static func title(for tab: Tabs) -> String { 25 | switch tab { 26 | case .overview: return "Overview" 27 | case .session: return "Session" 28 | case .progress: return "Progress" 29 | case .settings: return "Settings" 30 | } 31 | } 32 | } 33 | 34 | enum NavBar { 35 | static let overview = "Today" 36 | static let session = "High Intensity Cardio" 37 | static let progress = "Workout Progress" 38 | static let settings = "Settings" 39 | } 40 | 41 | enum Overview { 42 | static let allWorcoutsButton = "All Workouts" 43 | } 44 | 45 | enum Session { 46 | static let navBarStart = "Start" 47 | static let navBarPause = "Pause" 48 | static let navBarFinish = "Finish" 49 | 50 | static let elapsedTime = "Elapsed Time" 51 | static let remainingTime = "Remaining Time" 52 | static let completed = "Completed" 53 | static let remaining = "Remaining" 54 | 55 | static let workoutStats = "Workout stats" 56 | static let averagePace = "Average pace" 57 | static let heartRate = "Heart rate" 58 | static let totalDistance = "Total distance" 59 | static let totalSteps = "Total steps" 60 | 61 | static let stepsCounter = "Steps Counter" 62 | } 63 | 64 | enum Progress { 65 | static let navBarLeft = "Export" 66 | static let navBarRight = "Details" 67 | 68 | static let dailyPerformance = "Daily performance" 69 | static let last7Days = "Last 7 days" 70 | static let monthlyPerformance = "Monthly performance" 71 | static let last10Months = "Last 10 months" 72 | } 73 | 74 | enum Settings {} 75 | } 76 | 77 | enum Images { 78 | enum TabBar { 79 | static func icon(for tab: Tabs) -> UIImage? { 80 | switch tab { 81 | case .overview: return UIImage(named: "overview_tab") 82 | case .session: return UIImage(named: "session_tab") 83 | case .progress: return UIImage(named: "progress_tab") 84 | case .settings: return UIImage(named: "settings_tab") 85 | } 86 | } 87 | } 88 | 89 | enum Common { 90 | static let downArrow = UIImage(named: "down_arrow") 91 | static let add = UIImage(named: "add_button") 92 | } 93 | 94 | enum Overview { 95 | static let checkmarkNotDone = UIImage(named: "checkmark_not_done") 96 | static let checkmarkDone = UIImage(named: "checkmark_done") 97 | static let rightArrow = UIImage(named: "right_arrow") 98 | } 99 | 100 | enum Session { 101 | enum Stats { 102 | static let averagePace = UIImage(named: "stats_averagePace") 103 | static let heartRate = UIImage(named: "stats_heartRate") 104 | static let totalDistance = UIImage(named: "stats_totalDistance") 105 | static let totalSteps = UIImage(named: "stats_totalSteps") 106 | } 107 | } 108 | } 109 | 110 | enum Fonts { 111 | static func helvelticaRegular(with size: CGFloat) -> UIFont { 112 | UIFont(name: "Helvetica", size: size) ?? UIFont() 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Base/Views/WAChartsView/Views/ChartView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChartView.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 20.12.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | final class ChartView: WABaseView { 11 | 12 | private let yAxisSeparator: UIView = { 13 | let view = UIView() 14 | view.backgroundColor = R.Colors.separator 15 | return view 16 | }() 17 | 18 | private let xAxisSeparator: UIView = { 19 | let view = UIView() 20 | view.backgroundColor = R.Colors.separator 21 | return view 22 | }() 23 | 24 | func configure(with data: [WAChartsView.Data], topChartOffset: Int) { 25 | 26 | layoutIfNeeded() 27 | drawDashLines() 28 | drawChart(with: data, topChartOffset: topChartOffset) 29 | } 30 | } 31 | 32 | extension ChartView { 33 | override func setupViews() { 34 | super.setupViews() 35 | 36 | setupView(yAxisSeparator) 37 | setupView(xAxisSeparator) 38 | } 39 | 40 | override func constaintViews() { 41 | super.constaintViews() 42 | 43 | NSLayoutConstraint.activate([ 44 | yAxisSeparator.leadingAnchor.constraint(equalTo: leadingAnchor), 45 | yAxisSeparator.topAnchor.constraint(equalTo: topAnchor), 46 | yAxisSeparator.bottomAnchor.constraint(equalTo: bottomAnchor), 47 | yAxisSeparator.widthAnchor.constraint(equalToConstant: 1), 48 | 49 | xAxisSeparator.leadingAnchor.constraint(equalTo: leadingAnchor), 50 | xAxisSeparator.trailingAnchor.constraint(equalTo: trailingAnchor), 51 | xAxisSeparator.bottomAnchor.constraint(equalTo: bottomAnchor), 52 | xAxisSeparator.heightAnchor.constraint(equalToConstant: 1) 53 | ]) 54 | } 55 | 56 | override func configureAppearance() { 57 | super.configureAppearance() 58 | 59 | backgroundColor = .clear 60 | } 61 | } 62 | 63 | private extension ChartView { 64 | func drawDashLines(with counts: Int = 9) { 65 | (0.. $1.value }).first?.value else { return } 88 | let valuePoints = data.enumerated().map { CGPoint(x: CGFloat($0), y: CGFloat($1.value)) } 89 | let chartHeight = bounds.height / CGFloat(maxValue + topChartOffset) 90 | 91 | let points = valuePoints.map { 92 | let x = bounds.width / CGFloat(valuePoints.count - 1) * $0.x 93 | let y = bounds.height - $0.y * chartHeight 94 | return CGPoint(x: x, y: y) 95 | } 96 | 97 | let chartPath = UIBezierPath() 98 | chartPath.move(to: points[0]) 99 | 100 | points.forEach { 101 | chartPath.addLine(to: $0) 102 | drawChartDot(at: $0) 103 | } 104 | 105 | let chartLayer = CAShapeLayer() 106 | chartLayer.path = chartPath.cgPath 107 | chartLayer.fillColor = UIColor.clear.cgColor 108 | chartLayer.strokeColor = R.Colors.active.cgColor 109 | chartLayer.lineWidth = 3 110 | chartLayer.strokeEnd = 1 111 | chartLayer.lineJoin = .round 112 | 113 | layer.addSublayer(chartLayer) 114 | } 115 | 116 | func drawChartDot(at point: CGPoint) { 117 | let dotPath = UIBezierPath() 118 | dotPath.move(to: point) 119 | dotPath.addLine(to: point) 120 | 121 | let dotLayer = CAShapeLayer() 122 | dotLayer.path = dotPath.cgPath 123 | dotLayer.strokeColor = R.Colors.active.cgColor 124 | dotLayer.lineCap = .round 125 | dotLayer.lineWidth = 10 126 | 127 | layer.addSublayer(dotLayer) 128 | } 129 | } 130 | 131 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Session/Views/Timer/ProgressView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProgressView.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 18.06.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | extension TimerView { 11 | final class ProgressView: UIView { 12 | func drawProgress(with percent: CGFloat) { 13 | layer.sublayers?.removeAll() 14 | 15 | // MARK: - Circle 16 | let circleFrame = UIScreen.main.bounds.width - (15 + 40) * 2 17 | let radius = circleFrame / 2 18 | let center = CGPoint(x: radius, y: radius) 19 | let startAngle = -CGFloat.pi * 7 / 6 20 | let endAngle = CGFloat.pi * 1 / 6 21 | 22 | let circlePath = UIBezierPath(arcCenter: center, 23 | radius: radius, 24 | startAngle: startAngle, 25 | endAngle: endAngle, 26 | clockwise: true) 27 | 28 | let defaultCircleLayer = CAShapeLayer() 29 | defaultCircleLayer.path = circlePath.cgPath 30 | defaultCircleLayer.strokeColor = R.Colors.separator.cgColor 31 | defaultCircleLayer.lineWidth = 20 32 | defaultCircleLayer.strokeEnd = 1 33 | defaultCircleLayer.fillColor = UIColor.clear.cgColor 34 | defaultCircleLayer.lineCap = .round 35 | 36 | let circleLayer = CAShapeLayer() 37 | circleLayer.path = circlePath.cgPath 38 | circleLayer.strokeColor = R.Colors.active.cgColor 39 | circleLayer.lineWidth = 20 40 | circleLayer.strokeEnd = percent 41 | circleLayer.fillColor = UIColor.clear.cgColor 42 | circleLayer.lineCap = .round 43 | 44 | // MARK: - Dot 45 | let dotAngle = CGFloat.pi * (7 / 6 - (8 / 6 * percent)) 46 | let dotPoint = CGPoint(x: cos(-dotAngle) * radius + center.x, 47 | y: sin(-dotAngle) * radius + center.y) 48 | 49 | let dotPath = UIBezierPath() 50 | dotPath.move(to: dotPoint) 51 | dotPath.addLine(to: dotPoint) 52 | 53 | let bigDotLayer = CAShapeLayer() 54 | bigDotLayer.path = dotPath.cgPath 55 | bigDotLayer.fillColor = UIColor.clear.cgColor 56 | bigDotLayer.strokeColor = R.Colors.active.cgColor 57 | bigDotLayer.lineCap = .round 58 | bigDotLayer.lineWidth = 20 59 | 60 | let dotLayer = CAShapeLayer() 61 | dotLayer.path = dotPath.cgPath 62 | dotLayer.fillColor = UIColor.clear.cgColor 63 | dotLayer.strokeColor = UIColor.white.cgColor 64 | dotLayer.lineCap = .round 65 | dotLayer.lineWidth = 8 66 | 67 | // MARK: - Bars 68 | let barsFrame = UIScreen.main.bounds.width - (15 + 40 + 25) * 2 69 | let barsRadius = barsFrame / 2 70 | 71 | let barsPath = UIBezierPath(arcCenter: center, 72 | radius: barsRadius, 73 | startAngle: startAngle, 74 | endAngle: endAngle, 75 | clockwise: true) 76 | 77 | let barsLayer = CAShapeLayer() 78 | barsLayer.path = barsPath.cgPath 79 | barsLayer.fillColor = UIColor.clear.cgColor 80 | barsLayer.strokeColor = UIColor.clear.cgColor 81 | barsLayer.lineWidth = 6 82 | 83 | let startBarRadius = barsRadius - barsLayer.lineWidth * 0.5 84 | let endBarRadius = startBarRadius + 6 85 | 86 | var angle: CGFloat = 7 / 6 87 | (1...9).forEach { _ in 88 | let barAngle = CGFloat.pi * angle 89 | let startBarPoint = CGPoint( 90 | x: cos(-barAngle) * startBarRadius + center.x, 91 | y: sin(-barAngle) * startBarRadius + center.y 92 | ) 93 | 94 | let endBarPoint = CGPoint( 95 | x: cos(-barAngle) * endBarRadius + center.x, 96 | y: sin(-barAngle) * endBarRadius + center.y 97 | ) 98 | 99 | let barPath = UIBezierPath() 100 | barPath.move(to: startBarPoint) 101 | barPath.addLine(to: endBarPoint) 102 | 103 | let barLayer = CAShapeLayer() 104 | barLayer.path = barPath.cgPath 105 | barLayer.fillColor = UIColor.clear.cgColor 106 | barLayer.strokeColor = angle >= (7 / 6 - (8 / 6 * percent)) 107 | ? R.Colors.active.cgColor : R.Colors.separator.cgColor 108 | barLayer.lineCap = .round 109 | barLayer.lineWidth = 4 110 | 111 | barsLayer.addSublayer(barLayer) 112 | 113 | angle -= 1 / 6 114 | } 115 | 116 | layer.addSublayer(defaultCircleLayer) 117 | layer.addSublayer(circleLayer) 118 | layer.addSublayer(bigDotLayer) 119 | layer.addSublayer(dotLayer) 120 | layer.addSublayer(barsLayer) 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Overview/OverviewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OverviewController.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 23.05.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | struct TraningData { 11 | struct Data { 12 | let title: String 13 | let subtitle: String 14 | let isDone: Bool 15 | } 16 | 17 | let date: Date 18 | let items: [Data] 19 | } 20 | 21 | class OverviewController: WABaseController { 22 | 23 | private let navBar = OverviewNavBar() 24 | 25 | private var dataSource: [TraningData] = [] 26 | 27 | private let collectionView: UICollectionView = { 28 | let layout = UICollectionViewFlowLayout() 29 | layout.minimumLineSpacing = 0 30 | 31 | let view = UICollectionView(frame: .zero, collectionViewLayout: layout) 32 | view.showsVerticalScrollIndicator = false 33 | view.backgroundColor = .clear 34 | 35 | return view 36 | }() 37 | } 38 | 39 | extension OverviewController { 40 | override func setupViews() { 41 | super.setupViews() 42 | 43 | view.setupView(navBar) 44 | view.setupView(collectionView) 45 | } 46 | 47 | override func constraintViews() { 48 | super.constraintViews() 49 | 50 | NSLayoutConstraint.activate([ 51 | navBar.topAnchor.constraint(equalTo: view.topAnchor), 52 | navBar.leadingAnchor.constraint(equalTo: view.leadingAnchor), 53 | navBar.trailingAnchor.constraint(equalTo: view.trailingAnchor), 54 | 55 | collectionView.topAnchor.constraint(equalTo: navBar.bottomAnchor), 56 | collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), 57 | collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16), 58 | collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor), 59 | ]) 60 | } 61 | 62 | override func configureAppearance() { 63 | super.configureAppearance() 64 | 65 | navigationController?.navigationBar.isHidden = true 66 | 67 | collectionView.register(TrainingCellView.self, forCellWithReuseIdentifier: TrainingCellView.id) 68 | collectionView.register(SectionHeaderView.self, 69 | forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, 70 | withReuseIdentifier: SectionHeaderView.id) 71 | 72 | collectionView.delegate = self 73 | collectionView.dataSource = self 74 | 75 | dataSource = [ 76 | .init(date: Date(), 77 | items: [ 78 | .init(title: "Warm Up Cardio", subtitle: "Stair Climber • 10 minutes", isDone: true), 79 | .init(title: "High Intensity Cardio", subtitle: "Treadmill • 50 minutes", isDone: false), 80 | ]), 81 | .init(date: Date(), 82 | items: [ 83 | .init(title: "Warm Up Cardio", subtitle: "Stair Climber • 10 minutes", isDone: false), 84 | .init(title: "Chest Workout", subtitle: "Bench Press • 3 sets • 10 reps", isDone: false), 85 | .init(title: "Tricep Workout", subtitle: "Overhead Extension • 5 sets • 8 reps", isDone: false), 86 | ]), 87 | .init(date: Date(), 88 | items: [ 89 | .init(title: "Cardio Interval Workout", subtitle: "Treadmill • 60 minutes", isDone: false), 90 | ]) 91 | ] 92 | collectionView.reloadData() 93 | } 94 | } 95 | 96 | // MARK: - UICollectionViewDataSource 97 | extension OverviewController: UICollectionViewDataSource { 98 | func numberOfSections(in collectionView: UICollectionView) -> Int { 99 | dataSource.count 100 | } 101 | 102 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 103 | dataSource[section].items.count 104 | } 105 | 106 | func collectionView(_ collectionView: UICollectionView, 107 | cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 108 | guard let cell = collectionView.dequeueReusableCell( 109 | withReuseIdentifier: TrainingCellView.id, for: indexPath 110 | ) as? TrainingCellView else { return UICollectionViewCell() } 111 | 112 | let item = dataSource[indexPath.section].items[indexPath.row] 113 | 114 | let roundedType: CellRoundedType 115 | if indexPath.row == 0 && indexPath.row == dataSource[indexPath.section].items.count - 1 { 116 | roundedType = .all 117 | } else if indexPath.row == 0 { 118 | roundedType = .top 119 | } else if indexPath.row == dataSource[indexPath.section].items.count - 1 { 120 | roundedType = .bottom 121 | } else { 122 | roundedType = .notRounded 123 | } 124 | 125 | cell.configure(with: item.title, subtitle: item.subtitle, isDone: item.isDone, roundedType: roundedType) 126 | return cell 127 | } 128 | 129 | func collectionView(_ collectionView: UICollectionView, 130 | viewForSupplementaryElementOfKind kind: String, 131 | at indexPath: IndexPath) -> UICollectionReusableView { 132 | guard let view = collectionView.dequeueReusableSupplementaryView( 133 | ofKind: kind, withReuseIdentifier: SectionHeaderView.id, for: indexPath 134 | ) as? SectionHeaderView else { return UICollectionReusableView() } 135 | 136 | view.configure(with: dataSource[indexPath.section].date) 137 | return view 138 | } 139 | } 140 | 141 | // MARK: - UICollectionViewDelegateFlowLayout 142 | extension OverviewController: UICollectionViewDelegateFlowLayout { 143 | func collectionView(_ collectionView: UICollectionView, 144 | layout collectionViewLayout: UICollectionViewLayout, 145 | sizeForItemAt indexPath: IndexPath) -> CGSize { 146 | CGSize(width: collectionView.frame.width, height: 70) 147 | } 148 | 149 | func collectionView(_ collectionView: UICollectionView, 150 | layout collectionViewLayout: UICollectionViewLayout, 151 | referenceSizeForHeaderInSection section: Int) -> CGSize { 152 | CGSize(width: collectionView.frame.width, height: 32) 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /WorkoutApp/Controllers/Session/Views/Timer/TimerView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimerView.swift 3 | // WorkoutApp 4 | // 5 | // Created by Viktor Prikolota on 18.06.2022. 6 | // 7 | 8 | import UIKit 9 | 10 | enum TimerState { 11 | case isRuning 12 | case isPaused 13 | case isStopped 14 | } 15 | 16 | final class TimerView: WABaseInfoView { 17 | 18 | private let elapsedTimeLable: UILabel = { 19 | let lable = UILabel() 20 | lable.text = R.Strings.Session.elapsedTime 21 | lable.font = R.Fonts.helvelticaRegular(with: 14) 22 | lable.textColor = R.Colors.inactive 23 | lable.textAlignment = .center 24 | return lable 25 | }() 26 | 27 | private let elapsedTimeValueLable: UILabel = { 28 | let lable = UILabel() 29 | lable.font = R.Fonts.helvelticaRegular(with: 46) 30 | lable.textColor = R.Colors.titleGray 31 | lable.textAlignment = .center 32 | return lable 33 | }() 34 | 35 | private let remainingTimeLable: UILabel = { 36 | let lable = UILabel() 37 | lable.text = R.Strings.Session.remainingTime 38 | lable.font = R.Fonts.helvelticaRegular(with: 13) 39 | lable.textColor = R.Colors.inactive 40 | lable.textAlignment = .center 41 | return lable 42 | }() 43 | 44 | private let remainingTimeValueLable: UILabel = { 45 | let lable = UILabel() 46 | lable.font = R.Fonts.helvelticaRegular(with: 13) 47 | lable.textColor = R.Colors.titleGray 48 | lable.textAlignment = .center 49 | return lable 50 | }() 51 | 52 | private let timeStackView: UIStackView = { 53 | let view = UIStackView() 54 | view.axis = .vertical 55 | view.distribution = .fillProportionally 56 | view.spacing = 10 57 | return view 58 | }() 59 | 60 | private let bottomStackView: UIStackView = { 61 | let view = UIStackView() 62 | view.distribution = .fillProportionally 63 | view.spacing = 25 64 | return view 65 | }() 66 | 67 | private let completedPercentView = PercentView() 68 | private let remainigPercetnView = PercentView() 69 | 70 | private let bottomSeparatorView: UIView = { 71 | let view = UIView() 72 | view.backgroundColor = R.Colors.separator 73 | return view 74 | }() 75 | 76 | private let progressView = ProgressView() 77 | 78 | private var timer = Timer() 79 | private var timerProgress: CGFloat = 0 80 | private var timerDuration = 0.0 81 | 82 | var state: TimerState = .isStopped 83 | var callBack: (() -> Void)? 84 | 85 | func configure(with duration: Double, progress: Double) { 86 | timerDuration = duration 87 | 88 | let tempCurrentValue = progress > duration ? duration : progress 89 | let goalValueDevider = duration == 0 ? 1 : duration 90 | let percent = tempCurrentValue / goalValueDevider 91 | let roundedPercent = Int(round(percent * 100)) 92 | 93 | elapsedTimeValueLable.text = getDisplayedString(from: Int(tempCurrentValue)) 94 | remainingTimeValueLable.text = getDisplayedString(from: Int(duration) - Int(tempCurrentValue)) 95 | completedPercentView.configure(with: R.Strings.Session.completed.uppercased(), 96 | andValue: roundedPercent) 97 | remainigPercetnView.configure(with: R.Strings.Session.remaining.uppercased(), 98 | andValue: 100 - roundedPercent) 99 | progressView.drawProgress(with: CGFloat(percent)) 100 | } 101 | 102 | func startTimer() { 103 | timer.invalidate() 104 | 105 | timer = Timer.scheduledTimer(withTimeInterval: 0.01, 106 | repeats: true, 107 | block: { [weak self] timer in 108 | guard let self = self else { return } 109 | self.timerProgress += 0.01 110 | 111 | if self.timerProgress > self.timerDuration { 112 | self.timerProgress = self.timerDuration 113 | timer.invalidate() 114 | } 115 | 116 | self.configure(with: self.timerDuration, progress: self.timerProgress) 117 | }) 118 | } 119 | 120 | func pauseTimer() { 121 | timer.invalidate() 122 | } 123 | 124 | func stopTimer() { 125 | guard self.timerProgress > 0 else { return } 126 | timer.invalidate() 127 | 128 | timer = Timer.scheduledTimer(withTimeInterval: 0.01, 129 | repeats: true, 130 | block: { [weak self] timer in 131 | guard let self = self else { return } 132 | self.timerProgress -= self.timerDuration * 0.02 133 | 134 | if self.timerProgress <= 0 { 135 | self.timerProgress = 0 136 | timer.invalidate() 137 | } 138 | 139 | self.configure(with: self.timerDuration, progress: self.timerProgress) 140 | }) 141 | } 142 | } 143 | 144 | extension TimerView { 145 | override func setupViews() { 146 | super.setupViews() 147 | 148 | setupView(progressView) 149 | setupView(timeStackView) 150 | setupView(bottomStackView) 151 | 152 | [ 153 | elapsedTimeLable, 154 | elapsedTimeValueLable, 155 | remainingTimeLable, 156 | remainingTimeValueLable 157 | ].forEach(timeStackView.addArrangedSubview) 158 | 159 | [ 160 | completedPercentView, 161 | bottomSeparatorView, 162 | remainigPercetnView 163 | ].forEach(bottomStackView.addArrangedSubview) 164 | } 165 | 166 | override func constaintViews() { 167 | super.constaintViews() 168 | 169 | NSLayoutConstraint.activate([ 170 | progressView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 40), 171 | progressView.topAnchor.constraint(equalTo: topAnchor, constant: 40), 172 | progressView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -40), 173 | progressView.heightAnchor.constraint(equalTo: progressView.widthAnchor), 174 | progressView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -40), 175 | 176 | timeStackView.centerYAnchor.constraint(equalTo: progressView.centerYAnchor), 177 | timeStackView.centerXAnchor.constraint(equalTo: progressView.centerXAnchor), 178 | 179 | bottomStackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -28), 180 | bottomStackView.centerXAnchor.constraint(equalTo: centerXAnchor), 181 | bottomStackView.heightAnchor.constraint(equalToConstant: 35), 182 | bottomStackView.widthAnchor.constraint(equalToConstant: 175), 183 | 184 | bottomSeparatorView.widthAnchor.constraint(equalToConstant: 1) 185 | ]) 186 | } 187 | 188 | override func configureAppearance() { 189 | super.configureAppearance() 190 | } 191 | } 192 | 193 | private extension TimerView { 194 | func getDisplayedString(from value: Int) -> String { 195 | let seconds = value % 60 196 | let minutes = (value / 60) % 60 197 | let hours = value / 3600 198 | 199 | let secondsStr = seconds < 10 ? "0\(seconds)" : "\(seconds)" 200 | let minutesStr = minutes < 10 ? "0\(minutes)" : "\(minutes)" 201 | let hoursStr = hours < 10 ? "0\(hours)" : "\(hours)" 202 | 203 | return hours == 0 204 | ? [minutesStr, secondsStr].joined(separator: ":") 205 | : [hoursStr, minutesStr, secondsStr].joined(separator: ":") 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /WorkoutApp.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 55; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 77040639285E17F9005ED76D /* TimerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77040638285E17F9005ED76D /* TimerView.swift */; }; 11 | 7704063C285E1873005ED76D /* ProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7704063B285E1873005ED76D /* ProgressView.swift */; }; 12 | 774B893629517830001ED31E /* ChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 774B893529517830001ED31E /* ChartView.swift */; }; 13 | 7766B53A284288C0000FF440 /* WeekView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7766B539284288C0000FF440 /* WeekView.swift */; }; 14 | 7766B53C28428992000FF440 /* WeekdayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7766B53B28428992000FF440 /* WeekdayView.swift */; }; 15 | 7766B53E28428BF9000FF440 /* Date + ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7766B53D28428BF9000FF440 /* Date + ext.swift */; }; 16 | 776BE29928F1974000316C05 /* StatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 776BE29828F1974000316C05 /* StatsView.swift */; }; 17 | 776BE29C28F1978300316C05 /* StatsItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 776BE29B28F1978300316C05 /* StatsItemView.swift */; }; 18 | 776BE2A628F1D8EF00316C05 /* StepsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 776BE2A528F1D8EF00316C05 /* StepsView.swift */; }; 19 | 776BE2A928F1D97E00316C05 /* WABarsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 776BE2A828F1D97E00316C05 /* WABarsView.swift */; }; 20 | 776BE2AC28F1D98B00316C05 /* WABarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 776BE2AB28F1D98B00316C05 /* WABarView.swift */; }; 21 | 77709145283BEDAC00AD7841 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77709144283BEDAC00AD7841 /* AppDelegate.swift */; }; 22 | 77709147283BEDAC00AD7841 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77709146283BEDAC00AD7841 /* SceneDelegate.swift */; }; 23 | 77709149283BEDAC00AD7841 /* OverviewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77709148283BEDAC00AD7841 /* OverviewController.swift */; }; 24 | 7770914E283BEDAD00AD7841 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7770914D283BEDAD00AD7841 /* Assets.xcassets */; }; 25 | 77709151283BEDAD00AD7841 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7770914F283BEDAD00AD7841 /* LaunchScreen.storyboard */; }; 26 | 7770915B283BEEEF00AD7841 /* TabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7770915A283BEEEF00AD7841 /* TabBarController.swift */; }; 27 | 7770915F283BEF8D00AD7841 /* UIColor + ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7770915E283BEF8D00AD7841 /* UIColor + ext.swift */; }; 28 | 77709162283BEFDE00AD7841 /* Resouces.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77709161283BEFDE00AD7841 /* Resouces.swift */; }; 29 | 77709164283BF45500AD7841 /* NavBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77709163283BF45500AD7841 /* NavBarController.swift */; }; 30 | 77709168283BF5A100AD7841 /* WABaseController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77709167283BF5A100AD7841 /* WABaseController.swift */; }; 31 | 7770916A283BF70400AD7841 /* UIView + ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77709169283BF70400AD7841 /* UIView + ext.swift */; }; 32 | 77709172283BF82600AD7841 /* SessionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77709171283BF82600AD7841 /* SessionController.swift */; }; 33 | 77709174283BF83D00AD7841 /* ProgressController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77709173283BF83D00AD7841 /* ProgressController.swift */; }; 34 | 77709176283BF84E00AD7841 /* SettingsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77709175283BF84E00AD7841 /* SettingsController.swift */; }; 35 | 77854D06284BC4EE0095D8A5 /* WABaseInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77854D05284BC4EE0095D8A5 /* WABaseInfoView.swift */; }; 36 | 77A322E6294056DC00905950 /* DailyPerformanceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A322E5294056DC00905950 /* DailyPerformanceView.swift */; }; 37 | 77A322E92940655500905950 /* MonthlyPerformanceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A322E82940655500905950 /* MonthlyPerformanceView.swift */; }; 38 | 77A322ED2940665C00905950 /* WAChartsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A322EC2940665C00905950 /* WAChartsView.swift */; }; 39 | 77A322EF294067D000905950 /* XAxisView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A322EE294067D000905950 /* XAxisView.swift */; }; 40 | 77A322F129406A0700905950 /* YAxisView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77A322F029406A0700905950 /* YAxisView.swift */; }; 41 | 77B44802283D4EC100ADDB6E /* WAButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77B44801283D4EC100ADDB6E /* WAButton.swift */; }; 42 | 77B44804283FE24400ADDB6E /* OverviewNavBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77B44803283FE24400ADDB6E /* OverviewNavBar.swift */; }; 43 | 77B44806283FE26E00ADDB6E /* WABaseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77B44805283FE26E00ADDB6E /* WABaseView.swift */; }; 44 | 77C41E652995F135004ABAE6 /* SectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C41E642995F135004ABAE6 /* SectionHeaderView.swift */; }; 45 | 77C41E672995F707004ABAE6 /* TrainingCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77C41E662995F707004ABAE6 /* TrainingCellView.swift */; }; 46 | 77F052AF28C608E600839EF6 /* PercentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77F052AE28C608E600839EF6 /* PercentView.swift */; }; 47 | /* End PBXBuildFile section */ 48 | 49 | /* Begin PBXFileReference section */ 50 | 77040638285E17F9005ED76D /* TimerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimerView.swift; sourceTree = ""; }; 51 | 7704063B285E1873005ED76D /* ProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressView.swift; sourceTree = ""; }; 52 | 774B893529517830001ED31E /* ChartView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartView.swift; sourceTree = ""; }; 53 | 7766B539284288C0000FF440 /* WeekView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeekView.swift; sourceTree = ""; }; 54 | 7766B53B28428992000FF440 /* WeekdayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeekdayView.swift; sourceTree = ""; }; 55 | 7766B53D28428BF9000FF440 /* Date + ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date + ext.swift"; sourceTree = ""; }; 56 | 776BE29828F1974000316C05 /* StatsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsView.swift; sourceTree = ""; }; 57 | 776BE29B28F1978300316C05 /* StatsItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsItemView.swift; sourceTree = ""; }; 58 | 776BE2A528F1D8EF00316C05 /* StepsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StepsView.swift; sourceTree = ""; }; 59 | 776BE2A828F1D97E00316C05 /* WABarsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WABarsView.swift; sourceTree = ""; }; 60 | 776BE2AB28F1D98B00316C05 /* WABarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WABarView.swift; sourceTree = ""; }; 61 | 77709141283BEDAC00AD7841 /* WorkoutApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WorkoutApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 62 | 77709144283BEDAC00AD7841 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 63 | 77709146283BEDAC00AD7841 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 64 | 77709148283BEDAC00AD7841 /* OverviewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverviewController.swift; sourceTree = ""; }; 65 | 7770914D283BEDAD00AD7841 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 66 | 77709150283BEDAD00AD7841 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 67 | 77709152283BEDAD00AD7841 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 68 | 7770915A283BEEEF00AD7841 /* TabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarController.swift; sourceTree = ""; }; 69 | 7770915E283BEF8D00AD7841 /* UIColor + ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor + ext.swift"; sourceTree = ""; }; 70 | 77709161283BEFDE00AD7841 /* Resouces.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Resouces.swift; sourceTree = ""; }; 71 | 77709163283BF45500AD7841 /* NavBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavBarController.swift; sourceTree = ""; }; 72 | 77709167283BF5A100AD7841 /* WABaseController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WABaseController.swift; sourceTree = ""; }; 73 | 77709169283BF70400AD7841 /* UIView + ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView + ext.swift"; sourceTree = ""; }; 74 | 77709171283BF82600AD7841 /* SessionController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionController.swift; sourceTree = ""; }; 75 | 77709173283BF83D00AD7841 /* ProgressController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressController.swift; sourceTree = ""; }; 76 | 77709175283BF84E00AD7841 /* SettingsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsController.swift; sourceTree = ""; }; 77 | 77854D05284BC4EE0095D8A5 /* WABaseInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WABaseInfoView.swift; sourceTree = ""; }; 78 | 77A322E5294056DC00905950 /* DailyPerformanceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DailyPerformanceView.swift; sourceTree = ""; }; 79 | 77A322E82940655500905950 /* MonthlyPerformanceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthlyPerformanceView.swift; sourceTree = ""; }; 80 | 77A322EC2940665C00905950 /* WAChartsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WAChartsView.swift; sourceTree = ""; }; 81 | 77A322EE294067D000905950 /* XAxisView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XAxisView.swift; sourceTree = ""; }; 82 | 77A322F029406A0700905950 /* YAxisView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YAxisView.swift; sourceTree = ""; }; 83 | 77B44801283D4EC100ADDB6E /* WAButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WAButton.swift; sourceTree = ""; }; 84 | 77B44803283FE24400ADDB6E /* OverviewNavBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverviewNavBar.swift; sourceTree = ""; }; 85 | 77B44805283FE26E00ADDB6E /* WABaseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WABaseView.swift; sourceTree = ""; }; 86 | 77C41E642995F135004ABAE6 /* SectionHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionHeaderView.swift; sourceTree = ""; }; 87 | 77C41E662995F707004ABAE6 /* TrainingCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrainingCellView.swift; sourceTree = ""; }; 88 | 77F052AE28C608E600839EF6 /* PercentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PercentView.swift; sourceTree = ""; }; 89 | /* End PBXFileReference section */ 90 | 91 | /* Begin PBXFrameworksBuildPhase section */ 92 | 7770913E283BEDAC00AD7841 /* Frameworks */ = { 93 | isa = PBXFrameworksBuildPhase; 94 | buildActionMask = 2147483647; 95 | files = ( 96 | ); 97 | runOnlyForDeploymentPostprocessing = 0; 98 | }; 99 | /* End PBXFrameworksBuildPhase section */ 100 | 101 | /* Begin PBXGroup section */ 102 | 77040637285E17E2005ED76D /* Views */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 7704063A285E1866005ED76D /* Timer */, 106 | 776BE29628F1958800316C05 /* Stats */, 107 | 776BE29728F1958F00316C05 /* Steps */, 108 | ); 109 | path = Views; 110 | sourceTree = ""; 111 | }; 112 | 7704063A285E1866005ED76D /* Timer */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | 77040638285E17F9005ED76D /* TimerView.swift */, 116 | 7704063B285E1873005ED76D /* ProgressView.swift */, 117 | 77F052AE28C608E600839EF6 /* PercentView.swift */, 118 | ); 119 | path = Timer; 120 | sourceTree = ""; 121 | }; 122 | 7766B538284288A9000FF440 /* WeekView */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | 7766B539284288C0000FF440 /* WeekView.swift */, 126 | 7766B53B28428992000FF440 /* WeekdayView.swift */, 127 | ); 128 | path = WeekView; 129 | sourceTree = ""; 130 | }; 131 | 7766B53F28429588000FF440 /* NavBar */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 7766B538284288A9000FF440 /* WeekView */, 135 | 77B44803283FE24400ADDB6E /* OverviewNavBar.swift */, 136 | ); 137 | path = NavBar; 138 | sourceTree = ""; 139 | }; 140 | 7766B540284295C4000FF440 /* Navigation */ = { 141 | isa = PBXGroup; 142 | children = ( 143 | 7770915A283BEEEF00AD7841 /* TabBarController.swift */, 144 | 77709163283BF45500AD7841 /* NavBarController.swift */, 145 | ); 146 | path = Navigation; 147 | sourceTree = ""; 148 | }; 149 | 776BE29628F1958800316C05 /* Stats */ = { 150 | isa = PBXGroup; 151 | children = ( 152 | 776BE29A28F1976600316C05 /* Views */, 153 | 776BE29828F1974000316C05 /* StatsView.swift */, 154 | ); 155 | path = Stats; 156 | sourceTree = ""; 157 | }; 158 | 776BE29728F1958F00316C05 /* Steps */ = { 159 | isa = PBXGroup; 160 | children = ( 161 | 776BE2A528F1D8EF00316C05 /* StepsView.swift */, 162 | ); 163 | path = Steps; 164 | sourceTree = ""; 165 | }; 166 | 776BE29A28F1976600316C05 /* Views */ = { 167 | isa = PBXGroup; 168 | children = ( 169 | 776BE29B28F1978300316C05 /* StatsItemView.swift */, 170 | ); 171 | path = Views; 172 | sourceTree = ""; 173 | }; 174 | 776BE2A728F1D95C00316C05 /* WABarsView */ = { 175 | isa = PBXGroup; 176 | children = ( 177 | 776BE2AA28F1D98100316C05 /* Views */, 178 | 776BE2A828F1D97E00316C05 /* WABarsView.swift */, 179 | ); 180 | path = WABarsView; 181 | sourceTree = ""; 182 | }; 183 | 776BE2AA28F1D98100316C05 /* Views */ = { 184 | isa = PBXGroup; 185 | children = ( 186 | 776BE2AB28F1D98B00316C05 /* WABarView.swift */, 187 | ); 188 | path = Views; 189 | sourceTree = ""; 190 | }; 191 | 77709138283BEDAC00AD7841 = { 192 | isa = PBXGroup; 193 | children = ( 194 | 77709143283BEDAC00AD7841 /* WorkoutApp */, 195 | 77709142283BEDAC00AD7841 /* Products */, 196 | ); 197 | sourceTree = ""; 198 | }; 199 | 77709142283BEDAC00AD7841 /* Products */ = { 200 | isa = PBXGroup; 201 | children = ( 202 | 77709141283BEDAC00AD7841 /* WorkoutApp.app */, 203 | ); 204 | name = Products; 205 | sourceTree = ""; 206 | }; 207 | 77709143283BEDAC00AD7841 /* WorkoutApp */ = { 208 | isa = PBXGroup; 209 | children = ( 210 | 7770915D283BEF7A00AD7841 /* Helpers */, 211 | 77709158283BEDF000AD7841 /* Application */, 212 | 77709165283BF58C00AD7841 /* Controllers */, 213 | 77709159283BEE0000AD7841 /* SupportingFiles */, 214 | ); 215 | path = WorkoutApp; 216 | sourceTree = ""; 217 | }; 218 | 77709158283BEDF000AD7841 /* Application */ = { 219 | isa = PBXGroup; 220 | children = ( 221 | 77709144283BEDAC00AD7841 /* AppDelegate.swift */, 222 | 77709146283BEDAC00AD7841 /* SceneDelegate.swift */, 223 | ); 224 | path = Application; 225 | sourceTree = ""; 226 | }; 227 | 77709159283BEE0000AD7841 /* SupportingFiles */ = { 228 | isa = PBXGroup; 229 | children = ( 230 | 7770914D283BEDAD00AD7841 /* Assets.xcassets */, 231 | 7770914F283BEDAD00AD7841 /* LaunchScreen.storyboard */, 232 | 77709152283BEDAD00AD7841 /* Info.plist */, 233 | ); 234 | path = SupportingFiles; 235 | sourceTree = ""; 236 | }; 237 | 7770915D283BEF7A00AD7841 /* Helpers */ = { 238 | isa = PBXGroup; 239 | children = ( 240 | 77709160283BEF9500AD7841 /* Extensions */, 241 | 77709161283BEFDE00AD7841 /* Resouces.swift */, 242 | ); 243 | path = Helpers; 244 | sourceTree = ""; 245 | }; 246 | 77709160283BEF9500AD7841 /* Extensions */ = { 247 | isa = PBXGroup; 248 | children = ( 249 | 7770915E283BEF8D00AD7841 /* UIColor + ext.swift */, 250 | 77709169283BF70400AD7841 /* UIView + ext.swift */, 251 | 7766B53D28428BF9000FF440 /* Date + ext.swift */, 252 | ); 253 | path = Extensions; 254 | sourceTree = ""; 255 | }; 256 | 77709165283BF58C00AD7841 /* Controllers */ = { 257 | isa = PBXGroup; 258 | children = ( 259 | 7766B540284295C4000FF440 /* Navigation */, 260 | 7770916C283BF7FB00AD7841 /* Overview */, 261 | 7770916D283BF80300AD7841 /* Session */, 262 | 7770916E283BF80A00AD7841 /* Progress */, 263 | 7770916F283BF80F00AD7841 /* Settings */, 264 | 7770916B283BF7EF00AD7841 /* Base */, 265 | ); 266 | path = Controllers; 267 | sourceTree = ""; 268 | }; 269 | 7770916B283BF7EF00AD7841 /* Base */ = { 270 | isa = PBXGroup; 271 | children = ( 272 | 77854D04284BC4DD0095D8A5 /* Views */, 273 | 77709167283BF5A100AD7841 /* WABaseController.swift */, 274 | ); 275 | path = Base; 276 | sourceTree = ""; 277 | }; 278 | 7770916C283BF7FB00AD7841 /* Overview */ = { 279 | isa = PBXGroup; 280 | children = ( 281 | 77B44800283D4E9900ADDB6E /* Views */, 282 | 77709148283BEDAC00AD7841 /* OverviewController.swift */, 283 | ); 284 | path = Overview; 285 | sourceTree = ""; 286 | }; 287 | 7770916D283BF80300AD7841 /* Session */ = { 288 | isa = PBXGroup; 289 | children = ( 290 | 77040637285E17E2005ED76D /* Views */, 291 | 77709171283BF82600AD7841 /* SessionController.swift */, 292 | ); 293 | path = Session; 294 | sourceTree = ""; 295 | }; 296 | 7770916E283BF80A00AD7841 /* Progress */ = { 297 | isa = PBXGroup; 298 | children = ( 299 | 77A322E2294056B800905950 /* Views */, 300 | 77709173283BF83D00AD7841 /* ProgressController.swift */, 301 | ); 302 | path = Progress; 303 | sourceTree = ""; 304 | }; 305 | 7770916F283BF80F00AD7841 /* Settings */ = { 306 | isa = PBXGroup; 307 | children = ( 308 | 77709175283BF84E00AD7841 /* SettingsController.swift */, 309 | ); 310 | path = Settings; 311 | sourceTree = ""; 312 | }; 313 | 77854D04284BC4DD0095D8A5 /* Views */ = { 314 | isa = PBXGroup; 315 | children = ( 316 | 77A322EA2940663D00905950 /* WAChartsView */, 317 | 776BE2A728F1D95C00316C05 /* WABarsView */, 318 | 77B44805283FE26E00ADDB6E /* WABaseView.swift */, 319 | 77854D05284BC4EE0095D8A5 /* WABaseInfoView.swift */, 320 | 77B44801283D4EC100ADDB6E /* WAButton.swift */, 321 | ); 322 | path = Views; 323 | sourceTree = ""; 324 | }; 325 | 77A322E2294056B800905950 /* Views */ = { 326 | isa = PBXGroup; 327 | children = ( 328 | 77A322E4294056C900905950 /* DailyPerformance */, 329 | 77A322E72940653000905950 /* MonthlyPerformnaceView */, 330 | ); 331 | path = Views; 332 | sourceTree = ""; 333 | }; 334 | 77A322E4294056C900905950 /* DailyPerformance */ = { 335 | isa = PBXGroup; 336 | children = ( 337 | 77A322E5294056DC00905950 /* DailyPerformanceView.swift */, 338 | ); 339 | path = DailyPerformance; 340 | sourceTree = ""; 341 | }; 342 | 77A322E72940653000905950 /* MonthlyPerformnaceView */ = { 343 | isa = PBXGroup; 344 | children = ( 345 | 77A322E82940655500905950 /* MonthlyPerformanceView.swift */, 346 | ); 347 | path = MonthlyPerformnaceView; 348 | sourceTree = ""; 349 | }; 350 | 77A322EA2940663D00905950 /* WAChartsView */ = { 351 | isa = PBXGroup; 352 | children = ( 353 | 77A322EB2940664D00905950 /* Views */, 354 | 77A322EC2940665C00905950 /* WAChartsView.swift */, 355 | ); 356 | path = WAChartsView; 357 | sourceTree = ""; 358 | }; 359 | 77A322EB2940664D00905950 /* Views */ = { 360 | isa = PBXGroup; 361 | children = ( 362 | 77A322EE294067D000905950 /* XAxisView.swift */, 363 | 77A322F029406A0700905950 /* YAxisView.swift */, 364 | 774B893529517830001ED31E /* ChartView.swift */, 365 | ); 366 | path = Views; 367 | sourceTree = ""; 368 | }; 369 | 77B44800283D4E9900ADDB6E /* Views */ = { 370 | isa = PBXGroup; 371 | children = ( 372 | 77C41E622995F111004ABAE6 /* CollectionView */, 373 | 7766B53F28429588000FF440 /* NavBar */, 374 | ); 375 | path = Views; 376 | sourceTree = ""; 377 | }; 378 | 77C41E622995F111004ABAE6 /* CollectionView */ = { 379 | isa = PBXGroup; 380 | children = ( 381 | 77C41E632995F124004ABAE6 /* Views */, 382 | ); 383 | path = CollectionView; 384 | sourceTree = ""; 385 | }; 386 | 77C41E632995F124004ABAE6 /* Views */ = { 387 | isa = PBXGroup; 388 | children = ( 389 | 77C41E642995F135004ABAE6 /* SectionHeaderView.swift */, 390 | 77C41E662995F707004ABAE6 /* TrainingCellView.swift */, 391 | ); 392 | path = Views; 393 | sourceTree = ""; 394 | }; 395 | /* End PBXGroup section */ 396 | 397 | /* Begin PBXNativeTarget section */ 398 | 77709140283BEDAC00AD7841 /* WorkoutApp */ = { 399 | isa = PBXNativeTarget; 400 | buildConfigurationList = 77709155283BEDAD00AD7841 /* Build configuration list for PBXNativeTarget "WorkoutApp" */; 401 | buildPhases = ( 402 | 7770913D283BEDAC00AD7841 /* Sources */, 403 | 7770913E283BEDAC00AD7841 /* Frameworks */, 404 | 7770913F283BEDAC00AD7841 /* Resources */, 405 | ); 406 | buildRules = ( 407 | ); 408 | dependencies = ( 409 | ); 410 | name = WorkoutApp; 411 | productName = WorkoutApp; 412 | productReference = 77709141283BEDAC00AD7841 /* WorkoutApp.app */; 413 | productType = "com.apple.product-type.application"; 414 | }; 415 | /* End PBXNativeTarget section */ 416 | 417 | /* Begin PBXProject section */ 418 | 77709139283BEDAC00AD7841 /* Project object */ = { 419 | isa = PBXProject; 420 | attributes = { 421 | BuildIndependentTargetsInParallel = 1; 422 | LastSwiftUpdateCheck = 1330; 423 | LastUpgradeCheck = 1330; 424 | TargetAttributes = { 425 | 77709140283BEDAC00AD7841 = { 426 | CreatedOnToolsVersion = 13.3.1; 427 | }; 428 | }; 429 | }; 430 | buildConfigurationList = 7770913C283BEDAC00AD7841 /* Build configuration list for PBXProject "WorkoutApp" */; 431 | compatibilityVersion = "Xcode 13.0"; 432 | developmentRegion = en; 433 | hasScannedForEncodings = 0; 434 | knownRegions = ( 435 | en, 436 | Base, 437 | ); 438 | mainGroup = 77709138283BEDAC00AD7841; 439 | productRefGroup = 77709142283BEDAC00AD7841 /* Products */; 440 | projectDirPath = ""; 441 | projectRoot = ""; 442 | targets = ( 443 | 77709140283BEDAC00AD7841 /* WorkoutApp */, 444 | ); 445 | }; 446 | /* End PBXProject section */ 447 | 448 | /* Begin PBXResourcesBuildPhase section */ 449 | 7770913F283BEDAC00AD7841 /* Resources */ = { 450 | isa = PBXResourcesBuildPhase; 451 | buildActionMask = 2147483647; 452 | files = ( 453 | 77709151283BEDAD00AD7841 /* LaunchScreen.storyboard in Resources */, 454 | 7770914E283BEDAD00AD7841 /* Assets.xcassets in Resources */, 455 | ); 456 | runOnlyForDeploymentPostprocessing = 0; 457 | }; 458 | /* End PBXResourcesBuildPhase section */ 459 | 460 | /* Begin PBXSourcesBuildPhase section */ 461 | 7770913D283BEDAC00AD7841 /* Sources */ = { 462 | isa = PBXSourcesBuildPhase; 463 | buildActionMask = 2147483647; 464 | files = ( 465 | 7766B53A284288C0000FF440 /* WeekView.swift in Sources */, 466 | 7766B53E28428BF9000FF440 /* Date + ext.swift in Sources */, 467 | 77709176283BF84E00AD7841 /* SettingsController.swift in Sources */, 468 | 77709149283BEDAC00AD7841 /* OverviewController.swift in Sources */, 469 | 77B44804283FE24400ADDB6E /* OverviewNavBar.swift in Sources */, 470 | 77A322E92940655500905950 /* MonthlyPerformanceView.swift in Sources */, 471 | 77C41E672995F707004ABAE6 /* TrainingCellView.swift in Sources */, 472 | 7770916A283BF70400AD7841 /* UIView + ext.swift in Sources */, 473 | 7770915B283BEEEF00AD7841 /* TabBarController.swift in Sources */, 474 | 776BE2AC28F1D98B00316C05 /* WABarView.swift in Sources */, 475 | 77709145283BEDAC00AD7841 /* AppDelegate.swift in Sources */, 476 | 77A322E6294056DC00905950 /* DailyPerformanceView.swift in Sources */, 477 | 77A322ED2940665C00905950 /* WAChartsView.swift in Sources */, 478 | 77709172283BF82600AD7841 /* SessionController.swift in Sources */, 479 | 77854D06284BC4EE0095D8A5 /* WABaseInfoView.swift in Sources */, 480 | 77040639285E17F9005ED76D /* TimerView.swift in Sources */, 481 | 7704063C285E1873005ED76D /* ProgressView.swift in Sources */, 482 | 77B44802283D4EC100ADDB6E /* WAButton.swift in Sources */, 483 | 776BE2A628F1D8EF00316C05 /* StepsView.swift in Sources */, 484 | 77A322F129406A0700905950 /* YAxisView.swift in Sources */, 485 | 77A322EF294067D000905950 /* XAxisView.swift in Sources */, 486 | 77709174283BF83D00AD7841 /* ProgressController.swift in Sources */, 487 | 77709164283BF45500AD7841 /* NavBarController.swift in Sources */, 488 | 776BE29C28F1978300316C05 /* StatsItemView.swift in Sources */, 489 | 7770915F283BEF8D00AD7841 /* UIColor + ext.swift in Sources */, 490 | 77F052AF28C608E600839EF6 /* PercentView.swift in Sources */, 491 | 77709162283BEFDE00AD7841 /* Resouces.swift in Sources */, 492 | 77B44806283FE26E00ADDB6E /* WABaseView.swift in Sources */, 493 | 77C41E652995F135004ABAE6 /* SectionHeaderView.swift in Sources */, 494 | 7766B53C28428992000FF440 /* WeekdayView.swift in Sources */, 495 | 776BE2A928F1D97E00316C05 /* WABarsView.swift in Sources */, 496 | 774B893629517830001ED31E /* ChartView.swift in Sources */, 497 | 776BE29928F1974000316C05 /* StatsView.swift in Sources */, 498 | 77709168283BF5A100AD7841 /* WABaseController.swift in Sources */, 499 | 77709147283BEDAC00AD7841 /* SceneDelegate.swift in Sources */, 500 | ); 501 | runOnlyForDeploymentPostprocessing = 0; 502 | }; 503 | /* End PBXSourcesBuildPhase section */ 504 | 505 | /* Begin PBXVariantGroup section */ 506 | 7770914F283BEDAD00AD7841 /* LaunchScreen.storyboard */ = { 507 | isa = PBXVariantGroup; 508 | children = ( 509 | 77709150283BEDAD00AD7841 /* Base */, 510 | ); 511 | name = LaunchScreen.storyboard; 512 | sourceTree = ""; 513 | }; 514 | /* End PBXVariantGroup section */ 515 | 516 | /* Begin XCBuildConfiguration section */ 517 | 77709153283BEDAD00AD7841 /* Debug */ = { 518 | isa = XCBuildConfiguration; 519 | buildSettings = { 520 | ALWAYS_SEARCH_USER_PATHS = NO; 521 | CLANG_ANALYZER_NONNULL = YES; 522 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 523 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 524 | CLANG_ENABLE_MODULES = YES; 525 | CLANG_ENABLE_OBJC_ARC = YES; 526 | CLANG_ENABLE_OBJC_WEAK = YES; 527 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 528 | CLANG_WARN_BOOL_CONVERSION = YES; 529 | CLANG_WARN_COMMA = YES; 530 | CLANG_WARN_CONSTANT_CONVERSION = YES; 531 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 532 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 533 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 534 | CLANG_WARN_EMPTY_BODY = YES; 535 | CLANG_WARN_ENUM_CONVERSION = YES; 536 | CLANG_WARN_INFINITE_RECURSION = YES; 537 | CLANG_WARN_INT_CONVERSION = YES; 538 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 539 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 540 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 541 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 542 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 543 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 544 | CLANG_WARN_STRICT_PROTOTYPES = YES; 545 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 546 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 547 | CLANG_WARN_UNREACHABLE_CODE = YES; 548 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 549 | COPY_PHASE_STRIP = NO; 550 | DEBUG_INFORMATION_FORMAT = dwarf; 551 | ENABLE_STRICT_OBJC_MSGSEND = YES; 552 | ENABLE_TESTABILITY = YES; 553 | GCC_C_LANGUAGE_STANDARD = gnu11; 554 | GCC_DYNAMIC_NO_PIC = NO; 555 | GCC_NO_COMMON_BLOCKS = YES; 556 | GCC_OPTIMIZATION_LEVEL = 0; 557 | GCC_PREPROCESSOR_DEFINITIONS = ( 558 | "DEBUG=1", 559 | "$(inherited)", 560 | ); 561 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 562 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 563 | GCC_WARN_UNDECLARED_SELECTOR = YES; 564 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 565 | GCC_WARN_UNUSED_FUNCTION = YES; 566 | GCC_WARN_UNUSED_VARIABLE = YES; 567 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 568 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 569 | MTL_FAST_MATH = YES; 570 | ONLY_ACTIVE_ARCH = YES; 571 | SDKROOT = iphoneos; 572 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 573 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 574 | }; 575 | name = Debug; 576 | }; 577 | 77709154283BEDAD00AD7841 /* Release */ = { 578 | isa = XCBuildConfiguration; 579 | buildSettings = { 580 | ALWAYS_SEARCH_USER_PATHS = NO; 581 | CLANG_ANALYZER_NONNULL = YES; 582 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 583 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 584 | CLANG_ENABLE_MODULES = YES; 585 | CLANG_ENABLE_OBJC_ARC = YES; 586 | CLANG_ENABLE_OBJC_WEAK = YES; 587 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 588 | CLANG_WARN_BOOL_CONVERSION = YES; 589 | CLANG_WARN_COMMA = YES; 590 | CLANG_WARN_CONSTANT_CONVERSION = YES; 591 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 592 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 593 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 594 | CLANG_WARN_EMPTY_BODY = YES; 595 | CLANG_WARN_ENUM_CONVERSION = YES; 596 | CLANG_WARN_INFINITE_RECURSION = YES; 597 | CLANG_WARN_INT_CONVERSION = YES; 598 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 599 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 600 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 601 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 602 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 603 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 604 | CLANG_WARN_STRICT_PROTOTYPES = YES; 605 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 606 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 607 | CLANG_WARN_UNREACHABLE_CODE = YES; 608 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 609 | COPY_PHASE_STRIP = NO; 610 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 611 | ENABLE_NS_ASSERTIONS = NO; 612 | ENABLE_STRICT_OBJC_MSGSEND = YES; 613 | GCC_C_LANGUAGE_STANDARD = gnu11; 614 | GCC_NO_COMMON_BLOCKS = YES; 615 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 616 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 617 | GCC_WARN_UNDECLARED_SELECTOR = YES; 618 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 619 | GCC_WARN_UNUSED_FUNCTION = YES; 620 | GCC_WARN_UNUSED_VARIABLE = YES; 621 | IPHONEOS_DEPLOYMENT_TARGET = 13.0; 622 | MTL_ENABLE_DEBUG_INFO = NO; 623 | MTL_FAST_MATH = YES; 624 | SDKROOT = iphoneos; 625 | SWIFT_COMPILATION_MODE = wholemodule; 626 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 627 | VALIDATE_PRODUCT = YES; 628 | }; 629 | name = Release; 630 | }; 631 | 77709156283BEDAD00AD7841 /* Debug */ = { 632 | isa = XCBuildConfiguration; 633 | buildSettings = { 634 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 635 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 636 | CODE_SIGN_STYLE = Automatic; 637 | CURRENT_PROJECT_VERSION = 1; 638 | GENERATE_INFOPLIST_FILE = YES; 639 | INFOPLIST_FILE = WorkoutApp/SupportingFiles/Info.plist; 640 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 641 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 642 | INFOPLIST_KEY_UIStatusBarHidden = NO; 643 | INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; 644 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 645 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 646 | LD_RUNPATH_SEARCH_PATHS = ( 647 | "$(inherited)", 648 | "@executable_path/Frameworks", 649 | ); 650 | MARKETING_VERSION = 1.0; 651 | PRODUCT_BUNDLE_IDENTIFIER = "VP-P.WorkoutApp"; 652 | PRODUCT_NAME = "$(TARGET_NAME)"; 653 | SWIFT_EMIT_LOC_STRINGS = YES; 654 | SWIFT_VERSION = 5.0; 655 | TARGETED_DEVICE_FAMILY = 1; 656 | }; 657 | name = Debug; 658 | }; 659 | 77709157283BEDAD00AD7841 /* Release */ = { 660 | isa = XCBuildConfiguration; 661 | buildSettings = { 662 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 663 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 664 | CODE_SIGN_STYLE = Automatic; 665 | CURRENT_PROJECT_VERSION = 1; 666 | GENERATE_INFOPLIST_FILE = YES; 667 | INFOPLIST_FILE = WorkoutApp/SupportingFiles/Info.plist; 668 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 669 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 670 | INFOPLIST_KEY_UIStatusBarHidden = NO; 671 | INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; 672 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 673 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 674 | LD_RUNPATH_SEARCH_PATHS = ( 675 | "$(inherited)", 676 | "@executable_path/Frameworks", 677 | ); 678 | MARKETING_VERSION = 1.0; 679 | PRODUCT_BUNDLE_IDENTIFIER = "VP-P.WorkoutApp"; 680 | PRODUCT_NAME = "$(TARGET_NAME)"; 681 | SWIFT_EMIT_LOC_STRINGS = YES; 682 | SWIFT_VERSION = 5.0; 683 | TARGETED_DEVICE_FAMILY = 1; 684 | }; 685 | name = Release; 686 | }; 687 | /* End XCBuildConfiguration section */ 688 | 689 | /* Begin XCConfigurationList section */ 690 | 7770913C283BEDAC00AD7841 /* Build configuration list for PBXProject "WorkoutApp" */ = { 691 | isa = XCConfigurationList; 692 | buildConfigurations = ( 693 | 77709153283BEDAD00AD7841 /* Debug */, 694 | 77709154283BEDAD00AD7841 /* Release */, 695 | ); 696 | defaultConfigurationIsVisible = 0; 697 | defaultConfigurationName = Release; 698 | }; 699 | 77709155283BEDAD00AD7841 /* Build configuration list for PBXNativeTarget "WorkoutApp" */ = { 700 | isa = XCConfigurationList; 701 | buildConfigurations = ( 702 | 77709156283BEDAD00AD7841 /* Debug */, 703 | 77709157283BEDAD00AD7841 /* Release */, 704 | ); 705 | defaultConfigurationIsVisible = 0; 706 | defaultConfigurationName = Release; 707 | }; 708 | /* End XCConfigurationList section */ 709 | }; 710 | rootObject = 77709139283BEDAC00AD7841 /* Project object */; 711 | } 712 | --------------------------------------------------------------------------------