First of all, I am new at this and I am most likely forgetting something very simple.
Question:
I am making an application that displays random images from imgur.com in a tableView. For some reason all of the cells are indented a small amount as seen in the picture below. I have fiddled around with many settings in storyboard and have had no luck in fixing the issue.
Here is the tableView code...
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
return (_images.count * 2);
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = nil;
if (indexPath.row % 2 == 0) {
//content cell
cell = [tableView dequeueReusableCellWithIdentifier:#"RandomImgurTableCell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"RandomImgurTableCell"];
}
long row = [indexPath row] / 2;
SDImageCache* myCache = [SDImageCache sharedImageCache];
cell.imageView.image = [myCache imageFromDiskCacheForKey:_images[row]];
}
else if (indexPath.row % 2 == 1) {
//separator cell
cell = [tableView dequeueReusableCellWithIdentifier:#"SeparatorCell"];
if(cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:#"SeparatorCell"];
}
}
if (indexPath.row == (_images.count * 2) - 3) {
[self checkToLoadMoreImages];
NSLog(#"Hit bottom of table, getting more images");
}
return cell;
}
Here is a picture of my tableView and cell settings...
In order to get rid of the indent, you set the separator insets to zero. The result is shown in the following image.
In Code
myTableView.separatorInset = UIEdgeInsetsZero
In the Interface Builder
Notes
If the cells are causing the indent, then you can add the following lines to tableView:cellForRowAtIndexPath:
cell.separatorInset = UIEdgeInsetsZero
cell.preservesSuperviewLayoutMargins = false
cell.layoutMargins = UIEdgeInsetsZero
If you are still supporting pre iOS 8 then see this answer for more details.
See also this Q&A.
Adjusting the separator inset should fix this. I believe that the default is 15px in from the left.
If you're referring to the horizontal indentation then you should try the following:
Make sure that the content view frame x origin is at 0:
yourTableViewCell.contentView.frame = CGRectMake(0.0f, 0.0f, SOME_WIDTH, SOME_HEIGHT);
You should make sure that the image view you're adding to the UITableViewCell is aligned with it's parent left edge. If you're using autolayout than do it in the interface builder. Otherwise:
yourImageView.autoresizingMask = UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleHeight;
See that when the image is scaled, it remains true to the view frame:
yourImageView.contentMode = UIViewContentModeScaleAspectFill;
Make sure that the cell has 0 indentation:
yourTableViewCell.indentationLevel = 0;
If none of this helps, set a breakpoint and examine the subviews of the cell using one of the following debugger command:
po [[UIWindow keyWindow] recursiveDescription]
po [yourTableViewCell recursiveDescription]
None of the above worked for me, I found this instead:
https://www.hackingwithswift.com/example-code/uikit/how-to-make-uitableviewcell-separators-go-edge-to-edge
You need to do two things. First, add these two lines of code to your
table view controller's viewDidLoad() method:
tableView.layoutMargins = UIEdgeInsets.zero
tableView.separatorInset = UIEdgeInsets.zero
Now look for your cellForRowAt method and add this:
cell.layoutMargins = UIEdgeInsets.zero
Related
when the table has more records than the screen let you see, and the user scroll to the bottom for the last records in the table, the last two rows are not accessible (just in iOS7, in iOS8 everything is fine). Once the user take the finger from screen, the table goes up, and the last two rows are hidden.
This is one of the method used for tableView but i dont know if this come from here:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString * cellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
// Configure the cell...
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
if (selectionCategMsg == 0)
{
if ([self.tableauMsgReceived count] < 1) {
cell.textLabel.text = NSLocalizedString(#"noMessagesYet", nil); //#"No messages yet !";
}
else
{
cell.textLabel.text = [NSString stringWithFormat:#"%#", [self.tableauMsgReceived objectAtIndex:indexPath.row]];
cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
}
}
else
{
if ([self.tableauMsgSent count] < 1) {
cell.textLabel.text = NSLocalizedString(#"noReplyYet", nil);
}
else
{
cell.textLabel.text = [NSString stringWithUTF8String:[NSString stringWithFormat:#"%#", [self.tableauMsgSent objectAtIndex:indexPath.row]].UTF8String];
cell.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
}
}
[cell setAccessoryType:UITableViewCellAccessoryNone];
return cell;
}
Can someone give an advice ? Thank you.
THIS IS THE SOLUTION THAT I FOUND:
// -> SET TABLE FRAME
CGFloat sideMargin = 0;
CGFloat originX = sideMargin;
CGFloat topBottomMargin = 100;
CGFloat originY = topBottomMargin;
// Width based on view size
CGFloat sizeWidth = self.view.bounds.size.width);
// Height based on view size
CGFloat sizeHeight = (self.view.bounds.size.height - topBottomMargin);
self.myTableView.frame = CGRectMake(originX, originY, sizeWidth, sizeHeight);
// <- END SET tableView frame
UITableView has the method scrollToRowAtIndexPath:atScrollPosition:animated:. That method lets you scroll the table view so a given indexPath is visible. Is that what you're looking for?
EDIT:
#Daij-Djan's post is a good theory on what's wrong. If your table view goes off the screen then it will position the last couple of cells inside it's view, but that view won't be visible. You're going to need to do some debugging to figure out what's wrong. You might try setting the table view's layer's borderWidth to a non-zero value so you can see the bounds of the table view.
In your viewDidLoad, add code like this:
myTableView.layer.borderWidth = 1;
myTableView.layer.borderColor= [UIColor redColor].CGColor;
(Where you'd replace "myTableView" with the name of your table view instance variable or property.)
You'll have to import QuartzCore.h in order for the code above to compile:
#import <QuartzCore/QuartzCore.h>
I am getting some strange behaviour when scrolling a TableView of custom UITableView cells.
When the app first opens, the content is fine:
But if I scroll my view some offsets appear in the embedded UIImageViews:
I have removed all layout constraints, and the problem still occurs.
I am at loss for reasons why this happens; any help is welcomed!
Here's my cellForRowAtIndexPath code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *StrechCellIdentifier = #"StrechCell";
//Place regular strechable cells.
GFStrechCell *cell = [tableView dequeueReusableCellWithIdentifier:StrechCellIdentifier];
if (cell == nil) {
cell = (GFStrechCell*) [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:StrechCellIdentifier];
}
cell.nameLabel.text = #"Chez Julien";
cell.descriptionLabel.text = #"A yummy place!";
cell.descriptionLabel.hidden = YES;
[cell.restaurantImage setImage:[UIImage imageNamed:[NSString stringWithFormat:#"restaurant%i.png", (indexPath.row%5 + 1)]]];
if(indexPath.row % 2 == 0){
[cell.favoriteButton.imageView setImage:[UIImage imageNamed:#"favoriteButtonPressed.png"]];
} else {
[cell.favoriteButton.imageView setImage:[UIImage imageNamed:#"favoriteButton.png"]];
}
return cell;
}
GFStrechCell is just a simple subclass of call with elements pointing to IBOutlets in storyboard:
That's odd, try this out just to see if it works
Below:
[cell.restaurantImage setImage:[UIImage imageNamed:[NSString stringWithFormat:#"restaurant%i.png", (indexPath.row%5 + 1)]]];
Try adding:
CGRect restaurantImageFrame = cell.restaurantImage.frame;
restaurantImageFrame.origin = CGPointZero; //or whatever it should be
cell.restaurantImage.frame = restaurantImageFrame;
This will set the image's origin to (0,0).
Edit: you could also try adding the following line instead.
[cell.restaurantImage sizeToFit];
Am I able to create a UILabel that is layouted upon many UITableViewCells?
I'm trying to make something like (that is just one section of my UITableView, each section can have one or more rows):
---------------------------------------------
| Multi-lined label | row1 values |
| with some useless | row2 values |
| text | row3 values |
---------------------------------------------
I managed to create a UILabel (in the first row of a section) that is multi-lined and is not clipping to bounds. That works really well (it was a bit tricky to count each sections row heights, but doable) besides one case: when I'm scrolling UITableView from bottom to top - UITableView renders last row (without UILabel) so it has "no evidence" of having UILabel (because it is maintained in the first row of section). Can I force some kind of relayouting first cell in section? I tried reloadRowsAtIndexPaths:withRowAnimation: with first row in each section whenever I layouted not first cell in section but it gave me layouting errors that I really do not understand. Or maybe there is another idea to do so?
-- EDITED
To be clear: I have a custom UITableViewCell with an IB view, it has a few labels that each row consist of and a label named labelName that I want to be "multi-lined" along rows in that section. LabelName.text is empty for each row besides first one in each section.
I am adding somescreenshots:
Good screenshot - when I am scrolling to bottom I'm getting proper effect:
Bad screenshot - when I am scrolling up, UITableView renders last row of section firstly, and afterwards renders upper rows - that gives effect of cut label (because multi-line label is in the first row)
I am not sure if code here will add anything to question - it is rather simple and almost whole logic is in tableView:heightForRowAtIndexPath. I can only present how do I create custom UITableViewCell:
CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[CustomTableViewCell reuseIdentifier]];
if (cell == nil) {
cell = [[CustomTableViewCell alloc] initWithOwner:self];
cell.clipsToBounds = NO;
cell.labelName.clipsToBounds = NO;
cell.contentView.superview.clipsToBounds = NO;
}
-- EDIT 2
Here is most of the code:
- (void) reloadData
{
NSUInteger index = 0;
for (NSDictionary *object in self.list) {
CGFloat height = [[object objectForKey:#"name"] sizeWithFont:self.labelFont constrainedToSize:self.labelSize].height;
[self.labelHeights addObject:NSNumberFloat(ceilf(height))];
index++;
}
[self.tableView reloadData];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *object = [self.list objectAtIndex:indexPath.section];
CGFloat height = [[self.labelHeights objectAtIndex:indexPath.section] floatValue];
NSUInteger count = [[object objectForKey:#"list"] count];
CGFloat cellHeight = 30.f;
if((indexPath.row + 1) == count){
cellHeight = MAX(8.f + height - 30.f * indexPath.row, 30.f);
}
return cellHeight;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [self.list count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[[self.list objectAtIndex:section] objectForKey:#"list"] count];
}
- (UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *person = [self.list objectAtIndex:indexPath.section];
NSDictionary *object = [[person objectForKey:#"list"] objectAtIndex:indexPath.row];
CustomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[CustomTableViewCell reuseIdentifier]];
if (cell == nil) {
cell = [[CustomTableViewCell alloc] initWithOwner:self];
cell.backgroundColor = [UIColor clearColor];
cell.userInteractionEnabled = NO;
cell.clipsToBounds = NO;
cell.labelName.clipsToBounds = NO;
[cell.contentView.superview setClipsToBounds:NO];
}
if(indexPath.row == 0){
cell.labelName.text = [person objectForKey:#"name"];
CGFloat height = [[self.labelHeights objectAtIndex:indexPath.section] floatValue];
cell.labelName.numberOfLines = (int)(height / self.fontSizeHeight);
cell.labelName.frame = CGRectChangeHeight(cell.labelName.frame, height);
}
else{
cell.labelName.text = #"";
}
CGFloat cellHeight = [self tableView:self.tableView heightForRowAtIndexPath:indexPath];
cell.borderTop.hidden = YES;
cell.borderBottom.hidden = YES;
cell.borderBottomSmall.hidden = NO;
if(indexPath.row == 0){
cell.borderTop.hidden = NO;
}
if(indexPath.row + 1 == [[person objectForKey:#"list"] count]){
cell.borderBottom.hidden = NO;
cell.borderBottom.frame = CGRectChangeY(cell.borderBottom.frame, cellHeight - 1.f);
cell.borderBottomSmall.hidden = YES;
}
cell.labelDate.text = [object objectForKey:#"date"];
cell.labelPremium.text = [[object objectForKey:#"premium"];
return cell;
}
-- PARTIAL ANSWER
I managed to create a hack, that makes multi-line UILabel visibile when scrolling bottom to up at some point:
- (void) scrollViewDidScroll:(UIScrollView *)scrollView
{
NSArray *cells = [self.tableView visibleCells];
UITableViewCell *cell = [cells objectAtIndex:0];
[cell.superview bringSubviewToFront:cell];
}
I noticed that the part of the UILabel is covered by a row thats below of the UILabels row and that hack makes it would be properly displayed. But it has a drawback, when scrolling slowly from bottom to top it generates a flicker when label is created (part of it should be visible before real creation of UILabel).
Up mentioned answers are not solutions, but "hacks".
In the cell == nil block should be only the initialization.
You should not add any subviews in cell in cellForRowAtIndexPath.
The reason is simple: I will reuse a cell with some labels already added and add a new label.
Either use the default cell.textLabel, either create a subclass for UITableViewCell, with a
-(void)setData:(dictionary or string)object;
and in implementation just set the proper data to proper UI controls.
Add/create controls either in init method in the subclass, or in IB/Storyboard.
Call the dictionary or string should be picked in correspondence to indexPath, so you will always get proper data for proper cell at proper indexPath.
Try This
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellId = #"cellId";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
if (cell==nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
}
for (UIView *subview in [cell.contentView subviews]) {
[subview removeFromSuperview];
}
/// your UI on cell goes here
return cell;
}
- (UITableViewCell *)cellForInfoWithCellIdentifier:(NSString *)cellIdentifier
forIndexPath:(NSIndexPath *)indexPath
inTableView:(UITableView *) tableView
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
NSLog(#"%d", cell.contentView.subviews.count);
if (cell.contentView.subviews.count > 0)
{
[cell.contentView.subviews[0] removeFromSuperview];
}
[cell.contentView addSubview:self.viewsForOptions[self.selectedIndex]];
NSLog(#"%d", cell.contentView.subviews.count);
return cell;
}
The above code is called for a certain section and row in cellForRowAtIndexPath. This cell is changed whenever a value of the segmented control object is changed.
The method is below:
- (void)testOptionsValueChanged:(id)sender
{
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:1];
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
I only have two cells; 1 in each section so the cell == nil condition is always false.
Issue:
When the tableView loads the console logs:
0
1
When I change the value of the segmented control, I still get:
0
1
After some more tries, I basically have height of the second view (for the second index) increasing. I can't seem to get a clue why this is really happening.
EDIT:
Other pieces of code:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0)
{
// test detail view is a constant height
if (indexPath.row == 0)
return 180.0;
}
else if (indexPath.section == 1)
{
// based on the view loaded from the viewForOptions array
if (indexPath.row == 0)
{
return ((UIView *)self.viewsForOptions[self.selectedIndex]).frame.size.height;
}
}
return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *DetailCellIdentifier = #"DetailCell";
static NSString *InfoCellIdentifier = #"InfoCell";
UITableViewCell *cell;
if (indexPath.section == 0)
{
// displays views and test button
if (indexPath.row == 0)
{
cell = [self cellForTestDetailWithCellIdentifier:DetailCellIdentifier forIndexPath:indexPath inTableView:tableView];
}
}
else if (indexPath.section == 1)
{
// display the view for information based on segmented control
if (indexPath.row == 0)
{
cell = [self cellForInfoWithCellIdentifier:InfoCellIdentifier forIndexPath:indexPath inTableView:tableView];
}
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
- (void)viewDidLoad
{
// set the selected index for the options segmented control
self.selectedIndex = 0;
// instantiate all the views for test segmented control options
self.aViewController = [[AViewController alloc] ...];
self.bViewController = [[BViewController alloc] ...];
self.cViewController = [[CViewController alloc] ...];
// add all the views to an array that will be used by the tableview
self.viewsForOptions = #[self.aViewController, self.bViewController, self.cViewController];
}
Your 0,1 to 1,1 is caused by the table view reuse mechanism. Basically, the table will not reuse any cell which is already in use. So when you first populate and first refresh the table, new cells will be created. After that, there are enough cells in the reuse queue that aren't being used that no new ones should need to be created (scrolling May create a couple more).
Your height issue could be caused by auto resizing / layout. When you add the subview you should specify what size it should be and how that size should be changed as the superview (the cell) size is changed. And the cell size is changed (log it when you add the subview).
The height of the cell is one part. Usually you would want to set:
UIView *subview = self.viewsForOptions[self.selectedIndex];
subview.frame = cell.contentView.bounds;
[cell.contentView addSubview:subview];
So that when the cell is resized the subview will have the correct size. But this depends on your auto resizing rules. If you set a layout constraint to pin the height and width then you wouldn't need to set the frame.
In either case, you need to specify what happens to the subview frame when the superview frame changes.
Your issue, I guess, is that the cell is resized before being reused and your subview is still attached. So, it gets resized too. Then, in heightForRowAtIndexPath: you use the height of the subview (now invalid, try logging it) to set the height of the row.
I'd look at changing the implementation of heightForRowAtIndexPath: to use a configuration based on the selected segment rather than the subview frame height.
My cells all have varying heights, determined by text entered by the user.
Whatever height the last cell takes on seems to determine (change) the height for all remaining blank cells.
It doesn't appear that heightForRowAtIndexPath is getting called for these remaining, empty cells, nor is CFRAIP.
Does anyone know how to fix this or why it is happening?
Put this code in viewDidLoad of the the viewController where Tableview is placed.
self.tableView.tableFooterView = [[UIView alloc] init];
I meet the problem too. And it frustrated me much. After some consideration, I think it may be a feature of tableview that it show blank cell. It's reasonable to use the last cell height for the blank cell. Otherwise, what height to choose to use?
I think it's not acceptable to show black cell. If you dislike the style like me, you can try this to get rid of blank cells:
self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
or
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
//then add view for separator line by yourself
or
UITableView *tableView = [[UITableView alloc] initWithFrame:self.tableView.frame style:UITableViewStyleGrouped];
tableView.backgroundColor = self.tableView.backgroundColor;
self.tableView = tableView;
If you must show blank cell and control the height, maybe it's the simplest way/workaround to add additional cell. For example:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.dataArray == nil || self.dataArray.count == 0 ? 1 : self.dataArray + 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
if(indexPath.count == self.dataArray.count){ //the last cell
cell = [[UITableViewCell alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 50)];
} else {
cell = .... //as uausal
}
return cell;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
if(indexPath.count == self.dataArray.count){
return 50;
} else {
return xxx; //as usual
}
}
Now we can control any height !.
Hope it can help you.
You can refer to the link as well: How can I modify the appearance of 'empty' cells in plain UITableView?