Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat/NST-13] 회의확정 확인뷰 #37

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Noostak_iOS/Noostak_iOS/Domain/Entity/Schedule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ struct ExtendedSchedule {
let startTime: String
///약속 종료시각(1순위, 확정)
let endTime: String
///나의 가능 여부
let myInfo: MemberStatus
///가능한 친구
let availableMembers: [User]
///불가능한 친구
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ final class MemberAvailabilityChip: UIView {
setUpLayout()
}

func update(name: String, status: MemberStatus) {
self.chipLabel.text = name
self.status = status
setUpUI()
setUpLayout()
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// MemberAvailabilityCVC.swift
// Noostak_iOS
//
// Created by 오연서 on 2/11/25.
//

import UIKit
import SnapKit
import Then
import ReactorKit

final class MemberAvailabilityCVC: UICollectionViewCell, View {

// MARK: Properties
static let identifier = "MemberAvailabilityCVC"
var disposeBag = DisposeBag()

// MARK: Views
var chip = MemberAvailabilityChip(name: "", status: .available)

// MARK: Init
override init(frame: CGRect) {
super.init(frame: frame)
setUpHierarchy()
setUpLayout()
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

// MARK: setUpHierarchy
private func setUpHierarchy() {
self.addSubview(chip)
}

// MARK: setUpLayout
private func setUpLayout() {
chip.snp.makeConstraints {
$0.edges.equalToSuperview()
}
}
}

extension MemberAvailabilityCVC {
func bind(reactor: MemberAvailabilityCellReactor) {
self.chip.update(name: reactor.currentState.user.name, status: reactor.currentState.status)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// MemberAvailabilityCellReactor.swift
// Noostak_iOS
//
// Created by 오연서 on 2/11/25.
//

import ReactorKit
import RxSwift

final class MemberAvailabilityCellReactor: Reactor {
typealias Action = NoAction
struct State {
let user: User
let status: MemberStatus
}
Comment on lines +11 to +16
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오! Reactor를 분리해서 관리하시나요! 좋습니다 👍👍👍
혹시 이렇게 진행하게된 이유를 여쭤봐도 될까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cell의 reactor를 가질 경우 별도의 바인딩 구현 없이 vc에서 바로 reactor를 주입할 수 있습니다 !


let initialState: State

init(user: User, status: MemberStatus) {
self.initialState = State(user: user, status: status)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
//
// ScheduleInfoView.swift
// Noostak_iOS
//
// Created by 오연서 on 2/11/25.
//

import UIKit
import Then
import SnapKit
import RxSwift
import RxCocoa
import RxDataSources
import ReactorKit

enum ScheduleState {
case inProgress
case confirmed
}

final class ScheduleInfoView: UIView {
// MARK: Properties
var disposeBag = DisposeBag()
private var state: ScheduleState

// MARK: Views
let scheduleDurationLabel = UILabel()
let likeButton = LikeButton()
private let scheduleInfoView = UIView()
private let scheduleTimeTitleLabel = UILabel()
let scheduleTimeLabel = UILabel()
private let scheduleCategoryLabel = UILabel()
var scheduleCategoryChip = ScheduleCategoryButton(category: .hobby, buttonType: .ReadOnly)
let availableLabel = UILabel()
var availableCollectionView: UICollectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
let unavailableLabel = UILabel()
var unavailableCollectionView: UICollectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())

// MARK: Init
init(state: ScheduleState) {
self.state = state
super.init(frame: .zero)
setUpFoundation()
setUpHierarchy()
setUpUI()
setUpLayout()
updateUI()
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

// MARK: setUpHierarchy
private func setUpHierarchy() {
self.addSubview(scheduleInfoView)
[scheduleDurationLabel, likeButton, scheduleTimeTitleLabel, scheduleTimeLabel,
scheduleCategoryLabel, scheduleCategoryChip,
availableLabel, availableCollectionView,
unavailableLabel, unavailableCollectionView].forEach {
scheduleInfoView.addSubview($0)
}
}

private func setUpFoundation() {
self.backgroundColor = .appWhite
}

// MARK: setUpUI
private func setUpUI() {
scheduleDurationLabel.do {
$0.font = .PretendardStyle.t4_b.font
$0.textColor = .appBlack
}

likeButton.do {
$0.isHidden = true
}

scheduleInfoView.do {
$0.layer.cornerRadius = 20
$0.layer.borderColor = UIColor.appGray100.cgColor
$0.layer.borderWidth = 1
}

scheduleTimeTitleLabel.do {
$0.text = "약속 시간"
$0.font = .PretendardStyle.c3_r.font
$0.textColor = .appBlack
}

scheduleTimeLabel.do {
$0.font = .PretendardStyle.b4_sb.font
$0.textColor = .appBlack
}

scheduleCategoryLabel.do {
$0.text = "약속 유형"
$0.font = .PretendardStyle.c3_r.font
$0.textColor = .appBlack
}

availableLabel.do {
$0.font = .PretendardStyle.c3_r.font
$0.textColor = .appBlack
}

unavailableLabel.do {
$0.font = .PretendardStyle.c3_r.font
$0.textColor = .appBlack
}
}

// MARK: setUpLayout
private func setUpLayout() {
scheduleInfoView.snp.makeConstraints {
$0.edges.equalToSuperview()
$0.bottom.equalTo(unavailableCollectionView.snp.bottom).offset(16)
}

scheduleDurationLabel.snp.makeConstraints {
$0.top.equalToSuperview().offset(20)
$0.leading.equalToSuperview().offset(16)
}

likeButton.snp.makeConstraints {
$0.top.equalToSuperview().offset(16)
$0.trailing.equalToSuperview().inset(16)
$0.height.equalTo(30)
$0.width.equalTo(50)
}

scheduleTimeTitleLabel.snp.makeConstraints {
$0.top.leading.equalToSuperview().offset(16)
}

scheduleTimeLabel.snp.makeConstraints {
$0.centerY.equalTo(scheduleTimeTitleLabel)
$0.trailing.equalToSuperview().inset(16)
}

scheduleCategoryLabel.snp.makeConstraints {
$0.top.equalTo(scheduleTimeTitleLabel.snp.bottom).offset(26)
$0.leading.equalTo(scheduleTimeTitleLabel)
}

scheduleCategoryChip.snp.makeConstraints {
$0.centerY.equalTo(scheduleCategoryLabel)
$0.trailing.equalTo(scheduleTimeLabel)
$0.height.equalTo(30)
$0.width.equalTo(53)
}

availableLabel.snp.makeConstraints {
$0.leading.equalTo(scheduleTimeTitleLabel)
}

availableCollectionView.snp.makeConstraints {
$0.top.equalTo(availableLabel.snp.bottom).offset(10)
$0.horizontalEdges.equalToSuperview().inset(16)
}

unavailableLabel.snp.makeConstraints {
$0.top.equalTo(availableCollectionView.snp.bottom).offset(20)
$0.leading.equalTo(scheduleTimeTitleLabel)
}

unavailableCollectionView.snp.makeConstraints {
$0.top.equalTo(unavailableLabel.snp.bottom).offset(10)
$0.horizontalEdges.equalToSuperview().inset(16)
}
}

private func updateUI() {
switch state {
case .inProgress:
scheduleDurationLabel.isHidden = false
likeButton.isHidden = false
scheduleTimeTitleLabel.isHidden = true
scheduleTimeLabel.isHidden = true
scheduleCategoryLabel.isHidden = true
scheduleCategoryChip.isHidden = true

availableLabel.snp.remakeConstraints {
$0.top.equalTo(scheduleDurationLabel.snp.bottom).offset(16)
$0.leading.equalTo(scheduleDurationLabel)
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remakeConstraint는 기존 제약을 모두 삭제하고 다시 만드는 메서드라서,
의도하신 방향과 부합한지 다시 한번 체크부탁드려요!
참고용으로, 필요한 제약사항만 업데이트 하는경우 updateConstraints도 있습니다!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오오 update가 나을거같네요

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그냥 setUpLayout에서 삼항연산자 썼습니다 ㅋㅋ


case .confirmed:
scheduleDurationLabel.isHidden = true
likeButton.isHidden = true
scheduleTimeTitleLabel.isHidden = false
scheduleTimeLabel.isHidden = false
scheduleCategoryLabel.isHidden = false
scheduleCategoryChip.isHidden = false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

여기 상태관리는 함수 하나로 통일해서 좋을 것 같아요
파라미터로 Bool값 받아서, 재사용 하면 보기 좋을 듯 !!


availableLabel.snp.remakeConstraints {
$0.top.equalTo(scheduleCategoryLabel.snp.bottom).offset(26)
$0.leading.equalTo(scheduleTimeTitleLabel)
}
}
}
}

// MARK: - 셀 좌측 정렬
final class LeftAlignedFlowLayout: UICollectionViewFlowLayout {
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
guard let attributes = super.layoutAttributesForElements(in: rect) else { return nil }

var rowAttributes: [UICollectionViewLayoutAttributes] = []
var previousY: CGFloat = -1
var rowStartX: CGFloat = 0

for attribute in attributes {
let frame = attribute.frame
let currentY = frame.origin.y

if currentY != previousY {
alignRow(rowAttributes, rowStartX: rowStartX)
rowAttributes.removeAll()
rowStartX = sectionInset.left
}
rowAttributes.append(attribute)
previousY = currentY
}
alignRow(rowAttributes, rowStartX: rowStartX) // 마지막 줄 정렬
return attributes
}

func alignRow(_ rowAttributes: [UICollectionViewLayoutAttributes], rowStartX: CGFloat) {
guard !rowAttributes.isEmpty else { return }

var currentX = rowStartX
for attribute in rowAttributes {
attribute.frame.origin.x = currentX
currentX += attribute.frame.width + minimumInteritemSpacing
}
}
}
3 changes: 3 additions & 0 deletions Noostak_iOS/Noostak_iOS/Global/Utils/NSTDateUtility.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public extension NSTDateUtility {
case HHmm
case EEMMdd
case MMddEE
case MMddHHmm

var format: String {
switch self {
Expand All @@ -77,6 +78,8 @@ public extension NSTDateUtility {
return "EE\nMM/dd"
case .MMddEE:
return "M월 d일 (EE)"
case .MMddHHmm:
return "MM/dd HH:mm"
Comment on lines +81 to +82
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

잘 쓰는 구만

}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ let mockInProgressData: [ExtendedSchedule] = [
date: "2024-09-05T10:00:00",
startTime: "2024-09-05T10:00:00",
endTime: "2024-09-05T18:00:00",
myInfo: .available,
availableMembers: [],
unavailableMembers: [],
groupMemberCount: 24,
Expand All @@ -86,6 +87,7 @@ let mockInProgressData: [ExtendedSchedule] = [
date: "2024-09-05T10:00:00",
startTime: "2024-09-05T10:00:00",
endTime: "2024-09-05T18:00:00",
myInfo: .available,
availableMembers: [],
unavailableMembers: [],
groupMemberCount: 23,
Expand All @@ -99,6 +101,7 @@ let mockInProgressData: [ExtendedSchedule] = [
date: "2024-09-05T10:00:00",
startTime: "2024-09-05T10:00:00",
endTime: "2024-09-05T18:00:00",
myInfo: .available,
availableMembers: [],
unavailableMembers: [],
groupMemberCount: 24,
Expand All @@ -114,6 +117,7 @@ let mockConfirmedData: [ExtendedSchedule] = [
date: "2024-09-05T10:00:00",
startTime: "2024-09-05T10:00:00",
endTime: "2024-09-05T18:00:00",
myInfo: .available,
availableMembers: [],
unavailableMembers: [],
groupMemberCount: 24,
Expand All @@ -127,6 +131,7 @@ let mockConfirmedData: [ExtendedSchedule] = [
date: "2024-09-05T10:00:00",
startTime: "2024-09-05T10:00:00",
endTime: "2024-09-05T18:00:00",
myInfo: .available,
availableMembers: [],
unavailableMembers: [],
groupMemberCount: 23,
Expand Down
Loading