选择细胞后,我想处理更改细胞外观。我想到了委托方法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;
}
并且选择状态按预期工作。
有什么想法吗?
答案 0 :(得分:73)
你的观察是正确的。由于重用单元格,这种情况正在发生。但是你不必对 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
}
答案 1 :(得分:24)
backgroundView
和selectedBackgroundView
后,框架将处理为您切换视图,请参阅Managing the Visual State for Selections and Highlights中的示例:
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;
}
这对我有用。
答案 2 :(得分:20)
UICollectionView在iOS 10中发生了变化,为上述解决方案带来了一些问题。
这是一个很好的指南: 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!
}
通过上述方法,您可以在选择单元格时对其进行控制,在屏幕上取消选择,回收并重新显示。
答案 3 :(得分:10)
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:
这对我来说就像是一种魅力。
答案 4 :(得分:6)
我有一个水平滚动集合视图(我在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
答案 5 :(得分:2)
在自定义单元格中创建公共方法:
- (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;
}
答案 6 :(得分:1)
只有@ stefanB解决方案适用于iOS 9.3
这里我需要更改 for 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
}
答案 7 :(得分:1)
我要解决的问题是在自定义单元格中进行更改。您在其类中有一个名为 DataSetCell 的自定义单元格,您可以执行以下操作(代码在swift中)
override var isSelected: Bool {
didSet {
if isSelected {
changeStuff
} else {
changeOtherStuff
}
}
}
这样做的是,每次从可重用队列中选择,取消选择,初始化或调用单元格时,该代码将运行并进行更改。希望这会对你有所帮助。
答案 8 :(得分:1)
您遇到的问题来自缺乏对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:
保留为空,选择过程将被完全处理,并随着单元格的回收而正常运行。
答案 9 :(得分:0)
您可以将单元格的selectedBackgroundView设置为backgroundColor = x。
现在,只要您点击单元格,他所选择的模式就会自动更改,并且会变为背景颜色以更改为x。
答案 10 :(得分:0)
感谢您的回答@ 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
}
答案 11 :(得分:0)
更改单元格属性(如单元格的背景颜色)不应该在UICollectionViewController本身上完成,它应该在CollectionViewCell类中完成。不要使用didSelect和didDeselect,只需使用它:
class MyCollectionViewCell: UICollectionViewCell
{
override var isSelected: Bool
{
didSet
{
// Your code
}
}
}