How can I make a custom sublayer in a UITableViewCell disappear in setSelected:animated:?
Context:
I've added a custom background layer to my UITableViewCell by adding a CAGradientLayer inside of drawRect:, like so:
- (void)drawRect:(CGRect)rect{
[super drawRect:rect];
[[self providerLabel] setTextColor:kOUBlue];
[self addGrayLayer];
}
- (void)addGrayLayer{
[[[self contentView] layer] insertSublayer:[self grayGradientLayer] atIndex:0];
}
- (CAGradientLayer *) grayGradientLayer{
if (!_grayGradientLayer) {
CAGradientLayer *gradient = [CAGradientLayer layer];
UIColor *white = [UIColor colorWithWhite:1 alpha:2];
UIColor *gray = [UIColor colorWithRed:0.96 green:0.96 blue:0.96 alpha:1];
[gradient setColors:#[(id)white.CGColor, (id)gray.CGColor]];
[gradient setFrame:[self bounds]];
_grayGradientLayer = gradient;
}
return _grayGradientLayer;
}
When the user taps on a cell, the blue highlighting doesn't appear. So I've attempted to hide the CAGradientLayer in setSelected:animated: like so:
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
if (selected) {
[[self grayGradientLayer] setHidden:YES];
}else{
[[self grayGradientLayer] setHidden:NO];
}
[[self contentView] setNeedsDisplay];
}
The blue background appears, but not until the push animation begins. Is there any way for me to make the default blue appear immediately? I've also tried to add a second colored CAGradientLayer and swap them manually, but it seems to me that the cell doesn't redraw immediately after I hide the layer in setSelected:.
I've tried to force the cell to redraw, using setNeedsDisplay with no luck. Any ideas?
After digging around, I found this question, I realized I was using the wrong method. It turns out I had to use the setHighlighted:animated: method.
My new code looks like this:
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated{
[super setHighlighted:highlighted animated:animated];
if (highlighted) {
[[self grayGradientLayer] setHidden:YES];
}else{
[[self grayGradientLayer] setHidden:NO];
}
}
Related
I'd like to know what makes the backgrounds of subviews (labels for example) of a UITableViewCell become transparent while selected/highlighted. I need to avoid that behaviour for some subviews of my content view. I tried overriding the setSelected / setHighlighted methods with some success, but that transparency I wasn't able to reproduce. Any thoughts?
I think I got a somewhat close behaviour using the following piece of code
- (void) setView:(UIView*) view asHighlighted:(BOOL) highlighted {
if([view isKindOfClass:[UILabel class]]){
UILabel* label = (UILabel*) view;
[label setHighlighted:highlighted];
}
else if([view isKindOfClass:[UIImageView class]]){
UIImageView* imageView = (UIImageView*) view;
[imageView setHighlighted:highlighted];
}
if(highlighted){
[view.undoManager registerUndoWithTarget:view
selector:#selector(setBackgroundColor:)
object:view.backgroundColor];
view.backgroundColor = [UIColor clearColor];
}
else {
[view.undoManager undo];
}
for(UIView* subview in view.subviews){
[self setView:subview asHighlighted:highlighted];
}
}
Not sure this is the right way to use UndoManager but it works. You can use this in both setSelected and setHighlighted to mimic selection behaviour of UITableViewCells
I have subclassed UITableViewCell and I have the following code:
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
if (selected)
self.textField.textColor = [UIColor whiteColor];
else
self.textField.textColor = [UIColor blackColor];
[super setSelected:selected animated:animated];
}
Basically I'm having only a UITextField in my cell but the color of the label won't change automatically to white so I need some kind of a way to change it on highlight. Any ideas?
You should use cell.textLabel.highlightedTextColor and didn't change it in setSelected:
I believe that IS the proper way to set the text color on aUITextField. But what I'm wondering is: is self.textField properly wired up to the correct instance? (via Interface Builder/StoryBoard or in code)
Try adding this code at the top of your setSelected method and look at the console while running your app:
NSLog(#"self.textField = %#", self.textField);
In your cellForRowAtIndexPath
cell.selectionStyle = UITableViewCellSelectionStyleNone;
and in your subviews have some thing like this
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
if (selected)
self.textLabel.textColor=[UIColor blackColor];
else
self.textLabel.textColor=[UIColor greenColor];
}
I have a ViewController with a radial gradient background. Inside that I have a UITableView with cells and sections. Now I want to show the gradient behind the table view as you scroll. The issue is that when the section locks at the top you can see the cells behind the section. I would set the section background color but if I do that it does not match the radial gradient background. Is there anyway to have the cells clip under the sections while keeping the clearColor background?
Simple way to get a transparent section header without creating your own header views.
- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section
{
if ([view isKindOfClass:[UITableViewHeaderFooterView class]]) {
UITableViewHeaderFooterView *headerView = (UITableViewHeaderFooterView *)view;
headerView.contentView.backgroundColor = [UIColor clearColor];
headerView.backgroundView.backgroundColor = [UIColor clearColor];
}
}
This piece of code works perfectly fine in iOS 7.
EDIT
I know this is a little late, but along with this you have to add 2 lines making sure the background color (and background color of the background view?) of the uitableview is also clear.
tableView.backgroundColor = [UIColor clearColor];
tableView.backgroundView.backgroundColor = [UIColor clearColor];
if I understand it, you want a completely clear tableview and tableview cell so you can see the background of your view controller.
You can achieve this by setting the tableview as having no background
tableview.backgroundColor = [UIColor clearColor];
tableView.opaque = NO;
tableView.backgroundView = nil;
and also the cell
cell.backgroundColor = [UIColor clearColor]
if you also want the section to be transparent, you can use this method:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
to make a custom view (for example create an image view with a transparent .png for the background)
I hope I understand your question correctly.
edit after clarification
to give the appearance that the cells stop before going underneath the section header:
make a class that is a subclass of uitableview with this ivar:
#interface CustomTableView : UITableView
{
CAGradientLayer* maskLayer;
}
obviously this will create a gradient but if you wanted you could either tweak it or use CALayer and instead of programmatically creating the mask, create a .png with the correct width/height of your section header.
ANYWAY: if you use the CAGradientLayer:
- (id)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self)
{
[self setupGradients]; //call your method
}
return self;
}
- (void)setupGradients
{
[self addObserver:self forKeyPath:#"contentOffset" options:0 context:nil];
*/ ***** if you choose to use CALayer instead of CAGradient ****** */
//maskLayer = [[CALayer layer] retain];
//maskLayer.contents = (id) [UIImage imageNamed:#"maskLayer.png"].CGImage;
//maskLayer.backgroundColor = [UIColor clearColor].CGColor;
*/ ***** if you choose to use CALayer instead of CAGradient ****** */
maskLayer = [[CAGradientLayer layer] retain];
CGColorRef outerColor = [UIColor colorWithWhite:1.0 alpha:0.0].CGColor;
CGColorRef innerColor = [UIColor colorWithWhite:1.0 alpha:1.0].CGColor;
maskLayer.colors = [NSArray arrayWithObjects:(id)outerColor,
(id)innerColor, (id)innerColor, (id)outerColor, nil];
maskLayer.locations = [NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0],
[NSNumber numberWithFloat:0.0],
[NSNumber numberWithFloat:0.9],
[NSNumber numberWithFloat:1.0], nil]; //this creates a gradient effect. Tweak these numbers for a hard line.
maskLayer.bounds = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
maskLayer.anchorPoint = CGPointZero;
self.layer.mask = maskLayer;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
[CATransaction begin];
[CATransaction setDisableActions:YES];
maskLayer.position = CGPointMake(0, self.contentOffset.y);
[CATransaction commit];
}
then in your view controller:
CustomTableView *_tableView; //ivar
_tableView = [[CustomTableView alloc] initWithFrame:YOUR_FRAME];
The basic idea is a mask, you create a mask shape on the top of your tableview that is the same size as your section header. it will appear that the cells "disappear" behind it. It's a bit tricky, but it will work.
I have a TableView in my app, and I needed to change the height and the color of the Separator. Browsing here in SO helped me to figure out the solution.
So I'm basically adding a UIView in my cell and using this as a "fake" separator:
UIView *colorSeparator = [[UIView alloc] initWithFrame:CGRectMake(0, 53, cell.frame.size.width, 4)];
colorSeparator.backgroundColor = [UIColor yellowColor];
[cell.contentView addSubview:colorSeparator];
[colorSeparator release];
But now I noticed that when the row is tapped, the selection colour applies to my fake separator. Does anyone know how could avoid it? Thx in advice for your time :)
You can restore color of your separator in setSelected:animated: and setHighlighted:animated: methods of UITableViewCell.
// just edited your function, it was missing a square bracket
- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated {
UIColor *c = [[colorSeparator.backgroundColor retain] autorelease];
[super setHighlighted:highlighted animated:animated];
colorSeparator.backgroundColor = c;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
UIColor *c = [[colorSeparator.backgroundColor retain] autorelease];
[super setSelected:selected animated:animated];
colorSeparator.backgroundColor = c;
}
I'm trying to add an image behind a MKPinAnnotationView. Seems like it should be rather easy to just do this in here:
- (void)mapView:(MKMapView *)mapView didAddAnnotationViews:(NSArray *)views {
for (MKAnnotationView *aView in views)
[[aView superview] addSubview:imageView];
}
But the problem I am having in doing that is that the child subview to the pin will render on top of it not BEHIND it.
I have also tried:
for (MKAnnotationView *aView in views)
[[aView superview] insertSubview:imageView atIndex:1];
The problem with this is that, while it IS behind the pin, as soon as the map is repositioned, the image floats off the screen.
Any suggestions? Thanks
Thanks for the input, here's basically what I ended up doing without subclassing:
- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id )annotation {
MKAnnotationView *annView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:nil];
UIImage *image = [UIImage imageNamed:#"image.png"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[annView addSubview:imageView];
[imageView release];
MKPinAnnotationView *pinView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:nil];
[annView addSubview:pinView];
[pinView release];
return annView;
}
I only needed one pin, so I set reuseIdentifier to nil.
I have subclassed the MKAnnotatonView and overrode the initWithAnnotation:resueIdentifier and drawRect methods to add another image behind the "front" image.
It seems that that is what Apple's MKAnnotationView class reference is suggesting us to do.
Only that it is tricky. If you are subclassing the MKAnnotationView, there is still the image property existing. They have done something about the properly so that it is linked with drawing. Perhaps, they have overridden the drawRect to draw the image AFTER the initialization is done.
Also, if you don't set the image property, the frame of your custom annotationView is set to size 0, and the drawRect method does not get called.
Finally, Apple says that the images should be totally filled, i.e. uses a transparent colour for the background of the drawings.
So: In your subclass:
- (id)initWithAnnotation:(id <MKAnnotation>)annotation reuseIdentifier:(NSString *)identifier
{
self = [super initWithAnnotation:annotation reuseIdentifier:identifier];
if (self)
{
UIButton *rightButton = [UIButton buttonWithType: UIButtonTypeDetailDisclosure];
[rightButton addTarget:self
action:#selector(myShowAnnotationAddress:)
forControlEvents:UIControlEventTouchUpInside];
self.rightCalloutAccessoryView = rightButton;
self.canShowCallout = YES;
self.multipleTouchEnabled = NO;
self.frame = CGRectMake(0, 0, 65, 100);
self.backgroundColor = [UIColor clearColor];
return self;
}
return nil;
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
CGPoint drawPoint = CGPointMake(0.0f, 0.0f);
UIImage *shadowImage = [UIImage imageNamed:#"shadow_image.png"];
[shadowImage drawAtPoint:drawPoint];
UIImage *frontImage = [UIImage imageNamed:#"front_image.png"];
[frontImage drawAtPoint:drawPoint];
CGContextRestoreGState(context);
}
After I made the answer, I realize that you actually wanted to draw behind MKPinAnnotationView. My answer is not that, although it shows where the drawing perhaps should be done. Obviously MKPinAnnottions has its own drawing method to present a Pin and its shadow.
I think that probably you can retrieve the Pin image from the self.image property. As to the shadow, I am not certain... It may be using OPEN GL drawing method to add the shadow, or simply combining a shadow image.
Finally, the image comes with animation. I am guessing that that is where the animation is run. At this stage, though, I have not tested.
Create a new composite annotationview that first adds your image and then the actual MKAnnotationView:
#implementation MyCustomAnnotationView
- (void) viewDidLoad
{
// First add the image to our subviews
UIImageView *view = [[UIImageView alloc] initWithImage: myImageProperty];
[self.view addSubview: view];
[view release];
// Then add the MKAnnotationView on top of it
MKAnnotationView *annotation = [[MKAnnotationView alloc] init];
[self.view addSubview: annotation];
[annotation release];
}
#end