iOS7's subview trimmed if out of parent view bounds [duplicate] - ios

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.

Related

Image on cells not all the way to the left

I am learning about UITableview on iOS and following a course online. I get the table showing fine, but the images on my cells are not all the way to the left (whereas the instructor's ones are). Here is a screenshot of the cells in question:
I don't want that gap, I want the images to be positioned right at the beggining of the cell, all the way to the left. I have done some research and it seems Apple has changed the default look of the cells between ios6 and ios7 so that now the images in cells show a little gap at the left. To get rid of it, I have tried UIEdgeInsets:
[tableView setSeparatorInset:UIEdgeInsetsZero];
and that's not working. I also have tried this approach:
cell.imageView.frame = CGRectMake( 0, 0, 50, 55 );
Nothing happens. So how would I go about it? Thanks
edit-------------------------------------------------------------------------------------------------------------------------------
Still not have found the answer to this. The solutions posted here don't work. I found this piece of code:
self.tableView.contentInset = UIEdgeInsetsMake(0, -50, 0, 0);
Which besides completely puzzling me (as the parameter affected should be the y?) I thought solved the issue by making the image on the cell appear all the way to the left, until I realised it only moved the whole view to the left (as I should have expected I guess) leaving an equal gap on the other side of the screen. All I want is for my images in the cells to appear all the way to the left of the cell as it used to be the case on previous ios. Thanks
It happens because default table content offset from left is 15, you should change it with 0.
See this once, you get idea Remove empty space before cells in UITableView
If you create custom cells. UITableViewCell have owner imageView. Change title of image in your cell.
If you use default cell, use custom cell with constraint Leading space = 0.
It is better not use default imageView of the cell. Drag and drop UIImageView from objective library, create a custom table view cell (Child class of UITableViewCell) then create and outlet of the image view just dragged.
The spacing in the UITableViewCell is because of the default TRUE returned by shouldIndentWhileEditingRowAtIndexPath method of UITableViewDelegate.
I was able to reproduce your problem by the below scenario:
UITableView is in editable mode:
self.tableView.editing = true
And you have implemented:
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView
editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewCellEditingStyleNone;
}
To correct your code:
If you do not want to set Editing Style then you can turn off the editing mode by
self.tableView.editing = false
and remove editingStyleForRowAtIndexPath.
Else if you need editing mode then set the appropiate Editing style(UITableViewCellEditingStyleDeleteor UITableViewCellEditingStyleInsert) or simply turn the indentation off.
- (BOOL)tableView:(UITableView *)tableView
shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath {
return FALSE;
}
You must create a custom cell, by adding a new class as a subclass of UITableViewCell. then you can design cell with autolayout and constraints which will resolve the issue.
there is a another concrete way to achieve this by creating subclass uitableviewcell (custom class).
steps to follow
create a class subclass of UITableViewCell.
in .h file create properties and outlets of UI components.
go to storyboard and add table view cell inside the tableview.
now add UI components like: imageview or button etc and set the x, y values according to.
make class of custom cell your className using identity inspector see image.
connect all outlets of UI components.
use below code uitableview
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *MyIdentifier = #"uniqueIdentifire";
yourCustomClassForCell *cell = (yourCustomClassForCell *)[tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil){
cell = [[yourCustomClassForCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier];
}
cell.imageView.image = [imageAry objectAtIndex:indexPath.row];
}
Dont forget to give identifire by selecting your cell using storyboard Attribute inspector uniqueIdentifire to identifire property see image.
Also you can give some vertical space between cells by just to add this below code (Method only) inside customeCellClass.
- (void)setFrame:(CGRect)frame { // method to insert gap between table view cell
frame.origin.y += 6;
frame.size.height -= 2 * 6;
[super setFrame:frame];
}
You can not really change the frame of the inbuilt subviews of uitableviewcell like imageview, accessoryview. But if you create a custom tableviewcell class(even if you do not add any other subelement to it), you can change the frame of the inbuilt imageview by overriding the layoutSubviews method inside the UITableViewCell. I have tried it and it works.
#import "TableViewCell.h"
#implementation TableViewCell
- (void)awakeFromNib {
[super awakeFromNib];
// Initialization code
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
-(void) layoutSubviews{
[super layoutSubviews];
CGRect frame = self.imageView.frame;
frame.origin.x = 0;
self.imageView.frame = frame;
}
#end

Adding UIVibrancyEffect to UITableView Cell

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.

Change view position when configuring cell. Strange behavior

I have a standard UINavigationController with a UITableViewController at it's root. In IB, I paint a prototype cell with a label and a UIView. The UIView contains a button. I'd like the UIView to be x-aligned after the label, as a function of the length of text in the label.
In IB, the view's left side is initially aligned with the label's left side. There are no layout constraints in IB.
Here's my cellForRowAtIndexPath ...
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 5;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSArray *labels = #[#"SOME STRING", #"SHORTER", #"A VERY MUCH LONGER ONE", #"REGULAR ONE", #"TINY"];
UILabel *label = (UILabel *)[cell viewWithTag:32];
label.text = labels[indexPath.row];
UIView *view = [cell viewWithTag:33];
CGSize size = [label.text sizeWithAttributes:#{NSFontAttributeName:label.font}];
view.frame = CGRectOffset(label.frame, size.width, 0);
return cell;
}
Two problems: I've set breakpoints and watch this code running the first time the view appears. I see the view.frame get changed for each row, but the view does not change position. I remains in it's IB-position, right on top of the label. If I scroll the table down, the views on lower cells (presumably reused) are in the desired position. If I scroll back up, the upper rows are also good. It just fails to work on the initial presentation of the upper cells.
Second problem is that the button contained in only the first row has a subtle, strange effect applied to it's text, like a blur. See attached...
First Row Button (zoomed in mac preview... see that extra blur on the left edge of the letters?)
Other Row Buttons
Stuff I tried:
I've tried a few variations, including using a regular view controller with a table view added (rather than a UITableViewController). I've found that if I reloadData on viewDidAppear, that solves the placement problem, but not the blurry button. (Also, I don't like the idea of needing to reload on viewDidAppear). Doing so on viewWillAppear has no effect at all. I've also tried animating the label change slowly. It happens, but again, only on the second time the cell is configured. I try changing the UIView color to prove the code is being run. The color change happens every time, including the first time, but not the view placement. Am I nuts?
For problem 1:
Try calling [cell layoutIfNeeded] before returning the cell, there should be no performance hit when it does not need relayout.
For problem 2:
try calling CGRectIntegral before you set the frame. ie
view.frame = CGRectIntegral( CGRectOffset(label.frame, size.width, 0) );

UITableViewCell hidden content overlaps on other cells

I have a table,with settings that cell height is 40px.
If my cells have a label, image or any other component starting at 41px it will still appear in the table overlaping the cells underneath.
How to resolve this? I do not want the rest of the cell to be shown, just the height that is
set in my table settings.
Thank you.
Please add up these methods and try,
-(float)tableView:(UITableView*)tableView heightForFooterInSection:(NSInteger)section
{
return 0.01;
}
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
UIView *view = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, tableView.frame.size.width, 1)] autorelease];
view.backgroundColor = [UIColor clearColor];
return view;
}
Using this will be considered as end of data from data source. And rest cells will not be displayed.
[Will solve this issue - If my cells have a label, image or any other component starting at 41px it will still appear in the table overlaping the cells underneath]
You want to hide the content of cell that is below the 40px level? Add UIView to cell.contentView with white colour above the content of cell with 41px origin.y
A little late, but just in case anyone else is running into this issue. If using IB, ensure "Clip Subviews" is checked on your table view cell. And this:
self.contentView.clipsToBounds = YES;
should do the same thing.
You didn't solve this problem because the point is not the UITableViewCell. It's Aspect Fill Mode of UIImageView.
Select the UIImageView in the SB, check this on!

UITableView - Sliding content inside each cell horizontally

This is my first native iOS app...
Exactly like the iTunes app on iOS does, by having a tableview that you can scroll vertically, and then each row, you can scroll independently horizontally. At least this is ow I imagine it to work.
how would I implement this? I imagine a view inside each tableCell that can scroll horizontally?
Can any one please shed some light on this and what I might read or try to do
You can add the scroll view into the cells content view and then just the content size property of the scroll view to whatever length you want. Here I have set the width of scroll view to 1000 and the height to 44 (which is the default size of the UITableViewCell).
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
UIScrollView *vwScroll = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
vwScroll.backgroundColor = [UIColor scrollViewTexturedBackgroundColor];
vwScroll.contentSize = CGSizeMake(1000, 44);
[cell.contentView addSubview:vwScroll];
return cell;
}
The only option I can think of is if you place a UIScrollView inside the UITableViewCell's view.
Note though, that this may cause problems in regard to the vertical scrolling behavior of the UITableView itself.
To achieve independently scrolling rows similar to the App Store app on iOS, one approach is to nest a Collection View inside a Table View Cell.
I wrote a Swift tutorial with step-by-step instructions on the setup, including how to wire the Collection View dataSource to the Table View Cell.
The tutorial includes a working sample project on GitHub and a link to an Objective-C tutorial.
I believe the free Sensible TableView framework provides these cells out of the box. Should give you a good head start since you're still starting out. Hope this helps.

Resources