【发布时间】:2021-10-11 00:19:55
【问题描述】:
我目前正在开发一个应用程序,该应用程序具有类似于表视图的集合视图和其他一些视图控制器。基本上,我的问题是如何在删除集合视图单元格之一时更新每个单元格的 indexPath。
我在下面附上了我的视图控制器文件,但这是应用程序上发生的事情。 当用户打开类似表视图的集合视图(在 EventCollectionVC 中)时,它会重新加载数据库中的数据并将它们呈现在集合视图上。我还在导航栏按钮项中添加了代码,用户可以将集合视图更改为编辑模式。在编辑模式下,集合视图单元格上会显示一个小的 ellipsis.circle(SF 符号)。当用户点击 ellipsis.circle 图标时,它会显示一个新的视图控制器 (ModalVC) 并让用户选择删除或编辑单元格。当用户选择删除时,它会显示删除单元格的警报,并使用模态关闭删除单元格信息(这意味着 ModalVC 已关闭,并且现在显示 MyCollectionVC)。
由于我必须让两个视图控制器(比如从 EventCollectionVC 中获取单元格信息并呈现在 ModalVC 中)相互通信,因此我需要使用 indexPath.row 来获取单元格的信息。在删除单元格之前,collection view 中 indexPath.row 的数量是这样的
[0,1,2,3,4,5]
但是,例如,在我删除第二个 (indexPath.row = 1) 单元格之后,当我尝试删除另一个项目时,indexPath 变为
[0,2,3,4,5]
我可以看到集合视图的索引没有刷新。
所以我的问题是如何在从集合视图中删除单元格后更新/刷新单元格的 indexPath.row 值?
这是带有一些解释的代码。
import UIKit
class EvnetCollectionViewController: UIViewController {
var EventDataSource: EventDataSource! // <- this is a class for the Model, and it stores array or Events
let ListView = ListView() // view file
var collectionViewDataSource: UICollectionViewDiffableDataSource<Section, Event>?
var targetEventIndex = Int() // variable to store the index of the event when event cell is tapped
override func loadView() {
view = ListView
}
override func viewDidLoad() {
super.viewDidLoad()
configureNavItem()
setupCollectionView()
displayEvents()
}
func configureNavItem() {
navigationItem.rightBarButtonItem = editButtonItem
}
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
if (editing){
ListView.collectionView.isEditing = true
} else {
ListView.collectionView.isEditing = false
}
}
func setupCollectionView() {
let cellRegistration = UICollectionView.CellRegistration<UICollectionViewListCell, Event> { cell, indexPath, Event in
var content = UIListContentConfiguration.cell()
content.text = Event.title
cell.contentConfiguration = content
let moreAction = UIAction(image: UIImage(systemName: "ellipsis.circle"),
handler: { _ in
let vc = EventActionModalViewController(); // when the user select the cell in edit mode, it displays action modal VC and then come back to this VC with dismiss later
vc.modalPresentationStyle = .overCurrentContext
self.targetEventIndex = indexPath.row // I need targetEvemtIndex when user selects delete event in EventActionModalVC, so just sotre value in here
})
let moreActionButton = UIButton(primaryAction: moreAction)
let moreActionAccessory = UICellAccessory.CustomViewConfiguration(
customView: moreActionButton,
placement: .trailing(displayed: .whenEditing, at: { _ in return 0 })
)
cell.accessories = [
.disclosureIndicator(displayed: .whenNotEditing),
.customView(configuration: moreActionAccessory)
]
}
collectionViewDataSource = UICollectionViewDiffableDataSource<Section, Event>(collectionView: ListView.collectionView) {
collectionView, indexPath, Event in
collectionView.dequeueConfiguredReusableCell(using: cellRegistration, for: indexPath, item: Event)
}
}
func displayEvents() {
EventDataSource = EventDataSource()
EventDataSource.loadData() // get Events in db and sore in an array Events
populate(with: EventDataSource.Events)
}
func populate(with Events: [Event]) {
var snapshot = NSDiffableDataSourceSnapshot<Section, Event>()
snapshot.appendSections([.List])
snapshot.appendItems(Events)
collectionViewDataSource?.apply(snapshot)
}
func showDeleteAlert() {
let deleteAction = UIAlertAction(title: "Delete", style: .destructive) { _ in
self.EventDataSource.delete(at: targetEventIndex)
self.refreshList()
}
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel)
self.showAlert(title: "Delete", message: nil, actions: [deleteAction, cancelAction], style: .actionSheet, completion: nil)
}
func refreshList() {
EventDataSource.loadData()
setupCollectionView() // since I write this code, it updates all of the indexPath in the collection view, but after deleting one item, the whole collection view is deleted and new collection view is reappeared.
populate(with: EventDataSource.Events)
}
}
我有点知道为什么会这样。我只配置一次单元格(在 let cellRegistration = UICollectionView.CellRegistration
如果没有在 refreshList 中写入 setupCollectionView(),则单元格的 indexPath 不会刷新,并且在我删除一个单元格并尝试删除另一个单元格后出现错误。所以,我想知道是否有一种方法可以避免重新创建整个集合视图,但在用户删除集合视图中的一个单元格时更新单元格的 indexPath。
【问题讨论】:
-
您使用的是 diffable 数据源,因此您无需担心索引路径。您应该从模型中删除相关元素。然后你需要通知你的其他视图控制器他们需要重新应用快照。您可以使用委托模式或
Notification甚至组合。 -
你是怎么得到 [0,2,3,4,5] 的?您是否删除了数据集中索引 == 1 的项目?
-
在 setupCollectionView() 的地方尝试写
collectionView.reloadData() -
@ShivamParmar 你的意思是刷新列表功能?
-
@ElTomato 当用户尝试从列表中删除项目时,我编写了一个打印语句来检查 indexPath。然后在我删除一个单元格后,indexPath.row 的值会跳过我刚刚删除的 indexParh.row 的数字。所以就像如果我在列表中有三个项目并且在我删除列表中的第二个项目之后(这意味着列表中有两个项目),第一个项目的 indexpath.row 是相同的(0),但第二个1 变为 2。
标签: ios swift xcode uicollectionview uicollectionviewcell