diff --git a/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj index 47c3013b..2ee617f1 100644 --- a/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj +++ b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj @@ -18,6 +18,9 @@ A3BC2F3D296468E500198261 /* UploadedCourseInfoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3BC2F3C296468E500198261 /* UploadedCourseInfoModel.swift */; }; A3BC2F3F2964706100198261 /* UploadedCourseInfoCVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3BC2F3E2964706100198261 /* UploadedCourseInfoCVC.swift */; }; CE0D9FD329648DA300CEB5CD /* CustomAlertVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE0D9FD229648DA300CEB5CD /* CustomAlertVC.swift */; }; + CE146770296568DC00DCEA1B /* RunTrackingVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE14676F296568DC00DCEA1B /* RunTrackingVC.swift */; }; + CE14677829658C7200DCEA1B /* Stopwatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE14677729658C7200DCEA1B /* Stopwatch.swift */; }; + CE14677A2965A80700DCEA1B /* CustomBottomSheetVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1467792965A80700DCEA1B /* CustomBottomSheetVC.swift */; }; CE17F02D2961BBA100E1DED0 /* ColorLiterals.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE17F02C2961BBA100E1DED0 /* ColorLiterals.swift */; }; CE17F0332961BEF800E1DED0 /* Pretendard-Medium.otf in Resources */ = {isa = PBXBuildFile; fileRef = CE17F02F2961BEF800E1DED0 /* Pretendard-Medium.otf */; }; CE17F0342961BEF800E1DED0 /* Pretendard-Bold.otf in Resources */ = {isa = PBXBuildFile; fileRef = CE17F0302961BEF800E1DED0 /* Pretendard-Bold.otf */; }; @@ -104,6 +107,9 @@ A3BC2F3C296468E500198261 /* UploadedCourseInfoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UploadedCourseInfoModel.swift; sourceTree = ""; }; A3BC2F3E2964706100198261 /* UploadedCourseInfoCVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UploadedCourseInfoCVC.swift; sourceTree = ""; }; CE0D9FD229648DA300CEB5CD /* CustomAlertVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomAlertVC.swift; sourceTree = ""; }; + CE14676F296568DC00DCEA1B /* RunTrackingVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunTrackingVC.swift; sourceTree = ""; }; + CE14677729658C7200DCEA1B /* Stopwatch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stopwatch.swift; sourceTree = ""; }; + CE1467792965A80700DCEA1B /* CustomBottomSheetVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomBottomSheetVC.swift; sourceTree = ""; }; CE17F02C2961BBA100E1DED0 /* ColorLiterals.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorLiterals.swift; sourceTree = ""; }; CE17F02F2961BEF800E1DED0 /* Pretendard-Medium.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-Medium.otf"; sourceTree = ""; }; CE17F0302961BEF800E1DED0 /* Pretendard-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-Bold.otf"; sourceTree = ""; }; @@ -259,6 +265,30 @@ path = UploadedCourseInfoCollectionView; sourceTree = ""; }; + CE14676C296568C000DCEA1B /* RunTracking */ = { + isa = PBXGroup; + children = ( + CE14676E296568CD00DCEA1B /* Views */, + CE14676D296568CA00DCEA1B /* VC */, + ); + path = RunTracking; + sourceTree = ""; + }; + CE14676D296568CA00DCEA1B /* VC */ = { + isa = PBXGroup; + children = ( + CE14676F296568DC00DCEA1B /* RunTrackingVC.swift */, + ); + path = VC; + sourceTree = ""; + }; + CE14676E296568CD00DCEA1B /* Views */ = { + isa = PBXGroup; + children = ( + ); + path = Views; + sourceTree = ""; + }; CE17F02E2961BEAE00E1DED0 /* Fonts */ = { isa = PBXGroup; children = ( @@ -448,6 +478,7 @@ CE17F03C2961C32C00E1DED0 /* CourseDiscovery */, CE17F03B2961C2F700E1DED0 /* MyPage */, CE17F03E2961C38100E1DED0 /* CourseDetail */, + CE14676C296568C000DCEA1B /* RunTracking */, ); path = Presentation; sourceTree = ""; @@ -549,6 +580,7 @@ CE6655C9295D84DD00C64E12 /* UserDefaultKeyList.swift */, CEC2A68F2962B06C00160BF7 /* convertLocationObject.swift */, CE29D583296416D800F47542 /* caculateStatusBarHeight.swift */, + CE14677729658C7200DCEA1B /* Stopwatch.swift */, ); path = Utils; sourceTree = ""; @@ -600,6 +632,7 @@ CEEC6B4A2961D89700D00E1E /* CustomNavigationBar.swift */, CEC2A6842961F92C00160BF7 /* CustomButton.swift */, CE0D9FD229648DA300CEB5CD /* CustomAlertVC.swift */, + CE1467792965A80700DCEA1B /* CustomBottomSheetVC.swift */, ); path = UIComponents; sourceTree = ""; @@ -842,6 +875,7 @@ CE665602295D918000C64E12 /* JsonCoder.swift in Sources */, CE4545CD295D7AF4003201E1 /* TaBarController.swift in Sources */, CE6655F4295D898400C64E12 /* UIViewController+.swift in Sources */, + CE14677829658C7200DCEA1B /* Stopwatch.swift in Sources */, CE5645162961B72E000A2856 /* ImageLiterals.swift in Sources */, CE6655CD295D856300C64E12 /* KeyPathFindable.swift in Sources */, CE29D582296402B500F47542 /* CourseDrawingVC.swift in Sources */, @@ -888,6 +922,7 @@ CE6655DE295D877F00C64E12 /* UIColor+.swift in Sources */, CEEC6B402961C55000D00E1E /* MyPageVC.swift in Sources */, CE665608295D921500C64E12 /* setImage.swift in Sources */, + CE146770296568DC00DCEA1B /* RunTrackingVC.swift in Sources */, CE665612295D92E400C64E12 /* UserDefaultWrapper.swift in Sources */, A3BC2F3A2963D0ED00198261 /* ActivityRecordInfoTVC.swift in Sources */, CE665610295D92C200C64E12 /* setTextLineHeight.swift in Sources */, @@ -895,6 +930,7 @@ A3BC2F322962E0DB00198261 /* GoalRewardInfoModel.swift in Sources */, CEC2A68C2962AE1B00160BF7 /* RNStartMarker.swift in Sources */, CE5875A4296015D2005D967E /* Encodable+.swift in Sources */, + CE14677A2965A80700DCEA1B /* CustomBottomSheetVC.swift in Sources */, CEEC6B4B2961D89700D00E1E /* CustomNavigationBar.swift in Sources */, CE17F02D2961BBA100E1DED0 /* ColorLiterals.swift in Sources */, CEC2A68E2962AF2C00160BF7 /* RNMarker.swift in Sources */, diff --git a/Runnect-iOS/Runnect-iOS/Global/Literal/ImageLiterals.swift b/Runnect-iOS/Runnect-iOS/Global/Literal/ImageLiterals.swift index 87fed01a..98648761 100644 --- a/Runnect-iOS/Runnect-iOS/Global/Literal/ImageLiterals.swift +++ b/Runnect-iOS/Runnect-iOS/Global/Literal/ImageLiterals.swift @@ -61,6 +61,7 @@ enum ImageLiterals { static var imgStorage: UIImage { .load(named: "img_storage") } static var imgLock: UIImage { .load(named: "img_lock") } static var imgTelescope: UIImage { .load(named: "img_telescope") } + static var imgAppIcon: UIImage { .load(named: "img_app_icon") } } extension UIImage { diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json index 13613e3e..cf438054 100644 --- a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,6 +1,7 @@ { "images" : [ { + "filename" : "runnect logo-01 1.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/AppIcon.appiconset/runnect logo-01 1.png b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/AppIcon.appiconset/runnect logo-01 1.png new file mode 100644 index 00000000..b97a9620 Binary files /dev/null and b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/AppIcon.appiconset/runnect logo-01 1.png differ diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_cancel.imageset/Contents.json b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_cancel.imageset/Contents.json index c2841928..96e14181 100644 --- a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_cancel.imageset/Contents.json +++ b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_cancel.imageset/Contents.json @@ -1,17 +1,17 @@ { "images" : [ { - "filename" : "btn_back.png", + "filename" : "back.png", "idiom" : "universal", "scale" : "1x" }, { - "filename" : "btn_back@2x.png", + "filename" : "back@2x.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "btn_back@3x.png", + "filename" : "back@3x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_cancel.imageset/back.png b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_cancel.imageset/back.png new file mode 100644 index 00000000..d7b01b91 Binary files /dev/null and b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_cancel.imageset/back.png differ diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_cancel.imageset/back@2x.png b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_cancel.imageset/back@2x.png new file mode 100644 index 00000000..980437f7 Binary files /dev/null and b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_cancel.imageset/back@2x.png differ diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_cancel.imageset/back@3x.png b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_cancel.imageset/back@3x.png new file mode 100644 index 00000000..90d692fa Binary files /dev/null and b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_cancel.imageset/back@3x.png differ diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_cancel.imageset/btn_back.png b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_cancel.imageset/btn_back.png deleted file mode 100644 index f70c8139..00000000 Binary files a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_cancel.imageset/btn_back.png and /dev/null differ diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_cancel.imageset/btn_back@2x.png b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_cancel.imageset/btn_back@2x.png deleted file mode 100644 index 727af254..00000000 Binary files a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_cancel.imageset/btn_back@2x.png and /dev/null differ diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_cancel.imageset/btn_back@3x.png b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_cancel.imageset/btn_back@3x.png deleted file mode 100644 index ddd9298c..00000000 Binary files a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_cancel.imageset/btn_back@3x.png and /dev/null differ diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_map_location.imageset/Contents.json b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_map_location.imageset/Contents.json index 32cca116..cb275ee6 100644 --- a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_map_location.imageset/Contents.json +++ b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_map_location.imageset/Contents.json @@ -1,17 +1,17 @@ { "images" : [ { - "filename" : "map_location.png", + "filename" : "btn.png", "idiom" : "universal", "scale" : "1x" }, { - "filename" : "map_location@2x.png", + "filename" : "btn@2x.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "map_location@3x.png", + "filename" : "btn@3x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_map_location.imageset/btn.png b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_map_location.imageset/btn.png new file mode 100644 index 00000000..5a15968c Binary files /dev/null and b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_map_location.imageset/btn.png differ diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_map_location.imageset/btn@2x.png b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_map_location.imageset/btn@2x.png new file mode 100644 index 00000000..206bbd9d Binary files /dev/null and b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_map_location.imageset/btn@2x.png differ diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_map_location.imageset/btn@3x.png b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_map_location.imageset/btn@3x.png new file mode 100644 index 00000000..97e42d1d Binary files /dev/null and b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_map_location.imageset/btn@3x.png differ diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_map_location.imageset/map_location.png b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_map_location.imageset/map_location.png deleted file mode 100644 index cf0f27ee..00000000 Binary files a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_map_location.imageset/map_location.png and /dev/null differ diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_map_location.imageset/map_location@2x.png b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_map_location.imageset/map_location@2x.png deleted file mode 100644 index 93d29cc8..00000000 Binary files a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_map_location.imageset/map_location@2x.png and /dev/null differ diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_map_location.imageset/map_location@3x.png b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_map_location.imageset/map_location@3x.png deleted file mode 100644 index 5bf7ad6d..00000000 Binary files a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_map_location.imageset/map_location@3x.png and /dev/null differ diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_star.imageset/Contents.json b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_star.imageset/Contents.json index 0e99b78b..dec3cc7e 100644 --- a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_star.imageset/Contents.json +++ b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_star.imageset/Contents.json @@ -19,5 +19,8 @@ "info" : { "author" : "xcode", "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "original" } } diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_star2.imageset/Contents.json b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_star2.imageset/Contents.json index df240cf1..32dd965f 100644 --- a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_star2.imageset/Contents.json +++ b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_star2.imageset/Contents.json @@ -19,5 +19,8 @@ "info" : { "author" : "xcode", "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "original" } } diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_app_icon.imageset/Contents.json b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_app_icon.imageset/Contents.json new file mode 100644 index 00000000..75546ec0 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_app_icon.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "runnect logo-01 1.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "runnect logo-01 1@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "runnect logo-01 1@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_app_icon.imageset/runnect logo-01 1.png b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_app_icon.imageset/runnect logo-01 1.png new file mode 100644 index 00000000..78af5cb3 Binary files /dev/null and b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_app_icon.imageset/runnect logo-01 1.png differ diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_app_icon.imageset/runnect logo-01 1@2x.png b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_app_icon.imageset/runnect logo-01 1@2x.png new file mode 100644 index 00000000..fb38ca69 Binary files /dev/null and b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_app_icon.imageset/runnect logo-01 1@2x.png differ diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_app_icon.imageset/runnect logo-01 1@3x.png b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_app_icon.imageset/runnect logo-01 1@3x.png new file mode 100644 index 00000000..8e9182fc Binary files /dev/null and b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_app_icon.imageset/runnect logo-01 1@3x.png differ diff --git a/Runnect-iOS/Runnect-iOS/Global/UIComponents/CustomAlertVC.swift b/Runnect-iOS/Runnect-iOS/Global/UIComponents/CustomAlertVC.swift index 0e887353..307e5116 100644 --- a/Runnect-iOS/Runnect-iOS/Global/UIComponents/CustomAlertVC.swift +++ b/Runnect-iOS/Runnect-iOS/Global/UIComponents/CustomAlertVC.swift @@ -28,7 +28,7 @@ final class CustomAlertVC: UIViewController { private let alertView = UIView() private let alertImageView = UIImageView().then { - $0.image = ImageLiterals.imgTelescope + $0.image = ImageLiterals.imgPaper } private let contentsLabel = UILabel().then { $0.text = "코스를 만들었어요!" diff --git a/Runnect-iOS/Runnect-iOS/Global/UIComponents/CustomBottomSheetVC.swift b/Runnect-iOS/Runnect-iOS/Global/UIComponents/CustomBottomSheetVC.swift new file mode 100644 index 00000000..662fa560 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/UIComponents/CustomBottomSheetVC.swift @@ -0,0 +1,103 @@ +// +// CustomBottomSheetVC.swift +// Runnect-iOS +// +// Created by sejin on 2023/01/04. +// + +import UIKit +import Combine + +final class CustomBottomSheetVC: UIViewController { + + // MARK: - Properties + + var completeButtonTapped: Driver { + completeButton.publisher(for: .touchUpInside) + .map { _ in } + .asDriver() + } + + // MARK: - UI Components + + private let bottomSheetView = UIView().then { + $0.backgroundColor = .w1 + $0.layer.cornerRadius = 20 + $0.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] + } + + private let contentsLabel = UILabel().then { + $0.text = "수고하셨습니다! 러닝을 완료했어요!" + $0.font = .h5 + $0.textColor = .g2 + } + + private let mainImageView = UIImageView().then { + $0.image = ImageLiterals.imgTelescope + } + + private let completeButton = CustomButton(title: "기록 보러가기") + + // MARK: - View Life Cycle + + override func viewDidLoad() { + super.viewDidLoad() + self.setUI() + self.setLayout() + } +} + +// MARK: - Methods + +extension CustomBottomSheetVC { + + /// 바텀 시트의 라벨에 들어갈 텍스트 설정 + @discardableResult + func setContentsText(text: String) -> Self { + self.contentsLabel.text = text + return self + } + + /// 하단 버튼의 텍스트 변경 + @discardableResult + public func setBottomButtonTitle(_ title: NSAttributedString) -> Self { + self.completeButton.changeTitle(attributedString: title) + return self + } +} + +// MARK: - UI & Layout + +extension CustomBottomSheetVC { + private func setUI() { + view.backgroundColor = .black.withAlphaComponent(0.8) + } + + private func setLayout() { + view.addSubviews(bottomSheetView) + bottomSheetView.addSubviews(contentsLabel, mainImageView, completeButton) + + bottomSheetView.snp.makeConstraints { make in + make.leading.bottom.trailing.equalToSuperview() + make.height.equalTo(330) + } + + contentsLabel.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalToSuperview().inset(26) + } + + mainImageView.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.top.equalTo(contentsLabel.snp.bottom).offset(8) + make.width.equalTo(223) + make.height.equalTo(180) + } + + completeButton.snp.makeConstraints { make in + make.top.equalTo(mainImageView.snp.bottom).offset(20) + make.height.equalTo(44) + make.leading.trailing.equalToSuperview().inset(16) + } + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/UIComponents/MapView/RNMapView.swift b/Runnect-iOS/Runnect-iOS/Global/UIComponents/MapView/RNMapView.swift index 0886b764..a571f326 100644 --- a/Runnect-iOS/Runnect-iOS/Global/UIComponents/MapView/RNMapView.swift +++ b/Runnect-iOS/Runnect-iOS/Global/UIComponents/MapView/RNMapView.swift @@ -139,7 +139,7 @@ extension RNMapView { @discardableResult func makeMarkersWithStartMarker(at locations: [NMGLatLng]) -> Self { if locations.count < 2 { return self } - makeStartMarker(at: locations[0]) + makeStartMarker(at: locations[0], withCameraMove: true) locations[1...].forEach { location in makeMarker(at: location) } @@ -329,8 +329,8 @@ extension RNMapView { } locationButton.snp.makeConstraints { make in - make.bottom.equalToSuperview().inset(98+bottomPadding) - make.trailing.equalToSuperview().inset(24) + make.bottom.equalToSuperview().inset(88+bottomPadding) + make.trailing.equalToSuperview().inset(12) } } diff --git a/Runnect-iOS/Runnect-iOS/Global/Utils/Stopwatch.swift b/Runnect-iOS/Runnect-iOS/Global/Utils/Stopwatch.swift new file mode 100644 index 00000000..febcfad0 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Utils/Stopwatch.swift @@ -0,0 +1,55 @@ +// +// Stopwatch.swift +// Runnect-iOS +// +// Created by sejin on 2023/01/04. +// + +import Combine +import Foundation + +class Stopwatch { + private var startTime: Date? + private var accumulatedTime: TimeInterval = 0 + private var timer: Cancellable? + + @Published var isRunning = false { + didSet { + if self.isRunning { + self.start() + } else { + self.stop() + } + } + } + + @Published private(set) var elapsedTime: TimeInterval = 0 + + private func start() { + self.startTime = Date() + self.timer?.cancel() + self.timer = Timer.publish(every: 1.0, on: .main, in: .common) + .autoconnect() + .sink { _ in + self.elapsedTime = self.getElapsedTime() + } + } + + private func stop() { + self.timer?.cancel() + self.timer = nil + self.accumulatedTime = self.getElapsedTime() + self.startTime = nil + } + + func reset() { + self.accumulatedTime = 0 + self.elapsedTime = 0 + self.startTime = nil + self.isRunning = false + } + + private func getElapsedTime() -> TimeInterval { + return -(self.startTime?.timeIntervalSinceNow ?? 0)+self.accumulatedTime + } +} diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDrawing/VC/CountDownVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDrawing/VC/CountDownVC.swift index ed63d9de..37c00fbe 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDrawing/VC/CountDownVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDrawing/VC/CountDownVC.swift @@ -6,12 +6,15 @@ // import UIKit +import NMapsMap final class CountDownVC: UIViewController { // MARK: - Properties private var count = 3 + var locations = [NMGLatLng]() + var distance: String? // MARK: - UI Components @@ -55,7 +58,14 @@ extension CountDownVC { if self.count > 0 { self.animateTimeLabel() } else { - print("Done") + let runTrackingVC = RunTrackingVC() + runTrackingVC.makePath(locations: self.locations, distance: self.distance ?? "0:0") + self.navigationController?.pushViewController(runTrackingVC, animated: true) + + // CountDownVC를 navigationController 스택에서 제거하여 pop 하였을 때 더 이전 뷰로 넘어가지도록 함 + self.navigationController?.viewControllers.removeAll { vc in + vc.isKind(of: CountDownVC.self) + } } }) } diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDrawing/VC/CourseDrawingVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDrawing/VC/CourseDrawingVC.swift index 8d8e08f7..d253778d 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDrawing/VC/CourseDrawingVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDrawing/VC/CourseDrawingVC.swift @@ -133,15 +133,9 @@ extension CourseDrawingVC { extension CourseDrawingVC { @objc private func decideDepartureButtonDidTap() { - showHiddenViews() + showHiddenViews(withDuration: 0.7) mapView.setDrawMode(to: true) - - UIView.animate(withDuration: 0.7) { - let naviBarContainerStackViewHeight = self.naviBarContainerStackView.frame.height - self.naviBarContainerStackView.transform = CGAffineTransform(translationX: 0, y: -naviBarContainerStackViewHeight) - self.departureInfoContainerView.transform = CGAffineTransform(translationX: 0, y: 172) - } } @objc private func undoButtonDidTap() { @@ -162,6 +156,8 @@ extension CourseDrawingVC { alertVC.rightButtonTapped.sink { [weak self] _ in guard let self = self else { return } let countDownVC = CountDownVC() + countDownVC.locations = self.mapView.getMarkersLatLng() + countDownVC.distance = self.distanceLabel.text self.navigationController?.pushViewController(countDownVC, animated: true) alertVC.dismiss(animated: true) }.store(in: cancelBag) @@ -263,12 +259,18 @@ extension CourseDrawingVC { } } - private func showHiddenViews() { + private func showHiddenViews(withDuration: TimeInterval = 0) { [naviBarForEditing, distanceContainerView, completeButton, undoButton].forEach { subView in view.bringSubviewToFront(subView) } - UIView.animate(withDuration: 0.7) { + UIView.animate(withDuration: withDuration) { + let naviBarContainerStackViewHeight = self.naviBarContainerStackView.frame.height + self.naviBarContainerStackView.transform = CGAffineTransform(translationX: 0, y: -naviBarContainerStackViewHeight) + self.departureInfoContainerView.transform = CGAffineTransform(translationX: 0, y: 172) + } + + UIView.animate(withDuration: withDuration) { self.naviBarForEditing.alpha = 1 self.distanceContainerView.transform = CGAffineTransform(translationX: 0, y: -151) self.completeButton.transform = CGAffineTransform(translationX: 0, y: -112) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/RunTracking/VC/RunTrackingVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/RunTracking/VC/RunTrackingVC.swift new file mode 100644 index 00000000..3a1c7c2b --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Presentation/RunTracking/VC/RunTrackingVC.swift @@ -0,0 +1,244 @@ +// +// RunTrackingVC.swift +// Runnect-iOS +// +// Created by sejin on 2023/01/04. +// + +import UIKit +import Combine + +import NMapsMap + +final class RunTrackingVC: UIViewController { + + // MARK: - Properties + + private let stopwatch = Stopwatch() + private var cancelBag = CancelBag() + var totalTime: Int = 0 + + // MARK: - UI Components + + private let statsView = UIView().then { + $0.backgroundColor = .w1 + $0.layer.cornerRadius = 40 + $0.layer.maskedCorners = [.layerMaxXMaxYCorner] + } + + private let backButton = UIButton(type: .system).then { + $0.setImage(ImageLiterals.icArrowBack, for: .normal) + $0.tintColor = .g1 + } + + private let distanceImageView = UIImageView().then { + $0.image = ImageLiterals.icDistance + $0.tintColor = .g2 + } + + private let distanceLabel = UILabel().then { + $0.text = "총 거리" + $0.font = .b4 + $0.textColor = .g2 + } + + private lazy var distanceInfoStackView = UIStackView( + arrangedSubviews: [distanceImageView, distanceLabel] + ).then { + $0.spacing = 8 + $0.alignment = .leading + } + + private lazy var totalDistanceLabel = UILabel().then { + $0.attributedText = makeAttributedLabelForDistance(distance: "0.0") + } + + private lazy var distanceStatsStackView = UIStackView( + arrangedSubviews: [distanceInfoStackView, totalDistanceLabel] + ).then { + $0.axis = .vertical + $0.alignment = .leading + $0.spacing = 14 + } + + private let timeImageView = UIImageView().then { + $0.image = ImageLiterals.icTime + $0.tintColor = .g2 + } + + private let timeLabel = UILabel().then { + $0.text = "시간" + $0.font = .b4 + $0.textColor = .g2 + } + + private lazy var timeInfoStackView = UIStackView( + arrangedSubviews: [timeImageView, timeLabel] + ).then { + $0.spacing = 8 + $0.alignment = .leading + } + + private let timeStatsLabel = UILabel().then { + $0.text = "00:00" + $0.font = .h1 + $0.textColor = .g1 + } + + private lazy var timeStatsStackView = UIStackView( + arrangedSubviews: [timeInfoStackView, timeStatsLabel] + ).then { + $0.axis = .vertical + $0.alignment = .leading + $0.spacing = 14 + } + + private lazy var statsStackView = UIStackView( + arrangedSubviews: [distanceStatsStackView, timeStatsStackView] + ).then { + $0.spacing = 38 + } + + private let bigStarImageView = UIImageView().then { + $0.image = ImageLiterals.icStar2 + } + + private let smallStarImageView = UIImageView().then { + $0.image = ImageLiterals.icStar + } + + private let mapView = RNMapView() + .showLocationButton(toShow: true) + .makeContentPadding(padding: UIEdgeInsets(top: 100, left: 0, bottom: 0, right: 0)) + .setPositionMode(mode: .normal) + + private let runningCompleteButton = CustomButton(title: "러닝 종료") + + // MARK: - View Life Cycle + + override func viewDidLoad() { + super.viewDidLoad() + self.setUI() + self.setLayout() + self.setAddTarget() + self.bindStopwatch() + } +} + +// MARK: - Methods + +extension RunTrackingVC { + func makePath(locations: [NMGLatLng], distance: String) { + self.mapView.makeMarkersWithStartMarker(at: locations) + self.totalDistanceLabel.attributedText = makeAttributedLabelForDistance(distance: distance) + } + + private func setAddTarget() { + self.backButton.addTarget(self, action: #selector(popToPreviousVC), for: .touchUpInside) + self.runningCompleteButton.addTarget(self, action: #selector(runningCompleteButtonDidTap), for: .touchUpInside) + } + + private func makeAttributedLabelForDistance(distance: String) -> NSMutableAttributedString { + let attributedString = NSMutableAttributedString( + string: distance, + attributes: [.font: UIFont.h1, .foregroundColor: UIColor.g1] + ) + attributedString.append( + NSAttributedString( + string: " Km", + attributes: [.font: UIFont.b4, .foregroundColor: UIColor.g2] + ) + ) + + return attributedString + } + + private func bindStopwatch() { + stopwatch.isRunning.toggle() + + stopwatch.$elapsedTime.sink { [weak self] time in + guard let self = self else { return } + let time = Int(time) + self.totalTime = time + self.setTimeLabel(with: time) + }.store(in: cancelBag) + } + + private func setTimeLabel(with totalSeconds: Int) { + var minutes: String = "\(totalSeconds / 60)" + if minutes.count == 1 { + minutes = "0\(minutes)" + } + + var seconds: String = "\(totalSeconds % 60)" + if seconds.count == 1 { + seconds = "0\(seconds)" + } + + timeStatsLabel.text = "\(minutes):\(seconds)" + } +} + +// MARK: - @objc Function + +extension RunTrackingVC { + @objc private func popToPreviousVC() { + self.navigationController?.popViewController(animated: true) + } + + @objc private func runningCompleteButtonDidTap() { + stopwatch.isRunning.toggle() + let bottomSheetVC = CustomBottomSheetVC() + bottomSheetVC.modalPresentationStyle = .overFullScreen + self.present(bottomSheetVC, animated: true) + } +} + +// MARK: - UI & Layout +extension RunTrackingVC { + private func setUI() { + view.backgroundColor = .w1 + statsView.layer.applyShadow(alpha: 0.2, x: 0, y: 5, blur: 6, spread: 0) + } + + private func setLayout() { + view.addSubviews(mapView, statsView, runningCompleteButton) + statsView.addSubviews(backButton, statsStackView, bigStarImageView, smallStarImageView) + + statsView.snp.makeConstraints { make in + make.leading.top.trailing.equalTo(view.safeAreaLayoutGuide) + make.height.equalTo(100) + } + + backButton.snp.makeConstraints { make in + make.leading.top.equalToSuperview() + make.width.height.equalTo(48) + } + + statsStackView.snp.makeConstraints { make in + make.leading.equalTo(backButton.snp.trailing) + make.top.equalToSuperview().inset(15) + } + + bigStarImageView.snp.makeConstraints { make in + make.centerY.equalTo(timeStatsLabel.snp.centerY) + make.trailing.equalToSuperview().inset(15) + } + + smallStarImageView.snp.makeConstraints { make in + make.top.equalTo(bigStarImageView.snp.bottom).offset(2) + make.centerX.equalTo(bigStarImageView.snp.leading).multipliedBy(0.99) + } + + mapView.snp.makeConstraints { make in + make.leading.top.trailing.equalTo(view.safeAreaLayoutGuide) + make.bottom.equalToSuperview() + } + + runningCompleteButton.snp.makeConstraints { make in + make.leading.trailing.equalTo(view.safeAreaLayoutGuide).inset(16) + make.height.equalTo(44) + make.bottom.equalToSuperview().inset(34) + } + } +}