I am trying to add the vibrancy effect to the text label of my table view cell and it sort of works, but not exactly right.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"UITableViewCell"];
NSDictionary *jobDictionary = [self.jobs objectAtIndex:[indexPath row]];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"UITableViewCell"];
}
if (cell) {
cell.backgroundColor = [UIColor clearColor];
cell.textLabel.textColor = [UIColor whiteColor];
cell.textLabel.text = [jobDictionary objectForKey:#"job"];
UIBlurEffect *blur = [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark];
UIVisualEffectView *blurView = [[UIVisualEffectView alloc]initWithEffect:blur];
blurView.frame = cell.bounds;
[cell addSubview:blurView];
UIVisualEffectView *vibrantView = [[UIVisualEffectView alloc]initWithEffect:[UIVibrancyEffect effectForBlurEffect:blur]];
vibrantView.frame = blurView.bounds;
[vibrantView.contentView addSubview:cell.textLabel];
[blurView.contentView addSubview:vibrantView];
}
return cell;
}
I think I see two major issues with your code. The first issue seems to be the fact that cells in cellForRowAtIndexPath assume a width of 320 when dequeued for display on an iPhone 4 or 5 screen size. If that screen shot is taken from a 6 or 6+, this would explain the weird gap. Somewhere after this method, the cell resizes to accommodate the full width of the table. That means that when you set blurView.frame = cell.bounds, the bounds at this point are actually too narrow. Hypothetically you could fix this by moving that code into the following method with one major caveat.
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
While adding your blur view here should get you the proper frame, adding a subview in either of these methods will lead to major problems as the cell is recycled. Every time that cell is dequeued, a new subview will get added to it and they will continue to stack up over time. This will cause visual issues if they aren't totally opaque and it will also leave the potential for your cell to require a HUGE amount of memory because you could in theory be stacking hundreds of subviews on a cell as the table scrolls.
I think the best solution to your problem would be to subclass UITableViewCell and add the view on -initWithStyle:reuseIdentifier:. There you can also constrain that view to top, bottom, leading, and trailing of the superview to ensure it will always resize correctly. An alternative is setting the frame of the subview in -layoutSubviews of your cell subclass.
Doing this ensures the view is added only once and the frame will be what you expect.
Related
I am building out a dynamic tableview cell. The height is dynamic based on the content that is loaded. I'm running into a hangup with height of the imageview. The images currently load aspect fill in an imageview that is 100% of the width. Is it possible to set an autolayout constraint on my imageview so that the imageview is hidden (has a height of 0) if there is no image for a tableview cell?
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TableViewTileCell *cell = [tableView dequeueReusableCellWithIdentifier:#"tileCell"];
if (!cell)
{
[tableView registerNib:[UINib nibWithNibName:#"TableViewTileCell" bundle:nil] forCellReuseIdentifier:#"tileCell"];
cell = [tableView dequeueReusableCellWithIdentifier:#"tileCell"];
}
cell.tileView.layer.borderColor = [UIColor blackColor].CGColor;
cell.tileView.layer.borderWidth = 1.0f;
[cell.contentView setBackgroundColor:[UIColor colorWithRed:0.96 green:0.96 blue:0.96 alpha:1.0]];
NSString *url=[self.resultsArray[indexPath.row] valueForKey:#"imageURL"];
cell.tileTitle.text = [self.resultsArray[indexPath.row] valueForKey:#"title"];
cell.tileDate.text = [self.resultsArray[indexPath.row] valueForKey:#"date"];
cell.tileContent.text = [self.resultsArray[indexPath.row] valueForKey:#"summary"];
if(![url isEqualToString:#""]){ //If we got a url value back load the image
[cell.tileImageview setImageWithURL:[NSURL URLWithString:url] placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
}
return cell;
}
Is it possible to set an autolayout constraint on my imageview so that the imageview is hidden (has a height of 0) if there is no image for a tableview cell?
Make the whole cell content a vertical UIStackView. In cellForRow, make the image view hidden. The stack view has the wonderful ability to change the constraints in exactly the way you describe: when the image view is hidden, the constraints will change and the image view will occupy zero space, with the other views occupying the whole space.
The alternative is not terrible: you simply have to do, yourself, what the UIStackView would do — remove the empty image view in code and adjust the constraints, in cellForRow. Swapping constraints and views together into and out of the interface is standard practice, and is easy to do.
This is my first iOS project, so I am learning a lot and have to learn more.
I learnt that in order to fit more items on UITableViewCell, I need to subclass it and then use it. I created TransactionCell and in my ViewController I use it as
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// make last cell visible: http://stackoverflow.com/questions/11928085/uitableview-not-visible-the-last-cell-when-scroll-down
tableView.contentInset = UIEdgeInsetsMake(0, 0, 120, 0);
[tableView registerNib:[UINib nibWithNibName:#"TransactionCell" bundle:nil] forCellReuseIdentifier:CellIdentifier];
TransactionCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[TransactionCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
TransactionModel *transactionModel = self.transactionsModel.transactions[(NSUInteger) indexPath.row];
cell.dateAndMonth.text = transactionModel.date;
cell.name.text = transactionModel.name;
cell.amount.text = transactionModel.amount;
cell.categoryImage.image = [self getShortImage:[UIImage imageNamed:transactionModel.category]];
cell.transactionTypeImage.image = [self getShortImage:[UIImage imageNamed:#"Debit"]];
cell.imageView.contentMode = UIViewContentModeCenter;
return cell;
}
Also, I set the height of cell as
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 100;
}
My xib looks like
When I run the project however, I see the following
Things don't fit up!
Question
- How do I make the labels appear completely on TransactionCell?
- How do I fit all the elements in a single row without cutting off
I am sure I need to learn something else, but not sure what
While your label is selected in the xib, go to the autoshrink section in the attributes inspector and change the option to minimum font scale, I usually set it to around 0.5.
Add three separate UILabel inside the cell and set the frame for three different size.
As #Shadowfiend told,you have to work with the auto layout and auto resizing.
Check this link: http://www.appcoda.com/self-sizing-cells/
It is nice tutorial on auto resizing the cells and well described too.
Hope it help.
This question already has answers here:
How to stop UITableView from clipping UITableViewCell contents in iOS 7
(2 answers)
Closed 6 years ago.
I've rumbled through new UI info from apple - didn't help.
Now let the code and the screenshots show you the problem i've ran into.
To ensure that is not my buggy code, i've created a new project, with a single file - a UIViewController that has a tableView inside id. the delegates are set.
I do the following:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 3;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 3;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"UITableViewCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
cell.textLabel.text = [NSString stringWithFormat:#"%d",indexPath.row];
// Configure the cell...
UIView * redView = [[UIView alloc] initWithFrame:CGRectMake(0, -10, 100, 20)];
redView.backgroundColor = [UIColor redColor];
[cell addSubview:redView];
return cell;
}
The table view is set on Grouped.
Lets run it on iOS 6:
Duh ofcourse, the Y origin is negative!
Yes, it is, and this is the result I am trying to achieve.
Lets see what it shows on iOS 7:
Hint: that doesn't occur if we add redView to a normal UIView.
Another hint: if i set tableView's background color blue, the gray lines between sections would be blue, not gray (as a demonstration that the gray is not a set header).
Another hint: same goes for ipad.
Why in iOS 7 it cuts everything that goes out of bounds? Please help!
It's because iOS 7 introduced some changes to the view hierarchy of UITableViewCells.
It used to be UITableViewCell view -> contentView.
Now it's more like UITableViewCell view -> scrollView -> contentView.
The solution is to set clipsToBounds = NO on the scrollView (which is set to YES by default). And the way to achieve that is through the superview property.
So basically in iOS6 and prior, to allow content to spill out of the cell bounds, you would do:
self.clipsToBounds = NO; //cell's view
self.contentView.clipsToBounds = NO; //contentView
In iOS7 you have to also prevent the scrollview from not clipping so you'd do something like:
self.clipsToBounds = NO; //cell's view
self.contentView.clipsToBounds = NO; //contentView
self.contentView.superview.clipsToBounds = NO; //scrollView
And the backwards compatible solution I use is:
self.clipsToBounds = NO;
self.contentView.clipsToBounds = NO;
if ([self.contentView.superview isKindOfClass:[NSClassFromString(#"UITableViewCellScrollView") class]]) self.contentView.superview.clipsToBounds = NO;
Keep in mind this is Hacky™ and if the view hierarchy changes again in iOS 8, you might be in trouble. Unfortunately it seems Apple doesn't want us to spill content out of UITableViewCells so AFAIK this is the only workable solution.
The following will fix it:
cell.layer.masksToBounds = NO
However, it will probably break something else, e.g. cell animations.
The problem you are having is caused by the fact that cells just don't support drawing content out of their bounds (actually, using subviews that extend the bounds of their superview is always a hacky solution).
The best advice is to redesign and avoid such funcionality at all.
Another solution could be to add the same view to the bottom of the previous cell or just add them as a subview of UITableView directly.
I don't think that the contents are actually trimmed in a masksToBounds-sense, but instead that the views are covered by the other (opaque) cells. You could try to fix it by changing the order ("z-Index") of the UITableView's subviews, using bringSubviewToFront: and similar methods whenever a cell appears to ensure that the cell closest to the bottom edge of the screen is the frontmost view of all the cells.
#All Hello
Its quite interesting problem i have.
Actually i have subviews which can be larger in height then cell height itself (Its required for my design). For example cell "A" height is 40 while subview height= 70.
I can show larger subview but when the cell A goes off the screen (i scroll to top) then its subview also disappear (Obvious) result. But that gives undesired effect as larger subview which before extends to cell say "B" beneath container cell suddenly disappear.
I have tried to set cell background as transparent in willDisplayCell delegate method but no luck.
Below is my related method if anyone wants to see
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:nil];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier] autorelease];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
NSArray *newArray = [[dataDictionary allKeys] sortedArrayUsingSelector: #selector(compare:)];
[cell.contentView addSubview: [[[dataDictionary objectForKey:[newArray objectAtIndex:indexPath.section]] objectForKey:#"DayView"] objectAtIndex:indexPath.row]];
return cell;
}
Pardon me if i am not very much clear but this is best i can explain my situation.
I want to show views like http://postimg.org/image/e5ok5hybl/ image.
If you must do this, you can use a simple trick:
Move the y-origin of the table to a negative value equal to the amount your view extends below the cell, e.g. origin -30px.
Make the table height taller by the same amount, i.e. make it 30px taller.
Increase the top content inset of the table by the same amount, i.e. increase by 30px.
What you've effectively done is extend the table view off-screen while maintaining the original position of the cells. Since the table view extends off-screen, the cell will not be dequeued until the part that extends below the cell has scrolled off screen.
Here is a sample project demonstrating the trick.
When I push a UIViewController onto my UINavigation controller like:
[(UINavigationController *)self.parentViewController pushViewController:[[[Fonts alloc] initWithNibName:#"Fonts" bundle:nil] autorelease] animated:YES];
Where Fonts.xib is a UIView with only UITableView controlled by a Fonts object that is a subclass of UIViewController and acts as the UITableView's dataSource and delegate.
In the Fonts object I create a UITableViewCell like:
- (UITableViewCell *) tableView: (UITableView *) tableView cellForRowAtIndexPath: (NSIndexPath *) indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: #"BlahTableViewCell"];
if (!cell) {
cell = [[UITableViewCell alloc]
initWithStyle: UITableViewCellStyleDefault
reuseIdentifier: #"BlahTableViewCell"];
[cell autorelease]; // Delete for ARC
}
return cell;
}
And then I change the font of the cell here:
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
[cell.textLabel setFont:[(UIFont *)[self.listOfFonts objectAtIndex:indexPath.row] fontWithSize:cell.textLabel.font.pointSize]];
cell.textLabel.text = [(UIFont *)[self.listOfFonts objectAtIndex:indexPath.row] fontName];
}
listOfFonts is an NSArray of UIFont objects.
When the view appears it looks like UITableView without changed fonts
If I call reloadData on the UITableView or if I drag the UITableViewCells off screen with my finger and let them bounce back they are redrawn and the view the cells display with the labels having their fonts changed.
It seems like the issue is the UITableViewCells are being drawn too early. If I delay the drawing of them everything looks correct but I want the UITableView to be displaying correctly when the UINavigationController slides my view into place.
Any idea what I am doing wrong?
EDIT: I uploaded a simple and straightforward example of my issue to Dropbox. http://dl.dropbox.com/u/5535847/UITableViewIssue.zip
SOLVED IT!
Ok so I was having exactly the same issues as the original poster and this was the problem.
The line that's causing issues is:
[cell.textLabel setFont:[(UIFont *)[self.listOfFonts objectAtIndex:indexPath.row] fontWithSize:cell.textLabel.font.pointSize]];
Specifically, your issue is because you're trying to feed the cell's textLabel its own pointSize, but pointSize doesn't exist yet so strange bugs occur instead. For me, I noticed that a "transform" was failing due to a singular matrix being non-invertible. As soon as I hardcoded a standard value as my pointSize I saw all my labels draw with the proper font instantly. Note: this makes sense as to why a redraw worked, because then your textLabel does indeed have a pointSize.
In any case, you need to explicitly set your pointSize here, no using what the textLabel "already has" because it doesn't have anything until you're "reloading" a cell.
Set the label font inside -tableView:cellForRowAtIndexPath:.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *identifier = #"identifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier] autorelease];
// do it here if your font doesn't change ....
}
// otherwise here with your font ...
cell.textLabel.font = [UIFont boldSystemFontOfSize:12];
return cell;
}
I'm not sure that table cells are designed to be customisable in this way. The table cell may assume that you won't customise the font, and so not draw itself in a way that's compatible with what you are trying to do.
You'd be better off creating a custom table cell, or appending a UILabel as a subview to the table cell when you create it, and them setting the font of that label instead.
It may seem like overkill for such a small customisation, but it's flexible and it's guaranteed to work.