Can't seem to add a sublayer to my UITableViewCell subclass - ios

This code is run from within a subclass of UITableViewCell
CALayer* greenLayer = [CALayer layer];
greenLayer.backgroundColor = [UIColor greenColor].CGColor;
CGRect frame = CGRectMake(0, 0, 320, self.bounds.size.height);
greenLayer.frame = frame;
[self.backgroundView.layer addSublayer:greenLayer];
But nothing happens

Instead of adding your sublayer to the backgroundView, add it to the cell's contentView:
[self.contentView.layer addSublayer:greenLayer];
Alternately, you could just directly set the backgroundColor property of the contentView:
self.contentView.backgroundColor = [UIColor greenColor];

It was another piece of code which caused the problem. I was retrieving the cell incorrectly.

Related

CAGradientLayer with shouldRasterize causes unwanted line

I use CAGradientLayer in header view of TableView. To make scroll smooth I've turned shouldRasterize property to YES and it's caused the problem.
Gradient layer is drawn like it has a border. All frames I set in layoutSubviews and all its fields are integers.
Here are screenshots:
How it appears:
How it have to look like:
Here is a code related to this gradient layer
- (void)createShadow {
CAGradientLayer *shadowLayer= [CAGradientLayer layer];
UIColor *startColor = [UIColor colorWithWholeRed:236 green:235 blue:236];
UIColor *destinationColor = [UIColor colorWithWholeRed:248 green:248 blue:250];
shadowLayer.borderColor = [UIColor clearColor].CGColor;
shadowLayer.borderWidth = 0;
shadowLayer.colors = #[(id)startColor.CGColor,(id)destinationColor.CGColor];
shadowLayer.shouldRasterize = YES;
[self.layer addSublayer:shadowLayer];
self.shadowLayer = shadowLayer;
}
- (void)updateShadow {
CGRect newRect = CGRectMake(0, 0,
self.frame.size.width, ceil(self.frame.size.height * 0.3));
[self.shadowLayer setFrame:newRect];
}
- (void)layoutSubviews {
[super layoutSubviews];
[self updateShadow];
}
Tell me please if you know how to solve this problem)

UILabel Background Color Leaks to Border

I'm creating a UILabel to which I set the background color and corner radius with the following code:
self.scoreLabel.backgroundColor = [UIColor DISRed];// custom red`
self.scoreLabel.layer.masksToBounds = YES;
self.scoreLabel.layer.cornerRadius = self.scoreLabel.frame.size.width/2;
self.scoreLabel.layer.borderWidth = 8.0;
self.scoreLabel.layer.borderColor = [[UIColor DISNavy] CGColor];
However the background's color seems to be leaking to the edge of the border (see image). Any ideas why? Any idea on how to fix it?
I was also facing the same problem. It was a silly mistake. I always forget to tick clipToBounds in case of cornerRadius.
So, just ticking the Clip to Bounds for UILabel in Storyboard fixed my problem.
And yes, we need to keep the below code too:
label.layer.masksToBounds = true
I ran into the same problem with the UIButton's background color leaking around the edge of its border.
Instead of setting the UIButton background color on the UIButton, set it on the UIButton's layer.
Replace:
self.scoreLabel.backgroundColor = [UIColor DISRed];// custom red`
With this:
self.scoreLabel.layer.backgroundColor = [[UIColor DISRed] CGColor];// custom red`
I created my own UILabel and background colour does not seem to be leaking.
Write this in .h file of your project.
UILabel *label;
Write this in .m file of your project.
label=[[UILabel alloc]initWithFrame:CGRectMake(100, 300, 100, 100)];//Set frame of label in your viewcontroller.
[label setBackgroundColor:[UIColor redColor]];//Set background color of label.
[label setText:#"Label"];//Set text in label.
[label setTextColor:[UIColor blackColor]];//Set text color in label.
[label setTextAlignment:NSTextAlignmentCenter];//Set text alignment in label.
[label.layer setCornerRadius:50.0];//Set corner radius of label to change the shape.
[label.layer setBorderWidth:8.0f];//Set border width of label.
[label setClipsToBounds:YES];//Set its to YES for Corner radius to work.
[label.layer setBorderColor:[UIColor greenColor].CGColor];//Set Border color.
[self.view addSubview:label];//Add it to the view of your choice.
It is probably anti aliasing issue. you can better fix it by adding a bezier path around the corners.
CAShapeLayer *subLayer = [[CAShapeLayer alloc] init];
[subLayer setFillColor:[UIColor clearColor].CGColor];
[subLayer setStrokeColor:[UIColor whiteColor].CGColor];
[subLayer setLineWidth:1.0];
[subLayer setPath:[UIBezierPath bezierPathWithRoundedRect:imageView.bounds cornerRadius:imageView.layer.cornerRadius].CGPath];
[imageView.layer addSublayer:subLayer];
For those who are still facing the issue of border color leaking out:
Go through the below code, please note you will need to set frames & border width as per your requirement, I'm setting the position as view's center
let badgeSize: CGFloat = 10
let redBadge = UIView(frame: CGRect(x: view.center.x, y:view.center.y, width: badgeSize, height: badgeSize))
redBadge.layer.borderColor = UIColor.white.cgColor
redBadge.layer.borderWidth = 2
redBadge.backgroundColor = .red
redBadge.layer.cornerRadius = badgeSize * 0.5
redBadge.clipsToBounds = true
redBadge.layer.masksToBounds = true
redBadge.maskLayerOnView(radius: badgeSize * 0.5)
view.addSubview(redBadge)
Secondly, we need to write an extension on UIView
extension UIView{
func maskLayerOnView(radius: CGFloat){
let maskLayer = CAShapeLayer()
maskLayer.path = UIBezierPath(roundedRect: self.bounds,
byRoundingCorners: [.allCorners],
cornerRadii: CGSize(width: radius,
height: radius)).cgPath
self.layer.mask = maskLayer
}
}
This code snippet removes the border color separating out, one can replicate this behaviour on any kind of views.
For detailed explanation please see this article.
Well, a lot of answers...
I found the problem persists, as long as the UIViews background color is used and not the background color of the UIViews 'layer'. In addition, of course, masking needs to be enabled.
For a UICollectionViewCell subclass the code is:
- (void)prepare
{
self.contentView.layer.backgroundColor = UIColor.redColor.CGColor;
self.contentView.layer.cornerRadius = 25.0;
self.contentView.layer.borderColor = UIColor.blackColor.CGColor;
self.contentView.layer.borderWidth = 1.0;
//Enable to optimize image views: self.contentView.layer.shouldRasterize = YES;
//Enable to optimize image views: self.contentView.layer.rasterizationScale = UIScreen.mainScreen.scale;
self.contentView.layer.masksToBounds = YES;
self.contentView.clipsToBounds = YES;
}
To make the setting of the background color more comfortable and less error prone, some could add this:
/*
setBackgroundColor:
*/
- (void)setBackgroundColor:(UIColor *)p_BackgroundColor
{
super.backgroundColor = nil;
self.contentView.layer.backgroundColor = p_BackgroundColor.CGColor;
self.contentView.layer.masksToBounds = YES;
self.contentView.clipsToBounds = YES;
}

iOS: programmatically draw a rectangle on top of a button

I need to draw a rectangle on top of a UIButton. Assume that there's a UIButton called hideMe, and this button has 100x50 size. I want to place a 100x10 rectangle on top of this button every time a user receives a message. If the user receives five messages, then the button will be completely covered by the rectangles. How do I do this?
CALayer *topBorder = [CALayer layer];
topBorder.borderColor = [UIColor redColor].CGColor;
topBorder.backgroundColor = [UIColor blackColor].CGColor;
topBorder.borderWidth = 2;
topBorder.frame = CGRectMake(0, 15, <tempButton>.frame.size.width, <tempButton>.frame.size.height-30);
[<tempButton>.layer addSublayer:topBorder];
The above code helps you to draw the rectangle.You can increase the layer frames to cover full.
try this
#import <QuartzCore/QuartzCore.h>
here we are drawing our own border rect and sub viewing it to our button
-(UIView*)borderRectWithYvalue:(CGFloat)yValue
{
UIView * rect = [[UIView alloc] initWithFrame:CGRectMake(0,yValue, 100, 10)];
rect.backgroundColor = [UIColor lightGrayColor];
rect.layer.borderColor = (__bridge CGColorRef)([UIColor redColor]);
rect.layer.borderWidth = 2.0f;
return rect;
}
and call this method when and where you want to add border to your button with your custom y value
for ex
[yourButton addSubview:[self borderRectWithYvalue:10.0]];

(UITableViewCell) Does overriding layoutSubviews compete with auto layout or storyboards?

I am making a subclass of UITableView cell.
I have set the cell contentView, backgroundView, and selectedBackgroundView all to a single CGInsetRect (Of the 180.0 rowHeight set in the tableView). My first problem is when I try to assign the inset rect to the image view- the image gets pushed +-5 points to the right. So I've resorted to making the rect manually.
Then I set the text label size and color, but the specific attributes I assigned do not appear until I click the cell.
When I don't set the text label manually- the image pushes the textLabel offscreen, only showing one letter and an "..." ellipsis.
It seems that every modification I make causes something to break, and it seems like my code is competing with some automatic system. Its very frustrating and I don't know what to do or even how to debug this. Any tips on the gotchas of layoutSubviews in UITableViewCell with storyboards would be awesome, because I seem to be missing something critical.
My layoutSubviews
-(void) layoutSubviews
{
[super layoutSubviews];
const CGRect insetRect = CGRectInset( self.bounds, 7, 4 );
self.contentView.frame = insetRect;
self.backgroundView.frame = insetRect;
self.selectedBackgroundView.frame = insetRect;
self.imageView.frame = CGRectMake(0, 0, 306, 172);
self.textLabel.frame = CGRectMake(0, 132, 306, 40);
self.textLabel.backgroundColor = [UIColor colorWithRed:0.5 green:0.5 blue:0.5 alpha:0.5];
self.backgroundColor = [UIColor clearColor];
self.contentView.backgroundColor = [UIColor whiteColor];
self.selectedBackgroundView.backgroundColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.1];
}

How can I access the standard viewForHeaderInSection for a tableView?

I've got an indexed UITableView with individual sections. I would like to use a different background color for the header views in each section. I know I can completely roll my own view by implementing tableView:viewForHeaderInSection: (for example, see question # 2898361), but that seems to be "too much work" to me - the standard view looks fine, I would just have to change its background color.
But how do I access this standard view? I can't use [super tableView:viewForHeaderInSection:] because this is a question of implementing a protocol and not an issue of inheritance. Any other way I can get the standard view?
I'm almost certain you can't do this easily. I used one of my tech support request on my dev account recently asking about altering the background and borders of UITableView sections. The apple engineer told me that this really wasn't an easy thing to do, and even if you managed to do it, you would probably affect performance. He also pointed me to cocoawithlove and an article about editing uitableviews:
http://cocoawithlove.com/2009/08/adding-shadow-effects-to-uitableview.html
Really, creating your own header isn't that much effort. Below is some code I pulled out of one of my projects - it was commented out, so might not work straight away - but you can get the idea:
- (CAGradientLayer *) greyGradient {
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.startPoint = CGPointMake(0.5, 0.0);
gradient.endPoint = CGPointMake(0.5, 1.0);
UIColor *color1 = [UIColor colorWithRed:255.0f/255.0f green:255.0f/255.0f blue:255.0f/255.0f alpha:1.0];
UIColor *color2 = [UIColor colorWithRed:240.0f/255.0f green:240.0f/255.0f blue:240.0f/255.0f alpha:1.0];
[gradient setColors:[NSArray arrayWithObjects:(id)color1.CGColor, (id)color2.CGColor, nil]];
return gradient;
}
- (UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
CGFloat width = CGRectGetWidth(tableView.bounds);
CGFloat height = [self tableView:tableView heightForHeaderInSection:section];
UIView *container = [[[UIView alloc] initWithFrame:CGRectMake(0,0,width,height)] autorelease];
container.layer.borderColor = [UIColor grayColor].CGColor;
container.layer.borderWidth = 1.0f;
CAGradientLayer *gradient = [self greyGradient];
gradient.frame = container.bounds;
[container.layer addSublayer:gradient];
UILabel *headerLabel = [[[UILabel alloc] initWithFrame:CGRectMake(12,0,width,height)] autorelease];
headerLabel.backgroundColor = [UIColor clearColor];
headerLabel.font= [UIFont boldSystemFontOfSize:19.0f];
headerLabel.shadowOffset = CGSizeMake(1, 1);
headerLabel.textColor = [UIColor whiteColor];
headerLabel.shadowColor = [UIColor darkGrayColor];
NSString *title = [self tableView:tableView titleForHeaderInSection:section];
headerLabel.text = title;
return container;
}
Make sure to
#import <QuartzCore/QuartzCore.h>
By the way... this isn't supposed to mimic the look of the standard headers - its just an example. But I'm sure with a bit of trial and error you could alter this to mimic the standard ones and then change the colors slightly.
Although the other answers correctly point out you cannot access the default view to make simple modifications to it, if you have nothing to customize for a particular section header, you can return nil from tableView:viewForHeaderInSection: and the table view will use the default view.
This is helpful if you only need to customize some of your headers.
For whatever reason this is undocumented.
There is one problem with #bandejapalsa solution: the previous cell's separator is still visible with this implementation where as it is not on the default iOS sectionHeaderView. The solution I found was to use a CALayer and offset it by 1 pix. The image needs to be 1pix taller than the view frame itself.
// Create the view for the header
CGRect aFrame =CGRectMake(0, 0, tableView.contentSize.width, IMIUICustomisation.sectionHeaderViewHeight);
UIView * aView = [[UIView alloc] initWithFrame:aFrame];
aView.backgroundColor = UIColor.clearColor;
// Create a stretchable image for the background that emulates the default gradient, only in green
UIImage *viewBackgroundImage = [[UIImage imageNamed:#"greenheader.png"] stretchableImageWithLeftCapWidth:12 topCapHeight:0];
// Cannot set this image directly as the background of the cell because
// the background needs to be offset by 1pix at the top to cover the previous cell border (Alex Deplov's requirement ^_^)
CALayer *backgroungLayer = [CALayer layer];
backgroungLayer.frame = CGRectMake(0, -1, tableView.contentSize.width, IMIUICustomisation.sectionHeaderViewHeight+1);
backgroungLayer.contents = (id) viewBackgroundImage.CGImage;
backgroungLayer.masksToBounds = NO;
backgroungLayer.opacity = 0.9;
[aView.layer addSublayer:backgroungLayer];
// Take care of the section title now
UILabel *aTitle = [[UILabel alloc] initWithFrame: CGRectMake(10, 0, aView.bounds.size.width-10, aView.bounds.size.height)];
aTitle.text = [delegate tableView:tableView titleForHeaderInSection:section];
aTitle.backgroundColor = UIColor.clearColor;
aTitle.font = [UIFont boldSystemFontOfSize:18];
aTitle.textColor = UIColor.whiteColor;
// Text shadow
aTitle.layer.shadowOffset = CGSizeMake(0, 1);
aTitle.layer.shadowRadius = .2;
aTitle.layer.masksToBounds = NO;
aTitle.layer.shadowOpacity = 0.5;
aTitle.layer.shadowColor = IMIUICustomisation.selectedElementTextShadowColor.CGColor;
[aView addSubview:aTitle];
return aView;

Resources