From d8669fd8e0d8fda405a43fb7d3e7ecc4d281d5b8 Mon Sep 17 00:00:00 2001 From: Zion Date: Tue, 3 Oct 2023 14:21:39 +0900 Subject: [PATCH 01/28] =?UTF-8?q?feat:=20Cell,=20TaskViewController=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EB=B0=8F=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=EA=B8=B0=EC=B4=88=20=EA=B5=AC=EC=84=B1=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProjectManager.xcodeproj/project.pbxproj | 76 +++++++++++++++-- .../{ => App}/AppDelegate.swift | 5 -- .../{ => App}/SceneDelegate.swift | 5 +- .../Controller/MainViewController.swift | 29 +++++++ .../Controller/TaskViewController.swift | 84 +++++++++++++++++++ .../AccentColor.colorset/Contents.json | 0 .../AppIcon.appiconset/Contents.json | 0 .../Assets.xcassets/Contents.json | 0 .../Util/Protocol/ReuseIdentifiable.swift | 16 ++++ .../Base.lproj/LaunchScreen.storyboard | 0 .../View/ListCollectionViewCell.swift | 79 +++++++++++++++++ .../ProjectManager/ViewController.swift | 19 ----- 12 files changed, 278 insertions(+), 35 deletions(-) rename ProjectManager/ProjectManager/{ => App}/AppDelegate.swift (99%) rename ProjectManager/ProjectManager/{ => App}/SceneDelegate.swift (94%) create mode 100644 ProjectManager/ProjectManager/Controller/MainViewController.swift create mode 100644 ProjectManager/ProjectManager/Controller/TaskViewController.swift rename ProjectManager/ProjectManager/{ => Resource}/Assets.xcassets/AccentColor.colorset/Contents.json (100%) rename ProjectManager/ProjectManager/{ => Resource}/Assets.xcassets/AppIcon.appiconset/Contents.json (100%) rename ProjectManager/ProjectManager/{ => Resource}/Assets.xcassets/Contents.json (100%) create mode 100644 ProjectManager/ProjectManager/Util/Protocol/ReuseIdentifiable.swift rename ProjectManager/ProjectManager/{ => View}/Base.lproj/LaunchScreen.storyboard (100%) create mode 100644 ProjectManager/ProjectManager/View/ListCollectionViewCell.swift delete mode 100644 ProjectManager/ProjectManager/ViewController.swift diff --git a/ProjectManager/ProjectManager.xcodeproj/project.pbxproj b/ProjectManager/ProjectManager.xcodeproj/project.pbxproj index fa92d9520..0dd4cb70d 100644 --- a/ProjectManager/ProjectManager.xcodeproj/project.pbxproj +++ b/ProjectManager/ProjectManager.xcodeproj/project.pbxproj @@ -7,18 +7,24 @@ objects = { /* Begin PBXBuildFile section */ + 5B1666B32ACBD8DB00478F1D /* TaskViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1666B22ACBD8DB00478F1D /* TaskViewController.swift */; }; + 5B9D60322ACBD4E9004905F6 /* ListCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B9D60312ACBD4E9004905F6 /* ListCollectionViewCell.swift */; }; + 5B9D60362ACBD531004905F6 /* ReuseIdentifiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B9D60352ACBD531004905F6 /* ReuseIdentifiable.swift */; }; C7431F0625F51E1D0094C4CF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7431F0525F51E1D0094C4CF /* AppDelegate.swift */; }; C7431F0825F51E1D0094C4CF /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7431F0725F51E1D0094C4CF /* SceneDelegate.swift */; }; - C7431F0A25F51E1D0094C4CF /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7431F0925F51E1D0094C4CF /* ViewController.swift */; }; + C7431F0A25F51E1D0094C4CF /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7431F0925F51E1D0094C4CF /* MainViewController.swift */; }; C7431F0F25F51E1E0094C4CF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C7431F0E25F51E1E0094C4CF /* Assets.xcassets */; }; C7431F1225F51E1E0094C4CF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C7431F1025F51E1E0094C4CF /* LaunchScreen.storyboard */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 5B1666B22ACBD8DB00478F1D /* TaskViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskViewController.swift; sourceTree = ""; }; + 5B9D60312ACBD4E9004905F6 /* ListCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListCollectionViewCell.swift; sourceTree = ""; }; + 5B9D60352ACBD531004905F6 /* ReuseIdentifiable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReuseIdentifiable.swift; sourceTree = ""; }; C7431F0225F51E1D0094C4CF /* ProjectManager.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ProjectManager.app; sourceTree = BUILT_PRODUCTS_DIR; }; C7431F0525F51E1D0094C4CF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; C7431F0725F51E1D0094C4CF /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; - C7431F0925F51E1D0094C4CF /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + C7431F0925F51E1D0094C4CF /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; C7431F0E25F51E1E0094C4CF /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; C7431F1125F51E1E0094C4CF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; C7431F1325F51E1E0094C4CF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -35,6 +41,57 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 5B9D602D2ACBD462004905F6 /* App */ = { + isa = PBXGroup; + children = ( + C7431F0525F51E1D0094C4CF /* AppDelegate.swift */, + C7431F0725F51E1D0094C4CF /* SceneDelegate.swift */, + ); + path = App; + sourceTree = ""; + }; + 5B9D602E2ACBD464004905F6 /* Controller */ = { + isa = PBXGroup; + children = ( + C7431F0925F51E1D0094C4CF /* MainViewController.swift */, + 5B1666B22ACBD8DB00478F1D /* TaskViewController.swift */, + ); + path = Controller; + sourceTree = ""; + }; + 5B9D602F2ACBD467004905F6 /* Resource */ = { + isa = PBXGroup; + children = ( + C7431F0E25F51E1E0094C4CF /* Assets.xcassets */, + ); + path = Resource; + sourceTree = ""; + }; + 5B9D60302ACBD469004905F6 /* View */ = { + isa = PBXGroup; + children = ( + C7431F1025F51E1E0094C4CF /* LaunchScreen.storyboard */, + 5B9D60312ACBD4E9004905F6 /* ListCollectionViewCell.swift */, + ); + path = View; + sourceTree = ""; + }; + 5B9D60332ACBD4F6004905F6 /* Util */ = { + isa = PBXGroup; + children = ( + 5B9D60342ACBD504004905F6 /* Protocol */, + ); + path = Util; + sourceTree = ""; + }; + 5B9D60342ACBD504004905F6 /* Protocol */ = { + isa = PBXGroup; + children = ( + 5B9D60352ACBD531004905F6 /* ReuseIdentifiable.swift */, + ); + path = Protocol; + sourceTree = ""; + }; C7431EF925F51E1D0094C4CF = { isa = PBXGroup; children = ( @@ -54,12 +111,12 @@ C7431F0425F51E1D0094C4CF /* ProjectManager */ = { isa = PBXGroup; children = ( - C7431F0525F51E1D0094C4CF /* AppDelegate.swift */, - C7431F0725F51E1D0094C4CF /* SceneDelegate.swift */, - C7431F0925F51E1D0094C4CF /* ViewController.swift */, - C7431F0E25F51E1E0094C4CF /* Assets.xcassets */, - C7431F1025F51E1E0094C4CF /* LaunchScreen.storyboard */, C7431F1325F51E1E0094C4CF /* Info.plist */, + 5B9D60332ACBD4F6004905F6 /* Util */, + 5B9D60302ACBD469004905F6 /* View */, + 5B9D602F2ACBD467004905F6 /* Resource */, + 5B9D602E2ACBD464004905F6 /* Controller */, + 5B9D602D2ACBD462004905F6 /* App */, ); path = ProjectManager; sourceTree = ""; @@ -133,9 +190,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - C7431F0A25F51E1D0094C4CF /* ViewController.swift in Sources */, + C7431F0A25F51E1D0094C4CF /* MainViewController.swift in Sources */, + 5B9D60362ACBD531004905F6 /* ReuseIdentifiable.swift in Sources */, C7431F0625F51E1D0094C4CF /* AppDelegate.swift in Sources */, + 5B9D60322ACBD4E9004905F6 /* ListCollectionViewCell.swift in Sources */, C7431F0825F51E1D0094C4CF /* SceneDelegate.swift in Sources */, + 5B1666B32ACBD8DB00478F1D /* TaskViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ProjectManager/ProjectManager/AppDelegate.swift b/ProjectManager/ProjectManager/App/AppDelegate.swift similarity index 99% rename from ProjectManager/ProjectManager/AppDelegate.swift rename to ProjectManager/ProjectManager/App/AppDelegate.swift index 8df091a06..3ca2c9ebd 100644 --- a/ProjectManager/ProjectManager/AppDelegate.swift +++ b/ProjectManager/ProjectManager/App/AppDelegate.swift @@ -8,9 +8,6 @@ import UIKit @main class AppDelegate: UIResponder, UIApplicationDelegate { - - - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true @@ -29,7 +26,5 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. // Use this method to release any resources that were specific to the discarded scenes, as they will not return. } - - } diff --git a/ProjectManager/ProjectManager/SceneDelegate.swift b/ProjectManager/ProjectManager/App/SceneDelegate.swift similarity index 94% rename from ProjectManager/ProjectManager/SceneDelegate.swift rename to ProjectManager/ProjectManager/App/SceneDelegate.swift index bdc69c139..23ffe7900 100644 --- a/ProjectManager/ProjectManager/SceneDelegate.swift +++ b/ProjectManager/ProjectManager/App/SceneDelegate.swift @@ -9,13 +9,12 @@ import UIKit class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? - func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } - let viewController = ViewController() + let mainViewController = MainViewController() window = UIWindow(windowScene: windowScene) - window?.rootViewController = viewController + window?.rootViewController = mainViewController window?.makeKeyAndVisible() } diff --git a/ProjectManager/ProjectManager/Controller/MainViewController.swift b/ProjectManager/ProjectManager/Controller/MainViewController.swift new file mode 100644 index 000000000..9a6bcf497 --- /dev/null +++ b/ProjectManager/ProjectManager/Controller/MainViewController.swift @@ -0,0 +1,29 @@ +// +// ProjectManager - MainViewController.swift +// Created by yagom. +// Copyright © yagom. All rights reserved. +// + +import UIKit + +final class MainViewController: UIViewController { + override func viewDidLoad() { + super.viewDidLoad() + + configureUI() + setUpConstraints() + setUpViewController() + } + + private func configureUI() { + + } + + private func setUpConstraints() { + + } + + private func setUpViewController() { + view.backgroundColor = .systemBackground + } +} diff --git a/ProjectManager/ProjectManager/Controller/TaskViewController.swift b/ProjectManager/ProjectManager/Controller/TaskViewController.swift new file mode 100644 index 000000000..156d592e6 --- /dev/null +++ b/ProjectManager/ProjectManager/Controller/TaskViewController.swift @@ -0,0 +1,84 @@ +// +// TaskViewController.swift +// ProjectManager +// +// Created by Hyungmin Lee on 2023/10/03. +// + +import UIKit + +final class TaskViewController: UIViewController { + private let stackView: UIStackView = { + let stackView = UIStackView() + + stackView.axis = .vertical + stackView.spacing = 10 + stackView.translatesAutoresizingMaskIntoConstraints = false + return stackView + }() + + private let titleTextField: UITextField = { + let textField = UITextField() + + textField.placeholder = "Title" + return textField + }() + + private let datePicker: UIDatePicker = { + let datePicker = UIDatePicker() + + datePicker.preferredDatePickerStyle = .wheels + return datePicker + }() + + private lazy var descriptionTextView: UITextView = { + let textView = UITextView() + + textView.delegate = self + return textView + }() + + override func viewDidLoad() { + super.viewDidLoad() + + configureUI() + setUpConstraints() + setUpViewController() + } + + private func configureUI() { + [titleTextField, datePicker, descriptionTextView].forEach { + stackView.addArrangedSubview($0) + } + + view.addSubview(stackView) + } + + private func setUpConstraints() { + NSLayoutConstraint.activate([ + stackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10), + stackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10), + stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), + stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), + ]) + + descriptionTextView.setContentHuggingPriority(.init(1), for: .vertical) + } + + private func setUpViewController() { + navigationItem.title = "TODO" + navigationItem.leftBarButtonItem = .init(systemItem: .cancel) + navigationItem.rightBarButtonItem = .init(systemItem: .done) + } +} + +// MARK: - TextView Delegate +extension TaskViewController: UITextViewDelegate { + func textViewDidBeginEditing(_ textView: UITextView) { + + } + + func textViewDidEndEditing(_ textView: UITextView) { + + } +} diff --git a/ProjectManager/ProjectManager/Assets.xcassets/AccentColor.colorset/Contents.json b/ProjectManager/ProjectManager/Resource/Assets.xcassets/AccentColor.colorset/Contents.json similarity index 100% rename from ProjectManager/ProjectManager/Assets.xcassets/AccentColor.colorset/Contents.json rename to ProjectManager/ProjectManager/Resource/Assets.xcassets/AccentColor.colorset/Contents.json diff --git a/ProjectManager/ProjectManager/Assets.xcassets/AppIcon.appiconset/Contents.json b/ProjectManager/ProjectManager/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from ProjectManager/ProjectManager/Assets.xcassets/AppIcon.appiconset/Contents.json rename to ProjectManager/ProjectManager/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/ProjectManager/ProjectManager/Assets.xcassets/Contents.json b/ProjectManager/ProjectManager/Resource/Assets.xcassets/Contents.json similarity index 100% rename from ProjectManager/ProjectManager/Assets.xcassets/Contents.json rename to ProjectManager/ProjectManager/Resource/Assets.xcassets/Contents.json diff --git a/ProjectManager/ProjectManager/Util/Protocol/ReuseIdentifiable.swift b/ProjectManager/ProjectManager/Util/Protocol/ReuseIdentifiable.swift new file mode 100644 index 000000000..9606f2ffc --- /dev/null +++ b/ProjectManager/ProjectManager/Util/Protocol/ReuseIdentifiable.swift @@ -0,0 +1,16 @@ +// +// ReuseIdentifiable.swift +// ProjectManager +// +// Created by Hyungmin Lee on 2023/10/03. +// + +protocol ReuseIdentifiable { + static var reuseIdentifier: String { get } +} + +extension ReuseIdentifiable { + static var reuseIdentifier: String { + return String(describing: self) + } +} diff --git a/ProjectManager/ProjectManager/Base.lproj/LaunchScreen.storyboard b/ProjectManager/ProjectManager/View/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from ProjectManager/ProjectManager/Base.lproj/LaunchScreen.storyboard rename to ProjectManager/ProjectManager/View/Base.lproj/LaunchScreen.storyboard diff --git a/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift b/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift new file mode 100644 index 000000000..73a13ad27 --- /dev/null +++ b/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift @@ -0,0 +1,79 @@ +// +// ListCollectionViewCell.swift +// ProjectManager +// +// Created by Hyungmin Lee on 2023/10/03. +// + +import UIKit + +final class ListCollectionViewCell: UICollectionViewListCell, ReuseIdentifiable { + private let stackView: UIStackView = { + let stackView = UIStackView() + + stackView.axis = .vertical + stackView.spacing = 10 + stackView.translatesAutoresizingMaskIntoConstraints = false + return stackView + }() + + private let titleLabel: UILabel = { + let label = UILabel() + + label.font = .systemFont(ofSize: 15) + label.textColor = .black + return label + }() + + private let descriptionLabel: UILabel = { + let label = UILabel() + + label.font = .systemFont(ofSize: 13) + label.textColor = .lightGray + return label + }() + + private let deadlineLabel: UILabel = { + let label = UILabel() + + label.font = .systemFont(ofSize: 10) + label.textColor = .black + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + configureUI() + setUpConstraints() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func setUpContents(title: String, description: String, deadline: String) { + titleLabel.text = title + descriptionLabel.text = description + deadlineLabel.text = deadline + } + + private func configureUI() { + [titleLabel, descriptionLabel, deadlineLabel].forEach { + stackView.addArrangedSubview($0) + } + + contentView.addSubview(stackView) + } + + private func setUpConstraints() { + NSLayoutConstraint.activate([ + stackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10), + stackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10), + stackView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 5), + stackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -5) + ]) + + descriptionLabel.setContentHuggingPriority(.init(1), for: .vertical) + } +} diff --git a/ProjectManager/ProjectManager/ViewController.swift b/ProjectManager/ProjectManager/ViewController.swift deleted file mode 100644 index 9de3d4258..000000000 --- a/ProjectManager/ProjectManager/ViewController.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// ProjectManager - ViewController.swift -// Created by yagom. -// Copyright © yagom. All rights reserved. -// - -import UIKit - -final class ViewController: UIViewController { - override func viewDidLoad() { - super.viewDidLoad() - - setUpViewController() - } - - func setUpViewController() { - view.backgroundColor = .systemBackground - } -} From 9c67f84090107dd84348e7161e55a63eb1e1d402 Mon Sep 17 00:00:00 2001 From: karen Date: Tue, 3 Oct 2023 15:16:14 +0900 Subject: [PATCH 02/28] =?UTF-8?q?feat:=20ListViewController=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=95=84=EC=9B=83=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProjectManager.xcodeproj/project.pbxproj | 4 ++ .../Controller/ListViewController.swift | 55 +++++++++++++++++++ 2 files changed, 59 insertions(+) create mode 100644 ProjectManager/ProjectManager/Controller/ListViewController.swift diff --git a/ProjectManager/ProjectManager.xcodeproj/project.pbxproj b/ProjectManager/ProjectManager.xcodeproj/project.pbxproj index 0dd4cb70d..280089d07 100644 --- a/ProjectManager/ProjectManager.xcodeproj/project.pbxproj +++ b/ProjectManager/ProjectManager.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 5B1666B32ACBD8DB00478F1D /* TaskViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1666B22ACBD8DB00478F1D /* TaskViewController.swift */; }; 5B9D60322ACBD4E9004905F6 /* ListCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B9D60312ACBD4E9004905F6 /* ListCollectionViewCell.swift */; }; 5B9D60362ACBD531004905F6 /* ReuseIdentifiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B9D60352ACBD531004905F6 /* ReuseIdentifiable.swift */; }; + 9C4F6E812ACBE29700FDC393 /* ListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C4F6E802ACBE29700FDC393 /* ListViewController.swift */; }; C7431F0625F51E1D0094C4CF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7431F0525F51E1D0094C4CF /* AppDelegate.swift */; }; C7431F0825F51E1D0094C4CF /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7431F0725F51E1D0094C4CF /* SceneDelegate.swift */; }; C7431F0A25F51E1D0094C4CF /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7431F0925F51E1D0094C4CF /* MainViewController.swift */; }; @@ -21,6 +22,7 @@ 5B1666B22ACBD8DB00478F1D /* TaskViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskViewController.swift; sourceTree = ""; }; 5B9D60312ACBD4E9004905F6 /* ListCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListCollectionViewCell.swift; sourceTree = ""; }; 5B9D60352ACBD531004905F6 /* ReuseIdentifiable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReuseIdentifiable.swift; sourceTree = ""; }; + 9C4F6E802ACBE29700FDC393 /* ListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListViewController.swift; sourceTree = ""; }; C7431F0225F51E1D0094C4CF /* ProjectManager.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ProjectManager.app; sourceTree = BUILT_PRODUCTS_DIR; }; C7431F0525F51E1D0094C4CF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; C7431F0725F51E1D0094C4CF /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -55,6 +57,7 @@ children = ( C7431F0925F51E1D0094C4CF /* MainViewController.swift */, 5B1666B22ACBD8DB00478F1D /* TaskViewController.swift */, + 9C4F6E802ACBE29700FDC393 /* ListViewController.swift */, ); path = Controller; sourceTree = ""; @@ -196,6 +199,7 @@ 5B9D60322ACBD4E9004905F6 /* ListCollectionViewCell.swift in Sources */, C7431F0825F51E1D0094C4CF /* SceneDelegate.swift in Sources */, 5B1666B32ACBD8DB00478F1D /* TaskViewController.swift in Sources */, + 9C4F6E812ACBE29700FDC393 /* ListViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ProjectManager/ProjectManager/Controller/ListViewController.swift b/ProjectManager/ProjectManager/Controller/ListViewController.swift new file mode 100644 index 000000000..61a5ba2a1 --- /dev/null +++ b/ProjectManager/ProjectManager/Controller/ListViewController.swift @@ -0,0 +1,55 @@ +// +// ListViewController.swift +// ProjectManager +// +// Created by karen on 2023/10/03. +// + +import UIKit + +final class ListViewController: UIViewController { + private lazy var collectionView: UICollectionView = { + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: listLayout) + + collectionView.translatesAutoresizingMaskIntoConstraints = false + return collectionView + }() + + private let listLayout: UICollectionViewCompositionalLayout = { + return UICollectionViewCompositionalLayout { sectionIndex, layoutEnvironment in + var listLayout = UICollectionLayoutListConfiguration(appearance: .grouped) + + listLayout.headerMode = .supplementary + + let section = NSCollectionLayoutSection.list(using: listLayout, layoutEnvironment: layoutEnvironment) + + section.interGroupSpacing = 10 + return section + } + }() + + override func viewDidLoad() { + super.viewDidLoad() + + configureUI() + setUpConstraints() + setUpViewController() + } + + private func configureUI() { + view.addSubview(collectionView) + } + + private func setUpConstraints() { + NSLayoutConstraint.activate([ + collectionView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), + collectionView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor), + collectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), + collectionView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor) + ]) + } + + private func setUpViewController() { + view.backgroundColor = .systemBackground + } +} From f803dddbadfb8367a5d0ba49b217531be486e216 Mon Sep 17 00:00:00 2001 From: Zion Date: Tue, 3 Oct 2023 15:42:21 +0900 Subject: [PATCH 03/28] =?UTF-8?q?feat:=20ListViewController=20DiffableData?= =?UTF-8?q?Source=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProjectManager.xcodeproj/project.pbxproj | 16 ++++ .../Controller/ListViewController.swift | 67 +++++++++++++++- .../Controller/TaskViewController.swift | 2 +- .../ProjectManager/Model/Task.swift | 15 ++++ .../Util/Protocol/ReuseIdentifiable.swift | 2 +- .../View/ListCollectionHeaderView.swift | 78 +++++++++++++++++++ .../View/ListCollectionViewCell.swift | 2 +- 7 files changed, 178 insertions(+), 4 deletions(-) create mode 100644 ProjectManager/ProjectManager/Model/Task.swift create mode 100644 ProjectManager/ProjectManager/View/ListCollectionHeaderView.swift diff --git a/ProjectManager/ProjectManager.xcodeproj/project.pbxproj b/ProjectManager/ProjectManager.xcodeproj/project.pbxproj index 280089d07..848945a4d 100644 --- a/ProjectManager/ProjectManager.xcodeproj/project.pbxproj +++ b/ProjectManager/ProjectManager.xcodeproj/project.pbxproj @@ -8,6 +8,8 @@ /* Begin PBXBuildFile section */ 5B1666B32ACBD8DB00478F1D /* TaskViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1666B22ACBD8DB00478F1D /* TaskViewController.swift */; }; + 5B1666B62ACBE99300478F1D /* Task.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1666B52ACBE99300478F1D /* Task.swift */; }; + 5B1666B82ACBEB7100478F1D /* ListCollectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1666B72ACBEB7100478F1D /* ListCollectionHeaderView.swift */; }; 5B9D60322ACBD4E9004905F6 /* ListCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B9D60312ACBD4E9004905F6 /* ListCollectionViewCell.swift */; }; 5B9D60362ACBD531004905F6 /* ReuseIdentifiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B9D60352ACBD531004905F6 /* ReuseIdentifiable.swift */; }; 9C4F6E812ACBE29700FDC393 /* ListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C4F6E802ACBE29700FDC393 /* ListViewController.swift */; }; @@ -20,6 +22,8 @@ /* Begin PBXFileReference section */ 5B1666B22ACBD8DB00478F1D /* TaskViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskViewController.swift; sourceTree = ""; }; + 5B1666B52ACBE99300478F1D /* Task.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Task.swift; sourceTree = ""; }; + 5B1666B72ACBEB7100478F1D /* ListCollectionHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListCollectionHeaderView.swift; sourceTree = ""; }; 5B9D60312ACBD4E9004905F6 /* ListCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListCollectionViewCell.swift; sourceTree = ""; }; 5B9D60352ACBD531004905F6 /* ReuseIdentifiable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReuseIdentifiable.swift; sourceTree = ""; }; 9C4F6E802ACBE29700FDC393 /* ListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListViewController.swift; sourceTree = ""; }; @@ -43,6 +47,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 5B1666B42ACBE98400478F1D /* Model */ = { + isa = PBXGroup; + children = ( + 5B1666B52ACBE99300478F1D /* Task.swift */, + ); + path = Model; + sourceTree = ""; + }; 5B9D602D2ACBD462004905F6 /* App */ = { isa = PBXGroup; children = ( @@ -75,6 +87,7 @@ children = ( C7431F1025F51E1E0094C4CF /* LaunchScreen.storyboard */, 5B9D60312ACBD4E9004905F6 /* ListCollectionViewCell.swift */, + 5B1666B72ACBEB7100478F1D /* ListCollectionHeaderView.swift */, ); path = View; sourceTree = ""; @@ -115,6 +128,7 @@ isa = PBXGroup; children = ( C7431F1325F51E1E0094C4CF /* Info.plist */, + 5B1666B42ACBE98400478F1D /* Model */, 5B9D60332ACBD4F6004905F6 /* Util */, 5B9D60302ACBD469004905F6 /* View */, 5B9D602F2ACBD467004905F6 /* Resource */, @@ -193,11 +207,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 5B1666B62ACBE99300478F1D /* Task.swift in Sources */, C7431F0A25F51E1D0094C4CF /* MainViewController.swift in Sources */, 5B9D60362ACBD531004905F6 /* ReuseIdentifiable.swift in Sources */, C7431F0625F51E1D0094C4CF /* AppDelegate.swift in Sources */, 5B9D60322ACBD4E9004905F6 /* ListCollectionViewCell.swift in Sources */, C7431F0825F51E1D0094C4CF /* SceneDelegate.swift in Sources */, + 5B1666B82ACBEB7100478F1D /* ListCollectionHeaderView.swift in Sources */, 5B1666B32ACBD8DB00478F1D /* TaskViewController.swift in Sources */, 9C4F6E812ACBE29700FDC393 /* ListViewController.swift in Sources */, ); diff --git a/ProjectManager/ProjectManager/Controller/ListViewController.swift b/ProjectManager/ProjectManager/Controller/ListViewController.swift index 61a5ba2a1..ebeb5eb74 100644 --- a/ProjectManager/ProjectManager/Controller/ListViewController.swift +++ b/ProjectManager/ProjectManager/Controller/ListViewController.swift @@ -2,12 +2,22 @@ // ListViewController.swift // ProjectManager // -// Created by karen on 2023/10/03. +// Created by Karen, Zion on 2023/10/03. // import UIKit +enum ListKind: String { + case todo = "TODO" + case doing = "DOING" + case done = "DONE" +} + final class ListViewController: UIViewController { + enum Section { + case main + } + private lazy var collectionView: UICollectionView = { let collectionView = UICollectionView(frame: .zero, collectionViewLayout: listLayout) @@ -28,12 +38,29 @@ final class ListViewController: UIViewController { } }() + private var diffableDataSource: UICollectionViewDiffableDataSource? + + private let listKind: ListKind + + init(listKind: ListKind) { + self.listKind = listKind + + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + override func viewDidLoad() { super.viewDidLoad() configureUI() setUpConstraints() setUpViewController() + setUpDiffableDataSource() + setUpDiffableDataSourceHeader() + setUpDiffableDataSourceSanpShot() } private func configureUI() { @@ -53,3 +80,41 @@ final class ListViewController: UIViewController { view.backgroundColor = .systemBackground } } + +// MARK: - Diffable DataSource +extension ListViewController { + func setUpDiffableDataSourceSanpShot(taskList: [Task] = []) { + var snapShot = NSDiffableDataSourceSnapshot() + + snapShot.appendSections([.main]) + snapShot.appendItems(taskList) + diffableDataSource?.apply(snapShot) + } + + private func setUpDiffableDataSource() { + let cellRegistration = UICollectionView.CellRegistration { cell, indexPath, task in + cell.setUpContents(title: task.title, + description: task.description, + deadline: task.deadline) + } + + diffableDataSource = UICollectionViewDiffableDataSource(collectionView: collectionView, cellProvider: { collectionView, indexPath, task in + return collectionView.dequeueConfiguredReusableCell(using: cellRegistration, + for: indexPath, + item: task) + }) + } + + private func setUpDiffableDataSourceHeader() { + let headerRegistration = UICollectionView.SupplementaryRegistration(elementKind: UICollectionView.elementKindSectionHeader) { [weak self] headerView, elementKind, indexPath in + guard let self = self else { return } + guard let taskList = self.diffableDataSource?.snapshot().itemIdentifiers else { return } + + headerView.setUpContents(title: self.listKind.rawValue, taskCount: "\(taskList.count)") + } + + diffableDataSource?.supplementaryViewProvider = { collectionView, kind, indexPath in + return collectionView.dequeueConfiguredReusableSupplementary(using: headerRegistration, for: indexPath) + } + } +} diff --git a/ProjectManager/ProjectManager/Controller/TaskViewController.swift b/ProjectManager/ProjectManager/Controller/TaskViewController.swift index 156d592e6..f48030b75 100644 --- a/ProjectManager/ProjectManager/Controller/TaskViewController.swift +++ b/ProjectManager/ProjectManager/Controller/TaskViewController.swift @@ -2,7 +2,7 @@ // TaskViewController.swift // ProjectManager // -// Created by Hyungmin Lee on 2023/10/03. +// Created by Karen, Zion on 2023/10/03. // import UIKit diff --git a/ProjectManager/ProjectManager/Model/Task.swift b/ProjectManager/ProjectManager/Model/Task.swift new file mode 100644 index 000000000..394b3264c --- /dev/null +++ b/ProjectManager/ProjectManager/Model/Task.swift @@ -0,0 +1,15 @@ +// +// Task.swift +// ProjectManager +// +// Created by Karen, Zion on 2023/10/03. +// + +import Foundation + +struct Task: Hashable, Identifiable { + var id = UUID() + var title: String + var description: String + var deadline: String +} diff --git a/ProjectManager/ProjectManager/Util/Protocol/ReuseIdentifiable.swift b/ProjectManager/ProjectManager/Util/Protocol/ReuseIdentifiable.swift index 9606f2ffc..4768ec29c 100644 --- a/ProjectManager/ProjectManager/Util/Protocol/ReuseIdentifiable.swift +++ b/ProjectManager/ProjectManager/Util/Protocol/ReuseIdentifiable.swift @@ -2,7 +2,7 @@ // ReuseIdentifiable.swift // ProjectManager // -// Created by Hyungmin Lee on 2023/10/03. +// Created by Karen, Zion on 2023/10/03. // protocol ReuseIdentifiable { diff --git a/ProjectManager/ProjectManager/View/ListCollectionHeaderView.swift b/ProjectManager/ProjectManager/View/ListCollectionHeaderView.swift new file mode 100644 index 000000000..59555c090 --- /dev/null +++ b/ProjectManager/ProjectManager/View/ListCollectionHeaderView.swift @@ -0,0 +1,78 @@ +// +// ListCollectionHeaderView.swift +// ProjectManager +// +// Created by Karen, Zion on 2023/10/03. +// + +import UIKit + +final class ListCollectionHeaderView: UICollectionReusableView { + private let stackView: UIStackView = { + let stackView = UIStackView() + + stackView.axis = .horizontal + stackView.spacing = 10 + stackView.translatesAutoresizingMaskIntoConstraints = false + return stackView + }() + + private let titleLabel: UILabel = { + let label = UILabel() + + label.font = .systemFont(ofSize: 25) + label.textColor = .black + return label + }() + + private let taskCountLabel: UILabel = { + let label = UILabel() + + label.font = .systemFont(ofSize: 20) + label.textColor = .white + label.backgroundColor = .black + return label + }() + + private let dummyView: UIView = { + let view = UIView() + + view.backgroundColor = .clear + return view + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + configureUI() + setUpConstraints() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func setUpContents(title: String, taskCount: String) { + titleLabel.text = title + taskCountLabel.text = taskCount + } + + private func configureUI() { + [titleLabel, taskCountLabel, dummyView].forEach { + stackView.addArrangedSubview($0) + } + + addSubview(stackView) + } + + private func setUpConstraints() { + NSLayoutConstraint.activate([ + stackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10), + stackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10), + stackView.topAnchor.constraint(equalTo: topAnchor, constant: 5), + stackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -5) + ]) + + dummyView.setContentHuggingPriority(.init(1), for: .horizontal) + } +} diff --git a/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift b/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift index 73a13ad27..49c0f38f0 100644 --- a/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift +++ b/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift @@ -2,7 +2,7 @@ // ListCollectionViewCell.swift // ProjectManager // -// Created by Hyungmin Lee on 2023/10/03. +// Created by Karen, Zion on 2023/10/03. // import UIKit From eb6c675b60422ba8d87535fd01c1488c96ce6839 Mon Sep 17 00:00:00 2001 From: karen Date: Tue, 3 Oct 2023 16:25:14 +0900 Subject: [PATCH 04/28] =?UTF-8?q?feat:=20MainViewController=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=95=84=EC=9B=83=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProjectManager/App/SceneDelegate.swift | 3 +- .../Controller/MainViewController.swift | 43 ++++++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/ProjectManager/ProjectManager/App/SceneDelegate.swift b/ProjectManager/ProjectManager/App/SceneDelegate.swift index 23ffe7900..3e2d32e9c 100644 --- a/ProjectManager/ProjectManager/App/SceneDelegate.swift +++ b/ProjectManager/ProjectManager/App/SceneDelegate.swift @@ -12,9 +12,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } let mainViewController = MainViewController() + let navigationController = UINavigationController(rootViewController: mainViewController) window = UIWindow(windowScene: windowScene) - window?.rootViewController = mainViewController + window?.rootViewController = navigationController window?.makeKeyAndVisible() } diff --git a/ProjectManager/ProjectManager/Controller/MainViewController.swift b/ProjectManager/ProjectManager/Controller/MainViewController.swift index 9a6bcf497..586fa8316 100644 --- a/ProjectManager/ProjectManager/Controller/MainViewController.swift +++ b/ProjectManager/ProjectManager/Controller/MainViewController.swift @@ -7,6 +7,22 @@ import UIKit final class MainViewController: UIViewController { + private let todoListViewController = ListViewController(listKind: .todo) + private let doingListViewController = ListViewController(listKind: .doing) + private let doneListViewController = ListViewController(listKind: .done) + + private let stackView: UIStackView = { + let stackView = UIStackView() + + stackView.axis = .horizontal + stackView.spacing = 10 + stackView.distribution = .fillEqually + stackView.backgroundColor = .systemBackground + stackView.translatesAutoresizingMaskIntoConstraints = false + + return stackView + }() + override func viewDidLoad() { super.viewDidLoad() @@ -16,14 +32,39 @@ final class MainViewController: UIViewController { } private func configureUI() { + [todoListViewController, doingListViewController, doneListViewController].forEach { + stackView.addArrangedSubview($0.view) + } + view.addSubview(stackView) } private func setUpConstraints() { - + NSLayoutConstraint.activate([ + stackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor), + stackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor), + stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), + stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor) + ]) } private func setUpViewController() { view.backgroundColor = .systemBackground + navigationItem.title = "Project Manager" + + let rightAddButtonAction: UIAction = .init { + action in self.didTappedRightAddButton() + } + + navigationItem.rightBarButtonItem = .init(systemItem: .add, primaryAction: rightAddButtonAction) + } +} + +extension MainViewController { + private func didTappedRightAddButton() { + let taskViewController = TaskViewController() + let navigationController = UINavigationController(rootViewController: taskViewController) + + present(navigationController, animated: true) } } From 39b8121dd5cea6558c01dc846de842893a03339d Mon Sep 17 00:00:00 2001 From: Zion Date: Tue, 3 Oct 2023 16:48:27 +0900 Subject: [PATCH 05/28] =?UTF-8?q?feat:=20Task=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=EC=8B=9C=20MainViewController=EC=97=90=EC=84=9C=20ListViewCont?= =?UTF-8?q?roller=EB=A5=BC=20=EA=B0=B1=EC=8B=A0=ED=95=98=EB=8F=84=EB=A1=9D?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controller/ListViewController.swift | 7 +- .../Controller/MainViewController.swift | 39 +++++++++- .../Controller/TaskViewController.swift | 72 +++++++++++++++++-- .../ProjectManager/Model/Task.swift | 1 + 4 files changed, 112 insertions(+), 7 deletions(-) diff --git a/ProjectManager/ProjectManager/Controller/ListViewController.swift b/ProjectManager/ProjectManager/Controller/ListViewController.swift index ebeb5eb74..f1b7e319f 100644 --- a/ProjectManager/ProjectManager/Controller/ListViewController.swift +++ b/ProjectManager/ProjectManager/Controller/ListViewController.swift @@ -42,6 +42,8 @@ final class ListViewController: UIViewController { private let listKind: ListKind + private var taskList: [Task] = [] + init(listKind: ListKind) { self.listKind = listKind @@ -86,8 +88,10 @@ extension ListViewController { func setUpDiffableDataSourceSanpShot(taskList: [Task] = []) { var snapShot = NSDiffableDataSourceSnapshot() + self.taskList = taskList snapShot.appendSections([.main]) snapShot.appendItems(taskList) + snapShot.reloadSections([.main]) diffableDataSource?.apply(snapShot) } @@ -108,9 +112,8 @@ extension ListViewController { private func setUpDiffableDataSourceHeader() { let headerRegistration = UICollectionView.SupplementaryRegistration(elementKind: UICollectionView.elementKindSectionHeader) { [weak self] headerView, elementKind, indexPath in guard let self = self else { return } - guard let taskList = self.diffableDataSource?.snapshot().itemIdentifiers else { return } - headerView.setUpContents(title: self.listKind.rawValue, taskCount: "\(taskList.count)") + headerView.setUpContents(title: self.listKind.rawValue, taskCount: "\(self.taskList.count)") } diffableDataSource?.supplementaryViewProvider = { collectionView, kind, indexPath in diff --git a/ProjectManager/ProjectManager/Controller/MainViewController.swift b/ProjectManager/ProjectManager/Controller/MainViewController.swift index 586fa8316..02cd2d2bd 100644 --- a/ProjectManager/ProjectManager/Controller/MainViewController.swift +++ b/ProjectManager/ProjectManager/Controller/MainViewController.swift @@ -10,6 +10,11 @@ final class MainViewController: UIViewController { private let todoListViewController = ListViewController(listKind: .todo) private let doingListViewController = ListViewController(listKind: .doing) private let doneListViewController = ListViewController(listKind: .done) + private var taskList: [Task] = [] { + didSet { + reloadTaskListViewControllers() + } + } private let stackView: UIStackView = { let stackView = UIStackView() @@ -58,13 +63,45 @@ final class MainViewController: UIViewController { navigationItem.rightBarButtonItem = .init(systemItem: .add, primaryAction: rightAddButtonAction) } + + private func reloadTaskListViewControllers() { + var todoList = [Task]() + var doingList = [Task]() + var doneList = [Task]() + + for task in taskList { + switch task.listKind { + case .todo: + todoList.append(task) + case .doing: + doingList.append(task) + case .done: + doneList.append(task) + } + } + + todoListViewController.setUpDiffableDataSourceSanpShot(taskList: todoList) + doingListViewController.setUpDiffableDataSourceSanpShot(taskList: doingList) + doneListViewController.setUpDiffableDataSourceSanpShot(taskList: doneList) + } } +// MARK: - Button Action extension MainViewController { private func didTappedRightAddButton() { - let taskViewController = TaskViewController() + let newTask = Task(title: "", description: "", deadline: "") + let taskViewController = TaskViewController(task: newTask) let navigationController = UINavigationController(rootViewController: taskViewController) + taskViewController.delegate = self + navigationController.modalPresentationStyle = .formSheet present(navigationController, animated: true) } } + +// MARK: - TaskViewController Delegate +extension MainViewController: TaskViewControllerDelegate { + func didTappedRightDoneButton(task: Task) { + taskList.append(task) + } +} diff --git a/ProjectManager/ProjectManager/Controller/TaskViewController.swift b/ProjectManager/ProjectManager/Controller/TaskViewController.swift index f48030b75..c4cfa0f74 100644 --- a/ProjectManager/ProjectManager/Controller/TaskViewController.swift +++ b/ProjectManager/ProjectManager/Controller/TaskViewController.swift @@ -7,7 +7,12 @@ import UIKit +protocol TaskViewControllerDelegate: AnyObject { + func didTappedRightDoneButton(task: Task) +} + final class TaskViewController: UIViewController { + weak var delegate: TaskViewControllerDelegate? private let stackView: UIStackView = { let stackView = UIStackView() @@ -27,6 +32,7 @@ final class TaskViewController: UIViewController { private let datePicker: UIDatePicker = { let datePicker = UIDatePicker() + datePicker.datePickerMode = .date datePicker.preferredDatePickerStyle = .wheels return datePicker }() @@ -38,6 +44,29 @@ final class TaskViewController: UIViewController { return textView }() + private let placeHolderLabel: UILabel = { + let label = UILabel() + + label.text = "여기는 할일 내용 입력하는 곳이지롱\n입력 가능한 글자수는 1000자로 제한합니다" + label.font = .systemFont(ofSize: 15) + label.textColor = .black + label.numberOfLines = 0 + label.translatesAutoresizingMaskIntoConstraints = false + return label + }() + + private var task: Task + + init(task: Task) { + self.task = task + + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + override func viewDidLoad() { super.viewDidLoad() @@ -51,7 +80,9 @@ final class TaskViewController: UIViewController { stackView.addArrangedSubview($0) } - view.addSubview(stackView) + [stackView, placeHolderLabel].forEach { + view.addSubview($0) + } } private func setUpConstraints() { @@ -60,25 +91,58 @@ final class TaskViewController: UIViewController { stackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10), stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), + + placeHolderLabel.leadingAnchor.constraint(equalTo: descriptionTextView.leadingAnchor, constant: 5), + placeHolderLabel.trailingAnchor.constraint(equalTo: descriptionTextView.trailingAnchor, constant: -5), + placeHolderLabel.topAnchor.constraint(equalTo: descriptionTextView.topAnchor, constant: 5), ]) descriptionTextView.setContentHuggingPriority(.init(1), for: .vertical) } private func setUpViewController() { + view.backgroundColor = .systemBackground navigationItem.title = "TODO" - navigationItem.leftBarButtonItem = .init(systemItem: .cancel) - navigationItem.rightBarButtonItem = .init(systemItem: .done) + + let leftCancelButtonAction: UIAction = .init { action in + self.didTappedLeftCancelButton() + } + + let rightDoneButtonAction: UIAction = .init { action in + self.didTappedRightDoneButton() + } + + navigationItem.leftBarButtonItem = .init(systemItem: .cancel, primaryAction: leftCancelButtonAction) + navigationItem.rightBarButtonItem = .init(systemItem: .done, primaryAction: rightDoneButtonAction) } } // MARK: - TextView Delegate extension TaskViewController: UITextViewDelegate { func textViewDidBeginEditing(_ textView: UITextView) { - + placeHolderLabel.isHidden = true } func textViewDidEndEditing(_ textView: UITextView) { + if textView.text.count == 0 { + placeHolderLabel.isHidden = false + } + } +} + +// MARK: - Button Action +extension TaskViewController { + private func didTappedLeftCancelButton() { + dismiss(animated: true) + } + + private func didTappedRightDoneButton() { + guard let title = titleTextField.text else { return } + task.title = title + task.description = descriptionTextView.text + task.deadline = "123213123123123" + delegate?.didTappedRightDoneButton(task: task) + dismiss(animated: true) } } diff --git a/ProjectManager/ProjectManager/Model/Task.swift b/ProjectManager/ProjectManager/Model/Task.swift index 394b3264c..2b344fc0e 100644 --- a/ProjectManager/ProjectManager/Model/Task.swift +++ b/ProjectManager/ProjectManager/Model/Task.swift @@ -12,4 +12,5 @@ struct Task: Hashable, Identifiable { var title: String var description: String var deadline: String + var listKind: ListKind = .todo } From 549bf84163bd4ee97a7f2b4bfadd15831a9c0963 Mon Sep 17 00:00:00 2001 From: Zion Date: Tue, 3 Oct 2023 16:50:28 +0900 Subject: [PATCH 06/28] =?UTF-8?q?fix:=20=EB=B6=88=ED=95=84=EC=9A=94=20?= =?UTF-8?q?=ED=83=80=EC=9E=85=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProjectManager.xcodeproj/project.pbxproj | 20 ------------------- .../Util/Protocol/ReuseIdentifiable.swift | 16 --------------- .../View/ListCollectionViewCell.swift | 2 +- 3 files changed, 1 insertion(+), 37 deletions(-) delete mode 100644 ProjectManager/ProjectManager/Util/Protocol/ReuseIdentifiable.swift diff --git a/ProjectManager/ProjectManager.xcodeproj/project.pbxproj b/ProjectManager/ProjectManager.xcodeproj/project.pbxproj index 848945a4d..608cb1542 100644 --- a/ProjectManager/ProjectManager.xcodeproj/project.pbxproj +++ b/ProjectManager/ProjectManager.xcodeproj/project.pbxproj @@ -11,7 +11,6 @@ 5B1666B62ACBE99300478F1D /* Task.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1666B52ACBE99300478F1D /* Task.swift */; }; 5B1666B82ACBEB7100478F1D /* ListCollectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1666B72ACBEB7100478F1D /* ListCollectionHeaderView.swift */; }; 5B9D60322ACBD4E9004905F6 /* ListCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B9D60312ACBD4E9004905F6 /* ListCollectionViewCell.swift */; }; - 5B9D60362ACBD531004905F6 /* ReuseIdentifiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B9D60352ACBD531004905F6 /* ReuseIdentifiable.swift */; }; 9C4F6E812ACBE29700FDC393 /* ListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C4F6E802ACBE29700FDC393 /* ListViewController.swift */; }; C7431F0625F51E1D0094C4CF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7431F0525F51E1D0094C4CF /* AppDelegate.swift */; }; C7431F0825F51E1D0094C4CF /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7431F0725F51E1D0094C4CF /* SceneDelegate.swift */; }; @@ -25,7 +24,6 @@ 5B1666B52ACBE99300478F1D /* Task.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Task.swift; sourceTree = ""; }; 5B1666B72ACBEB7100478F1D /* ListCollectionHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListCollectionHeaderView.swift; sourceTree = ""; }; 5B9D60312ACBD4E9004905F6 /* ListCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListCollectionViewCell.swift; sourceTree = ""; }; - 5B9D60352ACBD531004905F6 /* ReuseIdentifiable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReuseIdentifiable.swift; sourceTree = ""; }; 9C4F6E802ACBE29700FDC393 /* ListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListViewController.swift; sourceTree = ""; }; C7431F0225F51E1D0094C4CF /* ProjectManager.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ProjectManager.app; sourceTree = BUILT_PRODUCTS_DIR; }; C7431F0525F51E1D0094C4CF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -92,22 +90,6 @@ path = View; sourceTree = ""; }; - 5B9D60332ACBD4F6004905F6 /* Util */ = { - isa = PBXGroup; - children = ( - 5B9D60342ACBD504004905F6 /* Protocol */, - ); - path = Util; - sourceTree = ""; - }; - 5B9D60342ACBD504004905F6 /* Protocol */ = { - isa = PBXGroup; - children = ( - 5B9D60352ACBD531004905F6 /* ReuseIdentifiable.swift */, - ); - path = Protocol; - sourceTree = ""; - }; C7431EF925F51E1D0094C4CF = { isa = PBXGroup; children = ( @@ -129,7 +111,6 @@ children = ( C7431F1325F51E1E0094C4CF /* Info.plist */, 5B1666B42ACBE98400478F1D /* Model */, - 5B9D60332ACBD4F6004905F6 /* Util */, 5B9D60302ACBD469004905F6 /* View */, 5B9D602F2ACBD467004905F6 /* Resource */, 5B9D602E2ACBD464004905F6 /* Controller */, @@ -209,7 +190,6 @@ files = ( 5B1666B62ACBE99300478F1D /* Task.swift in Sources */, C7431F0A25F51E1D0094C4CF /* MainViewController.swift in Sources */, - 5B9D60362ACBD531004905F6 /* ReuseIdentifiable.swift in Sources */, C7431F0625F51E1D0094C4CF /* AppDelegate.swift in Sources */, 5B9D60322ACBD4E9004905F6 /* ListCollectionViewCell.swift in Sources */, C7431F0825F51E1D0094C4CF /* SceneDelegate.swift in Sources */, diff --git a/ProjectManager/ProjectManager/Util/Protocol/ReuseIdentifiable.swift b/ProjectManager/ProjectManager/Util/Protocol/ReuseIdentifiable.swift deleted file mode 100644 index 4768ec29c..000000000 --- a/ProjectManager/ProjectManager/Util/Protocol/ReuseIdentifiable.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// ReuseIdentifiable.swift -// ProjectManager -// -// Created by Karen, Zion on 2023/10/03. -// - -protocol ReuseIdentifiable { - static var reuseIdentifier: String { get } -} - -extension ReuseIdentifiable { - static var reuseIdentifier: String { - return String(describing: self) - } -} diff --git a/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift b/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift index 49c0f38f0..0a930cf7f 100644 --- a/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift +++ b/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift @@ -7,7 +7,7 @@ import UIKit -final class ListCollectionViewCell: UICollectionViewListCell, ReuseIdentifiable { +final class ListCollectionViewCell: UICollectionViewListCell { private let stackView: UIStackView = { let stackView = UIStackView() From f91a534021de68a96a5b6b7c73be2ecf60f646c1 Mon Sep 17 00:00:00 2001 From: karen Date: Thu, 5 Oct 2023 17:25:31 +0900 Subject: [PATCH 07/28] =?UTF-8?q?feat:=20taskCountLabel=20=EB=9D=BC?= =?UTF-8?q?=EC=9A=B4=EB=94=A9=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProjectManager/View/ListCollectionHeaderView.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ProjectManager/ProjectManager/View/ListCollectionHeaderView.swift b/ProjectManager/ProjectManager/View/ListCollectionHeaderView.swift index 59555c090..f49753f7f 100644 --- a/ProjectManager/ProjectManager/View/ListCollectionHeaderView.swift +++ b/ProjectManager/ProjectManager/View/ListCollectionHeaderView.swift @@ -31,6 +31,9 @@ final class ListCollectionHeaderView: UICollectionReusableView { label.font = .systemFont(ofSize: 20) label.textColor = .white label.backgroundColor = .black + label.textAlignment = .center + label.layer.masksToBounds = true + label.layer.cornerRadius = 15 return label }() @@ -70,7 +73,8 @@ final class ListCollectionHeaderView: UICollectionReusableView { stackView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10), stackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10), stackView.topAnchor.constraint(equalTo: topAnchor, constant: 5), - stackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -5) + stackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -5), + taskCountLabel.widthAnchor.constraint(equalTo: taskCountLabel.heightAnchor) ]) dummyView.setContentHuggingPriority(.init(1), for: .horizontal) From c37229d18defb669deb5431c01353999d87cd4bc Mon Sep 17 00:00:00 2001 From: karen Date: Thu, 5 Oct 2023 17:38:17 +0900 Subject: [PATCH 08/28] =?UTF-8?q?feat:=20shadow=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controller/TaskViewController.swift | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/ProjectManager/ProjectManager/Controller/TaskViewController.swift b/ProjectManager/ProjectManager/Controller/TaskViewController.swift index c4cfa0f74..d8971d58e 100644 --- a/ProjectManager/ProjectManager/Controller/TaskViewController.swift +++ b/ProjectManager/ProjectManager/Controller/TaskViewController.swift @@ -25,6 +25,14 @@ final class TaskViewController: UIViewController { private let titleTextField: UITextField = { let textField = UITextField() + textField.font = .preferredFont(forTextStyle: .title2) + textField.borderStyle = .roundedRect + textField.layer.borderColor = UIColor.label.cgColor + textField.layer.borderWidth = 0.3 + textField.layer.shadowColor = UIColor.systemGray.cgColor + textField.layer.shadowOffset = CGSize(width: 2, height: 3) + textField.layer.shadowOpacity = 0.5 + textField.layer.shadowRadius = 3 textField.placeholder = "Title" return textField }() @@ -40,6 +48,13 @@ final class TaskViewController: UIViewController { private lazy var descriptionTextView: UITextView = { let textView = UITextView() + textView.layer.borderColor = UIColor.label.cgColor + textView.layer.borderWidth = 0.3 + textView.layer.shadowColor = UIColor.systemGray.cgColor + textView.layer.shadowOffset = CGSize(width: 2, height: 3) + textView.layer.shadowOpacity = 0.5 + textView.layer.shadowRadius = 3 + textView.layer.masksToBounds = false textView.delegate = self return textView }() @@ -90,7 +105,7 @@ final class TaskViewController: UIViewController { stackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 10), stackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -10), stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), - stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), + stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -10), placeHolderLabel.leadingAnchor.constraint(equalTo: descriptionTextView.leadingAnchor, constant: 5), placeHolderLabel.trailingAnchor.constraint(equalTo: descriptionTextView.trailingAnchor, constant: -5), From 34238a3c107cf0d48d744aed5f9eaee604c11972 Mon Sep 17 00:00:00 2001 From: Zion Date: Thu, 5 Oct 2023 18:38:23 +0900 Subject: [PATCH 09/28] =?UTF-8?q?feat:=20Task=20Update=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controller/ListViewController.swift | 44 ++++++++++++++++- .../Controller/MainViewController.swift | 48 +++++++++++++++++-- .../Controller/TaskViewController.swift | 25 ++++++++-- .../ProjectManager/Model/Task.swift | 2 +- 4 files changed, 107 insertions(+), 12 deletions(-) diff --git a/ProjectManager/ProjectManager/Controller/ListViewController.swift b/ProjectManager/ProjectManager/Controller/ListViewController.swift index f1b7e319f..76542bd83 100644 --- a/ProjectManager/ProjectManager/Controller/ListViewController.swift +++ b/ProjectManager/ProjectManager/Controller/ListViewController.swift @@ -13,14 +13,20 @@ enum ListKind: String { case done = "DONE" } +protocol ListViewControllerDelegate: AnyObject { + func didTappedRightDoneButtonForUpdate(updateTask: Task) +} + final class ListViewController: UIViewController { enum Section { case main } + weak var delegate: ListViewControllerDelegate? private lazy var collectionView: UICollectionView = { let collectionView = UICollectionView(frame: .zero, collectionViewLayout: listLayout) + collectionView.delegate = self collectionView.translatesAutoresizingMaskIntoConstraints = false return collectionView }() @@ -81,6 +87,16 @@ final class ListViewController: UIViewController { private func setUpViewController() { view.backgroundColor = .systemBackground } + + private func convertFormattedDeadline(deadline: Double) -> String { + let dateFormatter = DateFormatter() + + dateFormatter.dateFormat = "yyyy. MM. dd." + dateFormatter.locale = Locale.current + dateFormatter.timeZone = TimeZone.current + + return dateFormatter.string(from: Date(timeIntervalSince1970: deadline)) + } } // MARK: - Diffable DataSource @@ -96,10 +112,13 @@ extension ListViewController { } private func setUpDiffableDataSource() { - let cellRegistration = UICollectionView.CellRegistration { cell, indexPath, task in + let cellRegistration = UICollectionView.CellRegistration { [weak self] cell, indexPath, task in + guard let self = self else { return } + let formattedDeadLine = self.convertFormattedDeadline(deadline: task.deadline) + cell.setUpContents(title: task.title, description: task.description, - deadline: task.deadline) + deadline: formattedDeadLine) } diffableDataSource = UICollectionViewDiffableDataSource(collectionView: collectionView, cellProvider: { collectionView, indexPath, task in @@ -121,3 +140,24 @@ extension ListViewController { } } } + +// MARK: - CollectionView Delegate +extension ListViewController: UICollectionViewDelegate { + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + let task = taskList[indexPath.row] + let taskViewController = TaskViewController(task: task, mode: .update) + let navigationController = UINavigationController(rootViewController: taskViewController) + + taskViewController.delegate = self + navigationController.modalPresentationStyle = .formSheet + present(navigationController, animated: true) + collectionView.deselectItem(at: indexPath, animated: true) + } +} + +// MARK: - TaskViewController Delegate +extension ListViewController: TaskViewControllerDelegate { + func didTappedRightDoneButton(task: Task) { + delegate?.didTappedRightDoneButtonForUpdate(updateTask: task) + } +} diff --git a/ProjectManager/ProjectManager/Controller/MainViewController.swift b/ProjectManager/ProjectManager/Controller/MainViewController.swift index 02cd2d2bd..78e892a5e 100644 --- a/ProjectManager/ProjectManager/Controller/MainViewController.swift +++ b/ProjectManager/ProjectManager/Controller/MainViewController.swift @@ -7,9 +7,27 @@ import UIKit final class MainViewController: UIViewController { - private let todoListViewController = ListViewController(listKind: .todo) - private let doingListViewController = ListViewController(listKind: .doing) - private let doneListViewController = ListViewController(listKind: .done) + private lazy var todoListViewController: ListViewController = { + let listViewController = ListViewController(listKind: .todo) + + listViewController.delegate = self + return listViewController + }() + + private lazy var doingListViewController: ListViewController = { + let listViewController = ListViewController(listKind: .doing) + + listViewController.delegate = self + return listViewController + }() + + private lazy var doneListViewController: ListViewController = { + let listViewController = ListViewController(listKind: .done) + + listViewController.delegate = self + return listViewController + }() + private var taskList: [Task] = [] { didSet { reloadTaskListViewControllers() @@ -89,8 +107,8 @@ final class MainViewController: UIViewController { // MARK: - Button Action extension MainViewController { private func didTappedRightAddButton() { - let newTask = Task(title: "", description: "", deadline: "") - let taskViewController = TaskViewController(task: newTask) + let newTask = Task(title: "", description: "", deadline: 0.0) + let taskViewController = TaskViewController(task: newTask, mode: .append) let navigationController = UINavigationController(rootViewController: taskViewController) taskViewController.delegate = self @@ -105,3 +123,23 @@ extension MainViewController: TaskViewControllerDelegate { taskList.append(task) } } + +// MARK: - ListViewController Delegate +extension MainViewController: ListViewControllerDelegate { + func didTappedRightDoneButtonForUpdate(updateTask: Task) { + let updatedTaskList = taskList.map { + if $0.id == updateTask.id { + var task = $0 + + task.title = updateTask.title + task.description = updateTask.description + task.deadline = updateTask.deadline + return task + } + + return $0 + } + + taskList = updatedTaskList + } +} diff --git a/ProjectManager/ProjectManager/Controller/TaskViewController.swift b/ProjectManager/ProjectManager/Controller/TaskViewController.swift index d8971d58e..23acd1edd 100644 --- a/ProjectManager/ProjectManager/Controller/TaskViewController.swift +++ b/ProjectManager/ProjectManager/Controller/TaskViewController.swift @@ -12,6 +12,11 @@ protocol TaskViewControllerDelegate: AnyObject { } final class TaskViewController: UIViewController { + enum Mode { + case append + case update + } + weak var delegate: TaskViewControllerDelegate? private let stackView: UIStackView = { let stackView = UIStackView() @@ -25,7 +30,6 @@ final class TaskViewController: UIViewController { private let titleTextField: UITextField = { let textField = UITextField() - textField.font = .preferredFont(forTextStyle: .title2) textField.borderStyle = .roundedRect textField.layer.borderColor = UIColor.label.cgColor textField.layer.borderWidth = 0.3 @@ -71,9 +75,11 @@ final class TaskViewController: UIViewController { }() private var task: Task + private let mode: Mode - init(task: Task) { + init(task: Task, mode: Mode) { self.task = task + self.mode = mode super.init(nibName: nil, bundle: nil) } @@ -88,6 +94,7 @@ final class TaskViewController: UIViewController { configureUI() setUpConstraints() setUpViewController() + setUpContents() } private func configureUI() { @@ -128,7 +135,17 @@ final class TaskViewController: UIViewController { } navigationItem.leftBarButtonItem = .init(systemItem: .cancel, primaryAction: leftCancelButtonAction) - navigationItem.rightBarButtonItem = .init(systemItem: .done, primaryAction: rightDoneButtonAction) + navigationItem.rightBarButtonItem = .init(systemItem: mode == .append ? .done : .edit, + primaryAction: rightDoneButtonAction) + } + + private func setUpContents() { + if task.title.count == 0 || task.description.count == 0 { return } + + titleTextField.text = task.title + descriptionTextView.text = task.description + datePicker.date = Date(timeIntervalSince1970: task.deadline) + placeHolderLabel.isHidden = true } } @@ -156,7 +173,7 @@ extension TaskViewController { task.title = title task.description = descriptionTextView.text - task.deadline = "123213123123123" + task.deadline = datePicker.date.timeIntervalSince1970 delegate?.didTappedRightDoneButton(task: task) dismiss(animated: true) } diff --git a/ProjectManager/ProjectManager/Model/Task.swift b/ProjectManager/ProjectManager/Model/Task.swift index 2b344fc0e..75c0e930d 100644 --- a/ProjectManager/ProjectManager/Model/Task.swift +++ b/ProjectManager/ProjectManager/Model/Task.swift @@ -11,6 +11,6 @@ struct Task: Hashable, Identifiable { var id = UUID() var title: String var description: String - var deadline: String + var deadline: Double var listKind: ListKind = .todo } From 386a36e6e73e0f24236795ee726f5d0e99685658 Mon Sep 17 00:00:00 2001 From: karen Date: Thu, 5 Oct 2023 19:32:06 +0900 Subject: [PATCH 10/28] =?UTF-8?q?feat:=20deadline=EC=97=90=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B8=20=ED=85=8D=EC=8A=A4=ED=8A=B8=20=EC=83=89=EC=83=81?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controller/ListViewController.swift | 14 +++-------- .../Controller/TaskViewController.swift | 1 + .../View/ListCollectionViewCell.swift | 24 +++++++++++++++++-- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/ProjectManager/ProjectManager/Controller/ListViewController.swift b/ProjectManager/ProjectManager/Controller/ListViewController.swift index 76542bd83..c256d8316 100644 --- a/ProjectManager/ProjectManager/Controller/ListViewController.swift +++ b/ProjectManager/ProjectManager/Controller/ListViewController.swift @@ -88,15 +88,6 @@ final class ListViewController: UIViewController { view.backgroundColor = .systemBackground } - private func convertFormattedDeadline(deadline: Double) -> String { - let dateFormatter = DateFormatter() - - dateFormatter.dateFormat = "yyyy. MM. dd." - dateFormatter.locale = Locale.current - dateFormatter.timeZone = TimeZone.current - - return dateFormatter.string(from: Date(timeIntervalSince1970: deadline)) - } } // MARK: - Diffable DataSource @@ -114,11 +105,12 @@ extension ListViewController { private func setUpDiffableDataSource() { let cellRegistration = UICollectionView.CellRegistration { [weak self] cell, indexPath, task in guard let self = self else { return } - let formattedDeadLine = self.convertFormattedDeadline(deadline: task.deadline) +// let formattedDeadLine = self.convertFormattedDeadline(deadline: task.deadline) + //여기 변경했음 cell.setUpContents(title: task.title, description: task.description, - deadline: formattedDeadLine) + deadline: task.deadline) } diffableDataSource = UICollectionViewDiffableDataSource(collectionView: collectionView, cellProvider: { collectionView, indexPath, task in diff --git a/ProjectManager/ProjectManager/Controller/TaskViewController.swift b/ProjectManager/ProjectManager/Controller/TaskViewController.swift index 23acd1edd..3ee202d0f 100644 --- a/ProjectManager/ProjectManager/Controller/TaskViewController.swift +++ b/ProjectManager/ProjectManager/Controller/TaskViewController.swift @@ -145,6 +145,7 @@ final class TaskViewController: UIViewController { titleTextField.text = task.title descriptionTextView.text = task.description datePicker.date = Date(timeIntervalSince1970: task.deadline) + placeHolderLabel.isHidden = true } } diff --git a/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift b/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift index 0a930cf7f..6fbc61a48 100644 --- a/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift +++ b/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift @@ -52,10 +52,30 @@ final class ListCollectionViewCell: UICollectionViewListCell { fatalError("init(coder:) has not been implemented") } - func setUpContents(title: String, description: String, deadline: String) { + func setUpContents(title: String, description: String, deadline: Double) { titleLabel.text = title descriptionLabel.text = description - deadlineLabel.text = deadline + deadlineLabel.text = convertFormattedDeadline(deadline) + if isPassDeadline(deadline: deadline) { + deadlineLabel.textColor = .red + } else { + deadlineLabel.textColor = .black + } + } + + private func isPassDeadline(deadline: Double) -> Bool { + let currentTime = Date().timeIntervalSince1970 + + return currentTime > deadline + } + + private func convertFormattedDeadline(_ deadline: Double) -> String { + let dateFormatter = DateFormatter() + + dateFormatter.dateFormat = "yyyy. MM. dd." + dateFormatter.locale = Locale.current + dateFormatter.timeZone = TimeZone.current + return dateFormatter.string(from: Date(timeIntervalSince1970: deadline)) } private func configureUI() { From 5acaed5cbb16ca8ced954959f3da50fbe8c0079a Mon Sep 17 00:00:00 2001 From: Zion Date: Thu, 5 Oct 2023 19:54:50 +0900 Subject: [PATCH 11/28] =?UTF-8?q?feat:=20isPassDeadline=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controller/ListViewController.swift | 6 +--- .../Controller/MainViewController.swift | 1 - .../View/ListCollectionViewCell.swift | 33 +++++++++---------- 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/ProjectManager/ProjectManager/Controller/ListViewController.swift b/ProjectManager/ProjectManager/Controller/ListViewController.swift index c256d8316..6b74ace3f 100644 --- a/ProjectManager/ProjectManager/Controller/ListViewController.swift +++ b/ProjectManager/ProjectManager/Controller/ListViewController.swift @@ -103,11 +103,7 @@ extension ListViewController { } private func setUpDiffableDataSource() { - let cellRegistration = UICollectionView.CellRegistration { [weak self] cell, indexPath, task in - guard let self = self else { return } -// let formattedDeadLine = self.convertFormattedDeadline(deadline: task.deadline) - - //여기 변경했음 + let cellRegistration = UICollectionView.CellRegistration { cell, indexPath, task in cell.setUpContents(title: task.title, description: task.description, deadline: task.deadline) diff --git a/ProjectManager/ProjectManager/Controller/MainViewController.swift b/ProjectManager/ProjectManager/Controller/MainViewController.swift index 78e892a5e..6f605fda1 100644 --- a/ProjectManager/ProjectManager/Controller/MainViewController.swift +++ b/ProjectManager/ProjectManager/Controller/MainViewController.swift @@ -42,7 +42,6 @@ final class MainViewController: UIViewController { stackView.distribution = .fillEqually stackView.backgroundColor = .systemBackground stackView.translatesAutoresizingMaskIntoConstraints = false - return stackView }() diff --git a/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift b/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift index 6fbc61a48..a607db91c 100644 --- a/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift +++ b/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift @@ -8,6 +8,15 @@ import UIKit final class ListCollectionViewCell: UICollectionViewListCell { + private let dateFormatter: DateFormatter = { + let dateFormatter = DateFormatter() + + dateFormatter.dateFormat = "yyyy. MM. dd." + dateFormatter.locale = Locale.current + dateFormatter.timeZone = TimeZone.current + return dateFormatter + }() + private let stackView: UIStackView = { let stackView = UIStackView() @@ -55,27 +64,17 @@ final class ListCollectionViewCell: UICollectionViewListCell { func setUpContents(title: String, description: String, deadline: Double) { titleLabel.text = title descriptionLabel.text = description - deadlineLabel.text = convertFormattedDeadline(deadline) - if isPassDeadline(deadline: deadline) { - deadlineLabel.textColor = .red - } else { - deadlineLabel.textColor = .black - } + deadlineLabel.text = dateFormatter.string(from: Date(timeIntervalSince1970: deadline)) + deadlineLabel.textColor = isPassDeadline(deadline: deadline) ? .red : .black } private func isPassDeadline(deadline: Double) -> Bool { - let currentTime = Date().timeIntervalSince1970 + let currentDateString = dateFormatter.string(from: Date()) + let deadlineDateString = dateFormatter.string(from: Date(timeIntervalSince1970: deadline)) + let currentDate = dateFormatter.date(from: currentDateString) ?? Date() + let deadlineDate = dateFormatter.date(from: deadlineDateString) ?? Date() - return currentTime > deadline - } - - private func convertFormattedDeadline(_ deadline: Double) -> String { - let dateFormatter = DateFormatter() - - dateFormatter.dateFormat = "yyyy. MM. dd." - dateFormatter.locale = Locale.current - dateFormatter.timeZone = TimeZone.current - return dateFormatter.string(from: Date(timeIntervalSince1970: deadline)) + return currentDate.timeIntervalSince1970 > deadlineDate.timeIntervalSince1970 } private func configureUI() { From eb4c7aef8f3361ea6503136eca02fd50162f7712 Mon Sep 17 00:00:00 2001 From: karen Date: Thu, 5 Oct 2023 20:04:09 +0900 Subject: [PATCH 12/28] =?UTF-8?q?fix:=20descriptionLabel=20=EC=A4=84=20?= =?UTF-8?q?=EC=88=98=20=EC=A0=9C=ED=95=9C=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ProjectManager/ProjectManager/View/ListCollectionViewCell.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift b/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift index a607db91c..7533866d4 100644 --- a/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift +++ b/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift @@ -38,6 +38,7 @@ final class ListCollectionViewCell: UICollectionViewListCell { let label = UILabel() label.font = .systemFont(ofSize: 13) + label.numberOfLines = 3 label.textColor = .lightGray return label }() From 1118c273ce0ba97173f745b2b5461104d1811d91 Mon Sep 17 00:00:00 2001 From: Zion Date: Thu, 5 Oct 2023 20:18:55 +0900 Subject: [PATCH 13/28] =?UTF-8?q?feat:=20swipeDelete=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProjectManager/Controller/MainViewController.swift | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ProjectManager/ProjectManager/Controller/MainViewController.swift b/ProjectManager/ProjectManager/Controller/MainViewController.swift index 6f605fda1..e51ded5b5 100644 --- a/ProjectManager/ProjectManager/Controller/MainViewController.swift +++ b/ProjectManager/ProjectManager/Controller/MainViewController.swift @@ -125,6 +125,15 @@ extension MainViewController: TaskViewControllerDelegate { // MARK: - ListViewController Delegate extension MainViewController: ListViewControllerDelegate { + func didSwipedDeleteTask(deleteTask: Task) { + for (index, task) in taskList.enumerated() { + if task.id == deleteTask.id { + taskList.remove(at: index) + break + } + } + } + func didTappedRightDoneButtonForUpdate(updateTask: Task) { let updatedTaskList = taskList.map { if $0.id == updateTask.id { From 353c110e06168e87a73c01cc27dc3b4f4a111fb1 Mon Sep 17 00:00:00 2001 From: karen Date: Thu, 5 Oct 2023 20:47:31 +0900 Subject: [PATCH 14/28] =?UTF-8?q?feat:=20trailingAction=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controller/ListViewController.swift | 45 +++++++++++++------ 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/ProjectManager/ProjectManager/Controller/ListViewController.swift b/ProjectManager/ProjectManager/Controller/ListViewController.swift index 6b74ace3f..9ed633fe2 100644 --- a/ProjectManager/ProjectManager/Controller/ListViewController.swift +++ b/ProjectManager/ProjectManager/Controller/ListViewController.swift @@ -15,6 +15,7 @@ enum ListKind: String { protocol ListViewControllerDelegate: AnyObject { func didTappedRightDoneButtonForUpdate(updateTask: Task) + func didSwipedDeleteTask(deleteTask: Task) } final class ListViewController: UIViewController { @@ -23,27 +24,15 @@ final class ListViewController: UIViewController { } weak var delegate: ListViewControllerDelegate? + private lazy var collectionView: UICollectionView = { - let collectionView = UICollectionView(frame: .zero, collectionViewLayout: listLayout) + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: listLayout()) collectionView.delegate = self collectionView.translatesAutoresizingMaskIntoConstraints = false return collectionView }() - private let listLayout: UICollectionViewCompositionalLayout = { - return UICollectionViewCompositionalLayout { sectionIndex, layoutEnvironment in - var listLayout = UICollectionLayoutListConfiguration(appearance: .grouped) - - listLayout.headerMode = .supplementary - - let section = NSCollectionLayoutSection.list(using: listLayout, layoutEnvironment: layoutEnvironment) - - section.interGroupSpacing = 10 - return section - } - }() - private var diffableDataSource: UICollectionViewDiffableDataSource? private let listKind: ListKind @@ -127,6 +116,29 @@ extension ListViewController { return collectionView.dequeueConfiguredReusableSupplementary(using: headerRegistration, for: indexPath) } } + + private func listLayout() -> UICollectionViewCompositionalLayout { + return UICollectionViewCompositionalLayout { sectionIndex, layoutEnvironment in + var listLayout = UICollectionLayoutListConfiguration(appearance: .grouped) + + listLayout.trailingSwipeActionsConfigurationProvider = { + indexPath in + let deleteAction = UIContextualAction(style: .destructive, title: "Delete") { [weak self] action, view, handler in + guard let self = self else { return } + + self.didSwipedDeleteTask(deleteTask: self.taskList[indexPath.row]) + } + return UISwipeActionsConfiguration(actions: [deleteAction]) + } + + listLayout.headerMode = .supplementary + + let section = NSCollectionLayoutSection.list(using: listLayout, layoutEnvironment: layoutEnvironment) + + section.interGroupSpacing = 10 + return section + } + } } // MARK: - CollectionView Delegate @@ -148,4 +160,9 @@ extension ListViewController: TaskViewControllerDelegate { func didTappedRightDoneButton(task: Task) { delegate?.didTappedRightDoneButtonForUpdate(updateTask: task) } + + func didSwipedDeleteTask(deleteTask: Task) { + delegate?.didSwipedDeleteTask(deleteTask: deleteTask) + } } + From 2d97e3dbb65b1ac3db7035de8cdd31bbab64d07c Mon Sep 17 00:00:00 2001 From: Zion Date: Thu, 5 Oct 2023 21:44:26 +0900 Subject: [PATCH 15/28] =?UTF-8?q?feat:=20Cell=20Move=20=EB=A1=9C=EC=A7=81?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProjectManager.xcodeproj/project.pbxproj | 20 +++++++ .../Controller/ListViewController.swift | 54 ++++++++++++++++--- .../Controller/MainViewController.swift | 17 ++++-- .../Protocol/AlertControllerShowable.swift | 31 +++++++++++ .../View/ListCollectionViewCell.swift | 34 ++++++++++-- 5 files changed, 142 insertions(+), 14 deletions(-) create mode 100644 ProjectManager/ProjectManager/Util/Protocol/AlertControllerShowable.swift diff --git a/ProjectManager/ProjectManager.xcodeproj/project.pbxproj b/ProjectManager/ProjectManager.xcodeproj/project.pbxproj index 608cb1542..4762e1f35 100644 --- a/ProjectManager/ProjectManager.xcodeproj/project.pbxproj +++ b/ProjectManager/ProjectManager.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 5B1666B32ACBD8DB00478F1D /* TaskViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1666B22ACBD8DB00478F1D /* TaskViewController.swift */; }; 5B1666B62ACBE99300478F1D /* Task.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1666B52ACBE99300478F1D /* Task.swift */; }; 5B1666B82ACBEB7100478F1D /* ListCollectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1666B72ACBEB7100478F1D /* ListCollectionHeaderView.swift */; }; + 5B1666DE2ACEDDC000478F1D /* AlertControllerShowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1666DD2ACEDDC000478F1D /* AlertControllerShowable.swift */; }; 5B9D60322ACBD4E9004905F6 /* ListCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B9D60312ACBD4E9004905F6 /* ListCollectionViewCell.swift */; }; 9C4F6E812ACBE29700FDC393 /* ListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C4F6E802ACBE29700FDC393 /* ListViewController.swift */; }; C7431F0625F51E1D0094C4CF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7431F0525F51E1D0094C4CF /* AppDelegate.swift */; }; @@ -23,6 +24,7 @@ 5B1666B22ACBD8DB00478F1D /* TaskViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskViewController.swift; sourceTree = ""; }; 5B1666B52ACBE99300478F1D /* Task.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Task.swift; sourceTree = ""; }; 5B1666B72ACBEB7100478F1D /* ListCollectionHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListCollectionHeaderView.swift; sourceTree = ""; }; + 5B1666DD2ACEDDC000478F1D /* AlertControllerShowable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertControllerShowable.swift; sourceTree = ""; }; 5B9D60312ACBD4E9004905F6 /* ListCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListCollectionViewCell.swift; sourceTree = ""; }; 9C4F6E802ACBE29700FDC393 /* ListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListViewController.swift; sourceTree = ""; }; C7431F0225F51E1D0094C4CF /* ProjectManager.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ProjectManager.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -53,6 +55,22 @@ path = Model; sourceTree = ""; }; + 5B1666DB2ACEDDA800478F1D /* Util */ = { + isa = PBXGroup; + children = ( + 5B1666DC2ACEDDB300478F1D /* Protocol */, + ); + path = Util; + sourceTree = ""; + }; + 5B1666DC2ACEDDB300478F1D /* Protocol */ = { + isa = PBXGroup; + children = ( + 5B1666DD2ACEDDC000478F1D /* AlertControllerShowable.swift */, + ); + path = Protocol; + sourceTree = ""; + }; 5B9D602D2ACBD462004905F6 /* App */ = { isa = PBXGroup; children = ( @@ -110,6 +128,7 @@ isa = PBXGroup; children = ( C7431F1325F51E1E0094C4CF /* Info.plist */, + 5B1666DB2ACEDDA800478F1D /* Util */, 5B1666B42ACBE98400478F1D /* Model */, 5B9D60302ACBD469004905F6 /* View */, 5B9D602F2ACBD467004905F6 /* Resource */, @@ -188,6 +207,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 5B1666DE2ACEDDC000478F1D /* AlertControllerShowable.swift in Sources */, 5B1666B62ACBE99300478F1D /* Task.swift in Sources */, C7431F0A25F51E1D0094C4CF /* MainViewController.swift in Sources */, C7431F0625F51E1D0094C4CF /* AppDelegate.swift in Sources */, diff --git a/ProjectManager/ProjectManager/Controller/ListViewController.swift b/ProjectManager/ProjectManager/Controller/ListViewController.swift index 9ed633fe2..b3da6daed 100644 --- a/ProjectManager/ProjectManager/Controller/ListViewController.swift +++ b/ProjectManager/ProjectManager/Controller/ListViewController.swift @@ -16,9 +16,10 @@ enum ListKind: String { protocol ListViewControllerDelegate: AnyObject { func didTappedRightDoneButtonForUpdate(updateTask: Task) func didSwipedDeleteTask(deleteTask: Task) + func moveCell(moveToListKind: ListKind, task: Task) } -final class ListViewController: UIViewController { +final class ListViewController: UIViewController, AlertControllerShowable { enum Section { case main } @@ -87,15 +88,15 @@ extension ListViewController { self.taskList = taskList snapShot.appendSections([.main]) snapShot.appendItems(taskList) - snapShot.reloadSections([.main]) diffableDataSource?.apply(snapShot) } private func setUpDiffableDataSource() { - let cellRegistration = UICollectionView.CellRegistration { cell, indexPath, task in - cell.setUpContents(title: task.title, - description: task.description, - deadline: task.deadline) + let cellRegistration = UICollectionView.CellRegistration { [weak self] cell, indexPath, task in + guard let self = self else { return } + + cell.delegate = self + cell.setUpContents(task: self.taskList[indexPath.row]) } diffableDataSource = UICollectionViewDiffableDataSource(collectionView: collectionView, cellProvider: { collectionView, indexPath, task in @@ -166,3 +167,44 @@ extension ListViewController: TaskViewControllerDelegate { } } +// MARK: - ListCollectionViewCell Delegate +extension ListViewController: ListCollectionViewCellDelegate { + func didLongPressCell(task: Task, cellFrame: CGRect) { + let (firstMoveAlertTitle, secondMoveAlerTitle) = convertAlertsTitle() + + let firstMoveAlertAction: UIAlertAction = .init(title: firstMoveAlertTitle, style: .default) { action in + switch self.listKind { + case .todo: + self.moveCell(moveToListKind: .doing, task: task) + case .doing, .done: + self.moveCell(moveToListKind: .todo, task: task) + } + } + + let secondMoveAlertAction: UIAlertAction = .init(title: secondMoveAlerTitle, style: .default) { action in + switch self.listKind { + case .todo, .doing: + self.moveCell(moveToListKind: .done, task: task) + case .done: + self.moveCell(moveToListKind: .doing, task: task) + } + } + + showPopOverAlertController(sourceRect: cellFrame, alertActions: [firstMoveAlertAction, secondMoveAlertAction]) + } + + private func moveCell(moveToListKind: ListKind, task: Task) { + delegate?.moveCell(moveToListKind: moveToListKind, task: task) + } + + private func convertAlertsTitle() -> (String, String) { + switch listKind { + case .todo: + return ("Move to DOING", "Move to DONE") + case .doing: + return ("Move to TODO", "Move to DONE") + case .done: + return ("Move to TODO", "Move to DOING") + } + } +} diff --git a/ProjectManager/ProjectManager/Controller/MainViewController.swift b/ProjectManager/ProjectManager/Controller/MainViewController.swift index e51ded5b5..c80d5fb4f 100644 --- a/ProjectManager/ProjectManager/Controller/MainViewController.swift +++ b/ProjectManager/ProjectManager/Controller/MainViewController.swift @@ -125,6 +125,10 @@ extension MainViewController: TaskViewControllerDelegate { // MARK: - ListViewController Delegate extension MainViewController: ListViewControllerDelegate { + func moveCell(moveToListKind: ListKind, task: Task) { + taskList = convertUpdatedTaskList(updateTask: task, moveTolistKind: moveToListKind) + } + func didSwipedDeleteTask(deleteTask: Task) { for (index, task) in taskList.enumerated() { if task.id == deleteTask.id { @@ -135,19 +139,26 @@ extension MainViewController: ListViewControllerDelegate { } func didTappedRightDoneButtonForUpdate(updateTask: Task) { - let updatedTaskList = taskList.map { + taskList = convertUpdatedTaskList(updateTask: updateTask) + } + + private func convertUpdatedTaskList(updateTask: Task, moveTolistKind: ListKind? = nil) -> [Task] { + return taskList.map { if $0.id == updateTask.id { var task = $0 task.title = updateTask.title task.description = updateTask.description task.deadline = updateTask.deadline + + if let moveTolistKind = moveTolistKind { + task.listKind = moveTolistKind + } + return task } return $0 } - - taskList = updatedTaskList } } diff --git a/ProjectManager/ProjectManager/Util/Protocol/AlertControllerShowable.swift b/ProjectManager/ProjectManager/Util/Protocol/AlertControllerShowable.swift new file mode 100644 index 000000000..61e641a0c --- /dev/null +++ b/ProjectManager/ProjectManager/Util/Protocol/AlertControllerShowable.swift @@ -0,0 +1,31 @@ +// +// AlertControllerShowable.swift +// ProjectManager +// +// Created by Hyungmin Lee on 2023/10/05. +// + +import UIKit + +protocol AlertControllerShowable where Self: UIViewController { + func showPopOverAlertController(sourceRect: CGRect, alertActions: [UIAlertAction]) +} + +extension AlertControllerShowable { + func showPopOverAlertController(sourceRect: CGRect, alertActions: [UIAlertAction]) { + let alertViewController = UIAlertController() + let popoverController = alertViewController.popoverPresentationController + + popoverController?.sourceView = self.view + popoverController?.sourceRect = CGRect(x: sourceRect.midX, + y: sourceRect.midY, + width: 0, + height: 0) + popoverController?.permittedArrowDirections = [.up, .down, .left, .right] + alertActions.forEach { + alertViewController.addAction($0) + } + + present(alertViewController, animated: true) + } +} diff --git a/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift b/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift index 7533866d4..7ebbc11f7 100644 --- a/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift +++ b/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift @@ -7,7 +7,13 @@ import UIKit +protocol ListCollectionViewCellDelegate: AnyObject { + func didLongPressCell(task: Task, cellFrame: CGRect) +} + final class ListCollectionViewCell: UICollectionViewListCell { + weak var delegate: ListCollectionViewCellDelegate? + private let dateFormatter: DateFormatter = { let dateFormatter = DateFormatter() @@ -51,22 +57,27 @@ final class ListCollectionViewCell: UICollectionViewListCell { return label }() + private var task: Task? + override init(frame: CGRect) { super.init(frame: frame) configureUI() setUpConstraints() + setUpLongPressGesture() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - func setUpContents(title: String, description: String, deadline: Double) { - titleLabel.text = title - descriptionLabel.text = description - deadlineLabel.text = dateFormatter.string(from: Date(timeIntervalSince1970: deadline)) - deadlineLabel.textColor = isPassDeadline(deadline: deadline) ? .red : .black + func setUpContents(task: Task) { + self.task = task + + titleLabel.text = task.title + descriptionLabel.text = task.description + deadlineLabel.text = dateFormatter.string(from: Date(timeIntervalSince1970: task.deadline)) + deadlineLabel.textColor = isPassDeadline(deadline: task.deadline) ? .red : .black } private func isPassDeadline(deadline: Double) -> Bool { @@ -96,4 +107,17 @@ final class ListCollectionViewCell: UICollectionViewListCell { descriptionLabel.setContentHuggingPriority(.init(1), for: .vertical) } + + private func setUpLongPressGesture() { + let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(didLongPress)) + + addGestureRecognizer(longPressGesture) + } + + @objc + private func didLongPress() { + guard let task = task else { return } + + delegate?.didLongPressCell(task: task, cellFrame: frame) + } } From 7f1c36f65efd32ed0196aa5f70c55f2c3ffe48d6 Mon Sep 17 00:00:00 2001 From: Zion Date: Fri, 6 Oct 2023 14:45:44 +0900 Subject: [PATCH 16/28] =?UTF-8?q?fix:=20Section=20=EA=B0=B1=EC=8B=A0?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controller/ListViewController.swift | 11 +++++++++-- .../Controller/MainViewController.swift | 6 +++--- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/ProjectManager/ProjectManager/Controller/ListViewController.swift b/ProjectManager/ProjectManager/Controller/ListViewController.swift index b3da6daed..adfc38081 100644 --- a/ProjectManager/ProjectManager/Controller/ListViewController.swift +++ b/ProjectManager/ProjectManager/Controller/ListViewController.swift @@ -23,7 +23,6 @@ final class ListViewController: UIViewController, AlertControllerShowable { enum Section { case main } - weak var delegate: ListViewControllerDelegate? private lazy var collectionView: UICollectionView = { @@ -40,6 +39,8 @@ final class ListViewController: UIViewController, AlertControllerShowable { private var taskList: [Task] = [] + private var headerView: ListCollectionHeaderView? + init(listKind: ListKind) { self.listKind = listKind @@ -82,7 +83,12 @@ final class ListViewController: UIViewController, AlertControllerShowable { // MARK: - Diffable DataSource extension ListViewController { - func setUpDiffableDataSourceSanpShot(taskList: [Task] = []) { + func reloadTaskList(taskList: [Task]) { + setUpDiffableDataSourceSanpShot(taskList: taskList) + headerView?.setUpContents(title: listKind.rawValue, taskCount: "\(taskList.count)") + } + + private func setUpDiffableDataSourceSanpShot(taskList: [Task] = []) { var snapShot = NSDiffableDataSourceSnapshot() self.taskList = taskList @@ -110,6 +116,7 @@ extension ListViewController { let headerRegistration = UICollectionView.SupplementaryRegistration(elementKind: UICollectionView.elementKindSectionHeader) { [weak self] headerView, elementKind, indexPath in guard let self = self else { return } + self.headerView = headerView headerView.setUpContents(title: self.listKind.rawValue, taskCount: "\(self.taskList.count)") } diff --git a/ProjectManager/ProjectManager/Controller/MainViewController.swift b/ProjectManager/ProjectManager/Controller/MainViewController.swift index c80d5fb4f..a986552c9 100644 --- a/ProjectManager/ProjectManager/Controller/MainViewController.swift +++ b/ProjectManager/ProjectManager/Controller/MainViewController.swift @@ -97,9 +97,9 @@ final class MainViewController: UIViewController { } } - todoListViewController.setUpDiffableDataSourceSanpShot(taskList: todoList) - doingListViewController.setUpDiffableDataSourceSanpShot(taskList: doingList) - doneListViewController.setUpDiffableDataSourceSanpShot(taskList: doneList) + todoListViewController.reloadTaskList(taskList: todoList) + doingListViewController.reloadTaskList(taskList: doingList) + doneListViewController.reloadTaskList(taskList: doneList) } } From c3d11609dc98b49797c7c041a47db2b8716cdb36 Mon Sep 17 00:00:00 2001 From: Zion Date: Fri, 6 Oct 2023 15:01:23 +0900 Subject: [PATCH 17/28] =?UTF-8?q?feat:=20showToast=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProjectManager.xcodeproj/project.pbxproj | 4 ++ .../Controller/TaskViewController.swift | 7 +++- .../Util/Protocol/ToastShowable.swift | 37 +++++++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 ProjectManager/ProjectManager/Util/Protocol/ToastShowable.swift diff --git a/ProjectManager/ProjectManager.xcodeproj/project.pbxproj b/ProjectManager/ProjectManager.xcodeproj/project.pbxproj index 4762e1f35..b40b62625 100644 --- a/ProjectManager/ProjectManager.xcodeproj/project.pbxproj +++ b/ProjectManager/ProjectManager.xcodeproj/project.pbxproj @@ -12,6 +12,7 @@ 5B1666B82ACBEB7100478F1D /* ListCollectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1666B72ACBEB7100478F1D /* ListCollectionHeaderView.swift */; }; 5B1666DE2ACEDDC000478F1D /* AlertControllerShowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1666DD2ACEDDC000478F1D /* AlertControllerShowable.swift */; }; 5B9D60322ACBD4E9004905F6 /* ListCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B9D60312ACBD4E9004905F6 /* ListCollectionViewCell.swift */; }; + 5BD9FD652ACFD8A700EE70B7 /* ToastShowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BD9FD642ACFD8A700EE70B7 /* ToastShowable.swift */; }; 9C4F6E812ACBE29700FDC393 /* ListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C4F6E802ACBE29700FDC393 /* ListViewController.swift */; }; C7431F0625F51E1D0094C4CF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7431F0525F51E1D0094C4CF /* AppDelegate.swift */; }; C7431F0825F51E1D0094C4CF /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7431F0725F51E1D0094C4CF /* SceneDelegate.swift */; }; @@ -26,6 +27,7 @@ 5B1666B72ACBEB7100478F1D /* ListCollectionHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListCollectionHeaderView.swift; sourceTree = ""; }; 5B1666DD2ACEDDC000478F1D /* AlertControllerShowable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertControllerShowable.swift; sourceTree = ""; }; 5B9D60312ACBD4E9004905F6 /* ListCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListCollectionViewCell.swift; sourceTree = ""; }; + 5BD9FD642ACFD8A700EE70B7 /* ToastShowable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastShowable.swift; sourceTree = ""; }; 9C4F6E802ACBE29700FDC393 /* ListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListViewController.swift; sourceTree = ""; }; C7431F0225F51E1D0094C4CF /* ProjectManager.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ProjectManager.app; sourceTree = BUILT_PRODUCTS_DIR; }; C7431F0525F51E1D0094C4CF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -67,6 +69,7 @@ isa = PBXGroup; children = ( 5B1666DD2ACEDDC000478F1D /* AlertControllerShowable.swift */, + 5BD9FD642ACFD8A700EE70B7 /* ToastShowable.swift */, ); path = Protocol; sourceTree = ""; @@ -207,6 +210,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 5BD9FD652ACFD8A700EE70B7 /* ToastShowable.swift in Sources */, 5B1666DE2ACEDDC000478F1D /* AlertControllerShowable.swift in Sources */, 5B1666B62ACBE99300478F1D /* Task.swift in Sources */, C7431F0A25F51E1D0094C4CF /* MainViewController.swift in Sources */, diff --git a/ProjectManager/ProjectManager/Controller/TaskViewController.swift b/ProjectManager/ProjectManager/Controller/TaskViewController.swift index 3ee202d0f..c28ba2e42 100644 --- a/ProjectManager/ProjectManager/Controller/TaskViewController.swift +++ b/ProjectManager/ProjectManager/Controller/TaskViewController.swift @@ -11,7 +11,7 @@ protocol TaskViewControllerDelegate: AnyObject { func didTappedRightDoneButton(task: Task) } -final class TaskViewController: UIViewController { +final class TaskViewController: UIViewController, ToastShowable { enum Mode { case append case update @@ -170,7 +170,10 @@ extension TaskViewController { } private func didTappedRightDoneButton() { - guard let title = titleTextField.text else { return } + guard let title = titleTextField.text, titleTextField.text?.count != 0 else { + showToast(message: "Empty Title") + return + } task.title = title task.description = descriptionTextView.text diff --git a/ProjectManager/ProjectManager/Util/Protocol/ToastShowable.swift b/ProjectManager/ProjectManager/Util/Protocol/ToastShowable.swift new file mode 100644 index 000000000..b15280680 --- /dev/null +++ b/ProjectManager/ProjectManager/Util/Protocol/ToastShowable.swift @@ -0,0 +1,37 @@ +// +// ToastShowable.swift +// ProjectManager +// +// Created by Hyungmin Lee on 2023/10/06. +// + +import UIKit + +protocol ToastShowable where Self: UIViewController { + func showToast(message: String) +} + +extension ToastShowable { + func showToast(message: String) { + let toastLabel = UILabel(frame: CGRect(x: view.frame.size.width/2 - 75, + y: view.frame.size.height-100, + width: 150, + height: 35)) + + toastLabel.backgroundColor = .black.withAlphaComponent(0.6) + toastLabel.textColor = .white + toastLabel.font = .systemFont(ofSize: 15) + toastLabel.textAlignment = .center + toastLabel.text = message + toastLabel.alpha = 1.0 + toastLabel.layer.cornerRadius = 10 + toastLabel.clipsToBounds = true + view.addSubview(toastLabel) + + UIView.animate(withDuration: 2.0, delay: 0.1, options: .curveEaseOut, animations: { + toastLabel.alpha = 0.0 + }, completion: { _ in + toastLabel.removeFromSuperview() + }) + } +} From 4a0ff417698d4f039925a1b44326f66b5ff3ba96 Mon Sep 17 00:00:00 2001 From: Zion Date: Fri, 6 Oct 2023 15:19:28 +0900 Subject: [PATCH 18/28] =?UTF-8?q?feat:=20MainViewController=20UseCase=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProjectManager.xcodeproj/project.pbxproj | 12 ++++++ .../ProjectManager/App/SceneDelegate.swift | 3 +- .../Controller/ListViewController.swift | 4 +- .../Controller/MainViewController.swift | 40 +++++++++---------- .../UseCase/MainViewControllerUseCase.swift | 38 ++++++++++++++++++ .../Protocol/AlertControllerShowable.swift | 2 +- .../Util/Protocol/ToastShowable.swift | 2 +- 7 files changed, 74 insertions(+), 27 deletions(-) create mode 100644 ProjectManager/ProjectManager/UseCase/MainViewControllerUseCase.swift diff --git a/ProjectManager/ProjectManager.xcodeproj/project.pbxproj b/ProjectManager/ProjectManager.xcodeproj/project.pbxproj index b40b62625..5d4643843 100644 --- a/ProjectManager/ProjectManager.xcodeproj/project.pbxproj +++ b/ProjectManager/ProjectManager.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 5B1666DE2ACEDDC000478F1D /* AlertControllerShowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B1666DD2ACEDDC000478F1D /* AlertControllerShowable.swift */; }; 5B9D60322ACBD4E9004905F6 /* ListCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B9D60312ACBD4E9004905F6 /* ListCollectionViewCell.swift */; }; 5BD9FD652ACFD8A700EE70B7 /* ToastShowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BD9FD642ACFD8A700EE70B7 /* ToastShowable.swift */; }; + 5BD9FD682ACFDCCF00EE70B7 /* MainViewControllerUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BD9FD672ACFDCCF00EE70B7 /* MainViewControllerUseCase.swift */; }; 9C4F6E812ACBE29700FDC393 /* ListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C4F6E802ACBE29700FDC393 /* ListViewController.swift */; }; C7431F0625F51E1D0094C4CF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7431F0525F51E1D0094C4CF /* AppDelegate.swift */; }; C7431F0825F51E1D0094C4CF /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7431F0725F51E1D0094C4CF /* SceneDelegate.swift */; }; @@ -28,6 +29,7 @@ 5B1666DD2ACEDDC000478F1D /* AlertControllerShowable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertControllerShowable.swift; sourceTree = ""; }; 5B9D60312ACBD4E9004905F6 /* ListCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListCollectionViewCell.swift; sourceTree = ""; }; 5BD9FD642ACFD8A700EE70B7 /* ToastShowable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastShowable.swift; sourceTree = ""; }; + 5BD9FD672ACFDCCF00EE70B7 /* MainViewControllerUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewControllerUseCase.swift; sourceTree = ""; }; 9C4F6E802ACBE29700FDC393 /* ListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListViewController.swift; sourceTree = ""; }; C7431F0225F51E1D0094C4CF /* ProjectManager.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ProjectManager.app; sourceTree = BUILT_PRODUCTS_DIR; }; C7431F0525F51E1D0094C4CF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -111,6 +113,14 @@ path = View; sourceTree = ""; }; + 5BD9FD662ACFDCBE00EE70B7 /* UseCase */ = { + isa = PBXGroup; + children = ( + 5BD9FD672ACFDCCF00EE70B7 /* MainViewControllerUseCase.swift */, + ); + path = UseCase; + sourceTree = ""; + }; C7431EF925F51E1D0094C4CF = { isa = PBXGroup; children = ( @@ -131,6 +141,7 @@ isa = PBXGroup; children = ( C7431F1325F51E1E0094C4CF /* Info.plist */, + 5BD9FD662ACFDCBE00EE70B7 /* UseCase */, 5B1666DB2ACEDDA800478F1D /* Util */, 5B1666B42ACBE98400478F1D /* Model */, 5B9D60302ACBD469004905F6 /* View */, @@ -214,6 +225,7 @@ 5B1666DE2ACEDDC000478F1D /* AlertControllerShowable.swift in Sources */, 5B1666B62ACBE99300478F1D /* Task.swift in Sources */, C7431F0A25F51E1D0094C4CF /* MainViewController.swift in Sources */, + 5BD9FD682ACFDCCF00EE70B7 /* MainViewControllerUseCase.swift in Sources */, C7431F0625F51E1D0094C4CF /* AppDelegate.swift in Sources */, 5B9D60322ACBD4E9004905F6 /* ListCollectionViewCell.swift in Sources */, C7431F0825F51E1D0094C4CF /* SceneDelegate.swift in Sources */, diff --git a/ProjectManager/ProjectManager/App/SceneDelegate.swift b/ProjectManager/ProjectManager/App/SceneDelegate.swift index 3e2d32e9c..26014cf33 100644 --- a/ProjectManager/ProjectManager/App/SceneDelegate.swift +++ b/ProjectManager/ProjectManager/App/SceneDelegate.swift @@ -11,7 +11,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } - let mainViewController = MainViewController() + let useCase: MainViewControllerUseCaseType = MainViewControllerUseCase() + let mainViewController = MainViewController(useCase: useCase) let navigationController = UINavigationController(rootViewController: mainViewController) window = UIWindow(windowScene: windowScene) diff --git a/ProjectManager/ProjectManager/Controller/ListViewController.swift b/ProjectManager/ProjectManager/Controller/ListViewController.swift index adfc38081..c0edd68c2 100644 --- a/ProjectManager/ProjectManager/Controller/ListViewController.swift +++ b/ProjectManager/ProjectManager/Controller/ListViewController.swift @@ -129,13 +129,13 @@ extension ListViewController { return UICollectionViewCompositionalLayout { sectionIndex, layoutEnvironment in var listLayout = UICollectionLayoutListConfiguration(appearance: .grouped) - listLayout.trailingSwipeActionsConfigurationProvider = { - indexPath in + listLayout.trailingSwipeActionsConfigurationProvider = { indexPath in let deleteAction = UIContextualAction(style: .destructive, title: "Delete") { [weak self] action, view, handler in guard let self = self else { return } self.didSwipedDeleteTask(deleteTask: self.taskList[indexPath.row]) } + return UISwipeActionsConfiguration(actions: [deleteAction]) } diff --git a/ProjectManager/ProjectManager/Controller/MainViewController.swift b/ProjectManager/ProjectManager/Controller/MainViewController.swift index a986552c9..975b8f3d6 100644 --- a/ProjectManager/ProjectManager/Controller/MainViewController.swift +++ b/ProjectManager/ProjectManager/Controller/MainViewController.swift @@ -45,6 +45,18 @@ final class MainViewController: UIViewController { return stackView }() + private let useCase: MainViewControllerUseCaseType + + init(useCase: MainViewControllerUseCaseType) { + self.useCase = useCase + + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + override func viewDidLoad() { super.viewDidLoad() @@ -126,7 +138,9 @@ extension MainViewController: TaskViewControllerDelegate { // MARK: - ListViewController Delegate extension MainViewController: ListViewControllerDelegate { func moveCell(moveToListKind: ListKind, task: Task) { - taskList = convertUpdatedTaskList(updateTask: task, moveTolistKind: moveToListKind) + taskList = useCase.convertUpdatedTaskList(taskList: taskList, + updateTask: task, + moveTolistKind: moveToListKind) } func didSwipedDeleteTask(deleteTask: Task) { @@ -139,26 +153,8 @@ extension MainViewController: ListViewControllerDelegate { } func didTappedRightDoneButtonForUpdate(updateTask: Task) { - taskList = convertUpdatedTaskList(updateTask: updateTask) - } - - private func convertUpdatedTaskList(updateTask: Task, moveTolistKind: ListKind? = nil) -> [Task] { - return taskList.map { - if $0.id == updateTask.id { - var task = $0 - - task.title = updateTask.title - task.description = updateTask.description - task.deadline = updateTask.deadline - - if let moveTolistKind = moveTolistKind { - task.listKind = moveTolistKind - } - - return task - } - - return $0 - } + taskList = useCase.convertUpdatedTaskList(taskList: taskList, + updateTask: updateTask, + moveTolistKind: updateTask.listKind) } } diff --git a/ProjectManager/ProjectManager/UseCase/MainViewControllerUseCase.swift b/ProjectManager/ProjectManager/UseCase/MainViewControllerUseCase.swift new file mode 100644 index 000000000..8493a709f --- /dev/null +++ b/ProjectManager/ProjectManager/UseCase/MainViewControllerUseCase.swift @@ -0,0 +1,38 @@ +// +// MainViewControllerUseCase.swift +// ProjectManager +// +// Created by Karen, Zion on 2023/10/06. +// + +import Foundation + +protocol MainViewControllerUseCaseType { + func convertUpdatedTaskList(taskList: [Task], + updateTask: Task, + moveTolistKind: ListKind?) -> [Task] +} + +final class MainViewControllerUseCase: MainViewControllerUseCaseType { + func convertUpdatedTaskList(taskList: [Task], + updateTask: Task, + moveTolistKind: ListKind? = nil) -> [Task] { + return taskList.map { + if $0.id == updateTask.id { + var task = $0 + + task.title = updateTask.title + task.description = updateTask.description + task.deadline = updateTask.deadline + + if let moveTolistKind = moveTolistKind { + task.listKind = moveTolistKind + } + + return task + } + + return $0 + } + } +} diff --git a/ProjectManager/ProjectManager/Util/Protocol/AlertControllerShowable.swift b/ProjectManager/ProjectManager/Util/Protocol/AlertControllerShowable.swift index 61e641a0c..07a456f80 100644 --- a/ProjectManager/ProjectManager/Util/Protocol/AlertControllerShowable.swift +++ b/ProjectManager/ProjectManager/Util/Protocol/AlertControllerShowable.swift @@ -2,7 +2,7 @@ // AlertControllerShowable.swift // ProjectManager // -// Created by Hyungmin Lee on 2023/10/05. +// Created by Karen, Zion on 2023/10/05. // import UIKit diff --git a/ProjectManager/ProjectManager/Util/Protocol/ToastShowable.swift b/ProjectManager/ProjectManager/Util/Protocol/ToastShowable.swift index b15280680..c68dba88a 100644 --- a/ProjectManager/ProjectManager/Util/Protocol/ToastShowable.swift +++ b/ProjectManager/ProjectManager/Util/Protocol/ToastShowable.swift @@ -2,7 +2,7 @@ // ToastShowable.swift // ProjectManager // -// Created by Hyungmin Lee on 2023/10/06. +// Created by Karen, Zion on 2023/10/06. // import UIKit From 7cf5bf10d10d30254b80c99b38c12056adb163c8 Mon Sep 17 00:00:00 2001 From: karen Date: Sun, 8 Oct 2023 15:14:12 +0900 Subject: [PATCH 19/28] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=BB=A8=EB=B2=A4=EC=85=98=20=EC=82=AD=EC=A0=9C,?= =?UTF-8?q?=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controller/ListViewController.swift | 6 ++--- .../Controller/MainViewController.swift | 12 +++++----- .../Controller/TaskViewController.swift | 22 +++++++++---------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/ProjectManager/ProjectManager/Controller/ListViewController.swift b/ProjectManager/ProjectManager/Controller/ListViewController.swift index c0edd68c2..2b9d64d39 100644 --- a/ProjectManager/ProjectManager/Controller/ListViewController.swift +++ b/ProjectManager/ProjectManager/Controller/ListViewController.swift @@ -14,7 +14,7 @@ enum ListKind: String { } protocol ListViewControllerDelegate: AnyObject { - func didTappedRightDoneButtonForUpdate(updateTask: Task) + func didTappedDoneButtonForUpdate(updateTask: Task) func didSwipedDeleteTask(deleteTask: Task) func moveCell(moveToListKind: ListKind, task: Task) } @@ -165,8 +165,8 @@ extension ListViewController: UICollectionViewDelegate { // MARK: - TaskViewController Delegate extension ListViewController: TaskViewControllerDelegate { - func didTappedRightDoneButton(task: Task) { - delegate?.didTappedRightDoneButtonForUpdate(updateTask: task) + func didTappedDoneButton(task: Task) { + delegate?.didTappedDoneButtonForUpdate(updateTask: task) } func didSwipedDeleteTask(deleteTask: Task) { diff --git a/ProjectManager/ProjectManager/Controller/MainViewController.swift b/ProjectManager/ProjectManager/Controller/MainViewController.swift index 975b8f3d6..166aa08b2 100644 --- a/ProjectManager/ProjectManager/Controller/MainViewController.swift +++ b/ProjectManager/ProjectManager/Controller/MainViewController.swift @@ -86,11 +86,11 @@ final class MainViewController: UIViewController { view.backgroundColor = .systemBackground navigationItem.title = "Project Manager" - let rightAddButtonAction: UIAction = .init { - action in self.didTappedRightAddButton() + let addButtonAction: UIAction = .init { + action in self.didTappedAddButton() } - navigationItem.rightBarButtonItem = .init(systemItem: .add, primaryAction: rightAddButtonAction) + navigationItem.rightBarButtonItem = .init(systemItem: .add, primaryAction: addButtonAction) } private func reloadTaskListViewControllers() { @@ -117,7 +117,7 @@ final class MainViewController: UIViewController { // MARK: - Button Action extension MainViewController { - private func didTappedRightAddButton() { + private func didTappedAddButton() { let newTask = Task(title: "", description: "", deadline: 0.0) let taskViewController = TaskViewController(task: newTask, mode: .append) let navigationController = UINavigationController(rootViewController: taskViewController) @@ -130,7 +130,7 @@ extension MainViewController { // MARK: - TaskViewController Delegate extension MainViewController: TaskViewControllerDelegate { - func didTappedRightDoneButton(task: Task) { + func didTappedDoneButton(task: Task) { taskList.append(task) } } @@ -152,7 +152,7 @@ extension MainViewController: ListViewControllerDelegate { } } - func didTappedRightDoneButtonForUpdate(updateTask: Task) { + func didTappedDoneButtonForUpdate(updateTask: Task) { taskList = useCase.convertUpdatedTaskList(taskList: taskList, updateTask: updateTask, moveTolistKind: updateTask.listKind) diff --git a/ProjectManager/ProjectManager/Controller/TaskViewController.swift b/ProjectManager/ProjectManager/Controller/TaskViewController.swift index c28ba2e42..cc8966ade 100644 --- a/ProjectManager/ProjectManager/Controller/TaskViewController.swift +++ b/ProjectManager/ProjectManager/Controller/TaskViewController.swift @@ -8,7 +8,7 @@ import UIKit protocol TaskViewControllerDelegate: AnyObject { - func didTappedRightDoneButton(task: Task) + func didTappedDoneButton(task: Task) } final class TaskViewController: UIViewController, ToastShowable { @@ -17,7 +17,7 @@ final class TaskViewController: UIViewController, ToastShowable { case update } - weak var delegate: TaskViewControllerDelegate? + weak var delegate: TaskViewControllerDelegate? private let stackView: UIStackView = { let stackView = UIStackView() @@ -126,17 +126,17 @@ final class TaskViewController: UIViewController, ToastShowable { view.backgroundColor = .systemBackground navigationItem.title = "TODO" - let leftCancelButtonAction: UIAction = .init { action in - self.didTappedLeftCancelButton() + let cancelButtonAction: UIAction = .init { action in + self.didTappedCancelButton() } - let rightDoneButtonAction: UIAction = .init { action in - self.didTappedRightDoneButton() + let doneButtonAction: UIAction = .init { action in + self.didTappedDoneButton() } - navigationItem.leftBarButtonItem = .init(systemItem: .cancel, primaryAction: leftCancelButtonAction) + navigationItem.leftBarButtonItem = .init(systemItem: .cancel, primaryAction: cancelButtonAction) navigationItem.rightBarButtonItem = .init(systemItem: mode == .append ? .done : .edit, - primaryAction: rightDoneButtonAction) + primaryAction: doneButtonAction) } private func setUpContents() { @@ -165,11 +165,11 @@ extension TaskViewController: UITextViewDelegate { // MARK: - Button Action extension TaskViewController { - private func didTappedLeftCancelButton() { + private func didTappedCancelButton() { dismiss(animated: true) } - private func didTappedRightDoneButton() { + private func didTappedDoneButton() { guard let title = titleTextField.text, titleTextField.text?.count != 0 else { showToast(message: "Empty Title") return @@ -178,7 +178,7 @@ extension TaskViewController { task.title = title task.description = descriptionTextView.text task.deadline = datePicker.date.timeIntervalSince1970 - delegate?.didTappedRightDoneButton(task: task) + delegate?.didTappedDoneButton(task: task) dismiss(animated: true) } } From 7240a33f9a91f0462cab63e03d48b609fb423701 Mon Sep 17 00:00:00 2001 From: karen Date: Sun, 8 Oct 2023 16:10:55 +0900 Subject: [PATCH 20/28] =?UTF-8?q?feat:=20descriptionTextView=EA=B8=80?= =?UTF-8?q?=EC=9E=90=EC=88=98=20=EC=A0=9C=ED=95=9C=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProjectManager/Controller/TaskViewController.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ProjectManager/ProjectManager/Controller/TaskViewController.swift b/ProjectManager/ProjectManager/Controller/TaskViewController.swift index cc8966ade..b3cc10811 100644 --- a/ProjectManager/ProjectManager/Controller/TaskViewController.swift +++ b/ProjectManager/ProjectManager/Controller/TaskViewController.swift @@ -161,6 +161,12 @@ extension TaskViewController: UITextViewDelegate { placeHolderLabel.isHidden = false } } + + func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { + let willChangeTextCount = textView.text.count - range.length + text.count + + return willChangeTextCount > 1000 ? false : true + } } // MARK: - Button Action From e09022467aaf0acf690ba3a9a842d2f470f7b01d Mon Sep 17 00:00:00 2001 From: Zion Date: Sun, 8 Oct 2023 16:19:50 +0900 Subject: [PATCH 21/28] =?UTF-8?q?refactor:=20listKind=20->=20TaskStatus?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controller/ListViewController.swift | 32 +++++++++---------- .../Controller/MainViewController.swift | 14 ++++---- .../ProjectManager/Model/Task.swift | 2 +- .../UseCase/MainViewControllerUseCase.swift | 8 ++--- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/ProjectManager/ProjectManager/Controller/ListViewController.swift b/ProjectManager/ProjectManager/Controller/ListViewController.swift index 2b9d64d39..f550e9250 100644 --- a/ProjectManager/ProjectManager/Controller/ListViewController.swift +++ b/ProjectManager/ProjectManager/Controller/ListViewController.swift @@ -7,7 +7,7 @@ import UIKit -enum ListKind: String { +enum TaskStatus: String { case todo = "TODO" case doing = "DOING" case done = "DONE" @@ -16,7 +16,7 @@ enum ListKind: String { protocol ListViewControllerDelegate: AnyObject { func didTappedDoneButtonForUpdate(updateTask: Task) func didSwipedDeleteTask(deleteTask: Task) - func moveCell(moveToListKind: ListKind, task: Task) + func moveCell(moveToTaskStatus: TaskStatus, task: Task) } final class ListViewController: UIViewController, AlertControllerShowable { @@ -35,14 +35,14 @@ final class ListViewController: UIViewController, AlertControllerShowable { private var diffableDataSource: UICollectionViewDiffableDataSource? - private let listKind: ListKind + private let taskStatus: TaskStatus private var taskList: [Task] = [] private var headerView: ListCollectionHeaderView? - init(listKind: ListKind) { - self.listKind = listKind + init(taskStatus: TaskStatus) { + self.taskStatus = taskStatus super.init(nibName: nil, bundle: nil) } @@ -85,7 +85,7 @@ final class ListViewController: UIViewController, AlertControllerShowable { extension ListViewController { func reloadTaskList(taskList: [Task]) { setUpDiffableDataSourceSanpShot(taskList: taskList) - headerView?.setUpContents(title: listKind.rawValue, taskCount: "\(taskList.count)") + headerView?.setUpContents(title: taskStatus.rawValue, taskCount: "\(taskList.count)") } private func setUpDiffableDataSourceSanpShot(taskList: [Task] = []) { @@ -117,7 +117,7 @@ extension ListViewController { guard let self = self else { return } self.headerView = headerView - headerView.setUpContents(title: self.listKind.rawValue, taskCount: "\(self.taskList.count)") + headerView.setUpContents(title: self.taskStatus.rawValue, taskCount: "\(self.taskList.count)") } diffableDataSource?.supplementaryViewProvider = { collectionView, kind, indexPath in @@ -180,32 +180,32 @@ extension ListViewController: ListCollectionViewCellDelegate { let (firstMoveAlertTitle, secondMoveAlerTitle) = convertAlertsTitle() let firstMoveAlertAction: UIAlertAction = .init(title: firstMoveAlertTitle, style: .default) { action in - switch self.listKind { + switch self.taskStatus { case .todo: - self.moveCell(moveToListKind: .doing, task: task) + self.moveCell(moveToTaskStatus: .doing, task: task) case .doing, .done: - self.moveCell(moveToListKind: .todo, task: task) + self.moveCell(moveToTaskStatus: .todo, task: task) } } let secondMoveAlertAction: UIAlertAction = .init(title: secondMoveAlerTitle, style: .default) { action in - switch self.listKind { + switch self.taskStatus { case .todo, .doing: - self.moveCell(moveToListKind: .done, task: task) + self.moveCell(moveToTaskStatus: .done, task: task) case .done: - self.moveCell(moveToListKind: .doing, task: task) + self.moveCell(moveToTaskStatus: .doing, task: task) } } showPopOverAlertController(sourceRect: cellFrame, alertActions: [firstMoveAlertAction, secondMoveAlertAction]) } - private func moveCell(moveToListKind: ListKind, task: Task) { - delegate?.moveCell(moveToListKind: moveToListKind, task: task) + private func moveCell(moveToTaskStatus: TaskStatus, task: Task) { + delegate?.moveCell(moveToTaskStatus: moveToTaskStatus, task: task) } private func convertAlertsTitle() -> (String, String) { - switch listKind { + switch taskStatus { case .todo: return ("Move to DOING", "Move to DONE") case .doing: diff --git a/ProjectManager/ProjectManager/Controller/MainViewController.swift b/ProjectManager/ProjectManager/Controller/MainViewController.swift index 166aa08b2..7c515ff1b 100644 --- a/ProjectManager/ProjectManager/Controller/MainViewController.swift +++ b/ProjectManager/ProjectManager/Controller/MainViewController.swift @@ -8,21 +8,21 @@ import UIKit final class MainViewController: UIViewController { private lazy var todoListViewController: ListViewController = { - let listViewController = ListViewController(listKind: .todo) + let listViewController = ListViewController(taskStatus: .todo) listViewController.delegate = self return listViewController }() private lazy var doingListViewController: ListViewController = { - let listViewController = ListViewController(listKind: .doing) + let listViewController = ListViewController(taskStatus: .doing) listViewController.delegate = self return listViewController }() private lazy var doneListViewController: ListViewController = { - let listViewController = ListViewController(listKind: .done) + let listViewController = ListViewController(taskStatus: .done) listViewController.delegate = self return listViewController @@ -99,7 +99,7 @@ final class MainViewController: UIViewController { var doneList = [Task]() for task in taskList { - switch task.listKind { + switch task.taskStatus { case .todo: todoList.append(task) case .doing: @@ -137,10 +137,10 @@ extension MainViewController: TaskViewControllerDelegate { // MARK: - ListViewController Delegate extension MainViewController: ListViewControllerDelegate { - func moveCell(moveToListKind: ListKind, task: Task) { + func moveCell(moveToTaskStatus: TaskStatus, task: Task) { taskList = useCase.convertUpdatedTaskList(taskList: taskList, updateTask: task, - moveTolistKind: moveToListKind) + moveToTaskStatus: moveToTaskStatus) } func didSwipedDeleteTask(deleteTask: Task) { @@ -155,6 +155,6 @@ extension MainViewController: ListViewControllerDelegate { func didTappedDoneButtonForUpdate(updateTask: Task) { taskList = useCase.convertUpdatedTaskList(taskList: taskList, updateTask: updateTask, - moveTolistKind: updateTask.listKind) + moveToTaskStatus: updateTask.taskStatus) } } diff --git a/ProjectManager/ProjectManager/Model/Task.swift b/ProjectManager/ProjectManager/Model/Task.swift index 75c0e930d..f19cc38f4 100644 --- a/ProjectManager/ProjectManager/Model/Task.swift +++ b/ProjectManager/ProjectManager/Model/Task.swift @@ -12,5 +12,5 @@ struct Task: Hashable, Identifiable { var title: String var description: String var deadline: Double - var listKind: ListKind = .todo + var taskStatus: TaskStatus = .todo } diff --git a/ProjectManager/ProjectManager/UseCase/MainViewControllerUseCase.swift b/ProjectManager/ProjectManager/UseCase/MainViewControllerUseCase.swift index 8493a709f..f51cd0b63 100644 --- a/ProjectManager/ProjectManager/UseCase/MainViewControllerUseCase.swift +++ b/ProjectManager/ProjectManager/UseCase/MainViewControllerUseCase.swift @@ -10,13 +10,13 @@ import Foundation protocol MainViewControllerUseCaseType { func convertUpdatedTaskList(taskList: [Task], updateTask: Task, - moveTolistKind: ListKind?) -> [Task] + moveToTaskStatus: TaskStatus?) -> [Task] } final class MainViewControllerUseCase: MainViewControllerUseCaseType { func convertUpdatedTaskList(taskList: [Task], updateTask: Task, - moveTolistKind: ListKind? = nil) -> [Task] { + moveToTaskStatus: TaskStatus? = nil) -> [Task] { return taskList.map { if $0.id == updateTask.id { var task = $0 @@ -25,8 +25,8 @@ final class MainViewControllerUseCase: MainViewControllerUseCaseType { task.description = updateTask.description task.deadline = updateTask.deadline - if let moveTolistKind = moveTolistKind { - task.listKind = moveTolistKind + if let moveToTaskStatus = moveToTaskStatus { + task.taskStatus = moveToTaskStatus } return task From 28957b6893ae5d003683e7998110f4586590d874 Mon Sep 17 00:00:00 2001 From: Zion Date: Sun, 8 Oct 2023 16:21:28 +0900 Subject: [PATCH 22/28] =?UTF-8?q?refactor:=20deleteTask=20=EC=A4=91?= =?UTF-8?q?=EB=B3=B5=20=EB=85=B8=EC=B6=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProjectManager/Controller/ListViewController.swift | 8 ++++---- .../ProjectManager/Controller/MainViewController.swift | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ProjectManager/ProjectManager/Controller/ListViewController.swift b/ProjectManager/ProjectManager/Controller/ListViewController.swift index f550e9250..5ac2ade4d 100644 --- a/ProjectManager/ProjectManager/Controller/ListViewController.swift +++ b/ProjectManager/ProjectManager/Controller/ListViewController.swift @@ -15,7 +15,7 @@ enum TaskStatus: String { protocol ListViewControllerDelegate: AnyObject { func didTappedDoneButtonForUpdate(updateTask: Task) - func didSwipedDeleteTask(deleteTask: Task) + func didSwipedDeleteTask(_ deleteTask: Task) func moveCell(moveToTaskStatus: TaskStatus, task: Task) } @@ -133,7 +133,7 @@ extension ListViewController { let deleteAction = UIContextualAction(style: .destructive, title: "Delete") { [weak self] action, view, handler in guard let self = self else { return } - self.didSwipedDeleteTask(deleteTask: self.taskList[indexPath.row]) + self.didSwipedDeleteTask(self.taskList[indexPath.row]) } return UISwipeActionsConfiguration(actions: [deleteAction]) @@ -169,8 +169,8 @@ extension ListViewController: TaskViewControllerDelegate { delegate?.didTappedDoneButtonForUpdate(updateTask: task) } - func didSwipedDeleteTask(deleteTask: Task) { - delegate?.didSwipedDeleteTask(deleteTask: deleteTask) + func didSwipedDeleteTask(_ deleteTask: Task) { + delegate?.didSwipedDeleteTask(deleteTask) } } diff --git a/ProjectManager/ProjectManager/Controller/MainViewController.swift b/ProjectManager/ProjectManager/Controller/MainViewController.swift index 7c515ff1b..30b1fbeba 100644 --- a/ProjectManager/ProjectManager/Controller/MainViewController.swift +++ b/ProjectManager/ProjectManager/Controller/MainViewController.swift @@ -143,7 +143,7 @@ extension MainViewController: ListViewControllerDelegate { moveToTaskStatus: moveToTaskStatus) } - func didSwipedDeleteTask(deleteTask: Task) { + func didSwipedDeleteTask(_ deleteTask: Task) { for (index, task) in taskList.enumerated() { if task.id == deleteTask.id { taskList.remove(at: index) From 293339595a5a5804003c7ab8749b6e710589a345 Mon Sep 17 00:00:00 2001 From: Zion Date: Sun, 8 Oct 2023 17:03:58 +0900 Subject: [PATCH 23/28] =?UTF-8?q?feat:=20ListViewControllerUseCase=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProjectManager.xcodeproj/project.pbxproj | 8 +++ .../ProjectManager/App/SceneDelegate.swift | 6 +- .../Controller/ListViewController.swift | 11 +++- .../Controller/MainViewController.swift | 22 +++++--- .../ProjectManager/Model/TaskDTO.swift | 17 ++++++ .../UseCase/ListViewControllerUseCase.swift | 55 +++++++++++++++++++ .../View/ListCollectionViewCell.swift | 29 ++++------ 7 files changed, 116 insertions(+), 32 deletions(-) create mode 100644 ProjectManager/ProjectManager/Model/TaskDTO.swift create mode 100644 ProjectManager/ProjectManager/UseCase/ListViewControllerUseCase.swift diff --git a/ProjectManager/ProjectManager.xcodeproj/project.pbxproj b/ProjectManager/ProjectManager.xcodeproj/project.pbxproj index 5d4643843..00c8c977b 100644 --- a/ProjectManager/ProjectManager.xcodeproj/project.pbxproj +++ b/ProjectManager/ProjectManager.xcodeproj/project.pbxproj @@ -14,6 +14,8 @@ 5B9D60322ACBD4E9004905F6 /* ListCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B9D60312ACBD4E9004905F6 /* ListCollectionViewCell.swift */; }; 5BD9FD652ACFD8A700EE70B7 /* ToastShowable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BD9FD642ACFD8A700EE70B7 /* ToastShowable.swift */; }; 5BD9FD682ACFDCCF00EE70B7 /* MainViewControllerUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BD9FD672ACFDCCF00EE70B7 /* MainViewControllerUseCase.swift */; }; + 5BD9FD6A2AD292C500EE70B7 /* ListViewControllerUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BD9FD692AD292C500EE70B7 /* ListViewControllerUseCase.swift */; }; + 5BD9FD6C2AD2931700EE70B7 /* TaskDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BD9FD6B2AD2931700EE70B7 /* TaskDTO.swift */; }; 9C4F6E812ACBE29700FDC393 /* ListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C4F6E802ACBE29700FDC393 /* ListViewController.swift */; }; C7431F0625F51E1D0094C4CF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7431F0525F51E1D0094C4CF /* AppDelegate.swift */; }; C7431F0825F51E1D0094C4CF /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7431F0725F51E1D0094C4CF /* SceneDelegate.swift */; }; @@ -30,6 +32,8 @@ 5B9D60312ACBD4E9004905F6 /* ListCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListCollectionViewCell.swift; sourceTree = ""; }; 5BD9FD642ACFD8A700EE70B7 /* ToastShowable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastShowable.swift; sourceTree = ""; }; 5BD9FD672ACFDCCF00EE70B7 /* MainViewControllerUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewControllerUseCase.swift; sourceTree = ""; }; + 5BD9FD692AD292C500EE70B7 /* ListViewControllerUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListViewControllerUseCase.swift; sourceTree = ""; }; + 5BD9FD6B2AD2931700EE70B7 /* TaskDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TaskDTO.swift; sourceTree = ""; }; 9C4F6E802ACBE29700FDC393 /* ListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListViewController.swift; sourceTree = ""; }; C7431F0225F51E1D0094C4CF /* ProjectManager.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ProjectManager.app; sourceTree = BUILT_PRODUCTS_DIR; }; C7431F0525F51E1D0094C4CF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -55,6 +59,7 @@ isa = PBXGroup; children = ( 5B1666B52ACBE99300478F1D /* Task.swift */, + 5BD9FD6B2AD2931700EE70B7 /* TaskDTO.swift */, ); path = Model; sourceTree = ""; @@ -117,6 +122,7 @@ isa = PBXGroup; children = ( 5BD9FD672ACFDCCF00EE70B7 /* MainViewControllerUseCase.swift */, + 5BD9FD692AD292C500EE70B7 /* ListViewControllerUseCase.swift */, ); path = UseCase; sourceTree = ""; @@ -226,7 +232,9 @@ 5B1666B62ACBE99300478F1D /* Task.swift in Sources */, C7431F0A25F51E1D0094C4CF /* MainViewController.swift in Sources */, 5BD9FD682ACFDCCF00EE70B7 /* MainViewControllerUseCase.swift in Sources */, + 5BD9FD6C2AD2931700EE70B7 /* TaskDTO.swift in Sources */, C7431F0625F51E1D0094C4CF /* AppDelegate.swift in Sources */, + 5BD9FD6A2AD292C500EE70B7 /* ListViewControllerUseCase.swift in Sources */, 5B9D60322ACBD4E9004905F6 /* ListCollectionViewCell.swift in Sources */, C7431F0825F51E1D0094C4CF /* SceneDelegate.swift in Sources */, 5B1666B82ACBEB7100478F1D /* ListCollectionHeaderView.swift in Sources */, diff --git a/ProjectManager/ProjectManager/App/SceneDelegate.swift b/ProjectManager/ProjectManager/App/SceneDelegate.swift index 26014cf33..9b8931771 100644 --- a/ProjectManager/ProjectManager/App/SceneDelegate.swift +++ b/ProjectManager/ProjectManager/App/SceneDelegate.swift @@ -11,8 +11,10 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } - let useCase: MainViewControllerUseCaseType = MainViewControllerUseCase() - let mainViewController = MainViewController(useCase: useCase) + let mainViewControllerUseCase: MainViewControllerUseCaseType = MainViewControllerUseCase() + let listViewControllerUseCase: ListViewControllerUseCaseType = ListViewControllerUseCase() + let mainViewController = MainViewController(mainViewControllerUseCase: mainViewControllerUseCase, + listViewControllerUseCase: listViewControllerUseCase) let navigationController = UINavigationController(rootViewController: mainViewController) window = UIWindow(windowScene: windowScene) diff --git a/ProjectManager/ProjectManager/Controller/ListViewController.swift b/ProjectManager/ProjectManager/Controller/ListViewController.swift index 5ac2ade4d..5b3a1ac82 100644 --- a/ProjectManager/ProjectManager/Controller/ListViewController.swift +++ b/ProjectManager/ProjectManager/Controller/ListViewController.swift @@ -37,12 +37,15 @@ final class ListViewController: UIViewController, AlertControllerShowable { private let taskStatus: TaskStatus + private let useCase: ListViewControllerUseCaseType + private var taskList: [Task] = [] private var headerView: ListCollectionHeaderView? - init(taskStatus: TaskStatus) { + init(taskStatus: TaskStatus, useCase: ListViewControllerUseCaseType) { self.taskStatus = taskStatus + self.useCase = useCase super.init(nibName: nil, bundle: nil) } @@ -100,9 +103,10 @@ extension ListViewController { private func setUpDiffableDataSource() { let cellRegistration = UICollectionView.CellRegistration { [weak self] cell, indexPath, task in guard let self = self else { return } + let taskDTO = self.useCase.convertTaskDTOFromTask(task: task) cell.delegate = self - cell.setUpContents(task: self.taskList[indexPath.row]) + cell.setUpContents(taskDTO: taskDTO) } diffableDataSource = UICollectionViewDiffableDataSource(collectionView: collectionView, cellProvider: { collectionView, indexPath, task in @@ -176,8 +180,9 @@ extension ListViewController: TaskViewControllerDelegate { // MARK: - ListCollectionViewCell Delegate extension ListViewController: ListCollectionViewCellDelegate { - func didLongPressCell(task: Task, cellFrame: CGRect) { + func didLongPressCell(taskDTO: TaskDTO, cellFrame: CGRect) { let (firstMoveAlertTitle, secondMoveAlerTitle) = convertAlertsTitle() + let task = useCase.convertTaskFromTaskDTO(taskDTO: taskDTO) let firstMoveAlertAction: UIAlertAction = .init(title: firstMoveAlertTitle, style: .default) { action in switch self.taskStatus { diff --git a/ProjectManager/ProjectManager/Controller/MainViewController.swift b/ProjectManager/ProjectManager/Controller/MainViewController.swift index 30b1fbeba..8ff5529db 100644 --- a/ProjectManager/ProjectManager/Controller/MainViewController.swift +++ b/ProjectManager/ProjectManager/Controller/MainViewController.swift @@ -8,21 +8,24 @@ import UIKit final class MainViewController: UIViewController { private lazy var todoListViewController: ListViewController = { - let listViewController = ListViewController(taskStatus: .todo) + let listViewController = ListViewController(taskStatus: .todo, + useCase: listViewControllerUseCase) listViewController.delegate = self return listViewController }() private lazy var doingListViewController: ListViewController = { - let listViewController = ListViewController(taskStatus: .doing) + let listViewController = ListViewController(taskStatus: .doing, + useCase: listViewControllerUseCase) listViewController.delegate = self return listViewController }() private lazy var doneListViewController: ListViewController = { - let listViewController = ListViewController(taskStatus: .done) + let listViewController = ListViewController(taskStatus: .done, + useCase: listViewControllerUseCase) listViewController.delegate = self return listViewController @@ -45,10 +48,13 @@ final class MainViewController: UIViewController { return stackView }() - private let useCase: MainViewControllerUseCaseType + private let mainViewControllerUseCase: MainViewControllerUseCaseType + private let listViewControllerUseCase: ListViewControllerUseCaseType - init(useCase: MainViewControllerUseCaseType) { - self.useCase = useCase + init(mainViewControllerUseCase: MainViewControllerUseCaseType, + listViewControllerUseCase: ListViewControllerUseCaseType) { + self.mainViewControllerUseCase = mainViewControllerUseCase + self.listViewControllerUseCase = listViewControllerUseCase super.init(nibName: nil, bundle: nil) } @@ -138,7 +144,7 @@ extension MainViewController: TaskViewControllerDelegate { // MARK: - ListViewController Delegate extension MainViewController: ListViewControllerDelegate { func moveCell(moveToTaskStatus: TaskStatus, task: Task) { - taskList = useCase.convertUpdatedTaskList(taskList: taskList, + taskList = mainViewControllerUseCase.convertUpdatedTaskList(taskList: taskList, updateTask: task, moveToTaskStatus: moveToTaskStatus) } @@ -153,7 +159,7 @@ extension MainViewController: ListViewControllerDelegate { } func didTappedDoneButtonForUpdate(updateTask: Task) { - taskList = useCase.convertUpdatedTaskList(taskList: taskList, + taskList = mainViewControllerUseCase.convertUpdatedTaskList(taskList: taskList, updateTask: updateTask, moveToTaskStatus: updateTask.taskStatus) } diff --git a/ProjectManager/ProjectManager/Model/TaskDTO.swift b/ProjectManager/ProjectManager/Model/TaskDTO.swift new file mode 100644 index 000000000..2f5c10123 --- /dev/null +++ b/ProjectManager/ProjectManager/Model/TaskDTO.swift @@ -0,0 +1,17 @@ +// +// TaskDTO.swift +// ProjectManager +// +// Created by Hyungmin Lee on 2023/10/08. +// + +import Foundation + +struct TaskDTO: Identifiable { + let id: UUID + let title: String + let description: String + let deadline: String + let isPassDeadline: Bool + let taskStatus: TaskStatus +} diff --git a/ProjectManager/ProjectManager/UseCase/ListViewControllerUseCase.swift b/ProjectManager/ProjectManager/UseCase/ListViewControllerUseCase.swift new file mode 100644 index 000000000..5f77997f4 --- /dev/null +++ b/ProjectManager/ProjectManager/UseCase/ListViewControllerUseCase.swift @@ -0,0 +1,55 @@ +// +// ListViewControllerUseCase.swift +// ProjectManager +// +// Created by Hyungmin Lee on 2023/10/08. +// + +import Foundation + +protocol ListViewControllerUseCaseType { + func convertTaskDTOFromTask(task: Task) -> TaskDTO + func convertTaskFromTaskDTO(taskDTO: TaskDTO) -> Task +} + +final class ListViewControllerUseCase: ListViewControllerUseCaseType { + private let dateFormatter: DateFormatter = { + let dateFormatter = DateFormatter() + + dateFormatter.dateFormat = "yyyy. MM. dd." + dateFormatter.locale = Locale.current + dateFormatter.timeZone = TimeZone.current + return dateFormatter + }() + + func convertTaskDTOFromTask(task: Task) -> TaskDTO { + let deadline = dateFormatter.string(from: Date(timeIntervalSince1970: task.deadline)) + let isPassDeadline = isPassDeadline(deadline: task.deadline) + + return TaskDTO(id: task.id, + title: task.title, + description: task.description, + deadline: deadline, + isPassDeadline: isPassDeadline, + taskStatus: task.taskStatus) + } + + func convertTaskFromTaskDTO(taskDTO: TaskDTO) -> Task { + let deadline = dateFormatter.date(from: taskDTO.deadline)?.timeIntervalSince1970 ?? 0 + + return Task(id: taskDTO.id, + title: taskDTO.title, + description: taskDTO.description, + deadline: deadline, + taskStatus: taskDTO.taskStatus) + } + + private func isPassDeadline(deadline: Double) -> Bool { + let currentDateString = dateFormatter.string(from: Date()) + let deadlineDateString = dateFormatter.string(from: Date(timeIntervalSince1970: deadline)) + let currentDate = dateFormatter.date(from: currentDateString) ?? Date() + let deadlineDate = dateFormatter.date(from: deadlineDateString) ?? Date() + + return currentDate.timeIntervalSince1970 > deadlineDate.timeIntervalSince1970 + } +} diff --git a/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift b/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift index 7ebbc11f7..138a770c8 100644 --- a/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift +++ b/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift @@ -8,7 +8,7 @@ import UIKit protocol ListCollectionViewCellDelegate: AnyObject { - func didLongPressCell(task: Task, cellFrame: CGRect) + func didLongPressCell(taskDTO: TaskDTO, cellFrame: CGRect) } final class ListCollectionViewCell: UICollectionViewListCell { @@ -57,7 +57,7 @@ final class ListCollectionViewCell: UICollectionViewListCell { return label }() - private var task: Task? + private var taskDTO: TaskDTO? override init(frame: CGRect) { super.init(frame: frame) @@ -71,22 +71,13 @@ final class ListCollectionViewCell: UICollectionViewListCell { fatalError("init(coder:) has not been implemented") } - func setUpContents(task: Task) { - self.task = task + func setUpContents(taskDTO: TaskDTO) { + self.taskDTO = taskDTO - titleLabel.text = task.title - descriptionLabel.text = task.description - deadlineLabel.text = dateFormatter.string(from: Date(timeIntervalSince1970: task.deadline)) - deadlineLabel.textColor = isPassDeadline(deadline: task.deadline) ? .red : .black - } - - private func isPassDeadline(deadline: Double) -> Bool { - let currentDateString = dateFormatter.string(from: Date()) - let deadlineDateString = dateFormatter.string(from: Date(timeIntervalSince1970: deadline)) - let currentDate = dateFormatter.date(from: currentDateString) ?? Date() - let deadlineDate = dateFormatter.date(from: deadlineDateString) ?? Date() - - return currentDate.timeIntervalSince1970 > deadlineDate.timeIntervalSince1970 + titleLabel.text = taskDTO.title + descriptionLabel.text = taskDTO.description + deadlineLabel.text = taskDTO.deadline + deadlineLabel.textColor = taskDTO.isPassDeadline ? .red : .black } private func configureUI() { @@ -116,8 +107,8 @@ final class ListCollectionViewCell: UICollectionViewListCell { @objc private func didLongPress() { - guard let task = task else { return } + guard let taskDTO = taskDTO else { return } - delegate?.didLongPressCell(task: task, cellFrame: frame) + delegate?.didLongPressCell(taskDTO: taskDTO, cellFrame: frame) } } From 01c3cf6f24deb4dea76cd4b30a56faa86353b609 Mon Sep 17 00:00:00 2001 From: Zion Date: Sun, 8 Oct 2023 17:19:09 +0900 Subject: [PATCH 24/28] =?UTF-8?q?fix:=20=EC=A0=9C=EB=AA=A9=EB=A7=8C=20?= =?UTF-8?q?=EB=93=A4=EC=96=B4=EA=B0=84=20=EA=B2=BD=EC=9A=B0=20titleLoad=20?= =?UTF-8?q?=EB=B6=88=EA=B0=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProjectManager/Controller/TaskViewController.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ProjectManager/ProjectManager/Controller/TaskViewController.swift b/ProjectManager/ProjectManager/Controller/TaskViewController.swift index b3cc10811..1ebd24ae2 100644 --- a/ProjectManager/ProjectManager/Controller/TaskViewController.swift +++ b/ProjectManager/ProjectManager/Controller/TaskViewController.swift @@ -140,13 +140,13 @@ final class TaskViewController: UIViewController, ToastShowable { } private func setUpContents() { - if task.title.count == 0 || task.description.count == 0 { return } + if task.title.count == 0 && task.description.count == 0 { return } titleTextField.text = task.title descriptionTextView.text = task.description datePicker.date = Date(timeIntervalSince1970: task.deadline) - placeHolderLabel.isHidden = true + placeHolderLabel.isHidden = descriptionTextView.text.count != 0 } } From 0787d9819e7060336e4408dc627b3cf9fd2c4032 Mon Sep 17 00:00:00 2001 From: Zion Date: Sun, 8 Oct 2023 17:22:46 +0900 Subject: [PATCH 25/28] =?UTF-8?q?fix:=20TextView=20=EC=98=81=EC=97=AD?= =?UTF-8?q?=EC=9D=84=20=EB=B2=97=EC=96=B4=EB=82=98=EC=84=9C=20=EB=85=B8?= =?UTF-8?q?=EC=B6=9C=EB=90=98=EB=8A=94=20=ED=85=8D=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=95=88=EB=B3=B4=EC=9D=B4=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ProjectManager/Controller/TaskViewController.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/ProjectManager/ProjectManager/Controller/TaskViewController.swift b/ProjectManager/ProjectManager/Controller/TaskViewController.swift index 1ebd24ae2..4b6deb4d0 100644 --- a/ProjectManager/ProjectManager/Controller/TaskViewController.swift +++ b/ProjectManager/ProjectManager/Controller/TaskViewController.swift @@ -60,6 +60,7 @@ final class TaskViewController: UIViewController, ToastShowable { textView.layer.shadowRadius = 3 textView.layer.masksToBounds = false textView.delegate = self + textView.clipsToBounds = true return textView }() From 9adceec0f50c9ce11aaf0aac97a0d05842361c6b Mon Sep 17 00:00:00 2001 From: karen Date: Sun, 8 Oct 2023 17:31:10 +0900 Subject: [PATCH 26/28] =?UTF-8?q?refactor:=20argument=20label=20to?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controller/ListViewController.swift | 14 +++++++------- .../Controller/MainViewController.swift | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ProjectManager/ProjectManager/Controller/ListViewController.swift b/ProjectManager/ProjectManager/Controller/ListViewController.swift index 5b3a1ac82..d93891cdb 100644 --- a/ProjectManager/ProjectManager/Controller/ListViewController.swift +++ b/ProjectManager/ProjectManager/Controller/ListViewController.swift @@ -16,7 +16,7 @@ enum TaskStatus: String { protocol ListViewControllerDelegate: AnyObject { func didTappedDoneButtonForUpdate(updateTask: Task) func didSwipedDeleteTask(_ deleteTask: Task) - func moveCell(moveToTaskStatus: TaskStatus, task: Task) + func moveCell(to moveToTaskStatus: TaskStatus, task: Task) } final class ListViewController: UIViewController, AlertControllerShowable { @@ -187,26 +187,26 @@ extension ListViewController: ListCollectionViewCellDelegate { let firstMoveAlertAction: UIAlertAction = .init(title: firstMoveAlertTitle, style: .default) { action in switch self.taskStatus { case .todo: - self.moveCell(moveToTaskStatus: .doing, task: task) + self.moveCell(to: .doing, task: task) case .doing, .done: - self.moveCell(moveToTaskStatus: .todo, task: task) + self.moveCell(to: .todo, task: task) } } let secondMoveAlertAction: UIAlertAction = .init(title: secondMoveAlerTitle, style: .default) { action in switch self.taskStatus { case .todo, .doing: - self.moveCell(moveToTaskStatus: .done, task: task) + self.moveCell(to: .done, task: task) case .done: - self.moveCell(moveToTaskStatus: .doing, task: task) + self.moveCell(to: .doing, task: task) } } showPopOverAlertController(sourceRect: cellFrame, alertActions: [firstMoveAlertAction, secondMoveAlertAction]) } - private func moveCell(moveToTaskStatus: TaskStatus, task: Task) { - delegate?.moveCell(moveToTaskStatus: moveToTaskStatus, task: task) + private func moveCell(to moveToTaskStatus: TaskStatus, task: Task) { + delegate?.moveCell(to: moveToTaskStatus, task: task) } private func convertAlertsTitle() -> (String, String) { diff --git a/ProjectManager/ProjectManager/Controller/MainViewController.swift b/ProjectManager/ProjectManager/Controller/MainViewController.swift index 8ff5529db..2949a70e5 100644 --- a/ProjectManager/ProjectManager/Controller/MainViewController.swift +++ b/ProjectManager/ProjectManager/Controller/MainViewController.swift @@ -143,7 +143,7 @@ extension MainViewController: TaskViewControllerDelegate { // MARK: - ListViewController Delegate extension MainViewController: ListViewControllerDelegate { - func moveCell(moveToTaskStatus: TaskStatus, task: Task) { + func moveCell(to moveToTaskStatus: TaskStatus, task: Task) { taskList = mainViewControllerUseCase.convertUpdatedTaskList(taskList: taskList, updateTask: task, moveToTaskStatus: moveToTaskStatus) From 076b3e01e32fbbbc068e3f3ad9685b82e4da17f5 Mon Sep 17 00:00:00 2001 From: Zion Date: Sun, 8 Oct 2023 17:38:14 +0900 Subject: [PATCH 27/28] =?UTF-8?q?refactor:=20convertTitle=EB=B6=80?= =?UTF-8?q?=EB=B6=84=20UseCase=EB=A1=9C=20=EC=98=AE=EA=B9=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controller/ListViewController.swift | 13 +------------ .../UseCase/ListViewControllerUseCase.swift | 12 ++++++++++++ .../View/ListCollectionViewCell.swift | 9 --------- 3 files changed, 13 insertions(+), 21 deletions(-) diff --git a/ProjectManager/ProjectManager/Controller/ListViewController.swift b/ProjectManager/ProjectManager/Controller/ListViewController.swift index d93891cdb..501e9a441 100644 --- a/ProjectManager/ProjectManager/Controller/ListViewController.swift +++ b/ProjectManager/ProjectManager/Controller/ListViewController.swift @@ -181,7 +181,7 @@ extension ListViewController: TaskViewControllerDelegate { // MARK: - ListCollectionViewCell Delegate extension ListViewController: ListCollectionViewCellDelegate { func didLongPressCell(taskDTO: TaskDTO, cellFrame: CGRect) { - let (firstMoveAlertTitle, secondMoveAlerTitle) = convertAlertsTitle() + let (firstMoveAlertTitle, secondMoveAlerTitle) = useCase.convertAlertsTitle(taskStatus: taskStatus) let task = useCase.convertTaskFromTaskDTO(taskDTO: taskDTO) let firstMoveAlertAction: UIAlertAction = .init(title: firstMoveAlertTitle, style: .default) { action in @@ -208,15 +208,4 @@ extension ListViewController: ListCollectionViewCellDelegate { private func moveCell(to moveToTaskStatus: TaskStatus, task: Task) { delegate?.moveCell(to: moveToTaskStatus, task: task) } - - private func convertAlertsTitle() -> (String, String) { - switch taskStatus { - case .todo: - return ("Move to DOING", "Move to DONE") - case .doing: - return ("Move to TODO", "Move to DONE") - case .done: - return ("Move to TODO", "Move to DOING") - } - } } diff --git a/ProjectManager/ProjectManager/UseCase/ListViewControllerUseCase.swift b/ProjectManager/ProjectManager/UseCase/ListViewControllerUseCase.swift index 5f77997f4..ad405af81 100644 --- a/ProjectManager/ProjectManager/UseCase/ListViewControllerUseCase.swift +++ b/ProjectManager/ProjectManager/UseCase/ListViewControllerUseCase.swift @@ -10,6 +10,7 @@ import Foundation protocol ListViewControllerUseCaseType { func convertTaskDTOFromTask(task: Task) -> TaskDTO func convertTaskFromTaskDTO(taskDTO: TaskDTO) -> Task + func convertAlertsTitle(taskStatus: TaskStatus) -> (String, String) } final class ListViewControllerUseCase: ListViewControllerUseCaseType { @@ -44,6 +45,17 @@ final class ListViewControllerUseCase: ListViewControllerUseCaseType { taskStatus: taskDTO.taskStatus) } + func convertAlertsTitle(taskStatus: TaskStatus) -> (String, String) { + switch taskStatus { + case .todo: + return ("Move to DOING", "Move to DONE") + case .doing: + return ("Move to TODO", "Move to DONE") + case .done: + return ("Move to TODO", "Move to DOING") + } + } + private func isPassDeadline(deadline: Double) -> Bool { let currentDateString = dateFormatter.string(from: Date()) let deadlineDateString = dateFormatter.string(from: Date(timeIntervalSince1970: deadline)) diff --git a/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift b/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift index 138a770c8..2c5557e26 100644 --- a/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift +++ b/ProjectManager/ProjectManager/View/ListCollectionViewCell.swift @@ -14,15 +14,6 @@ protocol ListCollectionViewCellDelegate: AnyObject { final class ListCollectionViewCell: UICollectionViewListCell { weak var delegate: ListCollectionViewCellDelegate? - private let dateFormatter: DateFormatter = { - let dateFormatter = DateFormatter() - - dateFormatter.dateFormat = "yyyy. MM. dd." - dateFormatter.locale = Locale.current - dateFormatter.timeZone = TimeZone.current - return dateFormatter - }() - private let stackView: UIStackView = { let stackView = UIStackView() From 587b1f66bd8cf36b1bc9901bf2b300a132694ec2 Mon Sep 17 00:00:00 2001 From: karen <124643896+karenyang835@users.noreply.github.com> Date: Mon, 9 Oct 2023 17:49:31 +0900 Subject: [PATCH 28/28] Update README.md --- README.md | 293 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 290 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9546adcc4..0411639c4 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,294 @@ -## iOS 커리어 스타터 캠프 +# 🗓️ 프로젝트 매니저 -### 프로젝트 관리 앱 저장소 +- **프로젝트 기간** : 2023.09.18~2023.10.06 +- **프로젝트 팀원** : Zion♏️, Karen♉️ (팀명: ✨카시온페어💫) +- **프로젝트 리뷰어** : delma -- 이 저장소를 자신의 저장소로 fork하여 프로젝트를 진행합니다 +## 🔖 목차 +1. [프로젝트 소개](#1.) +2. [실행 화면](#2.) +3. [시각적 프로젝트 구조](#3.) +4. [트러블 슈팅](#4.) +5. [참고 링크](#5.) +6. [about TEAM](#6.) + + +--- +
+ + + +## 1. 💬 프로젝트 소개 +> `CoreData`와 `FireBase`로 데이터를 관리하여 자신만의 계획을 정해놓은 날짜에 맞춰 계획 실행 여부를 손쉽게 관리할 수 있는 프로젝트 관리앱입니다. + +--- +
+ + + + + +## 2.📱실행 화면 + +| 일정 추가 | +| :--------: | +|| + + + +| 일정 수정 및 삭제 | +| :--------: | +|| + + +| 일정 이동 (deadLine) | +| :--------: | +|| + +--- + +
+ + + +
+ + + +## 3.📊 시각적 프로젝트 구조 +
+ +### 📂 폴더 구조 + +```swift +┌── ProjectManager +│ ├── UseCase +│ │ ├── MainViewControllerUseCase +│ │ └── ListViewControllerUseCase +│ ├── Util +│ │ └── Protocol +│ │ ├── AlertControllerShowable +│ │ └── ToastShowable +│ ├── Model +│ │ ├── Task +│ │ └── TaskDTO +│ ├── View +│ │ ├── LaunchScreen +│ │ ├── ListCollectionViewCell +│ │ └── ListCollectionHeaderView +│ ├── Resource +│ │ └── Assets +│ ├── Controller +│ │ ├── MainViewController +│ │ ├── TaskViewController +│ │ └── ListViewController +│ └── App +│ ├── AppDelegate +│ └── SceneDelegate +│ +└── README.md +``` + + +### 🎨 Class Diagram +![image](https://github.com/karenyang835/pr-exercise/assets/124643896/3a8ca668-3a11-4250-9b8c-fe5589b8d23f) + + +--- + +
+ + + + + +## 4.🚨 트러블 슈팅 + +### 1️⃣ 잔상이 남는 문제 +#### ⛔️ 문제점 +- Swiped로 Delete시 잔상이 남는 문제가 발생했습니다. +- 살펴보니 Delete뿐 아니라 셀이 이동하는 경우에도 이와 같은 문제가 발생되는 것을 확인했습니다. +![프젝메-잔상](https://github.com/karenyang835/pr-exercise/assets/124643896/f5420f0b-0e30-4599-8659-beee1b963215) + + +#### ✅ 해결 방법 +- reloadSections을 할 경우 이전에 가지고 있던 indexPath가 문제가 되면서 발생되어지는 현상이었습니다. +- reloadSections명령어를 사용하지 않으면 현재 작성했던 코드에서는 headerView를 갱신할 수가 없어서 HeadView를 ListViewController가 가지고 있게하여 갱신해야될 때 headerView에 직접적으로 컨텐츠를 넣어주어 추가적으로 발생한 문제점도 해결해 주었습니다. + +
+코드 상세 + +#### 수정 전 +```swift +func setUpDiffableDataSourceSanpShot(taskList: [Task] = []) { + var snapShot = NSDiffableDataSourceSnapshot() + + self.taskList = taskList + snapShot.appendSections([.main]) + snapShot.appendItems(taskList) + snapShot.reloadSections([.main]) + diffableDataSource?.apply(snapShot) +} + + ... + +private func setUpDiffableDataSourceHeader() { + let headerRegistration = UICollectionView.SupplementaryRegistration(elementKind: UICollectionView.elementKindSectionHeader) { [weak self] headerView, elementKind, indexPath in + guard let self = self else { return } + + headerView.setUpContents(title: self.listKind.rawValue, taskCount: "\(self.taskList.count)") + } + + diffableDataSource?.supplementaryViewProvider = { collectionView, kind, indexPath in + return collectionView.dequeueConfiguredReusableSupplementary(using: headerRegistration, for: indexPath) + } +} + +``` + +#### 수정 후 +```swift +func setUpDiffableDataSourceSanpShot(taskList: [Task] = []) { + var snapShot = NSDiffableDataSourceSnapshot() + + self.taskList = taskList + snapShot.appendSections([.main]) + snapShot.appendItems(taskList) + diffableDataSource?.apply(snapShot) +} +... + +private func setUpDiffableDataSourceHeader() { + let headerRegistration = UICollectionView.SupplementaryRegistration(elementKind: UICollectionView.elementKindSectionHeader) { [weak self] headerView, elementKind, indexPath in + guard let self = self else { return } + + self.headerView = headerView + headerView.setUpContents(title: self.taskStatus.rawValue, taskCount: "\(self.taskList.count)") + } + + diffableDataSource?.supplementaryViewProvider = { collectionView, kind, indexPath in + return collectionView.dequeueConfiguredReusableSupplementary(using: headerRegistration, for: indexPath) + } +} + + +func reloadTaskList(taskList: [Task]) { + setUpDiffableDataSourceSanpShot(taskList: taskList) + headerView?.setUpContents(title: taskStatus.rawValue, taskCount: "\(taskList.count)") +} + +``` + +
+ +
+ +### 2️⃣ TextView 여러줄 입력시 Mask 되지 않는 문제 발생 +#### ⛔️ 문제점 + +- 일정 상세 내용 입력하는 descriptionTextView에 여러줄을 입력하는 경우 mask가 제대로 씌워지지 않아 DatePicker영역을 침범해서 보여주는 문제점이 발생했습니다. + + +#### ✅ 해결 방법 +- subview가 view의 경계를 넘어갈 시 잘리게 보여주는 clipsToBounds를 true로 주어 해결하였습니다. + + +
+코드 상세 + +#### 수정 전 +```swift +private lazy var descriptionTextView: UITextView = { + let textView = UITextView() + + textView.layer.borderColor = UIColor.label.cgColor + textView.layer.borderWidth = 0.3 + textView.layer.shadowColor = UIColor.systemGray.cgColor + textView.layer.shadowOffset = CGSize(width: 2, height: 3) + textView.layer.shadowOpacity = 0.5 + textView.layer.shadowRadius = 3 + textView.layer.masksToBounds = false + textView.delegate = self + return textView + }() + +``` + +#### 수정 후 + +```swift +private lazy var descriptionTextView: UITextView = { + let textView = UITextView() + + textView.layer.borderColor = UIColor.label.cgColor + textView.layer.borderWidth = 0.3 + textView.layer.shadowColor = UIColor.systemGray.cgColor + textView.layer.shadowOffset = CGSize(width: 2, height: 3) + textView.layer.shadowOpacity = 0.5 + textView.layer.shadowRadius = 3 + textView.layer.masksToBounds = false + textView.delegate = self + textView.clipsToBounds = true //추가 + return textView + }() + +``` + +--- + +
+ + + +## 5.🔗 참고 링크 + +🍎 [Swift API Design Guidelines](https://www.swift.org/documentation/api-design-guidelines/)
+🍎 [The Swift Language Guide - Preventing Overrides](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/inheritance/#Preventing-Overrides)
+🍎 [The Swift Language Guide - Result Type](https://developer.apple.com/documentation/swift/result)
+🍏 [Apple Developer - UINavigationController](https://developer.apple.com/documentation/uikit/uinavigationcontroller)
+🍏 [Apple Developer - List](https://developer.apple.com/documentation/swiftui/list)
+🍏 [Apple Developer - Pickers](https://developer.apple.com/design/human-interface-guidelines/pickers)
+🍏 [Apple Developer - DatePicker](https://developer.apple.com/documentation/swiftui/datepicker)
+🍏 [Apple Developer - popover](https://developer.apple.com/documentation/swiftui/view/popover(ispresented:attachmentanchor:arrowedge:content:))
+🍏 [Apple Developer - Handling notifications and notification-related actions](https://developer.apple.com/documentation/usernotifications/handling_notifications_and_notification-related_actions)
+🍏 [Apple Developer - UndoManager](https://developer.apple.com/documentation/foundation/undomanager)
+🍏 [Apple Developer - Scheduling a notification locally from your app](https://developer.apple.com/documentation/usernotifications/scheduling_a_notification_locally_from_your_app)
+🍏 [Apple Developer - Localizations](https://developer.apple.com/kr/localization/)
+ + +--- + +
+ + + + +## 6. 🎩 aboutTEAM +| Zion ♏️ |Karen ♉️| +| :-: | :-: | +| | | +|https://github.com/LeeZion94 |https://github.com/karenyang835| + +
⏰ 타임 라인 (펼쳐보기) + + +|**날 짜**|**내 용**| +|:-:|-| +| 2023.09.18. |🔍 프로젝트에서 필요로 하는 핵심기능 검토 - `UI`, `LocalDB`, `RemoteDB`, `Architecture`, `Dependecy Manager`, `Concurrency`
| +| 2023.09.19. |📝 프로젝트에서 필요로 하는 핵심기능 공부 - `MVVM패턴`
🆕 코드베이스 준비 | +| 2023.09.20. |📝 프로젝트에서 필요로 하는 핵심기능 공부 - `MVVM패턴`
📝 프로젝트에서 필요로 하는 핵심기능 공부 - `Clean Architecture` | +| 2023.09.21. |📝 프로젝트에서 필요로 하는 핵심기능 공부 - `MVVM패턴`
📝 프로젝트에서 필요로 하는 핵심기능 공부 - `Clean Architecture`| +| 2023.09.22. |📝 프로젝트에서 필요로 하는 핵심기능 공부 - `MVVM패턴`
📝 프로젝트에서 필요로 하는 핵심기능 공부 - `Clean Architecture`| +| 2023.10.03. | 🆕 Cell, TaskViewController 구현 및 프로젝트 기초 구성 구현
🆕 ListViewController 레이아웃 구현
🆕 ListViewCotroller DiffableDataSource 적용
🆕 MainViewController 레이아웃 구현
🆕 Task 생성시 MainViewController에서 ListViewController를 갱신하도록 변경
✅ 불필요 타입 삭제 | +| 2023.10.05. | 🆕 taskCountLabel 라운딩 적용
🆕 shadow적용
Task Update로직 추가
🆕 deadline에 따른 텍스트 색상변경 적용
🆕 isPassDeadline 로직 수정
✅ descriptionLabel 줄 수 제한 적용
🆕 swipeDelete 로직추가
🆕 trailingAction 적용
🆕 Cell Move 로직추가 | +| 2023.10.06. |✅ Section 갱신로직 수정
🆕 showToast 로직 추가
🆕 MainViewController UseCase 생성



| +| 2023.10.08. |🛠️ 불필요한 컨벤션 삭제, 네이밍 수정
🆕 descriptionTextView글자수 제한 적용
🛠️ listKind -> TaskStatus로 수정
🛠️ deleteTask 중복 노출 수정
🆕 ListViewControllerUseCase 생성
✅ 제목만 들어간 경우 titleLoad 불가 수정
✅ TextView 영역을 벗어나서 노출되는 텍스트 안보이도록 수정
🛠️ argument label to로 수정
🛠️ convertTitle부분 UseCase로 옮김| + +
+ + +--- + +