Add gradient background for only last 2 section on UITableView - ios

I'm creating a UITableView have 3 sections with custom cells. At 1st section I set it to white background color. Now I wanna set a gradient background like image below for only section 2nd and 3rd (this background will start from section 2 and end at last row of section 3):
How can I do that? Can someone give me a solution? Thanks.

This is actually pretty easy.
First, make sure each cell has a transparent background color. The default is opaque white. You can set this in your storyboard, or you can do it in your tableView:cellForRowAtIndexPath: method like this:
cell.backgroundColor = [UIColor clearColor];
Next, make a subclass of UITableView and override layoutSubviews. Be sure to call [super layoutSubviews].
In your override of layoutSubviews, the first time it's called, make a CAGradientLayer with your chosen gradient and add it as a sublayer of the table view's layer at index 0 (so it's behind all of the table view's other sublayers). On every call, update the frame of the gradient layer to be the union of [self rectForSection:1] and [self rectForSection:2].
Also, remember to set the custom class of the table view in your storyboard.
Here's the table view subclass I used to make the demo.
GradientTableView.h
#import <UIKit/UIKit.h>
#interface GradientTableView : UITableView
#end
GradientTableView.m
#import "GradientTableView.h"
#implementation GradientTableView {
CAGradientLayer *gradientLayer;
}
- (void)layoutSubviews {
[super layoutSubviews];
[self layoutGradient];
}
- (void)layoutGradient {
[self createGradientLayerIfNeeded];
[self updateGradientLayerFrame];
}
- (void)createGradientLayerIfNeeded {
if (gradientLayer != nil) {
return;
}
gradientLayer = [CAGradientLayer layer];
gradientLayer.colors = #[
(__bridge id)[UIColor redColor].CGColor,
(__bridge id)[UIColor orangeColor].CGColor
];
gradientLayer.locations = #[ #0, #1 ];
gradientLayer.startPoint = CGPointZero;
gradientLayer.endPoint = CGPointMake(0, 1);
gradientLayer.type = kCAGradientLayerAxial;
[self.layer insertSublayer:gradientLayer atIndex:0];
}
- (void)updateGradientLayerFrame {
CGRect frame = [self rectForSection:1];
frame = CGRectUnion(frame, [self rectForSection:2]);
gradientLayer.frame = frame;
}
#end

Edit:
This answer contains some general information on table views and an easy solution to your question if a coarse gradient as shown in the screenshot below suffices. If you need a more fine-grained solution please refer to Rob's answer.
Apart from the fact that it's really hard to recognize that your background image has a gradient at all (it looks like a plain red image to me and you should consider just using a red background color instead) there is no way to just set an image as the background of a whole section using the standard UIKit API. And there's a reason for that:
A UITableView doesn't load all its cells when it appears on screen. Instead it only loads those cells that are visible at the moment plus a couple of buffer cells below (and above) in order to enable smooth scrolling. A table view section is rather an abstract concept that's intended to help you present your data in a structured fashion as it gives you headers and footers for each section and the ability to give your cells a different format (e.g. a different background color) depending on the section index. However, a table view section is not a view that is placed behind your cells. And thus you cannot apply a background image or a color to it.
You can see that when you look at an arbitrary table view in Xcode's view debugger:
The selected light blue view is the table view's wrapper view. The upper next view layer is already the table view cells. So there's no "section view" or something in between.
As you can see from the screenshot it's possible and quite easy to get a rough gradient effect by simply setting the cell's background color depending on its index path. I achieved this effect with the following two lines of code in the cellForRowAtIndexPath method:
Swift:
let redColorValue = CGFloat(indexPath.row)/CGFloat(tableView.numberOfRowsInSection(indexPath.section))
cell.backgroundColor = UIColor.init(red: redColorValue, green: 0.5, blue: 0.8, alpha: 1)
Objective-C:
CGFloat redColorValue = (CGFloat)(indexPath.row) / aSection;
cell.backgroundColor = [UIColor colorWithRed:redColorValue green:0.5 blue:0.8 alpha:1];
This way the first and the last cell in the section will always have the same color so the gradient will dynamically change its height depending on the number of cells in your section.

I think you need to set the background to the cell, so it moves with scrolling. You need to know where your sections begins (0, 5, 10):
if (indexPath.row < 5)
{
[cell setBackgroundColor:[UIColor whiteColor]];
}
if (indexPath.row > 5 && < 10)
{
[cell setBackgroundColor:[UIColor gradientColor]];
}
if (indexPath.row > 10)
{
[cell setBackgroundColor:[UIColor gradientColor2]];
}

Related

Can't tap UICollectionView item

I'm building an app whereas I've got a UICollectionView with a custom layout. I have a problem whereas I cannot tap some rows. Here's an overview over what happens
1) App starts and presents a UICollectionView
2) 3 test items are displayed, using the visual debugger in Xcode, you can see that there is something not right with the hierarchy
The red row can't be tapped now, but the yellow and green rows can.
3) User taps the yellow item, and segues to another page
4) User pops the shown UIViewController and returns to the UICollectionView whereas the viewWillAppear method reloads the UICollectionView like so:
[self.collectionView reloadData];
5) Re-entering the visual debugger shows that the hierarchy seems shifted, and the yellow row is now untappable, but the red and green rows can be tapped.
What could be the reason for this? I'll post any relevant code, ask for what parts you'd like to see.
Update
The UIViewController displaying the UICollectionView
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
ShareViewCell *view = [collectionView dequeueReusableCellWithReuseIdentifier:#"share" forIndexPath:indexPath];
view.share = [self.sharesController shareAtIndex:indexPath.item];
return view;
}
Custom cell:
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
self.leftBorderView = [UIView new];
[self.contentView addSubview:self.leftBorderView];
self.label = [UILabel new];
self.label.font = [UIFont boldSystemFontOfSize:10];
self.label.numberOfLines = 0;
self.label.lineBreakMode = NSLineBreakByWordWrapping;
[self.contentView addSubview:self.label];
}
return self;
}
- (void)layoutSubviews
{
[super layoutSubviews];
self.leftBorderView.frame = CGRectMake(0, 0, 1.5, self.bounds.size.height);
CGSize labelSize = [self.label sizeThatFits:CGSizeMake(self.bounds.size.width - 10.0f, MAXFLOAT)];
NSTimeInterval durationOfShare = [self.share.endSpan timeIntervalSinceDate:self.share.startSpan] / 3600;
CGFloat middleOfCell = self.bounds.size.height / 2;
CGFloat yPositionToUse = middleOfCell - (labelSize.height / 2);
if (durationOfShare < 0.25)
{
[self.label setHidden:YES];// to small to be shown
} else if (durationOfShare > 0.5)
{
yPositionToUse = 8; // show it at the top
}
self.label.frame = CGRectMake(8, yPositionToUse, labelSize.width, labelSize.height);
}
Update 2
Is the UICollectionReusableView blocking tap of the UICollectionViewCell?
Ok,
First, you should be more precise in your question : you're using some code found on some question on StackOverflow.
The code seems to be this Calendar UI made with UICollectionView.
This is a sample code, quickly built for the sake of a StackOverflow answer (with a bounty). It's not finished, has no contributors, and you should try to read it and improve over it !
From your debugger's captures, it seems you have some view which overlays on top of your collectionView's cells.
From reading this code, I see it uses some supplementary view to present 'hour' blocks. This supplementary view class is HourReusableView. And it's the view coming on top on your debugger's captures
CalendarViewLayout is responsible to compute these supplementary views frame, as it does for colored event blocks (see method - (void)prepareLayout)
I might bet that these supplementary views's Z-order isn't predictable - all views have a default zIndex of 0. One way to fix it ? After line 67 of this file, set a negative zIndex on the UICollectionViewLayoutAttributes of the hour block. This way, you make sure hour supplementary views are always behind your cells, and don't intercept the cell's touch

If I add a background color to a UIButton, how do I inset it so the background is smaller?

If I have a UIButton and give it a red background, and the background size is a little too small or too big for my liking (but the tap target size is perfect), is there any way to change the size of them?
Basically the equivalent of adding padding in CSS so that it either takes up more area or less? Purely an aesthetic change.
Say with the background color applied to the button it visually takes up a 100px * 30px area. I want it to be 90px * 25px. Is this possible?
One way to do this, is to set the color of a sublayer of the button rather than the background color of the button itself.
- (void)viewDidLoad {
[super viewDidLoad];
CALayer *sub = [CALayer new];
sub.frame = CGRectInset(self.topButton.bounds, 5, 2.5); // this will make the layer 90x25 for a button that is 100x30
sub.backgroundColor = [UIColor redColor].CGColor;
[self.topButton.layer addSublayer:sub];
}

Tableview scrolling lack of performance using cornerRadius, whats alternatives?

I can't find a solution for my issue, so I hope some experts could help me.
I have tableview with lots of cells. For each cell I have an image view with rounded corners:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UIImageView *exampleView;
...
exampleView.layer.cornerRadius = 5.f;
exampleView.clipsToBounds = YES;
[cell.contentView addSubview:exampleView];
...
}
Resulting in a big lack of scrolling performance issue - when using cornerRadius and clipsToBounds = YES, any alternative solutions?
(The rounded corner image view contains pictures, and if the corner isn't set. The scrolling is smooth. So the problem is here)
EDIT #1: to clarify that I have already read other posts:
...
exampleView.frame = CGRectMake(5.0f,size.height-15.0f,30.0f,30.0f);
exampleView.layer.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.5].CGColor;
exampleView.layer.cornerRadius = 5.0f;
exampleView.layer.borderWidth = 0.3f;
exampleView.layer.borderColor = [UIColor blackColor].CGColor;
exampleView.layer.masksToBounds = NO;
exampleView.layer.shouldRasterize = YES;
//exampleView.clipsToBounds = NO;
[exampleView setImage: myImage ];
EDIT #2:
It's like myImage is lapping over the rounded corner view. How do I make the image fit the rounded corner view correct? or am I missing something?
Just change the following line
exampleView.layer.masksToBounds = YES;
To round out the corners in your imageView. See images below.
The tableview scrolling is a issue with view compositing. In addition to suggestions above, this will certainly help -
Set the opaque property of the UITableViewCell's contentView property, and backgroundView property. Go:
[cell.contentView setOpaque:YES];
[cell.backgroundView setOpaque:YES];
This will help reduce the view compositing effort at the earliest possible stage.
There are additional, advanced, techniques which would require you to use asynchronous processing to process your images as you scroll. They may be overkill if the above techniques work.

How to make Drawing on Fully Transparent View Visible

I am using Quartz for drawing as in custom UIView say "Paint"
I have controller view "viewC"
I did
[self.view addSubView:somePaintInstance]; in my viewC at some event
Now when i am drawing or painting , the default background is "black" for Paint.
I want to do a Full Transparent Painting/Drawing on top of my "viewC" view
So i did
somePaintInstance.alpha=0.5; (before adding it as subview)
But that also make my drawing/painting transparent with the Paint's Background , so if i make it fully transparent (alpha = 0.0) , i couldn't see the drawing itself with the paint's view
What i tried and not working:
somePaintInstance.setbackgroundcolor
somePaintInstance.opaque
Other answers on web are also NOT concrete for this kind of implementation
I looked the answer by rob here:
How to add a transparent gradient mask to a context
But Still not clear how do i implement exactly what i want?
Let me know other details if required
Regards,
I tried to set a background with alpha value 0 or to work with StartTransparentLayer. None of that worked.
What worked for me was to set the views opaque property to NO.
In my view's initializer i put the following code:
-(id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
// set default values
self.colorNormal = [UIHelper ricolaYellow];
self.colorActice = [UIColor colorWithHexString:CIRCLE_COLOR_ACTIVE];
self.capacities = nil;
self.opaque = NO;
}
return self;
}
This line should work,
somePaintInstance.backGroundColor = [UIColor clearColor];

UITableViewCell balloons like iChat

I'm doing something like a chat app for iPhone, and I found this piece of code from Sam Soffes called SSMessagesViewController What I found is that it works perfect until my table has many rows, then I supose that drawrect is reusing cells because is repeating same row content every 6 (but data is different)... any idea on how I can resolve that?
- (void)drawRect:(CGRect)frame {
UIImage *bubbleImage = _messageStyle == SSMessageStyleLeft ? _leftBackgroundImage : _rightBackgroundImage;
CGSize bubbleSize = [[self class] bubbleSizeForText:_messageText];
CGRect bubbleFrame = CGRectMake((_messageStyle == SSMessageStyleRight ? self.frame.size.width - bubbleSize.width : 0.0f), kMarginTop, bubbleSize.width, bubbleSize.height);
[bubbleImage drawInRect:bubbleFrame];
CGSize textSize = [[self class] textSizeForText:_messageText];
CGFloat textX = (CGFloat)bubbleImage.leftCapWidth - 3.0f + ((_messageStyle == SSMessageStyleRight) ? bubbleFrame.origin.x : 0.0f);
CGRect textFrame = CGRectMake(textX, kPaddingTop + kMarginTop, textSize.width, textSize.height);
[_messageText drawInRect:textFrame withFont:kFont lineBreakMode:kLineBreakMode alignment:(_messageStyle == SSMessageStyleRight) ? UITextAlignmentRight : UITextAlignmentLeft];
}
I guess at this stage you are having two images. Left bubble image and right bubble image, Its possible to add images into layers.
Imagine you have two cells one left pointed and another one right pointed You have to Subclass the TableCell. Define a custom style and, depending on style value assigned you can add these images to the sublayer of Backround view you have added(exlicitly added). http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreAnimation_guide/Articles/ProvidingCALayerContent.html
Also do one thing. override the method layoutsubviews. CALayers didn't get resized on its UIView's bounds change. Why? its an example.
At this point reset the size of the sublayer of BackgroundView to the size of table cell. Now you have to just sepcify the Tablecellheight in delgate method http://developer.apple.com/library/ios/documentation/uikit/reference/UITableViewDelegate_Protocol/Reference/Reference.html#//apple_ref/doc/uid/TP40006942-CH3-SW25

Resources