diff --git a/Runnect-iOS/Runnect-iOS/Global/Literal/ImageLiterals.swift b/Runnect-iOS/Runnect-iOS/Global/Literal/ImageLiterals.swift index 29c229b6..20cc7de1 100644 --- a/Runnect-iOS/Runnect-iOS/Global/Literal/ImageLiterals.swift +++ b/Runnect-iOS/Runnect-iOS/Global/Literal/ImageLiterals.swift @@ -44,9 +44,6 @@ 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") } @@ -74,6 +71,9 @@ enum ImageLiterals { static var imgSpaceship: UIImage { .load(named: "img_spaceship") } static var imgAppIcon: UIImage { .load(named: "img_app_icon") } static var imgAd: UIImage { .load(named: "img_ad") } + static var imgBanner1: UIImage { .load(named: "img_banner1") } + static var imgBanner2: UIImage { .load(named: "img_banner2") } + static var imgBanner3: UIImage { .load(named: "img_banner3") } static var imgAppleLogin: UIImage { .load(named: "img_apple_login")} static var imgKakaoLogin: UIImage { .load(named: "img_kakao_login")} } 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 deleted file mode 100644 index ab4ce3c8..00000000 Binary files a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_frame_edit.imageset/Frame 2064.png and /dev/null 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 deleted file mode 100644 index 663664e4..00000000 Binary files a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_frame_edit.imageset/Frame 2064@2x.png and /dev/null 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 deleted file mode 100644 index ab0b291a..00000000 Binary files a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_frame_edit.imageset/Frame 2064@3x.png and /dev/null differ 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/img_banner1.imageset/Contents.json similarity index 69% rename from Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_frame_edit.imageset/Contents.json rename to Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner1.imageset/Contents.json index b4faaa73..d0f1d61e 100644 --- a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/ic_frame_edit.imageset/Contents.json +++ b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner1.imageset/Contents.json @@ -1,17 +1,15 @@ { "images" : [ { - "filename" : "Frame 2064.png", + "filename" : "ios 1.png", "idiom" : "universal", "scale" : "1x" }, { - "filename" : "Frame 2064@2x.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "Frame 2064@3x.png", "idiom" : "universal", "scale" : "3x" } diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner1.imageset/ios 1.png b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner1.imageset/ios 1.png new file mode 100644 index 00000000..5e28081c Binary files /dev/null and b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner1.imageset/ios 1.png differ diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner2.imageset/Contents.json b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner2.imageset/Contents.json new file mode 100644 index 00000000..2ff49d2e --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner2.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "ios 2.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner2.imageset/ios 2.png b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner2.imageset/ios 2.png new file mode 100644 index 00000000..af98c776 Binary files /dev/null and b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner2.imageset/ios 2.png differ diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner3.imageset/Contents.json b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner3.imageset/Contents.json new file mode 100644 index 00000000..89d4d7ce --- /dev/null +++ b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner3.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "ios 3.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner3.imageset/ios 3.png b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner3.imageset/ios 3.png new file mode 100644 index 00000000..15c777ef Binary files /dev/null and b/Runnect-iOS/Runnect-iOS/Global/Resource/Assets.xcassets/img_banner3.imageset/ios 3.png differ diff --git a/Runnect-iOS/Runnect-iOS/Network/Router/PublicCourseRouter.swift b/Runnect-iOS/Runnect-iOS/Network/Router/PublicCourseRouter.swift index bf710584..90df9a48 100644 --- a/Runnect-iOS/Runnect-iOS/Network/Router/PublicCourseRouter.swift +++ b/Runnect-iOS/Runnect-iOS/Network/Router/PublicCourseRouter.swift @@ -15,6 +15,7 @@ enum PublicCourseRouter { case getUploadedCourseDetail(publicCourseId: Int) case getUploadedCourseInfo case updatePublicCourse(publicCourseId: Int, editCourseRequestDto: EditCourseRequestDto) + case deleteUploadedCourse(publicCourseIdList: [Int]) } extension PublicCourseRouter: TargetType { @@ -38,6 +39,8 @@ extension PublicCourseRouter: TargetType { return "/public-course/user" case .updatePublicCourse(let publicCourseId, _): return "/public-course/\(publicCourseId)" + case .deleteUploadedCourse: + return "/public-course" } } @@ -49,6 +52,8 @@ extension PublicCourseRouter: TargetType { return .post case .updatePublicCourse: return .patch + case .deleteUploadedCourse: + return .put } } @@ -66,6 +71,8 @@ extension PublicCourseRouter: TargetType { return .requestParameters(parameters: try param.asParameter(), encoding: JSONEncoding.default) } catch { fatalError("Encoding 실패")} + case .deleteUploadedCourse(let publicCourseIdList): + return .requestParameters(parameters: ["publicCourseIdList": publicCourseIdList], encoding: JSONEncoding.default) 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 616a3ecf..23224506 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseDetailVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseDetailVC.swift @@ -147,14 +147,14 @@ extension CourseDetailVC { 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) + let deleteAlertVC = RNAlertVC(description: "러닝 기록을 정말로 삭제하시겠어요?").setButtonTitle("취소", "삭제하기") + deleteAlertVC.rightButtonTapAction = { [weak self] in + deleteAlertVC.dismiss(animated: false) self?.deleteCourse() } - deleteVC.modalPresentationStyle = .overFullScreen + deleteAlertVC.modalPresentationStyle = .overFullScreen let deleteAction = UIAlertAction(title: "삭제하기", style: .destructive, handler: {(_: UIAlertAction!) in - self.present(deleteVC, animated: false, completion: nil)}) + self.present(deleteAlertVC, animated: false, completion: nil)}) [ editAction, deleteAction, cancelAction].forEach { editAlertController.addAction($0) } present(editAlertController, animated: false, completion: nil) } else { @@ -204,7 +204,7 @@ extension CourseDetailVC { 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 diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseEditVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseEditVC.swift index 31e14614..dcd06a94 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseEditVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDetail/VC/CourseEditVC.swift @@ -5,7 +5,6 @@ // Created by YEONOO on 2023/05/06. // - import UIKit import SnapKit diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/AdImageCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/AdImageCollectionViewCell.swift index e5aa4670..d7a689ae 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/AdImageCollectionViewCell.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/AdImageCollectionViewCell.swift @@ -12,14 +12,33 @@ import Then class AdImageCollectionViewCell: UICollectionViewCell { + // MARK: - collectionview + + private lazy var bannerCollectionView: UICollectionView = { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .horizontal + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) + collectionView.backgroundColor = .clear + collectionView.translatesAutoresizingMaskIntoConstraints = false + collectionView.isScrollEnabled = false + collectionView.showsVerticalScrollIndicator = false + return collectionView + }() + // MARK: - Constants + + final let collectionViewInset = UIEdgeInsets(top: 28, left: 16, bottom: 28, right: 16) + // MARK: - UI Components - private let adImageView = UIImageView().then { - $0.image = ImageLiterals.imgAd - } + var imgBanners: [UIImage] = [ImageLiterals.imgBanner1, ImageLiterals.imgBanner2, ImageLiterals.imgBanner3] + var currentPage: Int = 0 + + private var pageControl = UIPageControl() // MARK: - Life cycle override init(frame: CGRect) { super.init(frame: frame) layout() + setDelegate() + startBannerSlide() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") @@ -28,14 +47,108 @@ class AdImageCollectionViewCell: UICollectionViewCell { // MARK: - Extensions extension AdImageCollectionViewCell { + private func setDelegate() { + bannerCollectionView.delegate = self + bannerCollectionView.dataSource = self + bannerCollectionView.isPagingEnabled = true + bannerCollectionView.showsHorizontalScrollIndicator = false + bannerCollectionView.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "BannerCell") + } + func startBannerSlide() { + + // 초기 페이지 설정 + currentPage = imgBanners.count + + // 제스처 인식기 추가 + let swipeLeftGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipeGesture(_:))) + swipeLeftGesture.direction = .left + bannerCollectionView.addGestureRecognizer(swipeLeftGesture) + + let swipeRightGesture = UISwipeGestureRecognizer(target: self, action: #selector(handleSwipeGesture(_:))) + swipeRightGesture.direction = .right + bannerCollectionView.addGestureRecognizer(swipeRightGesture) + + // 페이지 컨트롤 설정 + pageControl.currentPage = 0 + pageControl.numberOfPages = imgBanners.count + pageControl.pageIndicatorTintColor = .lightGray // 페이지를 암시하는 동그란 점의 색상 + pageControl.currentPageIndicatorTintColor = .white + } + + @objc func handleSwipeGesture(_ gesture: UISwipeGestureRecognizer) { + // 배너를 스와이프하여 수동으로 슬라이드 + if gesture.direction == .left { + currentPage += 1 + } else if gesture.direction == .right { + currentPage -= 1 + } + // 첫 번째 배너에서 이전으로 스와이프하면 마지막 배너로 이동 + if currentPage < 0 { + currentPage = imgBanners.count * 2 - 1 + } + + // 마지막 배너에서 다음으로 스와이프하면 첫 번째 배너로 이동 + if currentPage >= imgBanners.count * 2 { + currentPage = 0 + } + + let indexPath = IndexPath(item: currentPage, section: 0) + bannerCollectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true) + + updatePageControl() + } + + // 페이지 컨트롤 업데이트 + func updatePageControl() { + let currentIndex = currentPage % imgBanners.count + pageControl.currentPage = currentIndex + } // MARK: - Layout Helpers func layout() { contentView.backgroundColor = .clear - contentView.addSubview(adImageView) - adImageView.snp.makeConstraints { make in + contentView.addSubview(bannerCollectionView) + contentView.addSubview(pageControl) + bannerCollectionView.snp.makeConstraints { make in make.edges.equalToSuperview() } + pageControl.snp.makeConstraints { make in + make.centerX.equalTo(self) + make.bottom.equalTo(bannerCollectionView.snp.bottom) + } + } +} + +// MARK: - UICollectionViewDelegate, UICollectionViewDataSource + +extension AdImageCollectionViewCell: UICollectionViewDelegate, UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return imgBanners.count*3 + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = bannerCollectionView.dequeueReusableCell(withReuseIdentifier: "BannerCell", for: indexPath) + + // 배너 이미지 설정 + let imageIndex = indexPath.item % imgBanners.count + let imageView = UIImageView(frame: cell.contentView.bounds) + imageView.image = imgBanners[imageIndex] + imageView.contentMode = .scaleAspectFill + imageView.clipsToBounds = true + cell.contentView.addSubviews(imageView) + return cell + } +} + +// MARK: - UICollectionViewDelegateFlowLayout + +extension AdImageCollectionViewCell: UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + return self.frame.size + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { + return 0 } } diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MapCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MapCollectionViewCell.swift index 1919b824..4c01ae6e 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MapCollectionViewCell.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/MapCollectionViewCell.swift @@ -12,6 +12,7 @@ import Then class MapCollectionViewCell: UICollectionViewCell { + // MARK: - collectionview private lazy var mapCollectionView: UICollectionView = { @@ -38,7 +39,6 @@ class MapCollectionViewCell: UICollectionViewCell { layout() register() setDelegate() - layout() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift index 37b421ad..365c1c34 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/TitleCollectionViewCell.swift @@ -22,14 +22,14 @@ class TitleCollectionViewCell: UICollectionViewCell { private let mainLabel: UILabel = { let label = UILabel() - label.text = "코스 추천" + label.text = "이런 코스 어때요?" label.font = UIFont.h4 label.textColor = UIColor.g1 return label }() private let subLabel: UILabel = { let label = UILabel() - label.text = "새로운 코스를 발견해나가요" + label.text = "상쾌한 하루를 시작하게 만들어주는 러닝코스" label.font = UIFont.b6 label.textColor = UIColor.g1 return label diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift index eeac343b..743e78ec 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift @@ -21,9 +21,11 @@ final class CourseDiscoveryVC: UIViewController { private var courseList = [PublicCourse]() + + // MARK: - UIComponents - private lazy var navibar = CustomNavigationBar(self, type: .title).setTitle("코스 발견") + private lazy var naviBar = CustomNavigationBar(self, type: .title).setTitle("코스 발견") private let searchButton = UIButton(type: .system).then { $0.setImage(ImageLiterals.icSearch, for: .normal) $0.tintColor = .g1 @@ -35,6 +37,9 @@ final class CourseDiscoveryVC: UIViewController { $0.titleEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 0) } + private let emptyView = ListEmptyView(description: "공유할 수 있는 코스가 없어요!\n코스를 그려주세요", + buttonTitle: "코스 그리기") + // MARK: - collectionview private lazy var mapCollectionView: UICollectionView = { @@ -74,11 +79,13 @@ extension CourseDiscoveryVC { private func setData(courseList: [PublicCourse]) { self.courseList = courseList mapCollectionView.reloadData() + self.emptyView.isHidden = !courseList.isEmpty } private func setDelegate() { mapCollectionView.delegate = self mapCollectionView.dataSource = self + emptyView.delegate = self } private func register() { @@ -118,28 +125,30 @@ extension CourseDiscoveryVC { private func setUI() { view.backgroundColor = .w1 mapCollectionView.backgroundColor = .w1 + self.emptyView.isHidden = true } private func setNavigationBar() { - view.addSubview(navibar) + view.addSubview(naviBar) view.addSubview(searchButton) - navibar.snp.makeConstraints { make in + naviBar.snp.makeConstraints { make in make.leading.top.trailing.equalTo(view.safeAreaLayoutGuide) make.height.equalTo(48) } searchButton.snp.makeConstraints { make in make.trailing.equalTo(self.view.safeAreaLayoutGuide).inset(16) - make.centerY.equalTo(navibar) + make.centerY.equalTo(naviBar) } } private func layout() { view.addSubviews(uploadButton, mapCollectionView) view.bringSubviewToFront(uploadButton) + mapCollectionView.addSubview(emptyView) mapCollectionView.snp.makeConstraints { - $0.top.equalTo(self.navibar.snp.bottom) + $0.top.equalTo(self.naviBar.snp.bottom) $0.leading.bottom.trailing.equalTo(view.safeAreaLayoutGuide) } @@ -149,6 +158,10 @@ extension CourseDiscoveryVC { make.height.equalTo(40) make.width.equalTo(92) } + emptyView.snp.makeConstraints { make in + make.top.equalTo(naviBar.snp.bottom).offset(300) + make.centerX.equalTo(naviBar) + } } } @@ -310,3 +323,11 @@ extension CourseDiscoveryVC { } } } + +// MARK: - Section Heading + +extension CourseDiscoveryVC: ListEmptyViewDelegate { + func emptyViewButtonTapped() { + self.tabBarController?.selectedIndex = 0 + } +} diff --git a/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/VC/CourseStorageVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/VC/CourseStorageVC.swift index 3a3bb9e6..18781140 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/VC/CourseStorageVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/VC/CourseStorageVC.swift @@ -13,7 +13,6 @@ import Moya final class CourseStorageVC: UIViewController { // MARK: - Properties - private let courseProvider = Providers.courseProvider private let scrapProvider = Providers.scrapProvider @@ -34,6 +33,12 @@ final class CourseStorageVC: UIViewController { private lazy var viewPager = ViewPager(pageTitles: ["내가 그린 코스", "스크랩 코스"]) .addPagedView(pagedView: [privateCourseListView, scrapCourseListView]) + + private var deleteCourseButton = CustomButton(title: "삭제하기").then { + $0.isHidden = true + $0.isEnabled = false + } + // MARK: - View Life Cycle override func viewDidLoad() { @@ -42,8 +47,8 @@ final class CourseStorageVC: UIViewController { self.setLayout() self.bindUI() self.setDelegate() + self.setDeleteButton() } - override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) guard UserManager.shared.userType != .visitor else { return } @@ -58,6 +63,8 @@ extension CourseStorageVC { private func setPrivateCourseData(courseList: [PrivateCourse]) { self.privateCourseList = courseList self.privateCourseListView.setData(courseList: courseList) + self.deleteCourseButton.isHidden = true + self.hideTabBar(wantsToHide: false) } private func setScrapCourseData(courseList: [ScrapCourse]) { @@ -65,6 +72,10 @@ extension CourseStorageVC { self.scrapCourseListView.setData(courseList: courseList) } + private func setDeleteButton() { + deleteCourseButton.addTarget(self, action: #selector(deleteCourseButtonDidTap), for: .touchUpInside) + } + private func bindUI() { privateCourseListView.courseDrawButtonTapped.sink { [weak self] in guard let self = self else { return } @@ -81,6 +92,7 @@ extension CourseStorageVC { let runningWaitingVC = RunningWaitingVC() runningWaitingVC.setData(courseId: self.privateCourseList[index].id, publicCourseId: nil) runningWaitingVC.hidesBottomBarWhenPushed = true + self.navigationController?.pushViewController(runningWaitingVC, animated: true) }.store(in: cancelBag) @@ -96,6 +108,54 @@ extension CourseStorageVC { private func setDelegate() { scrapCourseListView.delegate = self + privateCourseListView.delegate = self + } + + private func showHiddenViews(withDuration: TimeInterval = 0) { + if let frame = tabBarController?.tabBar.frame { + let factor: CGFloat = -1 + let y = frame.origin.y + (frame.size.height * factor) + UIView.animate(withDuration: 0.7, animations: { + self.tabBarController?.tabBar.frame = CGRect(x: frame.origin.x, y: y, width: frame.width, height: frame.height) + }) + } + UIView.animate(withDuration: withDuration) { + self.deleteCourseButton.transform = CGAffineTransform(translationX: 0, y: 34) + } + } + + private func hideHiddenViews(withDuration: TimeInterval = 0) { + if let frame = tabBarController?.tabBar.frame { + let factor: CGFloat = 1 + let y = frame.origin.y + (frame.size.height * factor) + UIView.animate(withDuration: 0.7, animations: { + self.tabBarController?.tabBar.frame = CGRect(x: frame.origin.x, y: y, width: frame.width, height: frame.height) + }) + } + view.bringSubviewToFront(deleteCourseButton) + UIView.animate(withDuration: withDuration) { + self.deleteCourseButton.transform = CGAffineTransform(translationX: 0, y: -34) + } + } +} + +// MARK: - @objc Function + +extension CourseStorageVC { + @objc func deleteCourseButtonDidTap(_sender: UIButton) { + guard let selectedList = privateCourseListView.courseListCollectionView.indexPathsForSelectedItems else { return } + var deleteToCourseId = [Int]() + for indexPath in selectedList { + let publicCourse = privateCourseList[indexPath.item] + deleteToCourseId.append(publicCourse.id) + } + let deleteAlertVC = RNAlertVC(description: "삭제하시겠습니까?") + deleteAlertVC.modalPresentationStyle = .overFullScreen + deleteAlertVC.rightButtonTapAction = { + deleteAlertVC.dismiss(animated: false) + self.deleteCourse(courseIdList: deleteToCourseId) + } + self.present(deleteAlertVC, animated: false) } } @@ -105,26 +165,30 @@ extension CourseStorageVC { private func setUI() { view.backgroundColor = .w1 } - private func setLayout() { view.addSubviews(naviBar) - + naviBar.snp.makeConstraints { make in make.leading.top.trailing.equalTo(view.safeAreaLayoutGuide) make.height.equalTo(48) } - + guard UserManager.shared.userType != .visitor else { self.showSignInRequestEmptyView() return } - view.addSubview(viewPager) + view.addSubviews(viewPager, deleteCourseButton) viewPager.snp.makeConstraints { make in make.top.equalTo(naviBar.snp.bottom) make.leading.bottom.trailing.equalTo(view.safeAreaLayoutGuide) } + deleteCourseButton.snp.makeConstraints { make in + make.bottom.equalToSuperview().inset(34) + make.leading.trailing.equalToSuperview().inset(16) + make.height.equalTo(44) + } } } @@ -136,8 +200,41 @@ extension CourseStorageVC: ScrapCourseListViewDelegate { } } -// MARK: - Network +// MARK: - PrivateCourseListViewDelegate +extension CourseStorageVC: PrivateCourseListViewDelegate { + + func courseListEditButtonTapped() { + if privateCourseListView.isEditMode == false { + self.deleteCourseButton.isHidden = false + self.deleteCourseButton.isEnabled = false + self.deleteCourseButton.setTitle(title: "삭제하기") + hideHiddenViews(withDuration: 0.7) + } + if privateCourseListView.isEditMode == true { + self.hideTabBar(wantsToHide: false) + self.deleteCourseButton.isHidden = false + showHiddenViews(withDuration: 0.7) + } + } + func selectCellDidTapped() { + guard let selectedCells = privateCourseListView.courseListCollectionView.indexPathsForSelectedItems else { return } + let countSelectCells = selectedCells.count + if privateCourseListView.isEditMode == true { + if privateCourseListView.isEditMode == false { + self.deleteCourseButton.isEnabled = false + self.deleteCourseButton.setTitle(title: "삭제하기") + } + self.deleteCourseButton.setTitle(title: "삭제하기(\(countSelectCells))") + self.deleteCourseButton.isEnabled = false + self.deleteCourseButton.setEnabled(true) + } + if selectedCells.count == 0 { + self.deleteCourseButton.isEnabled = false + } + } +} +// MARK: - Network extension CourseStorageVC { private func getPrivateCourseList() { LoadingIndicator.showLoading() @@ -216,4 +313,30 @@ extension CourseStorageVC { } } } + + private func deleteCourse(courseIdList: [Int]) { + LoadingIndicator.showLoading() + courseProvider.request(.deleteCourse(courseIdList: courseIdList)) { [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("삭제 성공") + self.getPrivateCourseList() + self.showHiddenViews(withDuration: 0.7) + + } + 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/CourseStorage/Views/CourseListView/PrivateCourseListView.swift b/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/Views/CourseListView/PrivateCourseListView.swift index 21b963ff..739ecfd3 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/Views/CourseListView/PrivateCourseListView.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/Views/CourseListView/PrivateCourseListView.swift @@ -8,14 +8,26 @@ import UIKit import Combine +protocol PrivateCourseListViewDelegate: AnyObject { + func courseListEditButtonTapped() + func selectCellDidTapped() +} + final class PrivateCourseListView: UIView { // MARK: - Properties var courseDrawButtonTapped = PassthroughSubject() + var cellDidTapped = PassthroughSubject() - private var courseList = [PrivateCourse]() + var courseList = [PrivateCourse]() + + weak var delegate: PrivateCourseListViewDelegate? + + weak var courseStorageVC: UIViewController? + + var isEditMode: Bool = false final let collectionViewInset = UIEdgeInsets(top: 28, left: 16, bottom: 28, right: 16) final let itemSpacing: CGFloat = 10 @@ -26,30 +38,27 @@ final class PrivateCourseListView: UIView { private let collectionViewLayout = UICollectionViewFlowLayout().then { $0.scrollDirection = .vertical } - private let beforeEditTopView = UIView().then{ + + 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개" + + private lazy var totalNumOfRecordlabel = UILabel().then { $0.font = .b6 $0.textColor = .g2 + $0.text = "총 코스 0개" } -// private let afterEditTopView = UIView() -// private let selectCouseLabel = UILabel().then { -// $0.text = "코스 선택" -// $0.font = .b6 -// $0.textColor = .g2 -// } - - - + private let editButton = UIButton(type: .custom).then { + $0.setTitle("편집", for: .normal) + $0.setTitleColor(.m1, for: .normal) + $0.titleLabel?.font = .b7 + $0.layer.borderColor = UIColor.m1.cgColor + $0.layer.borderWidth = 1 + $0.layer.cornerRadius = 11 + } - private lazy var courseListCollectionView = UICollectionView( + lazy var courseListCollectionView = UICollectionView( frame: .zero, collectionViewLayout: collectionViewLayout ).then { @@ -66,9 +75,9 @@ final class PrivateCourseListView: UIView { self.setUI() self.setLayout() self.setDelegate() + self.setAddTarget() self.register() } - required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -81,12 +90,14 @@ extension PrivateCourseListView { self.courseList = courseList self.courseListCollectionView.reloadData() self.emptyView.isHidden = !courseList.isEmpty + self.beforeEditTopView.isHidden = courseList.isEmpty + totalNumOfRecordlabel.text = "총 코스 \(courseList.count)개" } private func setDelegate() { courseListCollectionView.delegate = self courseListCollectionView.dataSource = self - + courseListCollectionView.allowsMultipleSelection = true emptyView.delegate = self } @@ -94,37 +105,65 @@ extension PrivateCourseListView { courseListCollectionView.register(CourseListCVC.self, forCellWithReuseIdentifier: CourseListCVC.className) } + + private func setAddTarget() { + self.editButton.addTarget(self, action: #selector(editButtonDidTap), for: .touchUpInside) + + } } +extension PrivateCourseListView { + @objc func editButtonDidTap() { + if isEditMode { + self.totalNumOfRecordlabel.text = "총 코스 \(self.courseList.count)개" + self.editButton.setTitle("편집", for: .normal) + self.delegate?.courseListEditButtonTapped() + self.courseListCollectionView.reloadData() + isEditMode = false + } else { + self.totalNumOfRecordlabel.text = "코스 선택" + self.delegate?.courseListEditButtonTapped() + self.editButton.setTitle("취소", for: .normal) + self.courseListCollectionView.reloadData() + isEditMode = true + } + } +} // MARK: - UI & Layout extension PrivateCourseListView { + private func setUI() { self.backgroundColor = .w1 self.emptyView.isHidden = true + self.beforeEditTopView.isHidden = false } private func setLayout() { - self.addSubviews(beforeEditTopView,courseListCollectionView) + self.addSubviews(beforeEditTopView, courseListCollectionView) courseListCollectionView.addSubviews(emptyView) + beforeEditTopView.addSubviews(totalNumOfRecordlabel, editButton) + beforeEditTopView.snp.makeConstraints { make in + make.top.equalToSuperview() + make.leading.trailing.equalToSuperview() + make.height.equalTo(38) + } - 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) + totalNumOfRecordlabel.snp.makeConstraints { make in + make.leading.equalToSuperview().inset(16) + make.top.equalToSuperview().offset(10) } - totalCourseNum.snp.makeConstraints { make in - make.top.equalToSuperview().offset(1) - make.leading.equalTo(self.safeAreaLayoutGuide).offset(16) + editButton.snp.makeConstraints { make in + make.trailing.equalToSuperview().inset(16) + make.width.equalTo(47) + make.height.equalTo(22) + make.top.equalToSuperview().offset(5) } - + courseListCollectionView.snp.makeConstraints { make in - make.top.equalTo(frameEditButton.snp.bottom) + make.top.equalTo(editButton.snp.bottom) make.leading.bottom.trailing.equalToSuperview() - } emptyView.snp.makeConstraints { make in make.center.equalToSuperview() @@ -136,13 +175,21 @@ extension PrivateCourseListView { // MARK: - UICollectionViewDelegate, UICollectionViewDataSource extension PrivateCourseListView: UICollectionViewDelegate, UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return courseList.count } - + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CourseListCVC.className, - for: indexPath) + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CourseListCVC.className, for: indexPath) + as? CourseListCVC else { return UICollectionViewCell() } + cell.setCellType(type: .title) + if let selectedCells = collectionView.indexPathsForSelectedItems, selectedCells.contains(indexPath) { + cell.selectCell(didSelect: true) + } else { + cell.selectCell(didSelect: false) + } + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CourseListCVC.className, for: indexPath) as? CourseListCVC else { return UICollectionViewCell() } cell.setCellType(type: .title) let model = courseList[indexPath.item] @@ -150,15 +197,39 @@ extension PrivateCourseListView: UICollectionViewDelegate, UICollectionViewDataS cell.setData(imageURL: model.image, title: cellTitle, location: nil, didLike: nil) return cell } + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + guard collectionView.cellForItem(at: indexPath) is CourseListCVC else { return } + guard let cell = collectionView.cellForItem(at: indexPath) as? CourseListCVC else { return } + if isEditMode { + cell.selectCell(didSelect: true) + delegate?.selectCellDidTapped() + } else { + collectionView.deselectItem(at: indexPath, animated: true) + cellDidTapped.send(indexPath.item) + delegate?.selectCellDidTapped() + } + } + func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) { + guard collectionView.cellForItem(at: indexPath) is CourseListCVC else { return } + guard let selectedCells = collectionView.indexPathsForSelectedItems else { + return } + guard let cell = collectionView.cellForItem(at: indexPath) as? CourseListCVC else { return } + if isEditMode { + let countSelectCells = selectedCells.count + delegate?.selectCellDidTapped() + cell.selectCell(didSelect: false) + } else { + collectionView.deselectItem(at: indexPath, animated: true) + delegate?.selectCellDidTapped() + } + } } - // MARK: - UICollectionViewDelegateFlowLayout extension PrivateCourseListView: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let cellWidth = (UIScreen.main.bounds.width - (self.itemSpacing + 2*self.collectionViewInset.left)) / 2 let cellHeight = CourseListCVCType.getCellHeight(type: .title, cellWidth: cellWidth) - return CGSize(width: cellWidth, height: cellHeight) } @@ -173,10 +244,6 @@ extension PrivateCourseListView: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { return self.lineSpacing } - - func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - cellDidTapped.send(indexPath.item) - } } // MARK: - Section Heading diff --git a/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/ActivityRecordDetailVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/ActivityRecordDetailVC.swift index 9f60cdd5..07988475 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/ActivityRecordDetailVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/ActivityRecordDetailVC.swift @@ -12,9 +12,9 @@ import Then import Moya final class ActivityRecordDetailVC: UIViewController { - + // MARK: - Properties - + private let recordProvider = Providers.recordProvider private var recordId: Int? @@ -34,7 +34,7 @@ final class ActivityRecordDetailVC: UIViewController { $0.isScrollEnabled = true $0.showsVerticalScrollIndicator = false } - + private let mapImageView = UIImageView() private let courseTitleLabel = UILabel().then { @@ -64,7 +64,7 @@ final class ActivityRecordDetailVC: UIViewController { } private let firstHorizontalDivideLine = UIView() - + private let secondHorizontalDivideLine = UIView() private lazy var recordDistanceLabel = SetInfoLayout.makeGreySmailTitleLabel().then { @@ -154,7 +154,6 @@ extension ActivityRecordDetailVC { } else { // 수정이 된 상태라면 팝업을 띄워주기 self.navibar.resetLeftButtonAction({ [weak self] in - //self?.navibar.leftButton.addTarget(self, action: #selector(self?.presentToQuitEditAlertVC), for: .touchUpInside) self?.presentToQuitEditAlertVC() }, .titleWithLeftButton) } @@ -187,7 +186,7 @@ extension ActivityRecordDetailVC { } @objc private func finishEditButtonDidTap() { - editRecordTitle() +// editRecordTitle() showToast(message: "제목 수정이 완료되었어요") // 수정이 완료되면 팝업 뜨지 않음 @@ -335,7 +334,6 @@ extension ActivityRecordDetailVC { make.top.leading.trailing.equalTo(view.safeAreaLayoutGuide) make.height.equalTo(48) } - moreButton.snp.makeConstraints { make in make.trailing.equalTo(self.view.safeAreaLayoutGuide) make.centerY.equalTo(navibar) @@ -402,15 +400,15 @@ extension ActivityRecordDetailVC { recordDistanceStackView.snp.makeConstraints { make in make.width.equalTo(stackViewWidth) } - + recordRunningTimeStackView.snp.makeConstraints { make in make.width.equalTo(stackViewWidth) } - + recordAveragePaceStackView.snp.makeConstraints { make in make.width.equalTo(stackViewWidth) } - + recordSubInfoStackView.snp.makeConstraints { make in make.top.equalTo(secondHorizontalDivideLine.snp.bottom).offset(23) make.centerX.equalToSuperview() 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 6f2498c7..4e7a8026 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/UploadedCourseInfoVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/InfoVC/UploadedCourseInfoVC.swift @@ -19,15 +19,23 @@ final class UploadedCourseInfoVC: UIViewController { private var uploadedCourseList = [PublicCourse]() + var isEditMode: Bool = false + + private var deleteToCourseId = [Int]() + // MARK: - Constants final let uploadedCourseInset: UIEdgeInsets = UIEdgeInsets(top: 16, left: 16, bottom: 25, right: 16) final let uploadedCourseLineSpacing: CGFloat = 20 final let uploadedCourseItemSpacing: CGFloat = 10 final let uplodaedCourseCellHeight: CGFloat = 124 - + // MARK: - UI Components + private let collectionViewLayout = UICollectionViewFlowLayout().then { + $0.scrollDirection = .vertical + } + private lazy var navibar = CustomNavigationBar(self, type: .titleWithLeftButton).setTitle("업로드한 코스") private lazy var UploadedCourseInfoCollectionView: UICollectionView = { @@ -41,6 +49,37 @@ final class UploadedCourseInfoVC: UIViewController { return collectionView }() + private let beforeEditTopView = UIView().then { + $0.backgroundColor = .clear + } + private lazy var totalNumOfRecordlabel = UILabel().then { + $0.font = .b6 + $0.textColor = .g2 + $0.text = "총 코스 0개" + } + + private let editButton = UIButton(type: .custom).then { + $0.setTitle("편집", for: .normal) + $0.setTitleColor(.m1, for: .normal) + $0.titleLabel?.font = .b7 + $0.layer.borderColor = UIColor.m1.cgColor + $0.layer.borderWidth = 1 + $0.layer.cornerRadius = 11 + } + + private lazy var deleteCourseButton = CustomButton(title: "삭제하기").then { + $0.isHidden = true + $0.isEnabled = false + } + + private lazy var courseListCollectionView = UICollectionView( + frame: .zero, collectionViewLayout: collectionViewLayout).then { + $0.backgroundColor = .clear + } + + private let emptyView = ListEmptyView(description: "아직 업로드한 코스가 없어요!\n내가 그린 코스를 공유해보세요", + buttonTitle: "코스 업로드하기") + // MARK: - View Life Cycle override func viewDidLoad() { @@ -51,6 +90,12 @@ final class UploadedCourseInfoVC: UIViewController { register() setDelegate() getUploadedCourseInfo() + self.setAddTarget() + self.setDeleteButton() + } + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + self.hideTabBar(wantsToHide: true) } } @@ -60,16 +105,72 @@ extension UploadedCourseInfoVC { private func setData(courseList: [PublicCourse]) { self.uploadedCourseList = courseList UploadedCourseInfoCollectionView.reloadData() + self.emptyView.isHidden = !courseList.isEmpty + self.deleteCourseButton.isHidden = true + self.beforeEditTopView.isHidden = courseList.isEmpty + totalNumOfRecordlabel.text = "총 코스 \(courseList.count)개" + } private func setDelegate() { self.UploadedCourseInfoCollectionView.delegate = self self.UploadedCourseInfoCollectionView.dataSource = self + emptyView.delegate = self + UploadedCourseInfoCollectionView.allowsMultipleSelection = true } private func register() { - UploadedCourseInfoCollectionView.register(UploadedCourseInfoCVC.self, - forCellWithReuseIdentifier: UploadedCourseInfoCVC.className) + UploadedCourseInfoCollectionView.register(CourseListCVC.self, + forCellWithReuseIdentifier: CourseListCVC.className) + } + private func setAddTarget() { + self.editButton.addTarget(self, action: #selector(editButtonDidTap), for: .touchUpInside) + + } + + private func setDeleteButton() { + deleteCourseButton.addTarget(self, action: #selector(deleteCourseButtonDidTap), for: .touchUpInside) + } +} + +// MARK: - @objc Function + +extension UploadedCourseInfoVC { + @objc func deleteCourseButtonDidTap(_sender: UIButton) { + guard let selectedList = UploadedCourseInfoCollectionView.indexPathsForSelectedItems else { return } + var deleteToCourseId = [Int]() + for indexPath in selectedList { + let publicCourse = uploadedCourseList[indexPath.item] + deleteToCourseId.append(publicCourse.id) + } + + let deleteAlertVC = RNAlertVC(description: "삭제하시겠습니까?") + deleteAlertVC.modalPresentationStyle = .overFullScreen + deleteAlertVC.rightButtonTapAction = { + deleteAlertVC.dismiss(animated: false) + self.deleteUploadedCourse(publicCourseIdList: deleteToCourseId) + + } + self.present(deleteAlertVC, animated: false) + } + + @objc func editButtonDidTap() { + if isEditMode { + self.totalNumOfRecordlabel.text = "총 코스 \(self.uploadedCourseList.count)개" + self.editButton.setTitle("편집", for: .normal) + self.deleteCourseButton.isEnabled = false + self.deleteCourseButton.setTitle(title: "삭제하기") + self.courseListCollectionView.reloadData() + isEditMode = false + self.deleteCourseButton.isHidden = true + } else { + self.totalNumOfRecordlabel.text = "기록 선택" + self.editButton.setTitle("취소", for: .normal) + + self.deleteCourseButton.isHidden = false + self.courseListCollectionView.reloadData() + isEditMode = true + } } } @@ -89,15 +190,47 @@ extension UploadedCourseInfoVC { private func setUI() { view.backgroundColor = .w1 UploadedCourseInfoCollectionView.backgroundColor = .w1 + self.emptyView.isHidden = true + self.beforeEditTopView.isHidden = false } private func setLayout() { - view.addSubview(UploadedCourseInfoCollectionView) + view.addSubviews(beforeEditTopView, UploadedCourseInfoCollectionView, deleteCourseButton) - UploadedCourseInfoCollectionView.snp.makeConstraints { make in + UploadedCourseInfoCollectionView.addSubview(emptyView) + + beforeEditTopView.addSubviews(totalNumOfRecordlabel, editButton) + beforeEditTopView.snp.makeConstraints { make in make.top.equalTo(navibar.snp.bottom) - make.leading.trailing.equalTo(view.safeAreaLayoutGuide) - make.bottom.equalTo(view.safeAreaLayoutGuide) + make.leading.trailing.equalToSuperview() + make.height.equalTo(38) + } + + totalNumOfRecordlabel.snp.makeConstraints { make in + make.leading.equalToSuperview().inset(16) + make.top.equalToSuperview().offset(10) + } + + editButton.snp.makeConstraints { make in + make.trailing.equalToSuperview().inset(16) + make.width.equalTo(47) + make.height.equalTo(22) + make.top.equalToSuperview().offset(5) + } + + deleteCourseButton.snp.makeConstraints { make in + make.bottom.equalToSuperview().inset(32) + make.leading.trailing.equalToSuperview().inset(16) + make.height.equalTo(44) + } + + UploadedCourseInfoCollectionView.snp.makeConstraints { make in + make.top.equalTo(editButton.snp.bottom) + make.leading.bottom.trailing.equalToSuperview() + } + emptyView.snp.makeConstraints { make in + make.center.equalToSuperview() + make.leading.trailing.equalToSuperview().inset(80) } } } @@ -115,18 +248,10 @@ extension UploadedCourseInfoVC: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { return uploadedCourseLineSpacing } - + 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 @@ -137,9 +262,67 @@ extension UploadedCourseInfoVC: UICollectionViewDataSource { } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - guard let uploadedCourseCell = collectionView.dequeueReusableCell(withReuseIdentifier: UploadedCourseInfoCVC.className, for: indexPath) as? UploadedCourseInfoCVC else { return UICollectionViewCell()} - uploadedCourseCell.setData(model: uploadedCourseList[indexPath.item]) - return uploadedCourseCell + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CourseListCVC.className, for: indexPath) + as? CourseListCVC else { return UICollectionViewCell() } + cell.setCellType(type: .title) + let model = uploadedCourseList[indexPath.item] + let cellTitle = "\(model.departure.region) \(model.departure.city)" + cell.setData(imageURL: model.image, title: cellTitle, location: nil, didLike: nil) + + if isEditMode { + // selectCell 표시 + if let selectedCells = collectionView.indexPathsForSelectedItems, selectedCells.contains(indexPath) { + cell.selectCell(didSelect: false) + } else { cell.selectCell(didSelect: true) + } + } else { + cell.setCellType(type: .title) + guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CourseListCVC.className, + for: indexPath) + as? CourseListCVC else { return UICollectionViewCell() } + cell.setCellType(type: .title) + } + return cell + } + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + guard collectionView.cellForItem(at: indexPath) is CourseListCVC else { return } + guard let selectedCells = collectionView.indexPathsForSelectedItems else { return } + guard let cell = collectionView.cellForItem(at: indexPath) as? CourseListCVC else { return } + let courseList = uploadedCourseList[indexPath.item] + if isEditMode { + self.deleteCourseButton.isEnabled = true + let countSelectCells = selectedCells.count + self.deleteCourseButton.setTitle(title: "삭제하기(\(countSelectCells))") + cell.selectCell(didSelect: true) + } else { + collectionView.deselectItem(at: indexPath, animated: true) + self.deleteCourseButton.setTitle(title: "삭제하기") + self.deleteCourseButton.setEnabled(true) + let courseDetailVC = CourseDetailVC() + courseDetailVC.hidesBottomBarWhenPushed = true + self.navigationController?.pushViewController(courseDetailVC, animated: true) + cell.selectCell(didSelect: false) + } + } + func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) { + guard collectionView.cellForItem(at: indexPath) is CourseListCVC else { return } + guard let selectedCells = collectionView.indexPathsForSelectedItems else { + self.deleteCourseButton.isEnabled = false + self.deleteCourseButton.setTitle(title: "삭제하기") + return } + guard let cell = collectionView.cellForItem(at: indexPath) as? CourseListCVC else { return } + cell.selectCell(didSelect: false) + if isEditMode { + self.deleteCourseButton.isEnabled = true + let countSelectCells = selectedCells.count + self.deleteCourseButton.setTitle(title: "삭제하기(\(countSelectCells))") + cell.selectCell(didSelect: false) + } else { + collectionView.deselectItem(at: indexPath, animated: true) + self.deleteCourseButton.setTitle(title: "삭제하기") + cell.selectCell(didSelect: false) + } } } @@ -173,4 +356,37 @@ extension UploadedCourseInfoVC { } } } + private func deleteUploadedCourse(publicCourseIdList: [Int]) { + LoadingIndicator.showLoading() + uploadedCourseProvider.request(.deleteUploadedCourse(publicCourseIdList: publicCourseIdList)) { [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("삭제 성공") + self.getUploadedCourseInfo() + } + if status >= 400 { + print("400 error") + self.showNetworkFailureToast() + } + case .failure(let error): + print(error.localizedDescription) + self.showNetworkFailureToast() + } + } + } +} + +// MARK: - Section Heading + +extension UploadedCourseInfoVC: ListEmptyViewDelegate { + func emptyViewButtonTapped() { + let myCourseSelectVC = MyCourseSelectVC() + self.navigationController?.pushViewController(myCourseSelectVC, animated: true) + + } } diff --git a/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/SettingVC/PersoanlInfoVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/SettingVC/PersoanlInfoVC.swift index 6c953a59..e857696e 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/SettingVC/PersoanlInfoVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/SettingVC/PersoanlInfoVC.swift @@ -15,7 +15,6 @@ class PersoanlInfoVC: UIViewController { // Do any additional setup after loading the view. } - /* // MARK: - Navigation diff --git a/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/SettingVC/TermsOfServiceVC.swift b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/SettingVC/TermsOfServiceVC.swift index eeeb0736..64d66520 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/SettingVC/TermsOfServiceVC.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/MyPage/VC/SettingVC/TermsOfServiceVC.swift @@ -15,7 +15,6 @@ class TermsOfServiceVC: UIViewController { // Do any additional setup after loading the view. } - /* // MARK: - Navigation diff --git a/Runnect-iOS/Runnect-iOS/Presentation/TabBar/TaBarController.swift b/Runnect-iOS/Runnect-iOS/Presentation/TabBar/TaBarController.swift index 01657dff..21d8ffab 100644 --- a/Runnect-iOS/Runnect-iOS/Presentation/TabBar/TaBarController.swift +++ b/Runnect-iOS/Runnect-iOS/Presentation/TabBar/TaBarController.swift @@ -8,7 +8,7 @@ import UIKit final class TabBarController: UITabBarController { - + // MARK: - View Life Cycle override func viewDidLoad() {