セルを選択したら、セルの外観の変更を処理します。デリゲートメソッドcollectionView:didSelectItemAtIndexPath:
&collectionView:didDeselectItemAtIndexPath:
は、セルを編集する場所であると考えました。
-(void)collectionView:(UICollectionView *)collectionView
didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
DatasetCell *datasetCell =
(DatasetCell *)[collectionView cellForItemAtIndexPath:indexPath];
[datasetCell replaceHeaderGradientWith:[UIColor skyBlueHeaderGradient]];
datasetCell.backgroundColor = [UIColor skyBlueColor];
}
-(void)collectionView:(UICollectionView *)collectionView
didDeselectItemAtIndexPath:(NSIndexPath *)indexPath {
DatasetCell *datasetCell =
(DatasetCell *)[collectionView cellForItemAtIndexPath:indexPath];
[datasetCell replaceHeaderGradientWith:[UIColor grayGradient]];
datasetCell.backgroundColor = [UIColor myDarkGrayColor];
}
これは、セルが再利用される場合を除き、正常に機能します。インデックス(0、0)のセルを選択すると、外観が変わりますが、下にスクロールすると、選択状態の別のセルがあります。
UICollectionViewCell
メソッド-(void)prepareForReuse
を使用してセルを再利用する準備をする(つまり、セルの外観を非選択状態に設定する)必要があると思いますが、それは困難です。
-(void)prepareForReuse {
if ( self.selected ) {
[self replaceHeaderGradientWith:[UIColor skyBlueHeaderGradient]];
self.backgroundColor = [UIColor skyBlueColor];
} else {
[self replaceHeaderGradientWith:[UIColor grayGradient]];
self.backgroundColor = [UIColor myDarkGrayColor];
}
}
上にスクロールすると、インデックス(0、0)のセルは選択解除された状態になります。
Cell.backgroundViewプロパティを使用したとき、これを防ぐには次のようにしました。
-(void)prepareForReuse {
self.selected = FALSE;
}
また、選択状態は意図したとおりに機能しました。
何か案は?
あなたの観察は正しいです。この動作は、セルの再利用が原因で発生しています。ただし、prepareForReuseを使用して何もする必要はありません。代わりにcellForItemでチェックを行い、それに応じてプロパティを設定します。何かのようなもの..
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cvCell" forIndexPath:indexPath];
if (cell.selected) {
cell.backgroundColor = [UIColor blueColor]; // highlight selection
}
else
{
cell.backgroundColor = [UIColor redColor]; // Default color
}
return cell;
}
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *datasetCell =[collectionView cellForItemAtIndexPath:indexPath];
datasetCell.backgroundColor = [UIColor blueColor]; // highlight selection
}
-(void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *datasetCell =[collectionView cellForItemAtIndexPath:indexPath];
datasetCell.backgroundColor = [UIColor redColor]; // Default color
}
フレームワークは、セルのbackgroundView
およびselectedBackgroundView
をセットアップすると、ビューの切り替えを処理します。 選択とハイライトの視覚的状態の管理 :
UIView* backgroundView = [[UIView alloc] initWithFrame:self.bounds];
backgroundView.backgroundColor = [UIColor redColor];
self.backgroundView = backgroundView;
UIView* selectedBGView = [[UIView alloc] initWithFrame:self.bounds];
selectedBGView.backgroundColor = [UIColor whiteColor];
self.selectedBackgroundView = selectedBGView;
UICollectionViewDelegate
を実装するクラスで必要なのは、次のようにセルを強調表示して選択できるようにすることだけです。
- (BOOL)collectionView:(UICollectionView *)collectionView
shouldHighlightItemAtIndexPath:(NSIndexPath *)indexPath
{
return YES;
}
- (BOOL)collectionView:(UICollectionView *)collectionView
shouldSelectItemAtIndexPath:(NSIndexPath *)indexPath;
{
return YES;
}
これでうまくいく。
IOS 10でUICollectionViewが変更され、上記のソリューションにいくつかの問題が発生しています。
ここに良いガイドがあります: https://littlebitesofcocoa.com/241-uicollectionview-cell-pre-fetching
セルは、画面外に出た後もしばらくは動きません。つまり、調整するためにdidDeselectItemAt indexPath
のセルを取得できない場合があります。その後、更新されず、リサイクルされずに画面に表示されます。 prepareForReuse
は、この角の場合には役立ちません。
最も簡単な解決策は、isPrefetchingEnabled
をfalseに設定して、新しいスクロールを無効にすることです。これにより、cellForItemAt
didSelect
didDeselect
を使用してセルの表示を管理することは従来どおりに機能します。
ただし、新しいスムーズなスクロール動作を維持する場合は、willDisplay
を使用することをお勧めします。
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
let customCell = cell as! CustomCell
if customCell.isSelected {
customCell.select()
} else {
customCell.unselect()
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! CustomCell
//Don't even need to set selection-specific things here as recycled cells will also go through willDisplay
return cell
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as? CustomCell
cell?.select()
}
func collectionView(_ collectionView: UICollectionView, didDeselectItemAt indexPath: IndexPath) {
let cell = collectionView.cellForItem(at: indexPath) as? CustomCell
cell?.unselect() // <----- this can be null here, and the cell can still come back on screen!
}
上記を使用すると、セルを選択、非選択、画面上で再利用、再表示、および再表示するときにセルを制御できます。
アニルは正しい軌道に乗っていました(彼のソリューションはうまくいくはずです、私は彼とは独立してこのソリューションを開発しました)。私はまだprepareForReuse:
メソッドを使用してセルのselected
をFALSE
に設定し、cellForItemAtIndexPath
でセルのインデックスが `collectionView.indexPathsForSelectedItems 'にあるかどうかを確認します。その場合は、強調表示します。
カスタムセル内:
-(void)prepareForReuse {
self.selected = FALSE;
}
cellForItemAtIndexPath:
で、再利用セルのハイライトとハイライト解除を処理します。
if ([collectionView.indexPathsForSelectedItems containsObject:indexPath]) {
[collectionView selectItemAtIndexPath:indexPath animated:FALSE scrollPosition:UICollectionViewScrollPositionNone];
// Select Cell
}
else {
// Set cell to non-highlight
}
didDeselectItemAtIndexPath:
およびdidSelectItemAtIndexPath:
でセルのハイライトとハイライト解除を処理します
これは私にとって魅力的です。
水平スクロールのコレクションビュー(Tableviewでコレクションビューを使用)があり、1つのアイテムを選択して右にスクロールすると、次の表示セットの他のセルが自動的に選択されます。 「選択」、強調表示などのカスタムセルプロパティを使用してこれを解決しようとしても、私は役に立たなかったので、以下の解決策を思い付きました。
ステップ1:
CollectionViewで変数を作成して、選択したインデックスを保存します。ここでは、selectedIndexというクラスレベルの変数を使用しています。
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
MyCVCell *cell = (MyCVCell*)[collectionView dequeueReusableCellWithReuseIdentifier:@"MyCVCell" forIndexPath:indexPath];
// When scrolling happens, set the selection status only if the index matches the selected Index
if (selectedIndex == indexPath.row) {
cell.layer.borderWidth = 1.0;
cell.layer.borderColor = [[UIColor redColor] CGColor];
}
else
{
// Turn off the selection
cell.layer.borderWidth = 0.0;
}
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
MyCVCell *cell = (MyCVCell *)[collectionView cellForItemAtIndexPath:indexPath];
// Set the index once user taps on a cell
selectedIndex = indexPath.row;
// Set the selection here so that selection of cell is shown to ur user immediately
cell.layer.borderWidth = 1.0;
cell.layer.borderColor = [[UIColor redColor] CGColor];
[cell setNeedsDisplay];
}
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath
{
MyCVCell *cell = (MyCVCell *)[collectionView cellForItemAtIndexPath:indexPath];
// Set the index to an invalid value so that the cells get deselected
selectedIndex = -1;
cell.layer.borderWidth = 0.0;
[cell setNeedsDisplay];
}
-アヌープ
これを解決するために行ったことは、カスタマイズされたセルに変更を加えることでした。クラスにDataSetCellというカスタムセルがあり、次のことができます(コードはSwiftにあります)
override var isSelected: Bool {
didSet {
if isSelected {
changeStuff
} else {
changeOtherStuff
}
}
}
これにより、セルが選択、選択解除、初期化、または再利用可能なキューから呼び出されるたびに、そのコードが実行され、変更が行われます。これがお役に立てば幸いです。
カスタムセルでパブリックメソッドを作成します。
- (void)showSelection:(BOOL)selection
{
self.contentView.backgroundColor = selection ? [UIColor blueColor] : [UIColor white];
}
-prepareForReuseセルメソッドの再定義も記述します。
- (void)prepareForReuse
{
[self showSelection:NO];
[super prepareForReuse];
}
また、ViewControllerには_selectedIndexPath変数が必要です。この変数は、-didSelectItemAtIndexPathで定義され、-didDeselectItemAtIndexPathで無効化されます
NSIndexPath *_selectedIndexPath;
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = @"Cell";
UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
if (_selectedIndexPath) {
[cell showSelection:[indexPath isEqual:_selectedIndexPath]];
}
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
[cell showSelection:![indexPath isEqual:_selectedIndexPath]];// on/off selection
_selectedIndexPath = [indexPath isEqual:_selectedIndexPath] ? nil : indexPath;
}
- (void)collectionView:(UICollectionView *)collectionView didDeselectItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
[cell showSelection:NO];
_selectedIndexPath = nil;
}
あなたが遭遇する問題は、super.prepareForReuse()
への呼び出しの欠如から来ます。
デリゲートの関数からセルのUIを更新することを提案する上記の他のソリューションは、セルの動作のロジックがクラスの外にあるという欠陥のある設計につながります。さらに、super.prepareForReuse()
を呼び出すことで簡単に修正できる追加のコードです。例えば :
class myCell: UICollectionViewCell {
// defined in interface builder
@IBOutlet weak var viewSelection : UIView!
override var isSelected: Bool {
didSet {
self.viewSelection.alpha = isSelected ? 1 : 0
}
}
override func prepareForReuse() {
// Do whatever you want here, but don't forget this :
super.prepareForReuse()
// You don't need to do `self.viewSelection.alpha = 0` here
// because `super.prepareForReuse()` will update the property `isSelected`
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
self.viewSelection.alpha = 0
}
}
このような設計では、デリゲートの関数collectionView:didSelectItemAt:
/collectionView:didDeselectItemAt:
をすべて空のままにしておくこともでき、選択プロセスは完全に処理され、セルのリサイクルで適切に動作します。
IOS 9.3では、@ stefanB ソリューションのみが機能しました
ここで私が変更しなければならないことSwift 2
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
//prepare your cell here..
//Add background view for normal cell
let backgroundView: UIView = UIView(frame: cell!.bounds)
backgroundView.backgroundColor = UIColor.lightGrayColor()
cell!.backgroundView = backgroundView
//Add background view for selected cell
let selectedBGView: UIView = UIView(frame: cell!.bounds)
selectedBGView.backgroundColor = UIColor.redColor()
cell!.selectedBackgroundView = selectedBGView
return cell!
}
func collectionView(collectionView: UICollectionView, shouldHighlightItemAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
func collectionView(collectionView: UICollectionView, shouldSelectItemAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
セルの背景色などのセルプロパティの変更は、UICollectionViewController自体で行うべきではなく、CollectionViewCellクラス内で行う必要があります。 didSelectとdidDeselectを使用せずに、これを使用してください。
class MyCollectionViewCell: UICollectionViewCell
{
override var isSelected: Bool
{
didSet
{
// Your code
}
}
}
セルのselectedBackgroundViewをbackgroundColor = xに設定するだけです。
セルをタップすると、選択したモードが自動的に変更され、背景色に変更されてxに変わります。
ご回答ありがとうございました@ RDC 。
次のコードはSwift 3で動作します
// MARK: - UICollectionViewDataSource protocol
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
//prepare your cell here..
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath as IndexPath) as! MyCell
cell.myLabel.text = "my text"
//Add background view for normal cell
let backgroundView: UIView = UIView(frame: cell.bounds)
backgroundView.backgroundColor = UIColor.lightGray
cell.backgroundView = backgroundView
//Add background view for selected cell
let selectedBGView: UIView = UIView(frame: cell.bounds)
selectedBGView.backgroundColor = UIColor.green
cell.selectedBackgroundView = selectedBGView
return cell
}
// MARK: - UICollectionViewDelegate protocol
func collectionView(_ collectionView: UICollectionView, shouldHighlightItemAt indexPath: IndexPath) -> Bool {
return true
}
func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
return true
}