diff --git a/Application/To-Do.xcodeproj/project.pbxproj b/Application/To-Do.xcodeproj/project.pbxproj index 0010993..e6b32ea 100644 --- a/Application/To-Do.xcodeproj/project.pbxproj +++ b/Application/To-Do.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 0119C5DC25F2DB580082A4E6 /* TaskDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BBE55C825245DBF00090F87 /* TaskDetailsViewController.swift */; }; 07F46C8525277C7E007DC6AC /* SortTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07F46C8425277C7E007DC6AC /* SortTypes.swift */; }; 3307EC692537AFB100963CC4 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3307EC682537AFB100963CC4 /* Extensions.swift */; }; 4F953E502531C25E0027144C /* EmptyState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F953E4F2531C25E0027144C /* EmptyState.swift */; }; @@ -17,7 +18,6 @@ 5BBE55AA2523A42C00090F87 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5BBE55A92523A42C00090F87 /* Assets.xcassets */; }; 5BBE55AD2523A42C00090F87 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5BBE55AB2523A42C00090F87 /* LaunchScreen.storyboard */; }; 5BBE55B72523A90A00090F87 /* TodoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BBE55B62523A90A00090F87 /* TodoViewController.swift */; }; - 5BBE55C925245DBF00090F87 /* TaskDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BBE55C825245DBF00090F87 /* TaskDetailsViewController.swift */; }; 5BBE55CB2524623900090F87 /* TextviewBorder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BBE55CA2524623900090F87 /* TextviewBorder.swift */; }; 5BC2F07B2532D10F00B648C2 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC2F07A2532D10F00B648C2 /* Constants.swift */; }; 603B4C66252647BD00579EC6 /* ResultsTableController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 603B4C65252647BD00579EC6 /* ResultsTableController.swift */; }; @@ -289,13 +289,13 @@ E924B484252D53F3004FD116 /* CameraHelper.swift in Sources */, 5BBE559E2523A42700090F87 /* AppDelegate.swift in Sources */, 5BBE55A02523A42700090F87 /* SceneDelegate.swift in Sources */, + 0119C5DC25F2DB580082A4E6 /* TaskDetailsViewController.swift in Sources */, 5BBE55A82523A42700090F87 /* To_Do.xcdatamodeld in Sources */, 5BC2F07B2532D10F00B648C2 /* Constants.swift in Sources */, 5BBE55CB2524623900090F87 /* TextviewBorder.swift in Sources */, E924B487252D63FE004FD116 /* ImageAttachmentCell.swift in Sources */, 603B4C66252647BD00579EC6 /* ResultsTableController.swift in Sources */, 8E6209BD25291AF0004CE4B9 /* OnboardingViewController.swift in Sources */, - 5BBE55C925245DBF00090F87 /* TaskDetailsViewController.swift in Sources */, FF2303B42529183700B88831 /* Alert.swift in Sources */, 3307EC692537AFB100963CC4 /* Extensions.swift in Sources */, FFA6683A2530E07F004E1DE3 /* TaskHistoryViewController.swift in Sources */, @@ -460,7 +460,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = F8CHS6PHQS; + DEVELOPMENT_TEAM = HER732L49Q; ENABLE_TESTABILITY = YES; INFOPLIST_FILE = "To-Do/Helpers/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 13.2; @@ -480,7 +480,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = F8CHS6PHQS; + DEVELOPMENT_TEAM = HER732L49Q; ENABLE_TESTABILITY = YES; INFOPLIST_FILE = "To-Do/Helpers/Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 13.2; diff --git a/Application/To-Do/Controller/TaskDetailsViewController.swift b/Application/To-Do/Controller/TaskDetailsViewController.swift index 2e245f0..4609eeb 100644 --- a/Application/To-Do/Controller/TaskDetailsViewController.swift +++ b/Application/To-Do/Controller/TaskDetailsViewController.swift @@ -16,11 +16,113 @@ protocol TaskDelegate: class { class TaskDetailsViewController: UIViewController{ // OUTLETS - @IBOutlet private weak var taskTitleTextField: UITextField! - @IBOutlet private weak var subTasksTextView: UITextView! - @IBOutlet private weak var endDateTextField: UITextField! - @IBOutlet private weak var saveButton: UIBarButtonItem! - @IBOutlet private weak var attachmentCollection: UICollectionView! + // @IBOutlet private weak var taskTitleTextField: UITextField! + // @IBOutlet private weak var subTasksTextView: UITextView! + // @IBOutlet private weak var endDateTextField: UITextField! + // @IBOutlet private weak var saveButton: UIBarButtonItem! + // @IBOutlet private weak var attachmentCollection: UICollectionView! + + + + // UI Elements + let taskLabel: UILabel = { + let l = UILabel() + l.translatesAutoresizingMaskIntoConstraints = false + l.text = "Task" + l.font = UIFont.systemFont(ofSize: 20, weight: .semibold) + + return l + }() + + let taskTitleTextField: UITextField = { + let tf = UITextField() + tf.translatesAutoresizingMaskIntoConstraints = false + tf.placeholder = "Enter task name" + tf.layer.borderWidth = 0.5 + tf.layer.borderColor = UIColor.systemGray.cgColor + tf.layer.cornerRadius = 5 + + + return tf + }() + + + let subtasksLabel: UILabel = { + let l = UILabel() + l.translatesAutoresizingMaskIntoConstraints = false + l.text = "Sub Tasks" + l.font = UIFont.systemFont(ofSize: 20, weight: .semibold) + + return l + }() + + let subTasksTextView: UITextView = { + let tv = UITextView() + tv.translatesAutoresizingMaskIntoConstraints = false + tv.backgroundColor = .secondarySystemGroupedBackground + tv.text = "Enter your subtask here" + tv.layer.cornerRadius = 5 + tv.layer.borderColor = UIColor.systemGray.cgColor + tv.layer.borderWidth = 0.5 + + return tv + }() + + let endDateLabel: UILabel = { + let l = UILabel() + l.translatesAutoresizingMaskIntoConstraints = false + l.text = "End Date" + l.font = UIFont.systemFont(ofSize: 20, weight: .semibold) + + + return l + }() + + let endDateTextField: UITextField = { + let tf = UITextField() + tf.translatesAutoresizingMaskIntoConstraints = false + tf.placeholder = "Select end date" + tf.layer.borderWidth = 0.5 + tf.layer.borderColor = UIColor.systemGray.cgColor + tf.layer.cornerRadius = 5 + + + return tf + }() + + let imageLabel: UILabel = { + let l = UILabel() + l.translatesAutoresizingMaskIntoConstraints = false + l.text = "Image attachments" + l.font = UIFont.systemFont(ofSize: 20, weight: .semibold) + + + return l + }() + + let addImageButton: UIButton = { + let b = UIButton() + b.translatesAutoresizingMaskIntoConstraints = false + b.setTitle("Add image", for: .normal) + b.setTitleColor(.systemBlue, for: .normal) + b.titleLabel?.font = UIFont.systemFont(ofSize: 15, weight: .regular) + b.addTarget(self, action: #selector(addPhotoTapped), for: .touchUpInside) + + return b + }() + + + let attachmentCollection: UICollectionView = { + let viewLayout = UICollectionViewFlowLayout() + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: viewLayout) + collectionView.translatesAutoresizingMaskIntoConstraints = false + collectionView.register(ImageAttachmentCell.self, forCellWithReuseIdentifier: ImageAttachmentCell.identifier) + + + + return collectionView + }() + // VARIABLES @@ -36,11 +138,27 @@ class TaskDetailsViewController: UIViewController{ var cameraHelper = CameraHelper() var hapticGenerator: UINotificationFeedbackGenerator? = nil - + + + override func viewDidLoad() { super.viewDidLoad() + + attachmentCollection.dataSource = self + attachmentCollection.delegate = self + + + // programmatically add the savebutton to the right bar button item + let saveButton = UIBarButtonItem(title: "Save", style: .done, target: self, action: #selector(saveTapped(_:))) + navigationItem.rightBarButtonItem = saveButton + + setupUI() + isUpdate = (task != nil) - endDatePicker = UIDatePicker() +// endDatePicker = UIDatePicker() + endDatePicker = UIDatePicker.init(frame: CGRect(x: 0, y: 0, width: view.bounds.size.width, height: 150)) + endDatePicker.contentHorizontalAlignment = .fill + endDatePicker.addTarget(self, action: #selector(didPickDate(_:)), for: .valueChanged) endDatePicker.minimumDate = Date() endDateTextField.inputView = endDatePicker @@ -48,27 +166,133 @@ class TaskDetailsViewController: UIViewController{ subTasksTextView.addBorder() loadTaskForUpdate() taskTitleTextField.delegate = self - // Tap outside to close the keybord + // Tap outside to close the keyboard let tap = UITapGestureRecognizer(target: self.view, action: #selector(UIView.endEditing)) view.addGestureRecognizer(tap) saveButton.title = isUpdate ? Constants.Action.update : Constants.Action.add } - @IBAction func addImageAttachment(_ sender: Any) { - // opening camera - cameraHelper.openCamera(in: self) { [weak self] image in - guard let image = image else { return } + // adding and constraining UI elements + func setupUI() { + + + + self.view.addSubview(taskLabel) + NSLayoutConstraint.activate([ + taskLabel.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor, constant: -30), + taskLabel.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20), + taskLabel.widthAnchor.constraint(equalToConstant: self.view.frame.width/2) + + + + ]) + + self.view.addSubview(taskTitleTextField) + NSLayoutConstraint.activate([ + taskTitleTextField.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20), + taskTitleTextField.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -20), + taskTitleTextField.topAnchor.constraint(equalTo: taskLabel.bottomAnchor, constant: 15), + taskTitleTextField.heightAnchor.constraint(equalToConstant: self.view.frame.height*0.05), - // Adding attachment + ]) + + self.view.addSubview(subtasksLabel) + NSLayoutConstraint.activate([ + subtasksLabel.topAnchor.constraint(equalTo: taskTitleTextField.bottomAnchor, constant: 30), + subtasksLabel.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20), + subtasksLabel.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -20) + + ]) + + self.view.addSubview(subTasksTextView) + NSLayoutConstraint.activate([ + subTasksTextView.topAnchor.constraint(equalTo: subtasksLabel.bottomAnchor, constant: 8), + subTasksTextView.heightAnchor.constraint(equalTo: self.view.heightAnchor, multiplier: 0.1428), + subTasksTextView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20), + subTasksTextView.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.80 ) + + + ]) + + self.view.addSubview(endDateLabel) + NSLayoutConstraint.activate([ + endDateLabel.topAnchor.constraint(equalTo: subTasksTextView.bottomAnchor, constant: 20 ), + endDateLabel.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20), + endDateLabel.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -20) + + ]) + + self.view.addSubview(endDateTextField) + NSLayoutConstraint.activate([ + endDateTextField.topAnchor.constraint(equalTo: endDateLabel.bottomAnchor, constant: 8), + endDateTextField.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20), + endDateTextField.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -20), + endDateTextField.heightAnchor.constraint(equalToConstant: self.view.frame.height*0.05) + + + ]) + + + // This will be added back in future PR + self.view.addSubview(imageLabel) + NSLayoutConstraint.activate([ + imageLabel.topAnchor.constraint(equalTo: endDateTextField.bottomAnchor, constant: 20), + imageLabel.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20), + + + ]) + self.view.addSubview(addImageButton) + NSLayoutConstraint.activate([ + addImageButton.topAnchor.constraint(equalTo: endDateTextField.bottomAnchor, constant: 20), + addImageButton.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -20), + imageLabel.trailingAnchor.constraint(equalTo: addImageButton.leadingAnchor), + addImageButton.leadingAnchor.constraint(equalTo: imageLabel.trailingAnchor) + ]) + + + self.view.addSubview(attachmentCollection) + attachmentCollection.backgroundColor = .black + NSLayoutConstraint.activate([ + attachmentCollection.topAnchor.constraint(equalTo: imageLabel.bottomAnchor, constant: 10), + attachmentCollection.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 10), + attachmentCollection.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: -10), + attachmentCollection.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: -10), +// attachmentCollection.heightAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.heightAnchor, multiplier: 0.2) + ]) + + + + + } + + @objc func addPhotoTapped(_ sender: Any) { + + cameraHelper.openCamera(in: self) { [weak self ] image in + guard let image = image else { return } self?.imagesAttached.append(image) self?.attachmentCollection.reloadData() + } + } - @IBAction func saveTapped(_ sender: UIBarButtonItem) { + +// @objc func addImageAttachment(_sender: Any) { +// // opening camera +// cameraHelper.openCamera(in: self) { [weak self] image in +// guard let image = image else { return } +// +// // Adding attachment +// // self?.imagesAttached.append(image) +// // self?.attachmentCollection.reloadData() +// } +// } + + + @objc func saveTapped(_ sender: UIBarButtonItem) { hapticGenerator = UINotificationFeedbackGenerator() hapticGenerator?.prepare() - + guard isValidTask() else { hapticGenerator?.notificationOccurred(.warning) return @@ -77,7 +301,7 @@ class TaskDetailsViewController: UIViewController{ self.navigationController?.popViewController(animated: true) return } - + hapticGenerator?.notificationOccurred(.success) if isUpdate { @@ -86,7 +310,7 @@ class TaskDetailsViewController: UIViewController{ self.delegate?.didTapSave(task: task) } self.navigationController?.popViewController(animated: true) - + hapticGenerator = nil } @@ -128,31 +352,31 @@ class TaskDetailsViewController: UIViewController{ return task } - func loadTaskForUpdate() { - guard let task = self.task else { - subTasksTextView.textColor = .placeholderText - return - } - taskTitleTextField.text = task.title - subTasksTextView.text = task.subTasks - endDateTextField.text = task.dueDate - - // Recover attachments - if let attachments = task.attachments { - imagesAttached = NSKeyedUnarchiver.unarchiveObject(with: attachments) as? [UIImage] ?? [] + func loadTaskForUpdate() { + guard let task = self.task else { + subTasksTextView.textColor = .placeholderText + return + } + taskTitleTextField.text = task.title + subTasksTextView.text = task.subTasks + endDateTextField.text = task.dueDate + + // Recover attachments + if let attachments = task.attachments { + imagesAttached = NSKeyedUnarchiver.unarchiveObject(with: attachments) as? [UIImage] ?? [] + } } - } - // IBOUTLET for datepicker - /// function is called when `Date is changed` - /// `Dateformatter` is used to convert `Date` to `String` - @objc func didPickDate(_ sender: UIDatePicker) { - dateFormatter.dateFormat = "MM/dd/yyyy hh:mm a" - let selectedDate = sender.date - self.selectedDateTimeStamp = sender.date.timeIntervalSince1970 - endDate = dateFormatter.string(from: selectedDate) - endDateTextField.text = endDate - } + // IBOUTLET for datepicker + /// function is called when `Date is changed` + /// `Dateformatter` is used to convert `Date` to `String` + @objc func didPickDate(_ sender: UIDatePicker) { + dateFormatter.dateFormat = "MM/dd/yyyy hh:mm a" + let selectedDate = sender.date + self.selectedDateTimeStamp = sender.date.timeIntervalSince1970 + endDate = dateFormatter.string(from: selectedDate) + endDateTextField.text = endDate + } } extension TaskDetailsViewController: UITextFieldDelegate, UITextViewDelegate { @@ -179,23 +403,30 @@ extension TaskDetailsViewController: UITextFieldDelegate, UITextViewDelegate { } } -extension TaskDetailsViewController: UICollectionViewDelegate, UICollectionViewDataSource { +extension TaskDetailsViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + return CGSize(width: 100, height: 100) + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return imagesAttached.count } - + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - - let cell = collectionView.dequeueReusableCell(withReuseIdentifier: Constants.Cell.photoCell, for: indexPath) as! ImageAttachmentCell + + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ImageAttachmentCell.identifier, for: indexPath) as! ImageAttachmentCell let image = imagesAttached[indexPath.row] - + cell.setImage(image) - + return cell } - + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { debugPrint("Click: \(indexPath.row) \(imagesAttached[indexPath.row])") } } + diff --git a/Application/To-Do/View/Base.lproj/Main.storyboard b/Application/To-Do/View/Base.lproj/Main.storyboard index a4dcf0e..29d6e68 100644 --- a/Application/To-Do/View/Base.lproj/Main.storyboard +++ b/Application/To-Do/View/Base.lproj/Main.storyboard @@ -1,10 +1,11 @@ - + - + + - + @@ -36,7 +37,7 @@ - + @@ -95,14 +96,14 @@ - + + - @@ -137,156 +138,9 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - @@ -323,7 +177,7 @@ - + @@ -359,7 +213,7 @@ - + @@ -395,7 +249,7 @@ - + @@ -426,14 +280,14 @@ - + - + @@ -460,9 +314,9 @@ - - + @@ -508,14 +362,14 @@ - + + - @@ -532,7 +386,7 @@ - + @@ -620,7 +474,7 @@ - + @@ -630,7 +484,7 @@ - - - - - - + + + + + + + + + + + + diff --git a/Application/To-Do/View/Cells/ImageAttachmentCell.swift b/Application/To-Do/View/Cells/ImageAttachmentCell.swift index c0434e0..49677ab 100644 --- a/Application/To-Do/View/Cells/ImageAttachmentCell.swift +++ b/Application/To-Do/View/Cells/ImageAttachmentCell.swift @@ -9,7 +9,35 @@ import UIKit class ImageAttachmentCell: UICollectionViewCell { - @IBOutlet private weak var imageView: UIImageView! +// @IBOutlet private weak var imageView: UIImageView! + static let identifier: String = "ImageAttachmentCell" + + let imageView: UIImageView = { + let iv = UIImageView() + iv.translatesAutoresizingMaskIntoConstraints = false + return iv + }() + + + override init(frame: CGRect) { + super.init(frame: frame) + + contentView.addSubview(imageView) + + NSLayoutConstraint.activate([ + imageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), + imageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), + imageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor), + imageView.topAnchor.constraint(equalTo: contentView.topAnchor) + ]) + + + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + func setImage(_ image: UIImage?) { imageView.image = image