我正在查看 iOS13 中可用的 DiffableDataSource(或在此处向后移植:https : //github.com/ra1028/DiffableDataSources)并且无法弄清楚如何在您的集合或 tableview 中支持多种单元格类型。
Apple 的示例代码1具有:
var dataSource: UICollectionViewDiffableDataSource<Section, OutlineItem>! = nil
Run Code Online (Sandbox Code Playgroud)
这似乎强制数据源为单个单元格类型。如果我为另一种单元格类型创建一个单独的数据源 - 那么不能保证两个数据源不会同时apply
调用它们 - 这会导致可怕的NSInternalInconsistencyException
- 任何试图动画的人都熟悉使用 手动插入/删除单元格performBatchUpdates
。
我错过了一些明显的东西吗?
我正在我的应用程序中使用UITableViewDiffableDataSource
. 每个单元格代表一个搜索命中并在单元格标题中突出显示搜索匹配项,有点像 Xcode 的 Open Quickly 窗口突出显示其结果项的部分。在搜索字段中输入文本时,我更新了结果列表。结果随着相关性的变化在列表中上下移动。
诀窍是每次搜索文本更改时我都需要强制每个单元格重新呈现,因为新的搜索字符串意味着更新单元格标题的突出显示部分。但我不想动画删除和插入,因为它仍然是同一个项目。如何使用快照告诉数据源它需要重新加载单元格?
我这样声明数据源:
@property (retain) UITableViewDiffableDataSource<NSString *, SearchHit *> *dataSource;
Run Code Online (Sandbox Code Playgroud)
SearchHit
代表一个搜索结果;它具有显示标题和要在标题中突出显示的范围数组的属性。并且它会覆盖hash
andisEqual:
以便唯一标识每个结果行。
我的代码看起来像这样:
-(void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
NSArray<SearchHit *> *hits = [self fetchHits:searchText];
NSDiffableDataSourceSnapshot<NSString *, SearchHit *> *snap = [[[NSDiffableDataSourceSnapshot alloc] init] autorelease];
[snap appendSectionsWithIdentifiers:@[@""]];
[snap appendItemsWithIdentifiers:hits];
[snap reloadItemsWithIdentifiers:hits];
[self.dataSource applySnapshot:snap animatingDifferences:YES];
}
Run Code Online (Sandbox Code Playgroud)
起初我没有在reloadItemsWithIdentifiers
那里打电话,然后一旦它出现在结果列表中,任何单元格都不会改变。添加reload
呼叫有帮助,但现在大多数单元格总是落后于一个更新。这在我的代码中的某处闻起来像是逻辑错误,但我已经验证传递给快照的命中是正确的,而传递给数据源的单元格创建回调的命中不是。
Donny Wals 的这篇文章和涉及 Steve Breen 的相关 Twitter 线程建议解决此问题的方法是使项目标识符类型仅表示显示单元格所需的属性。所以我更新了SearchHit
的散列和相等比较以包含标题的突出显示部分,而他们以前没有。然后我在每次更新时删除并插入所有单元格的动画,这是我不想要的。
这看起来reloadItemsWithIdentifiers
应该怎么做……对吧?
示例项目在这里 …
objective-c uitableview diffabledatasource nsdiffabledatasourcesnapshot
Diffable 数据源需要指定 aSectionIdentifierType
和 anItemIdentifierType
并且这些类型必须符合Hashable
据说它们必须符合,Hashable
以便数据源可以进行比较。
那么,为什么即使 == 和哈希函数相同,它的行为也会根据标识符类型是类还是结构而有所不同呢?或者甚至重写类的 === 函数,使其更像值类型?
例子:
import UIKit
public class DebugViewController: UIViewController {
typealias SectionType = IntWrapper
typealias ItemType = IntWrapper
public class IntWrapper: Hashable {
public static func == (lhs: DebugViewController.IntWrapper, rhs: DebugViewController.IntWrapper) -> Bool {
lhs.number == rhs.number
}
public static func === (lhs: DebugViewController.IntWrapper, rhs: DebugViewController.IntWrapper) -> Bool {
lhs.number == rhs.number
}
public func hash(into hasher: inout Hasher) {
hasher.combine(number)
}
var number: Int …
Run Code Online (Sandbox Code Playgroud) 我正在使用UICollectionViewDiffableDataSource
forUICollectionView
在多个部分中显示内容。
我正在使用在 WWDC'19 上引入的Collection View Compositional Layout 和 Diffable Datasources链接来呈现的多节布局UICollectionView
我有一个简单的设置,每个部分的页眉显示该部分中的项目数,页脚显示该部分所有项目的摘要。
section 1 Header --> January 2020 - 5 Trips
section 1 item 1 --> Trip 1
section 1 item 2 --> Trip 2
section 1 item 3 --> Trip 3
section 1 item 4 --> Trip 4
section 1 item 5 --> 行程 5
现在如果行程被删除,DiffableDataSource 会通过动画更新更改,但不会重新加载各部分的标题。这看起来不一致。例如,如果行程 4 被删除,则标题仍显示该部分中有 5 个行程。我怎样才能用 DiffableDataSource 重新加载标头?
对于临时修复,我只是collectionView.reloadData()
在显示 Diffing 动画的延迟后调用
,然后我硬重新加载数据,这也强制重新加载标头。
private func configureTripDataSource(){
tripDataSource …
Run Code Online (Sandbox Code Playgroud) ios uicollectionview uicollectionviewlayout swift diffabledatasource
我正在使用UITableViewDiffableDataSource
渲染单元格,我想知道如何对行进行粒度动画。
有一个属性
self.dataSource?.defaultRowAnimation = .fade
Run Code Online (Sandbox Code Playgroud)
适用于tableView的所有行,但我想要某些单元格的动画,而其他单元格没有动画。“defaultRowAnimation”中的“default”表明还有其他方法。
不幸的是,关于 subjet 的 Apple 文档有点少
例如,是否有RowAnimation
根据给定的方法询问 a 的方法IndexPath
?
我很难理解 DiffableDataSource 是如何工作的。我有这样的 ViewModel
struct ViewModel: Hashable {
var id: Int
var value: String
func hash(into hasher: inout Hasher) {
hasher.combine(id)
}
}
Run Code Online (Sandbox Code Playgroud)
我有 tableView 由 cachedItems 填充,如上面的 ViewModele。当 API 响应到达时,我想添加一个新的,删除缺失的一个,刷新 tableView 中已经存在的项目的 viewModel.value 并最终订购它。除了一件事 - 重新加载物品外,一切正常。
我对 DiffableDataSource 的理解是它比较 item.hash() 以检测项目是否已经存在,如果存在,那么如果 cachedItem != apiItem,它应该重新加载。不幸的是,这不起作用,快照确实删除和插入而不是重新加载。
DiffableDataSource 应该这样做吗?
当然,我有一个解决方案 - 为了使它工作,我需要遍历 cachedItems,当新项目包含相同的 id 时,我更新 cachedItem,然后我在没有动画的情况下应用快照,然后我终于可以应用带有动画的删除/插入/订购动画。
但是这个解决方案似乎更像是一个黑客而不是一个有效的代码。有没有更清洁的方法来实现这一目标?
更新:
有代码显示问题。它应该在操场上工作。例如。items 和 newItems 包含 id == 0 的 viewModel。哈希值相同,因此 diffableDataSource 应该重新加载,因为副标题不同。但是有可见的删除/插入而不是重新加载
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
let tableView = UITableView() …
Run Code Online (Sandbox Code Playgroud) 我正在将我的集合视图转换为新的 iOS13 UICollectionViewDiffableDataSource ......所以我需要根据需要更新单元格信息。
这是我的代码:
let snap = self.diffDataSouce.snapshot
snap?.reloadItems(withIdentifiers: [itemToUpdate]) //reload cell info
self.diffDataSouce.apply(snap, animatingDifferences: true)
Run Code Online (Sandbox Code Playgroud)
但我明白了Invalid parameter not satisfying: indexPath || ignoreInvalidItems
……为什么?我的当前snap
包含itemToUpdate
以及我的模型数组...
我认为这是因为snap.indexOfItemIdentifier(itemToUpdate)
未找到返回值(NSNotFound)……但根据数据模型,这应该是不可能的。
你有什么提示吗?
我已经使用UICollectionView
自定义UICollectionViewCell
并UIContentConfiguration
使用新的iOS 14
API 实现了一个列表。我一直在关注本教程:https://swiftsenpai.com/development/uicollectionview-list-custom-cell/(以及Apple的示例项目)
基本上你现在有 a UICollectionViewCell
、 aUIContentConfiguration
和 a UIContentView
。仅仅cell
设置其配置,content configuration
保存单元的数据及其所有可能的状态,并且是替换 的content view
实际值。UIView
UICollectionViewCell.contentView
我已经让它工作了,它非常棒而且干净。但有一点我不明白:
您如何向UIContentView
或 中添加回调以将单元格中所做的更改(例如UISwitch
切换或更改)传达到?和 单元格之间的唯一连接位于创建 的数据源时的单元格注册内部:UITextField
viewController
viewController
collectionView
// Cell
class Cell: UICollectionViewListCell {
var event: Event?
var onEventDidChange: ((_ event: Event) -> Void)?
//...
}
// Example cell registration in ViewController
let eventCellRegistration = UICollectionView.CellRegistration<Event.Cell, Event> { [weak self] (cell, …
Run Code Online (Sandbox Code Playgroud) 应用快照后,如何阻止可比较数据源将视图滚动到顶部。我目前有这个...
fileprivate func configureDataSource() {
self.datasource = UICollectionViewDiffableDataSource<Section, PostDetail>(collectionView: self.collectionView) {
(collectionView: UICollectionView, indexPath: IndexPath, userComment: PostDetail) -> UICollectionViewCell? in
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: PostDetailCell.reuseIdentifier, for: indexPath) as? PostDetailCell else { fatalError("Cannot create cell")}
cell.user = self.user
cell.postDetail = userComment
cell.likeCommentDelegate = self
return cell
}
var snapshot = NSDiffableDataSourceSnapshot<Section, PostDetail>()
snapshot.appendSections([.main])
snapshot.appendItems(self.userComments)
self.datasource.apply(snapshot, animatingDifferences: true)
}
fileprivate func applySnapshot() {
//let contentOffset = self.collectionView.contentOffset
var snapshot = NSDiffableDataSourceSnapshot<Section, PostDetail>()
snapshot.appendSections([.main])
snapshot.appendItems(self.userComments)
self.datasource.apply(snapshot, animatingDifferences: false)
//self.collectionView.contentOffset = contentOffset
} …
Run Code Online (Sandbox Code Playgroud) 我目前正在开发一个简单的 IOS 应用程序,使用集合视图和可比较数据源。
我初始化视图控制器配置中的所有内容,并在每次更新源数据时调用 reloadView 函数。
当我调用 reloadView 函数时,应用程序崩溃并出现以下错误。但前提是集合视图之前为空。如果那里已经有项目,一切都会正常工作。
Assertion failure in -[_UICollectionCompositionalLayoutSolver _queryClientForSectionDefintionForSectionIndex:]
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid section definition. Please specify a valid section definition when content is to be rendered for a section. This is a client error.'
Run Code Online (Sandbox Code Playgroud)
这就是我的代码的样子:
private func configureHierarchy() {
let layout = collectionViewHelper.createLayout(structure: self.structure)
collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
collectionView.delegate = self
collectionView.register(RecentCollectionViewCell.self, forCellWithReuseIdentifier: RecentCollectionViewCell.reuseIdentifier)
collectionView.register(OverviewCollectionViewCell.self, forCellWithReuseIdentifier: OverviewCollectionViewCell.reuseIdentifier)
collectionView.register(MessageItemCollectionViewCell.self, forCellWithReuseIdentifier: MessageItemCollectionViewCell.reuseIdentifier)
view.addSubview(collectionView)
}
private func configureDataSource() {
dataSource …
Run Code Online (Sandbox Code Playgroud) swift ×7
ios ×5
uitableview ×3
ios13 ×2
nsdiffabledatasourcesnapshot ×2
objective-c ×1
uikit ×1
xcode ×1