可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
According to the Collection View Programming Guide one should handle the visual state of the cell highlights in the UICollectionViewDelegate
. Like this:
- (void)collectionView:(PSUICollectionView *)collectionView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath
{
MYCollectionViewCell *cell = (MYCollectionViewCell*)[collectionView cellForItemAtIndexPath:indexPath];
[cell highlight];
}
- (void)collectionView:(UICollectionView *)collectionView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath
{
MYCollectionViewCell *cell = (MYCollectionViewCell*)[collectionView cellForItemAtIndexPath:indexPath];
[cell unhighlight];
}
What I don't like about this approach is that it adds logic to the delegate that is very specific to the cell. In fact, UICollectionViewCell
manages its highlighted state independently, via the highlighted
property.
Wouldn't overriding setHighlighted:
be a cleaner solution, then?
- (void)setHighlighted:(BOOL)highlighted
{
[super setHighlighted:highlighted];
if (highlighted) {
[self highlight];
} else {
[self unhighlight];
}
}
Are there any disadvantages to this approach instead of the delegate approach?
回答1:
As the documentation says, you can rely on highlighted
property to be changed while the cell is highlighted. For example the following code will make the cell red when highlighted (not its subviews though):
- (void)setHighlighted:(BOOL)highlighted {
[super setHighlighted:highlighted];
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect {
[super drawRect:rect];
if (self.highlighted) {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(context, 1, 0, 0, 1);
CGContextFillRect(context, self.bounds);
}
}
And if you add something like this the background will become purple (red + opaque blue):
- (void)collectionView:(UICollectionView *)colView didHighlightItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [colView cellForItemAtIndexPath:indexPath];
cell.contentView.backgroundColor = [UIColor colorWithRed:0 green:0 blue:1 alpha:0.5];
}
- (void)collectionView:(UICollectionView *)colView didUnhighlightItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewCell *cell = [colView cellForItemAtIndexPath:indexPath];
cell.contentView.backgroundColor = nil;
}
So you can use both together (not necessarily both changing the cell appearance). The difference is that with delegate methods you also have indexPath
. It might be used to create multi-selection (you will use this methods together with selection delegate methods), to show some preview while the cell is highlighted, to show some animation with other views... There's quite a few appliance for this delegate methods in my opinion.
As a conclusion, I would leave the cell appearance to be handled by the cell itself and use delegate methods to let controller make something cool in the same time.
回答2:
Two possible approaches are outlined below.
Cell Subclassing
Cleaner approach if already subclassing from UICollectionViewCell
.
class CollectionViewCell: UICollectionViewCell {
override var highlighted: Bool {
didSet {
self.contentView.backgroundColor = highlighted ? UIColor(white: 217.0/255.0, alpha: 1.0) : nil
}
}
}
UICollectionViewDelegate
Less clean, requires the collection view delegate to know about the presentation logic of the cells.
func collectionView(collectionView: UICollectionView, didHighlightItemAtIndexPath indexPath: NSIndexPath) {
if let cell = collectionView.cellForItemAtIndexPath(indexPath) {
cell.contentView.backgroundColor = UIColor(white: 217.0/255.0, alpha: 1.0) // Apple default cell highlight color
}
}
func collectionView(collectionView: UICollectionView, didUnhighlightItemAtIndexPath indexPath: NSIndexPath) {
if let cell = collectionView.cellForItemAtIndexPath(indexPath) {
cell.contentView.backgroundColor = nil
}
}
回答3:
Well...as all of these methods are correct. I've found the way that seems like the easiest one to me. Just override the setSelected: method (for example to change background color):
-(void)setSelected:(BOOL)selected{
self.backgroundColor = selected?[UIColor greenColor]:[UIColor grayColor];
[super setSelected:selected];
}
...it works "out of the box" (even with collectionView.allowsMultipleSelection)
回答4:
Notice that UICollectionViewCell
has a selectedBackgroundView
property. By default, it's nil. Just create a view for this property, and it will appear when the user touches the cell.
override func awakeFromNib() {
super.awakeFromNib()
let view = UIView(frame: contentView.bounds)
view.isUserInteractionEnabled = false
view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
view.backgroundColor = UIColor(white: 0.94, alpha: 1.0)
selectedBackgroundView = view
}
回答5:
As taken directly from UICollectionViewCell.h - overriding both setSelected
and setHighlighted
are correct. Depending upon your situation you might consider assigning custom views to backgroundView
and selectedBackgroundView
which are swapped automatically on selection.
// Cells become highlighted when the user touches them.
// The selected state is toggled when the user lifts up from a highlighted cell.
// Override these methods to provide custom UI for a selected or highlighted state.
// The collection view may call the setters inside an animation block.
@property (nonatomic, getter=isSelected) BOOL selected;
@property (nonatomic, getter=isHighlighted) BOOL highlighted;
// The background view is a subview behind all other views.
// If selectedBackgroundView is different than backgroundView, it will be placed above the background view and animated in on selection.
@property (nonatomic, retain) UIView *backgroundView;
@property (nonatomic, retain) UIView *selectedBackgroundView;
回答6:
It is enough for highlighting cell (Swift 4)
class MyCollectionViewCell: UICollectionViewCell {
...
override var isHighlighted: Bool {
didSet {
if isHighlighted {
self.contentView.alpha = 0.6
}
else {
self.contentView.alpha = 1.0
}
}
}
}
回答7:
Swift 3: (based on the answer of A-Live)
import UIKit
class MyCollectionViewCell: UICollectionViewCell {
override var highlighted: Bool {
didSet {
self.setNeedsDisplay()
}
}
override func drawRect(rect: CGRect) {
super.drawRect(rect)
myImageView.highlighted = self.highlighted
}
}
Swift 4
import UIKit
class MyCollectionViewCell: UICollectionViewCell {
override var isHighlighted: Bool {
didSet {
self.setNeedsDisplay()
}
}
override func draw(_ rect: CGRect) {
super.draw(rect)
myImageView.isHighlighted = self.isHighlighted
}
}