I am trying to 'decorate' UICollectionViewCells using the Decorator pattern.
For example, if I have a
BaseCell : UICollectionViewCell
I would like to be able to do something like this:
BaseCell *cell = [[BaseCell alloc] initWithFrame]
cell = [[GlowingCell alloc] initWithCell:cell];
cell = [[BorderedCell alloc] initWithCell:cell];
cell = [[LabelledCell alloc] initWithCell:cell];
// cell is now a glowing, bordered, labelled cell.
I think the Decorator pattern is pretty neat for this sort of thing, but I'm having difficulty applying it to collection views.
Firstly, in UICollectionViewControllers you need to register a class, like this:
[self.collectionView registerClass:cellClass forCellWithReuseIdentifier:cellIdentifier];
So I don't have an opportunity to make my own instance.
Secondly, I can't see how the Decorator can ever be useful for decorating 'non-pure' objects, that is, objects that I didn't create from the ground up but have their own properties and behaviours (such as UICollectionViewCell). Since in the example above, cell
represents a new instance of LabelledCell, and if UICollectionView makes a call on a method, eg, isSelected
, this will call aLabelledCellInstance.isSelected
unless I specifically do this in my Decorator base class:
- (BOOL)isSelected {
return self.decoratedCell.isSelected;
}
Which is fine for one method but it doesn't seem right to have to override every method in UICollectionViewCell
. Should I be using forwardInvocation:
?
Am I abusing this pattern, and are there alternatives? Because it works very nicely in books when you just have to override basic methods like
getPrice() {
return decoratedObject.getPrice() + 1.10f;
}
.. but seems hard to fit to the purpose of actually decorating existing UI elements with custom behaviour.
Thanks
EDIT: What I am trying to avoid is having classes like this:
On paper the Decorator is the perfect candidate for what I am trying to achieve but the implementation is absolutely stumping me.
Firstly, the Decorator pattern requires you to override all the base methods in the BaseDecorator
so you can forward the calls to the decorated object.
And you can do this either by overriding every single method or, preferably, just use forwardInvocation:
. And since all other decorators will be subclasses of BaseDecorator
, you can now just override the methods you want to change.
Secondly, for the CollectionView
issue, I suggest to use the Decorator pattern with normal UIView
s, and then use the decorated view as the contentView
of the cell.
Let's see an example:
We have BaseCellView
class which will be the super class for all decorators.
BaseCellView : UIView;
GlowingCellView: BaseCellView;
BorderedCell: BaseCellView;
LabelledCell: BaseCellView;
And we still have our BaseCell
class which is a subclass of UICollectionViewCell
:
BaseCell : UICollectionViewCell;
Now, UICollectionViewControllers
will always create an instance of BaseCell
and give you the chance to configure it, where you'll do the following:
BaseCellView *cellView = [[BaseCellView alloc] initWithFrame]
cellView = [[GlowingCellView alloc] initWithCellView:cellView];
cellView = [[BorderedCellView alloc] initWithCellView:cellView];
cellView = [[LabelledCellView alloc] initWithCellView:cellView];
cell.contentView = cellView;
And you still can forward any UICollectionViewCell
to the decorator if you want that.
Here's a post in which I've described this technique. Although I've chosen to decorate the UITableView
and not the cell, it could be easily adapted to your collection view. It's a pretty long read so I will only make a short summary here:
respondsToSelector
and forwardingTargetForSelector
e.g.:
dec = [[DEFooterDecorator alloc] initWithDecoratedObject:dec];
dec = [[DEHeaderDecorator alloc] initWithDecoratedObject:dec];
dec = [[DEGreenCellDecorator alloc] initWithDecoratedObject:dec];
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With