Within the first a part of this text, we had been in a position to design the UI for our Observe App and likewise carried out the Create and Learn operations of our app.
Let’s take it a step additional by implementing the Replace and Delete operations.
Let’s get began!
Create a brand new view controller and identify it NoteDetailViewController
; Once more, make certain to not examine “Additionally create XIB file” possibility.
This may show the be aware and likewise permit us edit it.
Now let’s give our view controller’s essential view a color of .systemBackground as follows.
view.backgroundColor = .systemBackground
Now let’s add the UI Views for our be aware. The NoteDetailViewController
will look so much just like the AddNoteViewController
however with some further parts.
Let’s add the title textual content area as an nameless closure:
personal var titleField: UITextField = {
let area = UITextField()
area.textColor = .label
area.font = UIFont.systemFont(
ofSize: 22, weight: .medium)
return area
}()
Additionally add the physique textual content view as an nameless closure:
personal var bodyTextView: UITextView = {
let view = UITextView()
view.font = UIFont.systemFont(ofSize: 18)
view.textColor = .label
view.clipsToBounds = true
return view
}()
Now let’s add the views as subviews to the controller’s essential view and outline their frames by overriding the viewWillLayoutSubviews
.
override func viewWillLayoutSubviews() {
tremendous.viewWillLayoutSubviews()
view.addSubViews(views: titleField, bodyTextView)
titleField.body = CGRect(
x: 12,
y: 120,
width: view.width - 24,
peak: 44)
bodyTextView.body = CGRect(
x: 8,
y: titleField.backside + 8,
width: view.width - 16,
peak: view.backside - 220)
}
Observe let’s create a public be aware
property in our NoteDetailViewController
as follows:
var be aware: Observe?
Bear in mind the NoteDetailViewController
shows the element of a specific be aware within the NotesViewController
, meaning it expects {that a} be aware
object might be handed to it whenever you faucet on a be aware from the listing of notes within the NotesViewController
.
Now, in our viewDidLoad
, let’s set worth to our titleTextField
and bodyTextView
as follows:
if let be aware = be aware {
titleField.textual content = be aware.title
bodyTextView.textual content = be aware.physique
}
As a result of the be aware
property is nullable, we examine if it is not null to make use of it.
Nice job!
Now let’s return to our NotesViewController
class to deal with choosing a be aware to view it is element.
Now we’ll implement the collectionView delegate methodology didSelectItemAt
indexPath as follows:
func collectionView(_ collectionView: UICollectionView,
didSelectItemAt indexPath: IndexPath) {
guard let be aware = self.dataSource.itemIdentifier(for:
indexPath) else {
collectionView.deselectItem(
at: indexPath, animated: true)
return
}
let noteVC = NoteDetailViewController()
noteVC.be aware = be aware
navigationController?.pushViewController(
noteVC, animated: true)
}
As a result of we’ve carried out our assortment view with a diffable datasource, to get the component at indexPath
, we name the itemIdentifier
of the dataSource
and go it the indexPath.
Now after we construct and run, we should always see one thing like the next:
Nice Job!
UPDATE MANAGED OBJECT
Not that we’ve been in a position to create and skim from Core information, let’s proceed to replace.
Do not forget that our NoteDetailViewController
, just like the AddNoteViewController
additionally makes use of UITextField for Title and UITextView for the physique of our notes.
Thus, let’s set the NoteDetailViewController
because the delegate
for the titleTextField
and the bodyTextView
. This may permit us to have the ability to look ahead to adjustments.
Add the next snippet to viewDidLoad
within the NoteDetailViewController
:
bodyTextView.delegate = self
titleField.delegate = self
This may instantly inform us to adapt to the UITextViewDelegate
and the UITextFieldDelegate
protocols
Add the next on the backside exterior of the category definition:
extension NoteDetailViewController: UITextViewDelegate,
UITextFieldDelegate {
}
Bear in mind we created some comfort properties and methodology as extensions to the UIView, let do the identical for String.
Add the next snippet to your Extensions.swift file:
extension String {
func trim() -> String {
self.trimmingCharacters(in: .whitespacesAndNewlines)
}
}
This can be a shorthand model of the usual trimmingCharaters(:)
String methodology.
Due to Swift, we will pass over the return
key phrase for a single line assertion.
In a be aware app, we would like the app to set off and replace when the title area detects that the title has modified and the physique textual content view to replace the physique content material when the that adjustments as effectively.
To assist us obtain this, we’re going to be implementing the textFieldDidEndEditing
delegate methodology of the UITextFieldDelegate
protocol and the textViewDidEndEditing
methodology of the UITextViewDelegate
.
Add the next contained in the NoteDetailViewController
extension we simply added on the backside:
extension NoteDetailViewController: UITextViewDelegate,
UITextFieldDelegate {
func textFieldDidEndEditing(_ textField: UITextField) {
}
func textViewDidEndEditing(_ textView: UITextView) {
}
}
Observe let’s add a property identify appDelegate to the NoteDetailViewController
slightly below the be aware declaration as follows:
let appDelegate = UIApplication.shared.delegate as! AppDelegate
Now, inside our textFieldDidEndEditing
delegate methodology, we would like core information to replace the title when the title textual content area detect a change and the consumer has ended modifying.
Add the next snippet:
resignFirstResponder()
guard let be aware = self.be aware else { return }
if textField == titleField &&
titleField.textual content!.trim() != be aware.title {
let managedContext =
appDelegate.persistentContainer.viewContext
be aware.title = titleField.textual content!.trim()
do {
attempt managedContext.save()
} catch let error as NSError {
fatalError("(error.userInfo)")
}
}
Additionally contained in the textViewDidEndEditing delegate methodology, to replace the physique, add the next:
resignFirstResponder()
guard let be aware = self.be aware else { return }
if textView == bodyTextView &&
bodyTextView.textual content.trim() != be aware.physique {
let managedContext =
appDelegate.persistentContainer.viewContext
be aware.physique = bodyTextView.textual content
do {
attempt managedContext.save()
} catch let error as NSError {
fatalError("(error.userInfo)")
}
}
Discover that we’ve got to first examine if the adjustments is just not the identical with the content material of the be aware title or the physique earlier than persisting adjustments.
Observe let’s construct and run.
We’ve now carried out Replace.
DELETING MANAGED OBJECT
Now let’s implement the Delete operation. To take action, let’s return to our NotesViewController
.
Do not forget that we used UICollectionLayoutListConfiguration
(i.e Listing Structure) which provides us an analogous view as a UITableView.
So as to have the ability to swipe to delete as within the case of UITableView, we have to add Swipe motion to our Listing format configuration. Now go contained in the personal methodology named createLayout
and add the next snippet simply earlier than the return assertion:
config.trailingSwipeActionsConfigurationProvider = {
indexPath in
let deleteAction = UIContextualAction(
model: .damaging, title: "Delete") {
[weak self] motion, view, completion in
self?.deleteItem(at: indexPath)
completion(true)
}
return UISwipeActionsConfiguration(
actions: [deleteAction])
}
You might additionally create one for the leadingSwipeActionsConfigurationProvider
if there’s want for that.
Now we have to create a way known as deleteItem(at:)
. So add the next snippet:
personal func deleteItem(at indexPath: IndexPath) {
guard let appDelegate = UIApplication.shared.delegate
as? AppDelegate else { return }
let managedContext =
appDelegate.persistentContainer.viewContext
let be aware = self.dataSource.itemIdentifier(for: indexPath)
guard let be aware = be aware else { return }
managedContext.delete(be aware)
do {
attempt managedContext.save()
var snapshot = dataSource.snapshot()
snapshot.deleteAllItems()
snapshot.appendSections([.main])
dataSource.apply(snapshot)
fetchNotes()
updateCollectionView()
} catch let error as NSError {
fatalError("(error.userInfo)")
}
}
Within the methodology above, first we get a managedContext
from the persistent container within the AppDelegate. Then we discover the be aware
on the swiped indexPath
.
Then we handed the be aware
to the delete methodology of the managed context. Lastly and importantly, we known as the save()
methodology on the managedContext
to commit the precise deletion.
Now to replace our UI (i.e take away the deleted from snapshot), we needed to clear the snapshot, reassigned the primary part earlier than fetching and updating the CollectionView.
Now let’s construct and run once more. If all the things is ok, we should always have the ability to swipe a cell and see it deleted when the delete motion button is tapped.
We have come to finish of the challenge. The whole supply code might be discovered here.