indexPath
が有効かどうかを確認するにはどうすればよいですか?
indexPath
までスクロールしたいのですが、UICollectionView
サブビューの読み込みが完了していない場合、エラーが発生することがあります。
確認できました
- numberOfSections
- numberOfItemsInSection:
あなたの UICollectionViewDataSource
は、indexPathが有効かどうかを確認します。
より簡潔なソリューションですか?
func indexPathIsValid(indexPath: NSIndexPath) -> Bool {
if indexPath.section >= numberOfSectionsInCollectionView(collectionView) {
return false
}
if indexPath.row >= collectionView.numberOfItemsInSection(indexPath.section) {
return false
}
return true
}
またはよりコンパクトですが、読みにくい...
func indexPathIsValid(indexPath: NSIndexPath) -> Bool {
return indexPath.section < numberOfSectionsInCollectionView(collectionView) && indexPath.row < collectionView.numberOfItemsInSection(indexPath.section)
}
@ABakerSmithの答えは近いですが、まったく正しくありません。
答えはモデルによって異なります。
複数のセクションからなるコレクションビュー(またはその点でテーブルビュー-同じ問題)がある場合、配列の配列を使用してデータを保存することは非常に一般的です。
外部配列にはセクションが含まれ、各内部配列にはそのセクションの行が含まれます。
したがって、次のようなものがあります。
struct TableViewData
{
//Dummy structure, replaced with whatever you might use instead
var heading: String
var subHead: String
var value: Int
}
typealias RowArray: [TableViewData]
typeAlias SectionArray: [RowArray]
var myTableViewData: SectionArray
その場合、indexPathが提示されると、モデルオブジェクト(上記の例ではmyTableViewData)に問い合わせる必要があります。
コードは次のようになります。
func indexPathIsValid(theIndexPath: NSIndexPath) -> Bool
{
let section = theIndexPath.section!
let row = theIndexPath.row!
if section > myTableViewData.count-1
{
return false
}
let aRow = myTableViewData[section]
return aRow.count < row
}
@ABakerSmithには興味深いひねりがあります。データソースに尋ねることです。そうすれば、データモデルに関係なく機能するソリューションを作成できます。彼のコードは近いですが、まだ完全に正しくありません。本当にこれになるはずです:
func indexPathIsValid(indexPath: NSIndexPath) -> Bool
{
let section = indexPath.section!
let row = indexPath.row!
let lastSectionIndex =
numberOfSectionsInCollectionView(collectionView) - 1
//Make sure the specified section exists
if section > lastSectionIndex
{
return false
}
let rowCount = self.collectionView(
collectionView, numberOfItemsInSection: indexPath.section) - 1
return row <= rowCount
}
Swift 4私が書いてしばらく使用しているスニペットです。IndexPathが利用可能な場合のみスクロールするか、IndexPathが利用できない場合はエラーをスローします。この状況で何をしたいかを制御できます。
こちらのコードをご覧ください:
https://Gist.github.com/freak4pc/0f244f41a5379f001571809197e72b9
次のいずれかを実行できます。
myCollectionView.scrollToItemIfAvailable(at: indexPath, at: .top, animated: true)
または
myCollectionView.scrollToItemOrThrow(at: indexPath, at: .top, animated: true)
後者は次のようなものを投げます:
expression unexpectedly raised an error: IndexPath [0, 2000] is not available. The last available IndexPath is [0, 36]
Swift拡張機能を使用:
extension UICollectionView {
func validate(indexPath: IndexPath) -> Bool {
if indexPath.section >= numberOfSections {
return false
}
if indexPath.row >= numberOfItems(inSection: indexPath.section) {
return false
}
return true
}
}
// Usage
let indexPath = IndexPath(item: 10, section: 0)
if sampleCollectionView.validate(indexPath: indexPath) {
sampleCollectionView.scrollToItem(at: indexPath, at: UICollectionViewScrollPosition.centeredHorizontally, animated: true)
}
データソースが追加されるインデックスパスの検証(将来の状態)と、現在の既存のインデックスパスの削除(現在の状態)を確認する必要があります。
extension UITableView {
func isValid(indexPath: IndexPath, inDataSource: Bool = false) -> Bool {
guard
let numberOfSections = inDataSource
? dataSource?.numberOfSections?(in: self)
: numberOfSections,
let numberOfRows = inDataSource
? dataSource?.tableView(self, numberOfRowsInSection: indexPath.section)
: numberOfRows(inSection: indexPath.section)
else {
preconditionFailure("There must be a datasource to validate an index path")
}
return indexPath.section < numberOfSections && indexPath.row < numberOfRows
}
使用法:
// insert
tableView.insertRows(at: indexPaths.filter({ tableView.isValid(indexPath: $0, inDataSource: true) }), with: .top)
// remove
tableView.deleteRows(at: indexPaths.filter({ tableView.isValid(indexPath: $0) }), with: .top)
出力:
true or false
インデックスパスが有効かどうかを知らずにコレクションビューでセルの状態を設定しようとしている場合、特別な状態のセルのインデックスを保存して、ロード中にセルの状態を設定できます。
Objective Cバージョン:
- (BOOL)indexPathIsValid:(NSIndexPath *)indexPath
{
return indexPath.section < [self.collectionView numberOfSections] && indexPath.row < [self.collectionView numberOfItemsInSection:indexPath.section];
}