diff --git a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Achieve/AchieveAPI.swift b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Achieve/AchieveAPI.swift index 8754a855..bef5eca0 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Network/API/Achieve/AchieveAPI.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Network/API/Achieve/AchieveAPI.swift @@ -40,17 +40,16 @@ final class AchieveAPI { // MARK: - GET func getAchieveCalendar(month: String, completion: @escaping (GeneralArrayResponse?) -> Void) { - achieveProvider.request(.achieveCalendar(month: month)) { response in - switch response { - case .success(let result): - do { - let responseData = try result.map(GeneralArrayResponse.self) - self.achieveCalendarData = responseData - completion(self.achieveCalendarData) - } catch let error { - print(error.localizedDescription) - completion(nil) - } + achieveProvider.request(.achieveCalendar(month: month)) { result in + switch result { + case .success(let response): + do { + self.achieveCalendarData = try response.map(GeneralArrayResponse?.self) + guard let achieveCalendarData = self.achieveCalendarData else { return completion(nil) } + completion(achieveCalendarData) + } catch let err { + print(err.localizedDescription, 500) + } case .failure(let err): print(err.localizedDescription) completion(nil) diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift index 5b10ab65..1f998bc4 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Achievement/ViewControllers/AchievementViewController.swift @@ -15,15 +15,10 @@ final class AchievementViewController: UIViewController { // MARK: - Properties + private var currentPage = Date() + private var dataSource: [String: Float] = [:] + private lazy var safeArea = self.view.safeAreaLayoutGuide - private var currentPage: Date? = Date() - var count: Int? - var dataSource: [String: Float] = [:] { - didSet { - self.monthCalendar.calendar.reloadData() - } - } - var selectDate: Date? // MARK: - UI Components @@ -47,6 +42,7 @@ final class AchievementViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() + setUI() setLayout() } @@ -56,19 +52,20 @@ final class AchievementViewController: UIViewController { extension AchievementViewController { - func reloadMonthData(month: String) { - requestMonthAPI(month: month) - } - private func setUI() { view.backgroundColor = .ntdBlack - scrollView.backgroundColor = .clear + + scrollView.do { + $0.backgroundColor = .clear + $0.showsVerticalScrollIndicator = false + } achievementLabel.do { $0.text = I18N.achievement $0.font = .Pretendard(.semiBold, size: 18) $0.textColor = .white } + monthCalendar.do { $0.layer.cornerRadius = 12 $0.layer.borderWidth = 1 @@ -76,19 +73,22 @@ extension AchievementViewController { $0.calendar.register(MissionCalendarCell.self, forCellReuseIdentifier: MissionCalendarCell.identifier) $0.calendar.delegate = self $0.calendar.dataSource = self - $0.monthCalendarClosure = { [self] result in - let month = result - self.reloadMonthData(month: month) + $0.monthCalendarClosure = { [weak self] month in + guard let self else { return } + self.requestMonthAPI(month: month) } } } + private func setLayout() { view.addSubview(scrollView) + scrollView.addSubviews(achievementLabel, monthCalendar, statisticsView) scrollView.snp.makeConstraints { $0.edges.equalTo(safeArea) } + achievementLabel.snp.makeConstraints { $0.top.equalToSuperview().offset(23) $0.leading.equalTo(safeArea).offset(21) @@ -107,39 +107,27 @@ extension AchievementViewController { $0.bottom.equalTo(scrollView.snp.bottom).inset(20) } } + func requestMonthAPI(month: String) { - AchieveAPI.shared.getAchieveCalendar(month: month) { [weak self] result in - guard let result = result?.data else { return } - self?.dataSource = [:] - guard let currentPage = self?.currentPage else { return } - let currentMonth = Calendar.current.component(.month, from: currentPage) - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "yyyy-MM-dd" - let actionDate = result.filter { result in - if let date = dateFormatter.date(from: result.actionDate) { - let month = Calendar.current.component(.month, from: date) - return month == currentMonth - } - return false - } - - actionDate.forEach { - self?.dataSource[$0.actionDate] = $0.percentage - self?.count = self?.dataSource.count - } - - self?.monthCalendar.calendar.reloadData() + AchieveAPI.shared.getAchieveCalendar(month: month) { [weak self] response in + + guard let self, let response = response, let data = response.data else { return } + + let calendarData = data.compactMap { ($0.actionDate, $0.percentage) } + self.dataSource = Dictionary(uniqueKeysWithValues: calendarData) + self.monthCalendar.calendar.collectionView.reloadData() } } } extension AchievementViewController: FSCalendarDelegate, FSCalendarDataSource, FSCalendarDelegateAppearance { + func calendarCurrentPageDidChange(_ calendar: FSCalendar) { self.currentPage = calendar.currentPage + requestMonthAPI(month: Utils.dateFormatterString(format: "yyyy-MM", date: calendar.currentPage)) monthCalendar.configureYearMonth(to: Utils.dateFormatterString(format: I18N.yearMonthTitle, date: calendar.currentPage)) - reloadMonthData(month: Utils.dateFormatterString(format: "yyyy-MM", date: calendar.currentPage)) } - + func calendar(_ calendar: FSCalendar, titleFor date: Date) -> String? { Utils.dateFormatterString(format: "dd", date: date) } @@ -160,61 +148,49 @@ extension AchievementViewController: FSCalendarDelegate, FSCalendarDataSource, F func calendar(_ calendar: FSCalendar, shouldSelect date: Date, at monthPosition: FSCalendarMonthPosition) -> Bool { Calendar.current.isDate(date, equalTo: calendar.currentPage, toGranularity: .month) } + func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, titleSelectionColorFor date: Date) -> UIColor? { - guard let count = self.count else { return .white } - let dateString = Utils.dateFormatterString(format: nil, date: date) - - if let percentage = self.dataSource[dateString] { - switch (count, percentage) { - case (_, 1.0): return .black - default: return .white - } - } - return .white + titleColorFor(date: date) } func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, titleDefaultColorFor date: Date) -> UIColor? { - guard let currentPage = self.currentPage else { return .white } let currentMonth = Calendar.current.component(.month, from: currentPage) let dateMonth = Calendar.current.component(.month, from: date) - - guard let count = self.count else { return .white } - let dateString = Utils.dateFormatterString(format: nil, date: date) - if let percentage = self.dataSource[dateString] { - switch (count, percentage) { - case (_, 1.0): return .black - default: return .white - } + + if let percentage = getPercentage(for: date) { + return percentage == 1.0 ? .black : .white } else { - if currentMonth != dateMonth { - return .gray3 - } + return currentMonth != dateMonth ? .gray3 : .white } - - return .white } func calendar(_ calendar: FSCalendar, cellFor date: Date, at position: FSCalendarMonthPosition) -> FSCalendarCell { let cell = calendar.dequeueReusableCell(withIdentifier: MissionCalendarCell.identifier, for: date, at: position) as! MissionCalendarCell - cell.iconView.contentMode = .scaleAspectFit - cell.iconView.snp.remakeConstraints { - $0.edges.equalToSuperview() - $0.center.equalToSuperview() - } - cell.titleLabel.snp.remakeConstraints { - $0.centerY.equalToSuperview().offset(-1) - $0.centerX.equalToSuperview() - } - guard let count = self.count else { return cell } + + guard let percentage = getPercentage(for: date) else { return cell } + cell.configure(percent: percentage) + + return cell + } +} + +// MARK: - Others + +extension AchievementViewController { + + private func getPercentage(for date: Date) -> Float? { + let dateString = Utils.dateFormatterString(format: nil, date: date) - if let percentage = self.dataSource[dateString] { - switch (count, percentage) { - case (_, 1.0): cell.configure(.rateFull, .week) - case (_, 0.0): cell.configure(.none, .week) - case (2, 0.5), (3, 0.0..<1.0), (_, _): cell.configure(.rateHalf, .week) - } + return self.dataSource[dateString] + } + + private func titleColorFor(date: Date) -> UIColor { + + if let percentage = getPercentage(for: date) { + return percentage == 1.0 ? .black : .white } - return cell + + return .white } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/MissionCalendarCell.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/MissionCalendarCell.swift index 377c8ed1..86f4ae4f 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/MissionCalendarCell.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Common/MissionCalendarCell.swift @@ -35,13 +35,20 @@ final class MissionCalendarCell: FSCalendarCell { static let identifier = "MissionCalendarCell" - var mode: FSCalendarScope = .week - var state: ToDoState = .none + var mode: FSCalendarScope = .week { + didSet { + setLayout() + } + } + var state: ToDoState = .none { + didSet { + updateUI(state: self.state) + } + } // MARK: - UI Components - let iconView = UIImageView() - private var padding = 8 + private let iconView = UIImageView() // MARK: - Life Cycle @@ -71,24 +78,46 @@ extension MissionCalendarCell { } private func setLayout() { - iconView.snp.makeConstraints { - $0.height.width.equalTo(contentView.bounds.width - 8) - $0.centerX.equalToSuperview() - $0.bottom.equalToSuperview().inset(5) - } - subtitleLabel.snp.updateConstraints { - $0.centerY.equalTo(iconView.snp.centerY) + switch mode { + case .month: + iconView.snp.remakeConstraints { + $0.height.equalToSuperview() + $0.width.equalTo(contentView.bounds.size.height) + $0.center.equalToSuperview() + } + + titleLabel.snp.remakeConstraints { + $0.centerY.equalToSuperview().offset(-1) + $0.centerX.equalToSuperview() + } + case .week: + iconView.snp.remakeConstraints { + $0.height.width.equalTo(contentView.bounds.width - 8) + $0.centerX.equalToSuperview() + $0.bottom.equalToSuperview().inset(5) + } + subtitleLabel.snp.updateConstraints { + $0.centerY.equalTo(iconView.snp.centerY) + } + + @unknown default: + break } + } private func updateUI(state: ToDoState) { iconView.image = state.icon } - func configure(_ state: ToDoState, _ mode: FSCalendarScope) { - self.mode = mode - self.state = state - setLayout() - updateUI(state: state) + func configure(percent: Float) { + switch percent { + case 0.0: + self.state = .none + case 1.0: + self.state = .rateFull + default: + self.state = .rateHalf + } } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift index 4e2755b0..6ab770e4 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Home/ViewControllers/HomeViewController.swift @@ -214,21 +214,7 @@ extension HomeViewController: FSCalendarDelegate, FSCalendarDataSource, FSCalend AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.Home.clickWeeklyDate(date: Utils.dateFormatterString(format: nil, date: date))) } - - func calendar(_ calendar: FSCalendar, cellFor date: Date, at position: FSCalendarMonthPosition) -> FSCalendarCell { - let cell = calendar.dequeueReusableCell(withIdentifier: MissionCalendarCell.identifier, for: date, at: position) as! MissionCalendarCell - - guard let percentage = getPercentage(for: date) else { return cell } - - switch percentage { - case 0.0: cell.configure(.none, .week) - case 1.0: cell.configure(.rateFull, .week) - default: cell.configure(.rateHalf, .week) - } - - return cell - } - + func calendar(_ calendar: FSCalendar, titleFor date: Date) -> String? { Utils.dateFormatterString(format: "EEEEEE", date: date) } @@ -244,6 +230,16 @@ extension HomeViewController: FSCalendarDelegate, FSCalendarDataSource, FSCalend func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, subtitleDefaultColorFor date: Date) -> UIColor? { return subtitleColorFor(date: date) } + + func calendar(_ calendar: FSCalendar, cellFor date: Date, at position: FSCalendarMonthPosition) -> FSCalendarCell { + + let cell = calendar.dequeueReusableCell(withIdentifier: MissionCalendarCell.identifier, for: date, at: position) as! MissionCalendarCell + + guard let percentage = getPercentage(for: date) else { return cell } + cell.configure(percent: percentage) + + return cell + } } // MARK: - Network