diff --git a/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj index ecd3af5e..12388f13 100644 --- a/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj +++ b/Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj @@ -145,6 +145,8 @@ CEEC6B4B2961D89700D00E1E /* CustomNavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEEC6B4A2961D89700D00E1E /* CustomNavigationBar.swift */; }; CEF3CD9A296DB305002723A1 /* CourseDetailResponseDto.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEF3CD99296DB305002723A1 /* CourseDetailResponseDto.swift */; }; CEFA9A2F29FC263700F2D0CF /* UserDeleteResponseDto.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFA9A2E29FC263700F2D0CF /* UserDeleteResponseDto.swift */; }; + DA0587F22A05D54100B72869 /* EditCourseRequestDto.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA0587F12A05D54100B72869 /* EditCourseRequestDto.swift */; }; + DA0587F42A05DEC000B72869 /* CourseEditVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA0587F32A05DEC000B72869 /* CourseEditVC.swift */; }; DA20D847296697A600F1581F /* MyCourseSelectVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA20D846296697A600F1581F /* MyCourseSelectVC.swift */; }; DA20D849296697B400F1581F /* CourseUploadVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA20D848296697B400F1581F /* CourseUploadVC.swift */; }; DA20D84E2966A9B300F1581F /* CourseSearchVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = DA20D84D2966A9B300F1581F /* CourseSearchVC.swift */; }; @@ -307,6 +309,8 @@ CEEC6B4A2961D89700D00E1E /* CustomNavigationBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomNavigationBar.swift; sourceTree = ""; }; CEF3CD99296DB305002723A1 /* CourseDetailResponseDto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseDetailResponseDto.swift; sourceTree = ""; }; CEFA9A2E29FC263700F2D0CF /* UserDeleteResponseDto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDeleteResponseDto.swift; sourceTree = ""; }; + DA0587F12A05D54100B72869 /* EditCourseRequestDto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditCourseRequestDto.swift; sourceTree = ""; }; + DA0587F32A05DEC000B72869 /* CourseEditVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseEditVC.swift; sourceTree = ""; }; DA20D846296697A600F1581F /* MyCourseSelectVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyCourseSelectVC.swift; sourceTree = ""; }; DA20D848296697B400F1581F /* CourseUploadVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseUploadVC.swift; sourceTree = ""; }; DA20D84D2966A9B300F1581F /* CourseSearchVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseSearchVC.swift; sourceTree = ""; }; @@ -441,6 +445,7 @@ CE10063329680BFD00FD31FB /* CourseDetailDto */ = { isa = PBXGroup; children = ( + DA0587F02A05D52100B72869 /* RequestDto */, CE4942AB296FCD1000736701 /* ResponseDto */, ); path = CourseDetailDto; @@ -719,6 +724,7 @@ isa = PBXGroup; children = ( A3BC2F422966A93100198261 /* CourseDetailVC.swift */, + DA0587F32A05DEC000B72869 /* CourseEditVC.swift */, ); path = VC; sourceTree = ""; @@ -1144,6 +1150,14 @@ path = VC; sourceTree = ""; }; + DA0587F02A05D52100B72869 /* RequestDto */ = { + isa = PBXGroup; + children = ( + DA0587F12A05D54100B72869 /* EditCourseRequestDto.swift */, + ); + path = RequestDto; + sourceTree = ""; + }; DA97A029296DC2020086760E /* RequestDto */ = { isa = PBXGroup; children = ( @@ -1365,6 +1379,7 @@ A3F67AE2296D33AC001598A2 /* MyPageDto.swift in Sources */, CE591EA1296D5EB5000FCBB3 /* PrivateCourseResponseDto.swift in Sources */, A3BC2F3F2964706100198261 /* UploadedCourseInfoCVC.swift in Sources */, + DA0587F42A05DEC000B72869 /* CourseEditVC.swift in Sources */, CE6655FE295D912300C64E12 /* calculateTopInset.swift in Sources */, CEEC6B492961C5E200D00E1E /* SplashVC.swift in Sources */, CE6655D0295D85FF00C64E12 /* CancelBag.swift in Sources */, @@ -1423,6 +1438,7 @@ CE17F02D2961BBA100E1DED0 /* ColorLiterals.swift in Sources */, CEC2A68E2962AF2C00160BF7 /* RNMarker.swift in Sources */, CE6655D2295D862A00C64E12 /* Publisher+Driver.swift in Sources */, + DA0587F22A05D54100B72869 /* EditCourseRequestDto.swift in Sources */, A3C2CACE29E313CC00EC525B /* SettingVC.swift in Sources */, CE21C02E299E601700F62AF5 /* StampRouter.swift in Sources */, CE6655E6295D887F00C64E12 /* UIStackView+.swift in Sources */, diff --git a/Runnect-iOS/Runnect-iOS/Global/Literal/ImageLiterals.swift b/Runnect-iOS/Runnect-iOS/Global/Literal/ImageLiterals.swift index c625596e..58eb8944 100644 --- a/Runnect-iOS/Runnect-iOS/Global/Literal/ImageLiterals.swift +++ b/Runnect-iOS/Runnect-iOS/Global/Literal/ImageLiterals.swift @@ -44,6 +44,9 @@ enum ImageLiterals { static var icLogoCircle: UIImage { .load(named: "ic_logo_circle") } static var icMore: UIImage { .load(named: "ic_more") } static var icPlus: UIImage { .load(named: "ic_plus") } + static var icFrameEdit: UIImage { + .load(named: "ic_frame_edit") + } // img static var imgBackground: UIImage { .load(named: "img_background") } diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_frame_edit.imageset/Contents.json b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_frame_edit.imageset/Contents.json new file mode 100644 index 00000000..b4faaa73 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_frame_edit.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Frame 2064.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Frame 2064@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Frame 2064@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_frame_edit.imageset/Frame 2064.png b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_frame_edit.imageset/Frame 2064.png new file mode 100644 index 00000000..ab4ce3c8 Binary files /dev/null and b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_frame_edit.imageset/Frame 2064.png differ diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_frame_edit.imageset/Frame 2064@2x.png b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_frame_edit.imageset/Frame 2064@2x.png new file mode 100644 index 00000000..663664e4 Binary files /dev/null and b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_frame_edit.imageset/Frame 2064@2x.png differ diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_frame_edit.imageset/Frame 2064@3x.png b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_frame_edit.imageset/Frame 2064@3x.png new file mode 100644 index 00000000..ab0b291a Binary files /dev/null and b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_frame_edit.imageset/Frame 2064@3x.png differ diff --git a/Runnect-iOS/Runnect-iOS/Global/UIComponents/RNAlertVC.swift b/Runnect-iOS/Runnect-iOS/Global/UIComponents/RNAlertVC.swift index fe610b6e..70da2d1a 100644 --- a/Runnect-iOS/Runnect-iOS/Global/UIComponents/RNAlertVC.swift +++ b/Runnect-iOS/Runnect-iOS/Global/UIComponents/RNAlertVC.swift @@ -25,6 +25,8 @@ final class RNAlertVC: UIViewController { private let descriptionLabel = UILabel().then { $0.font = .b4 $0.textColor = .g2 + $0.numberOfLines = 0 + $0.textAlignment = .center } private lazy var yesButton = UIButton(type: .custom).then { @@ -105,7 +107,7 @@ extension RNAlertVC { make.centerX.equalToSuperview() make.centerY.equalToSuperview() make.leading.trailing.equalToSuperview().inset(30) - make.height.equalTo(126) +// make.height.equalTo(126) } containerView.addSubviews(descriptionLabel, yesButton, noButton) @@ -116,6 +118,7 @@ extension RNAlertVC { } noButton.snp.makeConstraints { make in + make.top.equalTo(descriptionLabel.snp.bottom).offset(20) make.leading.equalToSuperview().offset(16) make.trailing.equalTo(containerView.snp.centerX).offset(-4) make.height.equalTo(44) @@ -124,6 +127,7 @@ extension RNAlertVC { } yesButton.snp.makeConstraints { make in + make.top.equalTo(noButton.snp.top) make.trailing.equalToSuperview().inset(16) make.leading.equalTo(containerView.snp.centerX).offset(4) make.height.equalTo(44) diff --git a/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDetailDto/RequestDto/EditCourseRequestDto.swift b/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDetailDto/RequestDto/EditCourseRequestDto.swift new file mode 100644 index 00000000..30939fcd --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDetailDto/RequestDto/EditCourseRequestDto.swift @@ -0,0 +1,14 @@ +// +// EditCourseReguestDto.swift +// Runnect-iOS +// +// Created by YEONOO on 2023/05/06. +// + +import Foundation + +// MARK: - EditCourseReguestDto + +struct EditCourseRequestDto: Codable { + let title, description: String +} diff --git a/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDetailDto/ResponseDto/UploadedCourseDetailResponseDto.swift b/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDetailDto/ResponseDto/UploadedCourseDetailResponseDto.swift index 4f973bd2..0c1de183 100644 --- a/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDetailDto/ResponseDto/UploadedCourseDetailResponseDto.swift +++ b/Runnect-iOS/Runnect-iOS/Network/Dto/CourseDetailDto/ResponseDto/UploadedCourseDetailResponseDto.swift @@ -20,4 +20,5 @@ struct UploadUser: Codable { let nickname: String let level: Int let image: String + let isNowUser: Bool? } diff --git a/Runnect-iOS/Runnect-iOS/Network/Router/CourseRouter.swift b/Runnect-iOS/Runnect-iOS/Network/Router/CourseRouter.swift index ca17dfe5..3153a5b7 100644 --- a/Runnect-iOS/Runnect-iOS/Network/Router/CourseRouter.swift +++ b/Runnect-iOS/Runnect-iOS/Network/Router/CourseRouter.swift @@ -14,6 +14,7 @@ enum CourseRouter { case getAllPrivateCourse case getPrivateCourseNotUploaded case getCourseDetail(courseId: Int) + case deleteCourse(courseIdList: [Int]) } extension CourseRouter: TargetType { @@ -35,6 +36,8 @@ extension CourseRouter: TargetType { return "/course/private/user" case .getCourseDetail(let courseId): return "/course/detail/\(courseId)" + case .deleteCourse: + return "/course" } } @@ -44,6 +47,8 @@ extension CourseRouter: TargetType { return .post case .getAllPrivateCourse, .getPrivateCourseNotUploaded, .getCourseDetail: return .get + case .deleteCourse: + return .put } } @@ -81,6 +86,8 @@ extension CourseRouter: TargetType { } return .uploadMultipart(multipartFormData) + case .deleteCourse(let courseIdList): + return .requestParameters(parameters: ["courseIdList": courseIdList], encoding: JSONEncoding.default) case .getAllPrivateCourse, .getPrivateCourseNotUploaded, .getCourseDetail: return .requestPlain } diff --git a/Runnect-iOS/Runnect-iOS/Network/Router/PublicCourseRouter.swift b/Runnect-iOS/Runnect-iOS/Network/Router/PublicCourseRouter.swift index 57143719..bf710584 100644 --- a/Runnect-iOS/Runnect-iOS/Network/Router/PublicCourseRouter.swift +++ b/Runnect-iOS/Runnect-iOS/Network/Router/PublicCourseRouter.swift @@ -14,7 +14,8 @@ enum PublicCourseRouter { case courseUploadingData(param: CourseUploadingRequestDto) case getUploadedCourseDetail(publicCourseId: Int) case getUploadedCourseInfo -} + case updatePublicCourse(publicCourseId: Int, editCourseRequestDto: EditCourseRequestDto) +} extension PublicCourseRouter: TargetType { @@ -35,6 +36,8 @@ extension PublicCourseRouter: TargetType { return "/public-course/detail/\(publicCourseId)" case .getUploadedCourseInfo: return "/public-course/user" + case .updatePublicCourse(let publicCourseId, _): + return "/public-course/\(publicCourseId)" } } @@ -44,6 +47,8 @@ extension PublicCourseRouter: TargetType { return .get case .courseUploadingData: return .post + case .updatePublicCourse: + return .patch } } @@ -56,6 +61,11 @@ extension PublicCourseRouter: TargetType { return .requestParameters(parameters: try param.asParameter(), encoding: JSONEncoding.default) } catch { fatalError("Encoding 실패")} + case .updatePublicCourse(_, let param): + do { + return .requestParameters(parameters: try param.asParameter(), encoding: JSONEncoding.default) + } catch { + fatalError("Encoding 실패")} case .getCourseData, .getUploadedCourseDetail, .getUploadedCourseInfo: return .requestPlain } diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseDetailVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseDetailVC.swift index 43b63349..2a6d0294 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseDetailVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseDetailVC.swift @@ -11,6 +11,7 @@ import SnapKit import Then import NMapsMap import Moya +import SafariServices final class CourseDetailVC: UIViewController { @@ -24,8 +25,11 @@ final class CourseDetailVC: UIViewController { private var courseModel: Course? + private var uploadedCourseDetailModel: UploadedCourseDetailResponseDto? + private var courseId: Int? private var publicCourseId: Int? + private var isMyCourse: Bool? // MARK: - UI Components private lazy var navibar = CustomNavigationBar(self, type: .titleWithLeftButton) @@ -78,7 +82,7 @@ final class CourseDetailVC: UIViewController { $0.textColor = .g1 $0.font = .h4 } - + private let courseDistanceInfoView = CourseDetailInfoView(title: "거리", description: "0.0km") private let courseDepartureInfoView = CourseDetailInfoView(title: "출발지", description: "위치") @@ -100,9 +104,8 @@ final class CourseDetailVC: UIViewController { $0.isScrollEnabled = false $0.sizeToFit() } - // MARK: - View Life Cycle - + override func viewDidLoad() { super.viewDidLoad() setNavigationBar() @@ -126,30 +129,50 @@ extension CourseDetailVC { } @objc func moreButtonDidTap() { - - let alertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) - - let saveAction = UIAlertAction(title: "저장하기", style: .default, handler: nil) - let reportAction = UIAlertAction(title: "신고하기", style: .destructive, handler: {(_: UIAlertAction!) in - //report action -}) - let cancelAction = UIAlertAction(title: "닫기", style: .cancel, handler: nil) - - [ saveAction, reportAction, cancelAction ].forEach { alertController.addAction($0) } - - present(alertController, animated: true, completion: nil) - - } + guard let isMyCourse = self.isMyCourse, let uploadedCourseDetailModel = self.uploadedCourseDetailModel else { return } + + let cancelAction = UIAlertAction(title: "닫기", style: .cancel, handler: nil) + + if isMyCourse == true { + let editAlertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) + let courseEditVC = CourseEditVC() + courseEditVC.loadData(model: uploadedCourseDetailModel) + courseEditVC.publicCourseId = self.publicCourseId + let editAction = UIAlertAction(title: "수정하기", style: .default, handler: {(_: UIAlertAction!) in + self.navigationController?.pushViewController(courseEditVC, animated: false) + }) + let deleteVC = RNAlertVC(description: "코스를 정말로 삭제하시겠어요?") + deleteVC.rightButtonTapAction = { [weak self] in + deleteVC.dismiss(animated: false) + self?.deleteCourse() + } + deleteVC.modalPresentationStyle = .overFullScreen + let deleteAction = UIAlertAction(title: "삭제하기", style: .destructive, handler: {(_: UIAlertAction!) in + self.present(deleteVC, animated: false, completion: nil)}) + [ editAction, deleteAction, cancelAction].forEach { editAlertController.addAction($0) } + present(editAlertController, animated: false, completion: nil) + } else { + // 신고폼 올라오는 거(유저아이디가 내가 아닌 경우) + let reportAlertController = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) + let formUrl = NSURL(string: "https://docs.google.com/forms/d/e/1FAIpQLSek2rkClKfGaz1zwTEHX3Oojbq_pbF3ifPYMYezBU0_pe-_Tg/viewform") + let formSafariView: SFSafariViewController = SFSafariViewController(url: formUrl! as URL) + let reportAction = UIAlertAction(title: "신고하기", style: .destructive, handler: {(_: UIAlertAction!) in + self.present(formSafariView, animated: true, completion: nil) + }) + [ reportAction, cancelAction ].forEach { reportAlertController.addAction($0) } + present(reportAlertController, animated: true, completion: nil) + } + } private func pushToCountDownVC() { guard let courseModel = self.courseModel, let path = courseModel.path, let distance = courseModel.distance else { return } - + let countDownVC = CountDownVC() let locations = path.map { NMGLatLng(lat: $0[0], lng: $0[1]) } - + let runningModel = RunningModel(courseId: self.courseId, publicCourseId: self.publicCourseId, locations: locations, @@ -172,12 +195,13 @@ extension CourseDetailVC { } func setData(model: UploadedCourseDetailResponseDto) { + self.uploadedCourseDetailModel = model self.mapImageView.setImage(with: model.publicCourse.image) self.profileImageView.image = GoalRewardInfoModel.stampNameImageDictionary[model.user.image] - self.profileNameLabel.text = model.user.nickname + self.profileNameLabel.text = model.user.nickname self.runningLevelLabel.text = "Lv. \(model.user.level)" self.courseTitleLabel.text = model.publicCourse.title - + self.isMyCourse = model.user.isNowUser guard let scrap = model.publicCourse.scrap else { return } self.likeButton.isSelected = scrap @@ -296,12 +320,12 @@ extension CourseDetailVC { make.top.equalTo(firstHorizontalDivideLine.snp.bottom).offset(16) make.leading.equalTo(view.safeAreaLayoutGuide).offset(16) } - + courseDetailStackView.snp.makeConstraints { make in make.top.equalTo(courseTitleLabel.snp.bottom).offset(19) make.leading.trailing.equalToSuperview().inset(16) } - + secondHorizontalDivideLine.snp.makeConstraints { make in make.top.equalTo(courseDetailStackView.snp.bottom).offset(27) make.leading.trailing.equalTo(view.safeAreaLayoutGuide) @@ -398,3 +422,29 @@ extension CourseDetailVC { } } } + +extension CourseDetailVC { + private func deleteCourse() { + guard let courseId = self.courseId else { return } + LoadingIndicator.showLoading() + courseProvider.request(.deleteCourse(courseIdList: [courseId])) { [weak self] response in + LoadingIndicator.hideLoading() + guard let self = self else { return } + switch response { + case .success(let result): + print("리절트", result) + let status = result.statusCode + if 200..<300 ~= status { + print("삭제 성공") + } + if status >= 400 { + print("400 error") + self.showNetworkFailureToast() + } + case .failure(let error): + print(error.localizedDescription) + self.showNetworkFailureToast() + } + } + } +} diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseEditVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseEditVC.swift new file mode 100644 index 00000000..31e14614 --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseEditVC.swift @@ -0,0 +1,353 @@ +// +// CourseEditVC.swift +// Runnect-iOS +// +// Created by YEONOO on 2023/05/06. +// + + +import UIKit + +import SnapKit +import Then +import Moya + +class CourseEditVC: UIViewController { + + // MARK: - Properties + private let PublicCourseProvider = Providers.publicCourseProvider + + private let courseTitleMaxLength = 20 + var publicCourseId: Int? + + // MARK: - UI Components + + private lazy var navibar = CustomNavigationBar(self, type: .titleWithLeftButton).setTitle("") + private let buttonContainerView = UIView() + private let editButton = CustomButton(title: "완료").setEnabled(false) + + private lazy var scrollView = UIScrollView() + private let mapImageView = UIImageView().then { + $0.image = UIImage(named: "") + } + private let courseTitleTextField = UITextField().then { + $0.attributedPlaceholder = NSAttributedString( + string: "글 제목", + attributes: [.font: UIFont.h4, .foregroundColor: UIColor.g3] + ) + $0.font = .h4 + $0.textColor = .g1 + $0.addLeftPadding(width: 2) + } + private let dividerView = UIView().then { + $0.backgroundColor = .g5 + } + private let distanceInfoView = CourseDetailInfoView(title: "거리", description: "0.0km") + private let departureInfoView = CourseDetailInfoView(title: "출발지", description: "") + private let placeholder = "코스에 대한 소개를 적어주세요.(난이도/풍경/지형)" + + let activityTextView = UITextView().then { + $0.font = .b4 + $0.backgroundColor = .m3 + $0.tintColor = .m1 + $0.textContainerInset = UIEdgeInsets(top: 14, left: 12, bottom: 14, right: 12) + } + + // MARK: - Life Cycle + + override func viewDidLoad() { + super.viewDidLoad() + setNavigationBar() + setUI() + setLayout() + setupTextView() + setAddTarget() + setKeyboardNotification() + setTapGesture() + addKeyboardObserver() + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + self.tabBarController?.tabBar.isHidden = false + } + + deinit { + NotificationCenter.default.removeObserver(self) + } +} + +// MARK: - Methods + +extension CourseEditVC { + private func setAddTarget() { + self.courseTitleTextField.addTarget(self, action: #selector(textFieldTextDidChange), for: .editingChanged) + self.editButton.addTarget(self, action: #selector(editButtonDidTap), for: .touchUpInside) + } + + // data 그대로 load하기 + func loadData(model: UploadedCourseDetailResponseDto) { + mapImageView.setImage(with: model.publicCourse.image) + courseTitleTextField.text = model.publicCourse.title + distanceInfoView.setDescriptionText(description: "\(model.publicCourse.distance ?? 0.0)") + let departure = "\(model.publicCourse.departure.region) \(model.publicCourse.departure.city)" + departureInfoView.setDescriptionText(description: departure) + + activityTextView.text = model.publicCourse.description + } + + // 키보드가 올라오면 scrollView 위치 조정 + private func setKeyboardNotification() { + NotificationCenter.default.addObserver( + self, + selector: #selector(keyboardWillShow), + name: UIResponder.keyboardWillShowNotification, + object: nil) + + NotificationCenter.default.addObserver( + self, + selector: #selector(keyboardWillHide), + name: UIResponder.keyboardWillHideNotification, + object: nil) + } + + // 화면 터치 시 키보드 내리기 + private func setTapGesture() { + let tap = UITapGestureRecognizer(target: view, action: #selector(UIView.endEditing)) + tap.cancelsTouchesInView = false + view.addGestureRecognizer(tap) + } + + private func addKeyboardObserver() { + NotificationCenter.default.addObserver( + self, + selector: #selector(keyboardWillShow), + name: UIResponder.keyboardWillShowNotification, + object: nil) + NotificationCenter.default.addObserver( + self, + selector: #selector(keyboardWillHide), + name: UIResponder.keyboardWillHideNotification, + object: nil) + } +} +// MARK: - @objc Function + +extension CourseEditVC { + @objc private func textFieldTextDidChange() { + guard let text = courseTitleTextField.text else { return } + + if text.count > courseTitleMaxLength { + let index = text.index(text.startIndex, offsetBy: courseTitleMaxLength) + let newString = text[text.startIndex.. 150 { + activityTextView.deleteBackward() + } + } + func textViewDidEndEditing(_ textView: UITextView) { + if textView.text.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || textView.text == placeholder { + activityTextView.textColor = .g3 + activityTextView.text = placeholder + } + } +} + +// MARK: - Network + +extension CourseEditVC { + private func editCourse() { + guard let titletext = courseTitleTextField.text else { return } + guard let descriptiontext = activityTextView.text else { return } + guard let publicCourseId = publicCourseId else { return } + let requsetDto = EditCourseRequestDto(title: titletext, description: descriptiontext) + + LoadingIndicator.showLoading() + PublicCourseProvider.request(.updatePublicCourse(publicCourseId: publicCourseId, editCourseRequestDto: requsetDto)) { [weak self] response in + LoadingIndicator.hideLoading() + guard let self = self else { return } + switch response { + case .success(let result): + let status = result.statusCode + if 200..<300 ~= status { + self.navigationController?.popViewController(animated: true) + } + if status >= 400 { + print("400 error") + self.showNetworkFailureToast() + } + case .failure(let error): + print(error.localizedDescription) + self.showNetworkFailureToast() + } + } + } +} diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseUploadVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseUploadVC.swift index 75a4b26e..ab274d81 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseUploadVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseUploadVC.swift @@ -14,7 +14,6 @@ import Moya class CourseUploadVC: UIViewController { // MARK: - Properties -// private var runningModel: RunningModel? private let PublicCourseProvider = Providers.publicCourseProvider private var courseModel: Course? @@ -142,7 +141,7 @@ extension CourseUploadVC { self.courseTitleTextField.text = String(newString) } - if !text.isEmpty && activityTextView.text != self.placeholder { + if text.count == 0 && activityTextView.text != self.placeholder && activityTextView.text.count == 0 { uploadButton.setEnabled(true) } else { uploadButton.setEnabled(false) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/VC/CourseStorageVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/VC/CourseStorageVC.swift index e72b546b..d7eb50bb 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/VC/CourseStorageVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/VC/CourseStorageVC.swift @@ -34,7 +34,6 @@ final class CourseStorageVC: UIViewController { private lazy var viewPager = ViewPager(pageTitles: ["내가 그린 코스", "스크랩 코스"]) .addPagedView(pagedView: [privateCourseListView, scrapCourseListView]) - // MARK: - View Life Cycle override func viewDidLoad() { diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/Views/CourseListView/PrivateCourseListView.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/Views/CourseListView/PrivateCourseListView.swift index 3e20bc09..21b963ff 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/Views/CourseListView/PrivateCourseListView.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/Views/CourseListView/PrivateCourseListView.swift @@ -26,6 +26,28 @@ final class PrivateCourseListView: UIView { private let collectionViewLayout = UICollectionViewFlowLayout().then { $0.scrollDirection = .vertical } + private let beforeEditTopView = UIView().then{ + $0.backgroundColor = .clear + } + private let frameEditButton = UIButton(type: .system).then { + $0.setImage(ImageLiterals.icFrameEdit, for: .normal) + $0.tintColor = .g1 + } + private let totalCourseNum = UILabel().then{ + $0.text = "총 100개" + $0.font = .b6 + $0.textColor = .g2 + } + +// private let afterEditTopView = UIView() +// private let selectCouseLabel = UILabel().then { +// $0.text = "코스 선택" +// $0.font = .b6 +// $0.textColor = .g2 +// } + + + private lazy var courseListCollectionView = UICollectionView( frame: .zero, @@ -83,14 +105,27 @@ extension PrivateCourseListView { } private func setLayout() { - self.addSubviews(courseListCollectionView) + self.addSubviews(beforeEditTopView,courseListCollectionView) courseListCollectionView.addSubviews(emptyView) - + + beforeEditTopView.addSubviews(frameEditButton,totalCourseNum) + + beforeEditTopView.snp.makeConstraints{ make in make.top.top.equalToSuperview().offset(11)} + frameEditButton.snp.makeConstraints { make in + make.top.equalToSuperview().offset(1) + make.trailing.equalTo(self.safeAreaLayoutGuide).inset(16) + } + + totalCourseNum.snp.makeConstraints { make in + make.top.equalToSuperview().offset(1) + make.leading.equalTo(self.safeAreaLayoutGuide).offset(16) + } + courseListCollectionView.snp.makeConstraints { make in - make.top.equalToSuperview() + make.top.equalTo(frameEditButton.snp.bottom) make.leading.bottom.trailing.equalToSuperview() + } - emptyView.snp.makeConstraints { make in make.center.equalToSuperview() make.leading.trailing.equalToSuperview().inset(80) diff --git a/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/UploadedCourseInfoVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/UploadedCourseInfoVC.swift index 2a13d3d7..283ec60c 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/UploadedCourseInfoVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/UploadedCourseInfoVC.swift @@ -119,6 +119,15 @@ extension UploadedCourseInfoVC: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { return uploadedCourseInset } + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + + let courseDetailVC = CourseDetailVC() + let courseModel = uploadedCourseList[indexPath.item] + courseDetailVC.setCourseId(courseId: courseModel.courseId, publicCourseId: courseModel.id) + courseDetailVC.hidesBottomBarWhenPushed = true + self.navigationController?.pushViewController(courseDetailVC, animated: true) + } } // MARK: - UICollectionViewDataSource