|
Do you have some sort of custom gesture recognizers on this screen?
|
|
No, I have not ...
This the complete viewcontroller (now with your style (from the blog) contextual actions:
class AnnotationsViewController: UIViewController {
//MARK: - Types
typealias Annotation = AnnotationsController.Annotation
typealias AnnotationType = AnnotationsController.AnnotationType
//MARK: - Properties
let annotationsController = AnnotationsController(with: ProjectsController.activeProject!)
var tableView: UITableView!
var dataSource: UITableViewDiffableDataSource<AnnotationType, Annotation>!
//MARK: - Viewcontroller Methods
override func viewDidLoad() {
super.viewDidLoad()
configureViewController()
configureTableView()
createDataSource()
updateData(on: annotationsController.filteredAnnotations())
// configureSearchController()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.navigationBar.prefersLargeTitles = true
navigationController?.navigationBar.tintColor = .systemOrange
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navigationController?.navigationBar.tintColor = .systemPurple
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
updateFlipButton()
}
override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
super.willTransition(to: newCollection, with: coordinator)
coordinator.animate(alongsideTransition: nil) { _ in
self.updateFlipButton()
}
}
//MARK: - SplitOrFlip functionality
private func updateFlipButton() {
if let splitOrFlipViewController = splitOrFlipViewController {
switch splitOrFlipViewController.displayMode {
case .sideBySide:
navigationItem.rightBarButtonItem = nil
break
case .one:
navigationItem.rightBarButtonItem = UIBarButtonItem(image: ProjectImages.BarButton.flip, style: .plain, target: self, action: #selector(flipAction))
break
}
}
}
@objc func flipAction() {
splitOrFlipViewController?.flipAction()
}
//MARK: - DataSource
func createDataSource() {
dataSource = UITableViewDiffableDataSource<AnnotationType, Annotation>(tableView: tableView) { (tableView, indexPath, annotation) in
return self.configure(AnnotationCell.self, with: annotation, for: indexPath)
}
}
func updateData(on annotations: [Annotation]) {
var snapshot = NSDiffableDataSourceSnapshot<AnnotationType, Annotation>()
//Append available sections
AnnotationType.allCases.forEach { snapshot.appendSections([$0]) }
//Append annotations to their corresponding sections
annotations.forEach { (annotation) in
snapshot.appendItems([annotation], toSection: annotation.type as AnnotationType)
}
//Force the update on the main thread to silence a warning about tableview not being in the hierarchy!
DispatchQueue.main.async {
self.dataSource.apply(snapshot, animatingDifferences: true)
}
}
///Configure any type of cell that conforms to selfConfiguringAnnotationCell!
func configure<T: SelfConfiguringAnnotationCell>(_ cellType: T.Type, with annotation: Annotation, for indexPath: IndexPath) -> T {
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellType.reuseIdentifier, for: indexPath) as? T else {
fatalError("Unable to dequeue \(cellType)")
}
cell.configure(with: annotation)
return cell
}
//MARK: - Actions
@objc private func closeProject() {
//TODO: - Save Projects & Annotations
//Reassign rootViewController
UIApplication.resetRootViewControllerTo(ProjectsViewController())
}
//MARK: - UI & layout
private func configureViewController() {
title = annotationsController.project.title
view.backgroundColor = .systemPurple
navigationController?.navigationBar.prefersLargeTitles = true
navigationController?.navigationBar.tintColor = .systemOrange
navigationItem.leftBarButtonItem = UIBarButtonItem(image: ProjectImages.BarButton.close, style: .plain, target: self, action: #selector(closeProject))
}
private func configureTableView() {
tableView = UITableView(frame: view.bounds, style: .grouped)
view.addSubview(tableView)
tableView.delegate = self
tableView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
tableView.backgroundColor = .clear
tableView.register(AnnotationCell.self, forCellReuseIdentifier: AnnotationCell.reuseIdentifier)
tableView.register(AnnotationHeaderCell.self, forHeaderFooterViewReuseIdentifier: AnnotationHeaderCell.reuseIdentifier)
//Dynamic sizing cells
tableView.sectionHeaderHeight = 40
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 70
}
}
//MARK: - Ext. TableViewDelegate
extension AnnotationsViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let annotationType = AnnotationType.allCases[section]
let cell = tableView.dequeueReusableHeaderFooterView(withIdentifier: AnnotationHeaderCell.reuseIdentifier) as! AnnotationHeaderCell
cell.configure(with: annotationType)
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// let viewController = UIViewController()
// viewController.view.frame = CGRect(x: 100, y: 100, width: 200, height: 200)
// viewController.modalPresentationStyle = .formSheet
// viewController.view.backgroundColor = .blue
//// navigationController?.pushViewController(viewController, animated: true)
// navigationController?.show(viewController, sender: self)
}
func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let completeAction = UIContextualAction(style: .normal, title: nil) { (_, _, completionHandler) in
// delete the item here
completionHandler(true)
}
completeAction.image = ProjectImages.Annotation.checkmark.withTintColor(.systemGreen)
completeAction.backgroundColor = .systemOrange
let configuration = UISwipeActionsConfiguration(actions: [completeAction])
return configuration
}
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let deleteAction = UIContextualAction(style: .destructive, title: nil) { (_, _, completionHandler) in
// delete the item here
completionHandler(true)
}
deleteAction.image = UIImage(systemName: "trash")
deleteAction.backgroundColor = .systemRed
let configuration = UISwipeActionsConfiguration(actions: [deleteAction])
return configuration
}
}
|
|
|
|
Oh no, you got to be kidding! Hate these inconsistencies. Should I really implement swipe actions on my own? (I did that on my collectionview already)
🥵
|
|
What about the solution of subclassing diffable datasource and allowing them as per SO answer?
|
|
I would definitely stick with system ones because they are highly polished and solve a lot for you
|
|
@Nemecek-Filip.... Yes I guess I have to subclass the datasource to ve able to implement canEditRowAt ...
You should subclass UITableViewDiffableDataSource and return true for the rows you want to enable this for in:
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool
Another puzzle to solve, gonna try that, no idea how yet though ....
|
|
I think you can get away with just creating something like SwipeEnabledDiffableDataSource , inherit from UITableViewDiffableDataSource and "just" override the method canEditRowAt to return true.
|
|
Yes, you are right:
class AnnotationDiffableDataSource: UITableViewDiffableDataSource<AnnotationsController.AnnotationType, AnnotationsController.Annotation> {
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
//
}
}
And in my viewController, only change the DataSource to be of your subclass and change the call to:
var dataSource: AnnotationDiffableDataSource!
//MARK: - DataSource
func createDataSource() {
dataSource = AnnotationDiffableDataSource(tableView: tableView) { (tableView, indexPath, annotation) in
return self.configure(AnnotationCell.self, with: annotation, for: indexPath)
}
}
This works. Would be neater to move more over to the Subclass, but I am unsure how to do that (No access to the tableview there)
Any resources on a fully subclassed datasource appreciated!
|
|
|
|
Heyy :) Here is another way that has worked for me, see editingStyle func:
class DataSource: UITableViewDiffableDataSource<TaskType, Task> {
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return TaskType.allCases[section].rawValue // "One Large" / "Three medium" etc..
}
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete{
var snapshot = self.snapshot()
if let item = itemIdentifier(for: indexPath) {
snapshot.deleteItems([item])
apply(snapshot, animatingDifferences: true)
}
}
}
}
|