web-dev-qa-db-ja.com

indexPathが有効かどうかを確認して、「無効なインデックスパスまでスクロールしようとする」エラーを回避するにはどうすればよいですか。

indexPathが有効かどうかを確認するにはどうすればよいですか?

indexPathまでスクロールしたいのですが、UICollectionViewサブビューの読み込みが完了していない場合、エラーが発生することがあります。

34
webmagnets

確認できました

- numberOfSections
- numberOfItemsInSection: 

あなたの UICollection​View​Data​Sourceは、indexPathが有効かどうかを確認します。

33
muffe

より簡潔なソリューションですか?

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)
}
22
Andres Canella

@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
}
11
Duncan C

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]

6
Shai Mishali

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)
}
5
Ashok

データソースが追加されるインデックスパスの検証(将来の状態)と、現在の既存のインデックスパスの削除(現在の状態)を確認する必要があります。

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
0
Lloyd Keijzer

インデックスパスが有効かどうかを知らずにコレクションビューでセルの状態を設定しようとしている場合、特別な状態のセルのインデックスを保存して、ロード中にセルの状態を設定できます。

0
carrotzoe

Objective Cバージョン:

- (BOOL)indexPathIsValid:(NSIndexPath *)indexPath
{
    return indexPath.section < [self.collectionView numberOfSections] && indexPath.row < [self.collectionView numberOfItemsInSection:indexPath.section];
}
0
wzbozon