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

기록 수정 기능 구현 #36

Merged
merged 56 commits into from
Aug 25, 2022
Merged

기록 수정 기능 구현 #36

merged 56 commits into from
Aug 25, 2022

Conversation

sujeong000
Copy link
Contributor

작업 내용

  • 기록 수정 기능 구현
    • 과목명 수정 기능 구현
    • 각 기록 시작/종료 시각 편집 기능 구현
  • 기록 추가 기능 구현
    • 새로운 과목의 기록 생성 기능 구현

동작 화면

인터렉션 뷰 전환 기록 수정 기록 추가
Simulator Screen Recording - iPhone 13 - 2022-08-16 at 16 44 42 Simulator Screen Recording - iPhone 13 - 2022-08-16 at 16 48 32 Simulator Screen Recording - iPhone 13 - 2022-08-16 at 16 53 25
뒤로가기 SAVE
Simulator Screen Recording - iPhone 13 - 2022-08-16 at 16 55 22 Simulator Screen Recording - iPhone 13 - 2022-08-16 at 16 57 55

고민과 해결 및 리뷰 포인트

  • 편집 상태 관리
    • 기존 기록 수정 중인지, 새 기록 추가 중인지, 아무 편집도 하고 있지 않은 상태인지를 뷰모델의 mode라는 프로퍼티에 저장하여 관리하였다.
    • mode 변경에 따라 인터렉션 뷰를 전환하고, 컬렉션 뷰의 빨간색 테두리 하이라이트를 표시/제거하였다.
    enum ModifyMode {
        case existingTask
        case newTask
        case none
    }

  • 인터렉션 뷰를 위한 모델 분리
    • TaskModifyInteractionView
      • 변경 사항이 인터렉션 뷰와 상단 3가지 그래프에 모두 즉시 반영된다.
    • TaskCreateInteractionView
      • 변경 사항이 인터렉션 뷰에만 반영되고, ADD 버튼을 터치해야 그래프에도 반영된다.
    • 두 기능의 차이를 구현하기 위하여, 인터렉션 뷰를 위한 모델을 따로 운영하였다.
      final class ModifyRecordVM {
          // 그래프는 currentDaily와 tasks에 따라 업데이트 됨
          @Published private(set) var currentDaily: Daily
          @Published private(set) var tasks: [TaskInfo] = []
      
          // 인터렉션 뷰에만 보여지는 내용
          @Published var selectedTask: String?
          @Published var selectedTaskHistorys: [TaskHistory]?
      }
    • 인터렉션 뷰의 내용을 그래프에도 반영해야할 때에는 아래와 같이 currentDaily를 업데이트하여 그래프가 업데이트 되도록 하였다.
      /// 선택한 과목의 index번 히스토리를 newHistory로 변경
      func modifyHistory(at index: Int, to newHistory: TaskHistory) {
          guard self.mode != .none,
                self.selectedTaskHistorys?[index] != newHistory else { return }
      
          self.selectedTaskHistorys?[index] = newHistory
          self.isModified = true
      
          // 기록 추가 모드가 아닌 경우, 그래프에도 반영하기 위해 Daily 업데이트
          if self.mode == .existingTask {
              self.updateDailysTaskHistory()
          }
      }

  • tasks와 timelineVM의 업데이트
    • 뷰모델 내 tasks와 timelineVM은 Daily에 종속적이다.
    • Daily가 변경될 때마다 다시 계산되어야 한다.
    • tasks와 timelineVM의 업데이트 방식으로 다음과 같은 방식을 고민하였는데,
      • 방법 1. daily 업데이트 때마다 메소드 호출로 수동 업데이트
      • 방법 2. didSet
      • 방법 3. Combine-sink
    • 수동 메소드 호출 방식은 업데이트가 누락될 가능성이 있어 배제하였고, currentDaily가 이미 @Puslihed 프로퍼티로 구현되어 있기 때문에 Combine-sink 방식을 선택하였다.
    final class ModifyRecordVM {
        @Published private(set) var currentDaily: Daily
        @Published private(set) var tasks: [TaskInfo] = []
    
        let timelineVM: TimelineVM
        private var cancellables: Set<AnyCancellable> = []
    
        init(daily: Daily) {
            ...
            
            self.$currentDaily
                .receive(on: DispatchQueue.main)
                .sink { [weak self] daily in
                    self?.timelineVM.update(daily: daily)
                    self?.tasks = daily.tasks.sorted(by: { $0.value > $1.value })
                        .map { TaskInfo(taskName: $0.key, taskTime: $0.value) }
                }
                .store(in: &self.cancellables)
        }
    }

  • 인터렉션 뷰의 전환을 어떻게 구현할 것인가?
    • 방법 1. removeFromSuperView & addSubview
      • 인터렉션 뷰 변경이 필요할 때마다 기존 뷰를 프레임뷰에서 제거하고 새 뷰를 추가한다.
      • superview에서 remove하는 경우 constraint도 사라지기 때문에 addSubview할 때마다 constraint 작업 필요하다.
    • 방법 2. isHidden
      • viewDidLoad() 시점에 모두 addSubview()하고, isHidden 프로퍼티로 보이기/가리기 처리한다.
    • 방법 1의 cost가 훨씬 더 크고, 각 인터렉션 뷰의 사이즈와 위치가 고정되어 있기 때문에 isHidden을 사용하는 방법을 선택하였다.

  • DatePicker와 EditHistory 사이 데이터 전달 방식
    • EditHistoryView에서 시작/종료 시각 버튼을 터치하면 popover 방식으로 DatePicker가 나타난다.
    • 이때 DatePicker의 값을 EditHistroyVC로 가저오는 방식에 대해 고민하였다.(=EditDateVC에서 EditHistoryVC로)
      • 방법 1. Delegate
      • 방법 2. 클로저 전달
    • Delegate 패턴은 데이터를 전달해야 하는 상황이 다양할 때 적합한 방법이라고 생각하였다. (프로토콜 내 다양한 함수 정의)
    • 이 상황의 경우 데이터를 전달해야 하는 상황이 DatePicker의 date값이 변경되었을 때 뿐이므로 단순 클로저 전달로 구현하였다.

  • TaskModifyInteractionView와 TaskCreateInteractionView 사이 코드 중복 문제
    • 두 뷰는 과목 편집 버튼과 OK/ADD 버튼을 제외하고는 모두 동일 subview를 가지고 있다.
    • 두 뷰 사이 코드 중복을 줄이기 위해 부모 뷰인 TaskInteractionView를 정의한 뒤 상속하였다.
    • 각 뷰에서는 과목 편집 버튼과 OK/ADD 버튼을 수정하여 사용하도록 하였다.
    class TaskCreateInteractionView: TaskInteractionView {
        convenience init() {
            self.init(frame: CGRect())
            self.configureFinishButton(title: "ADD")
            self.disableFinishButton()
            self.configureEditTaskButton(image: UIImage(systemName: "plus.circle"))
        }
    }
    
    class TaskModifyInteractionView: TaskInteractionView {
        convenience init() {
            self.init(frame: CGRect())
            self.configureFinishButton(title: "OK")
            self.enableFinishButton()
            self.configureEditTaskButton(image: UIImage(systemName: "pencil.circle"))
        }
    }

레퍼런스

없음

minsangKang and others added 30 commits August 13, 2022 01:41
@sujeong000 sujeong000 added the feature 기능 추가/변경/삭제 label Aug 16, 2022
@sujeong000 sujeong000 self-assigned this Aug 16, 2022
@minsangKang minsangKang self-requested a review August 16, 2022 13:16
Copy link
Member

@minsangKang minsangKang left a comment

Choose a reason for hiding this comment

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

디자인과 요구사항만으로 개발하시는게 어려우셨을텐데 고생많으셨습니다!
원하는 느낌대로 잘 구현해주셔서 감사합니다 :)

코드리뷰를 하기 전에 앞서 직접 돌려보면서 발견한 기능 관련 리뷰를 먼저 남길께요🙌
PR 반영할 수정 및 개선사항들 내역은 현재 PR 에 다음주까지 반영해주시면 rebase 하겠습니다.

로직 수정 필요내용, 필요한 추가기능들 및 코드리뷰 내역은 해당 PR 이 아닌 추가 PR에서 반영하는 식으로 진행할께요
이부분의 경우 수정님 개발범위를 벗어나겠다 싶은 경우 제가 이어서 작업합니다.

@minsangKang
Copy link
Member

PR 반영할 수정 및 개선사항들

  • Light & DarkMode 전환시 그림자, color 및 graph update 반영
  • 과목선택시 color 반영
  • 기록수정창: 시간선택시 초단위 00 으로 설정
  • Historys: 우측 누적시간 hour 2자리 -> 1자리로 수정
  • 기록수정창: 종료 시각 디폴트값: 시작 시각 설정값으로 수정
  • 기록수정 후 DailysVC 표시시 reload 후 변경된 graph 표시

@minsangKang
Copy link
Member

로직 수정 필요내용

  • 현재날짜의 기록 수정시 RecordTimes 값 동기화 로직 구현 (기록수정 후 Timer, Stopwatch 표시 기록이 다른 상황)
  • 기록 추가시 동일기록명이 있는 경우 로직 (동일기록이 존재합니다 popup 표시)
  • Historys 기록 추가시 겹치는 시간이 존재하는 경우 로직 (기록이 겹칩니다 시간대를 확인해주세요)

@minsangKang
Copy link
Member

필요한 추가기능들

  • Historys: 기록삭제 기능 구현 (UI적으로 고민 필요)
  • 기록수정창: 과거형식의 기록 선택시 팝업알림 및 Historys 기록추가 안내 / OK 대신 Next 표시 / 모든과목 수정이 완료된 경우 OK로 표시 / OK 클릭시 daily 생성하여 표시
  • DailysVC 에서 Daily 값이 nil 일 경우 Create 로 변경 / popup 알림으로 업데이트 예정 표시

Copy link
Member

@minsangKang minsangKang left a comment

Choose a reason for hiding this comment

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

수고하셨습니다!~~!!!!!!~😵


@IBAction func modifyRecord(_ sender: Any) {
guard let viewModel = viewModel,
let targetDaily = viewModel.currentDaily else { return }
Copy link
Member

Choose a reason for hiding this comment

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

기록이 존재하지 않는 경우 popup 알림 또는 Create 로 수정된 로직 추가 필요

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Alert 팝업으로 구현 완료했습니다.

Comment on lines 15 to 16
var date: Date = Date()
var changeHandler: DateChangeHandler?
Copy link
Member

Choose a reason for hiding this comment

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

public func configure(date: Date, handler: DateChangeHandler) 식의 함수로 묶는것도 좋을 것 같아요

Copy link
Contributor Author

Choose a reason for hiding this comment

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

반영 완료했습니다!

Comment on lines 13 to 19
class EditHistoryVC: UIViewController {
@IBOutlet weak var startTimeButton: UIButton!
@IBOutlet weak var endTimeButton: UIButton!
@IBOutlet weak var intervalLabel: UILabel!

@Published var history: TaskHistory = TaskHistory(startDate: Date(), endDate: Date())
private var cancellables = Set<AnyCancellable>()
Copy link
Member

Choose a reason for hiding this comment

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

외부에서 해당 VC 의 color index 값을 넣어줘야 할 것 같아요 해당 property 가 필요할 것 같아요

Copy link
Contributor Author

Choose a reason for hiding this comment

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

반영했습니다:) 각 과목 컬러 반영 기능 구현 완료했습니다.

Comment on lines +38 to +40
self?.timelineVM.update(daily: daily)
self?.tasks = daily.tasks.sorted(by: { $0.value > $1.value })
.map { TaskInfo(taskName: $0.key, taskTime: $0.value) }
Copy link
Member

Choose a reason for hiding this comment

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

timelineVM 의 updateColor 관련 로직이 필요

Copy link
Contributor Author

Choose a reason for hiding this comment

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

반영 완료!

// TODO: 새벽 12시-4시는 다음 날짜로 처리
// TODO: 시작시각 > 종료시각인 경우 예외처리
self.selectedTaskHistorys?.append(history)
self.selectedTaskHistorys?.sort(by: { $0.startDate < $1.startDate })
Copy link
Member

Choose a reason for hiding this comment

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

AM 05:00:00 ~ AM 04:59:59 순으로 sort 로직으로 수정 필요

Copy link
Contributor Author

Choose a reason for hiding this comment

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

수정 완료했습니다.

}

extension HistoryCell {
func configure(with taskHistory: TaskHistory?) {
Copy link
Member

Choose a reason for hiding this comment

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

index 값을 받아와 color 를 계산하는 로직이 필요

Copy link
Contributor Author

Choose a reason for hiding this comment

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

반영 완료!

Comment on lines 303 to 310
private func emptyInteractionFrameView() {
self.taskInteractionFrameView.subviews.forEach { $0.isHidden = true }
}

private func showTaskInteractionViewPlaceholder() {
self.emptyInteractionFrameView()
self.taskInteratcionViewPlaceholder.isHidden = false
}
Copy link
Member

Choose a reason for hiding this comment

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

추후 hidden -> alpha 로직으로 수정 필요

Copy link
Contributor Author

Choose a reason for hiding this comment

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

수정했습니다!

Comment on lines 450 to 455
if taskInfo.taskName == self.viewModel?.selectedTask {
cell.layer.borderWidth = 2
cell.layer.borderColor = UIColor.red.cgColor
} else {
cell.layer.borderWidth = 0
}
Copy link
Member

Choose a reason for hiding this comment

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

StandardDailyTaskCell 내 function 으로 수정

Copy link
Contributor Author

Choose a reason for hiding this comment

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

수정 완료했습니다.

graph == .standardDailyGraphView,
let taskName = self.viewModel?.tasks[indexPath.row].taskName else { return }
// 그래프에서 Task 선택 -> 기존 Task 편집 모드로 전환
self.viewModel?.changeToExistingTaskMode(task: taskName)
Copy link
Member

Choose a reason for hiding this comment

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

viewModel.changeToExistingTaskMode 내에서 과거형식 로직 확인 필요

extension ModifyRecordVC {
@objc func saveButtonTapped() {
self.viewModel?.save()
self.showOKAlert(title: "저장 완료", message: "변경 사항이 저장되었습니다") { [weak self] in
Copy link
Member

Choose a reason for hiding this comment

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

localized 가 필요

Copy link
Contributor Author

Choose a reason for hiding this comment

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

localized 전부 반영했습니다:)

@sujeong000 sujeong000 changed the title Feature/modify record 기록 수정 기능 구현 Aug 22, 2022
Copy link
Member

@minsangKang minsangKang left a comment

Choose a reason for hiding this comment

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

수정사항이 많았는데 많이 도와주셔서 감사합니다ㅠㅠ
그간 정말 힘이되었어요 고생정말 많으셨습니다!!
개발하신것들이 취업때 도움이 되었으면 좋겠네요ㅎㅎ
🫵👍😎

Comment on lines +122 to +134
self.selectedTaskHistorys?.sort(by: {
var lhsHour = $0.startDate.hour
var rhsHour = $1.startDate.hour

if lhsHour < 5 { lhsHour += 24 }
if rhsHour < 5 { rhsHour += 24 }

if lhsHour != rhsHour {
return lhsHour < rhsHour
} else {
return $0.startDate < $1.startDate
}
})
Copy link
Member

Choose a reason for hiding this comment

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

💯 👍

@minsangKang minsangKang merged commit c75e805 into dev Aug 25, 2022
@minsangKang minsangKang deleted the feature/modifyRecord branch August 25, 2022 07:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature 기능 추가/변경/삭제
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants