[英]UICollectionView cell selection and cell reuse
選擇單元格后,我想處理更改單元格外觀。 我想到了委托方法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;
}
這對我有用。
UICollectionView在iOS 10中發生了變化,為上述解決方案帶來了一些問題。
這是一個很好的指南: https : //littlebitesofcocoa.com/241-uicollectionview-cell-pre-fetching
離開屏幕后,細胞現在可以保持一段時間。 這意味着有時我們可能無法在didDeselectItemAt indexPath
中獲取一個單元格來調整它。 然后它可以在屏幕上顯示未更新和未回收。 prepareForReuse
沒有幫助這個角落的情況。
最簡單的解決方案是通過將isPrefetchingEnabled
設置為false來禁用新滾動。 有了這個,使用cellForItemAt
管理單元格的顯示didSelect
didDeselect
就像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!
}
通過上述操作,您可以在選擇單元格時控制單元格,在屏幕上取消選擇,回收並重新顯示。
Anil走在正確的軌道上(他的解決方案看起來應該可行,我開發了獨立於他的解決方案)。 我仍然使用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:
處理單元格突出顯示和取消高亮顯示didSelectItemAtIndexPath:
這對我來說就像一個魅力。
我有一個水平滾動集合視圖(我在Tableview中使用集合視圖),我也遇到了單元重用的問題,每當我選擇一個項目並向右滾動時,下一個可見集合中的一些其他單元格會自動選擇。 嘗試使用任何自定義單元格屬性,如“選定”,突出顯示等解決這個問題並沒有幫助我,所以我提出了以下解決方案,這對我有用。
步驟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];
}
-anoop
我為解決這個問題所做的是在自定義單元格中進行更改。 您在其類中有一個名為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;
}
只有@ stefanB解決方案適用於iOS 9.3
在這里我要為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
}
您遇到的問題來自缺少對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:
全部清空,並且選擇過程將完全處理,並且在單元格回收時表現正常。
你可以將單元格的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
}
更改單元屬性(如單元格的背景顏色)不應該在UICollectionViewController本身上完成,它應該在CollectionViewCell類中完成。 不要使用didSelect和didDeselect,只需使用:
class MyCollectionViewCell: UICollectionViewCell
{
override var isSelected: Bool
{
didSet
{
// Your code
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.