Skip to content

Commit

Permalink
[Feat/#30] 약속상세뷰.....
Browse files Browse the repository at this point in the history
  • Loading branch information
oyslucy committed Feb 3, 2025
1 parent e751186 commit 5466776
Show file tree
Hide file tree
Showing 19 changed files with 1,080 additions and 22 deletions.
2 changes: 1 addition & 1 deletion Noostak_iOS/Noostak_iOS/Application/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
guard let windowScene = (scene as? UIWindowScene) else { return }

let window = UIWindow(windowScene: windowScene)
let vc = ViewController()
let vc = GroupDetailViewController(reactor: GroupDetailReactor())
window.rootViewController = vc
self.window = window
window.makeKeyAndVisible()
Expand Down
28 changes: 19 additions & 9 deletions Noostak_iOS/Noostak_iOS/Domain/Entity/Schedule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,30 +22,40 @@ struct Schedule {
///약속 카테고리
let category: ScheduleCategory
///약속 생성기간
let dates: [Date]
let selectionDates: [Date]
///약속생성 시작일
var startDate: Date? {
return dates.sorted().first
var selectionStartDate: Date? {
return selectionDates.sorted().first
}
///약속생성 종료일
var endDate: Date? {
return dates.sorted().last
var selectionEndDate: Date? {
return selectionDates.sorted().last
}
///약속 시작시각
let startTime: Date
///약속 종료시각
let endTime: Date
///약속생성 시작시각
let selectionStartTime: Date
///약속생성 종료시각
let selectionEndTime: Date
///소요시간
var duration: Int?
}

struct ExtendedSchedule {
///스케쥴
let schedule: Schedule
///약속 날짜(1순위, 확정)
let date: String
///약속 시작시각(1순위, 확정)
let startTime: String
///약속 종료시각(1순위, 확정)
let endTime: String
///가능한 친구
let availableMembers: [User]
///불가능한 친구
let unavailableMembers: [User]
///그룹 총인원
var groupMemberCount: Int
///가능한 인원
var availableMemberCount: Int
}

extension ScheduleCategory {
Expand Down
20 changes: 11 additions & 9 deletions Noostak_iOS/Noostak_iOS/Global/Extension/UIFont+.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ extension UIFont {
case h1_sb
case h2_b
case h3_sb
case h3_22_SB
case h4_b
case h4_sb
case h5_b
Expand All @@ -63,14 +64,15 @@ extension UIFont {
case c2_sb
case c3_r
case c4_r

var font: UIFont {
switch self {
case .h1_b: return UIFont.pretendard(.bold, size: 56)
case .h1_sb: return UIFont.pretendard(.semibold, size: 56)
case .h2_b: return UIFont.pretendard(.bold, size: 27)
case .h1_b: return UIFont.pretendard(.bold, size: 27)
case .h1_sb: return UIFont.pretendard(.semibold, size: 27)
case .h2_b: return UIFont.pretendard(.bold, size: 24)
case .h3_sb: return UIFont.pretendard(.semibold, size: 24)
case .h4_b: return UIFont.pretendard(.bold, size: 24)
case .h3_22_SB: return UIFont.pretendard(.semibold, size: 22)
case .h4_b: return UIFont.pretendard(.bold, size: 20)
case .h4_sb: return UIFont.pretendard(.semibold, size: 20)
case .h5_b: return UIFont.pretendard(.bold, size: 18)
case .t1_sb: return UIFont.pretendard(.semibold, size: 18)
Expand All @@ -89,13 +91,13 @@ extension UIFont {
case .c4_r: return UIFont.pretendard(.regular, size: 11)
}
}

// Line Height (LHLHUnit)
var lineHeightUnit: CGFloat {
switch self {
case .h1_b, .h1_sb, .h2_b, .h3_sb, .h4_b, .h4_sb, .h5_b,
.t1_sb, .t2_r, .t3_b, .t4_b, .b1_sb, .b2_r, .b4_sb,
.b4_sb_1percent, .b4_r, .b5_r, .c1_b, .c2_sb, .c3_r, .c4_r:
case .h1_b, .h1_sb, .h2_b, .h3_sb, .h3_22_SB, .h4_b, .h4_sb, .h5_b,
.t1_sb, .t2_r, .t3_b, .t4_b, .b1_sb, .b2_r, .b4_sb,
.b4_sb_1percent, .b4_r, .b5_r, .c1_b, .c2_sb, .c3_r, .c4_r:
return 140
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Supervisor account.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "icon_share.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Vector.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 33 additions & 1 deletion Noostak_iOS/Noostak_iOS/Global/Utils/NSTDateUtility.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ public extension NSTDateUtility {
}

extension NSTDateUtility {
///타임테이블 뷰 : "요일 월/일"
static func dateList(_ dateStrings: [String]) -> [String] {
let formatter = NSTDateUtility(format: .yyyyMMddTHHmmss) // ISO 8601 형식
let displayFormatter = NSTDateUtility(format: .MMddEE) // 출력 형식
Expand All @@ -102,7 +103,8 @@ extension NSTDateUtility {
}
}
}


///타임테이블 뷰 : "00시"
static func timeList(_ startTime: String, _ endTime: String) -> [String] {
let formatter = NSTDateUtility(format: .yyyyMMddTHHmmss) // ISO 8601 형식
var result: [String] = []
Expand All @@ -126,4 +128,34 @@ extension NSTDateUtility {
}
return result
}

///약속상세뷰 : "9월 7일 (일) 10:00~12:00"
static func durationList(_ startTime: String, _ endTime: String) -> String {
let formatter = NSTDateUtility(format: .yyyyMMddTHHmmss) // ISO 8601 형식
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "ko_KR")
dateFormatter.timeZone = TimeZone(identifier: "Asia/Seoul")

// 날짜 포맷: "9월 7일 (일)"
dateFormatter.dateFormat = "M월 d일 (E)"

// 시간 포맷: "10:00"
let timeFormatter = DateFormatter()
timeFormatter.locale = Locale(identifier: "ko_KR")
timeFormatter.timeZone = TimeZone(identifier: "Asia/Seoul")
timeFormatter.dateFormat = "HH:mm"

switch (formatter.date(from: startTime), formatter.date(from: endTime)) {
case (.success(let start), .success(let end)):
let dateString = dateFormatter.string(from: start) // "9월 7일 (일)"
let startTimeString = timeFormatter.string(from: start) // "10:00"
let endTimeString = timeFormatter.string(from: end) // "12:00"

return "\(dateString) \(startTimeString)~\(endTimeString)"

default:
print("Failed to parse start or end time.")
return ""
}
}
}
160 changes: 158 additions & 2 deletions Noostak_iOS/Noostak_iOS/Presentation/Dummy/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,168 @@
//

import UIKit
import SnapKit
import RxSwift
import RxCocoa
import RxDataSources

class ViewController: UIViewController {
let mockDateList: [String] = [
"2024-09-05T10:00:00",
"2024-09-06T10:00:00",
"2024-09-09T10:00:00",
"2024-09-10T10:00:00",
"2024-09-11T10:00:00",
"2024-09-12T10:00:00"
]
let mockDateList2: [String] = [
"2024-09-05T10:00:00",
"2024-09-06T10:00:00",
"2024-09-09T10:00:00"
]
let mockStartTime: String = "2024-09-05T08:00:00"
let mockEndTime: String = "2024-09-05T10:00:00"
let mockEndTime2: String = "2024-09-05T17:00:00"
let participants: Int = 5
let mockMemberStartTime: [String] = [
"2024-09-05T08:00:00", // 1명
"2024-09-05T09:00:00",
"2024-09-05T10:00:00",
"2024-09-05T11:00:00",
"2024-09-05T11:00:00",
"2024-09-05T12:00:00",
"2024-09-05T12:00:00",
"2024-09-06T11:00:00",
"2024-09-06T11:00:00",
"2024-09-06T11:00:00",
"2024-09-06T11:00:00",
"2024-09-06T12:00:00", // 5명
"2024-09-06T12:00:00",
"2024-09-06T12:00:00",
"2024-09-06T12:00:00",
"2024-09-06T12:00:00",
"2024-09-06T13:00:00", // 3명
"2024-09-06T13:00:00",
"2024-09-06T13:00:00",
"2024-09-06T14:00:00", // 2명
"2024-09-06T14:00:00",
"2024-09-06T14:00:00",
"2024-09-06T15:00:00", // 1명
"2024-09-06T16:00:00", // 4명
"2024-09-06T16:00:00",
"2024-09-06T16:00:00",
"2024-09-06T16:00:00",
"2024-09-05T15:00:00", // 1명
"2024-09-05T16:00:00", // 4명
"2024-09-05T16:00:00",
"2024-09-05T16:00:00",
"2024-09-05T16:00:00", // 0명
"2024-09-05T18:00:00", // 3명
"2024-09-05T18:00:00",
"2024-09-05T18:00:00",
"2024-09-05T19:00:00", // 5명
"2024-09-05T19:00:00",
"2024-09-05T19:00:00",
"2024-09-05T20:00:00", // 2명
"2024-09-05T20:00:00",
"2024-09-05T21:00:00" // 1명
]
let dateHeaders: [String] = NSTDateUtility.dateList(mockDateList)
let timeHeaders: [String] = NSTDateUtility.timeList(mockStartTime, mockEndTime)
let dateHeaders2: [String] = NSTDateUtility.dateList(mockDateList2)
let timeHeaders2: [String] = NSTDateUtility.timeList(mockStartTime, mockEndTime2)
let totalRows = timeHeaders.count + 1
let totalColumns = dateHeaders.count + 1
let totalRows2 = timeHeaders2.count + 1
let totalColumns2 = dateHeaders2.count + 1

final class ViewController: UIViewController {
private lazy var schedulePicker1 = SchedulePicker(timeHeaders: timeHeaders, dateHeaders: dateHeaders, mode: .editMode)
private lazy var schedulePicker2 = SchedulePicker(timeHeaders: timeHeaders2, dateHeaders: dateHeaders2, mode: .readMode)
private let disposeBag = DisposeBag()

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
setupView()
bindCollectionView()
}

private func setupView() {
view.backgroundColor = .white
view.addSubview(schedulePicker1)
view.addSubview(schedulePicker2)

schedulePicker1.translatesAutoresizingMaskIntoConstraints = false
schedulePicker2.translatesAutoresizingMaskIntoConstraints = false

schedulePicker1.snp.makeConstraints {
$0.horizontalEdges.equalToSuperview().inset(16)
$0.top.equalToSuperview()
$0.height.equalTo(55 + totalRows * 32)
}
schedulePicker2.snp.makeConstraints {
$0.horizontalEdges.equalToSuperview().inset(16)
$0.top.equalTo(schedulePicker1.snp.bottom).offset(10)
$0.height.equalTo(UIScreen.main.bounds.height - 170)
}
}

private func bindCollectionView() {
let items = Observable.just([
SectionModel(model: "Section 1", items: Array(repeating: "", count: totalRows * totalColumns))
])
let items2 = Observable.just([
SectionModel(model: "Section 1", items: Array(repeating: "", count: totalRows2 * totalColumns2))
])

let dataSource = RxCollectionViewSectionedReloadDataSource<SectionModel<String, String>>(
configureCell: { [weak self] _, collectionView, indexPath, _ in
guard let self = self,
let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: SchedulePickerCell.identifier, for: indexPath
) as? SchedulePickerCell else {
return UICollectionViewCell()
}
cell.configureHeader(for: indexPath, dateHeaders: dateHeaders, timeHeaders: timeHeaders)
cell.configureTableRoundness(for: indexPath, dateHeaders: dateHeaders, timeHeaders: timeHeaders)
self.schedulePicker1.configureCellBackground(cell, for: indexPath, participants: participants)
return cell
}
)

let dataSource2 = RxCollectionViewSectionedReloadDataSource<SectionModel<String, String>>(
configureCell: { [weak self] _, collectionView, indexPath, _ in
guard let self = self,
let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: SchedulePickerCell.identifier, for: indexPath
) as? SchedulePickerCell else {
return UICollectionViewCell()
}
cell.configureHeader(for: indexPath, dateHeaders: dateHeaders2, timeHeaders: timeHeaders2)
cell.configureTableRoundness(for: indexPath, dateHeaders: dateHeaders2, timeHeaders: timeHeaders2)
self.schedulePicker2.configureCellBackground(cell, for: indexPath, participants: participants)
self.schedulePicker2.updateCellAvailability(with: mockDateList, startTimes: mockMemberStartTime)
return cell
}
)
items
.bind(to: schedulePicker1.rx.items(dataSource: dataSource))
.disposed(by: disposeBag)
items2
.bind(to: schedulePicker2.rx.items(dataSource: dataSource2))
.disposed(by: disposeBag)

schedulePicker1.rx.itemSelected
.subscribe(onNext: { [weak self] indexPath in
guard let self = self else { return }
let row = indexPath.item / totalColumns
let column = indexPath.item % totalColumns

guard row > 0, column > 0 else {
self.schedulePicker1.deselectItem(at: indexPath, animated: true) // 첫 번째 행과 첫 번째 열은 선택 불가
return
}
self.schedulePicker1.addSelectedCell(at: indexPath)
})
.disposed(by: disposeBag)
}
}
Loading

0 comments on commit 5466776

Please sign in to comment.