how do you increase the padding for a UITableView - ios

When I increase the size of a UITableViewCell, the section headers styling becomes messy. The code I am using, was suggested in "How to set the width of a cell in a UITableView in grouped style" and is as follows:
I have inherited from UITableViewCell and created this class:
#implementation UITableViewCellHistory {
}
-(bool) isIPAD
{
return UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad;
}
- (void)setFrame:(CGRect)frame {
if ([self isIPAD])
{
NSInteger inset = 40;
frame.origin.y += inset;
frame.size.height -= 2 * inset;
[super setFrame:frame];
}
}
#end
then in my class with the table view in it I have used the above class as my cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == [data numberOfDaysWithData])
{
NSAssert([data hasMore], #"how come we have more sections than days when more is low?");
UITableViewCell *cell = [[UITableViewCellHistory alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
But when I increase the size of the padding further I the headings in the tableview get overlapped by rows. Am I missing something?

Related

iOS UITableView scroll position jumps when scrolling up

Here's what I'm trying to do.
I have a UITableViewCell lets say with fixed height of 300 (it is actually a variable size height but I'm trying to simplify the example)
What I want to achieve is that when I scroll back up - I will have a "thumbnailed" version of the cell - with height of 75
I managed to make it happen, but now the problem is that when I scroll up the previous cell heights are adjusted and the scroll position "jumps" once the cell sizes are smaller, which causes the view to "jump back down" when he scrolls up.
How can I adjust it?
The code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell;
if (indexPath.row < lastViewedChapter)
{
cell = [self generateChapterCell:tableView indexPath:indexPath collapsed:YES];
}
else
{
cell = [self generateChapterCell:tableView indexPath:indexPath collapsed:NO];
if (indexPath.row > lastViewedChapter)
{
lastViewedChapter = indexPath.row;
}
}
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row < lastViewedChapter)
{
return 73;
}
else
{
return 300; //actually here is a code that calculates the height
}
}
You've reduced height of the upper cell and then other cells moved up to fill that space while you were still scrolling right?
Try to set new tableView.contentOffset when you change the cell's height.
In your case the contentOffset.y should be (old contentOffset.y - (300 - 73)) when you return the cell's height as 73.
I didn't test on this but I think it may help and you must calculate new contentOffset for other case too (when scroll down, when table reload data).
static NSInteger _lastRow = -1;
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (_lastRow == -1) {
_lastRow = indexPath.row;
return 300;
} else {
if (_lastRow > indexPath.row) {
_lastRow = indexPath.row;
if ([tableView rectForRowAtIndexPath:indexPath].size.height == 300) {
[tableView setContentOffset:CGPointMake(tableView.contentOffset.x, (tableView.contentOffset.y - (300 - 73)))];
}
return 73;
} else {
_lastRow = indexPath.row;
return 300;
}
}
}
This code work fine but still has some bugs (the first row height when first load data is like you have scroll up to it once, when you scroll up to top fast it bounced not normally) but I hope this should help you.
This is something that will definitely happen since you have changed cell heights.
The question is how to mitigate this kind of bad user experience.
UITableView are subclassed from UIScrollView. UIScrollViews provide delegate which is available in UITableView class as well.
Do the following.
self.tableView.delegate = self;
And then implement the following function. In the following, location is a CGPoint variable defined in your header.
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
location = tableView.contentOffset;
}
-(void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
CGPoint newLocation = tableView.contentOffset;
if (CGPointEqualToPoint(location, newLocation))
{
NSLog(#"are equal");
tableView.contentOffset = CGPointMake(location.x, location.y-227);
}
}

Layout constraints changing after scroll

So i have a tableview with N amount of rows. The tableview is designed like a swimming track. Each row has a swimmer image and the swimmer's X location is based upon their position in the race.
The UITableViewCell has been designed with IB and using Autolayout Constraints.
Each cell swimmer gets their position from an index of an array, ie...
// Method - cellForRowAtIndexPath:
cell.swimmerLeading.constant = [_array[indexPath.row] integerValue]
Whenever i scroll, the swimmerLeading.constant gets reset back to its original value of 8;
Any clues as to why?
- (void)viewDidLoad {
...
[self registerNib:[UINib nibWithNibName:#"RacePredictorTrackTableViewCell" bundle:nil] forCellReuseIdentifier:#"TrackCell"];
...
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == TableSectionTrack) {
RacePredictorTrackTableViewCell *cell = (RacePredictorTrackTableViewCell *)[tableView dequeueReusableCellWithIdentifier:#"TrackCell" forIndexPath:indexPath];
cell.delegate = self;
cell.indexPath = indexPath;
Runner *runner = _runners[indexPath.row];
cell.isStarred = NO;
cell.cellPositionlabel.text = runner.predictorPlace;
cell.cellPercentageFloat = runner.predictorRanking.floatValue;
return cell;
}
}
cell.cellPercentageFloat is just a setter method that sets the value to two constraints, swimmer and label for swimmer
RacePredictorTrackTableViewCell.m
- (void)setCellPercentageFloat:(CGFloat)cellPercentageFloat {
cellPercentageFloat = cellPercentageFloat / 100;
CGFloat labelWidth = _cellHorseLabel.intrinsicContentSize.width;
CGFloat labelRaceLineBuffer = 8;
_horseImageViewLeading.constant = (self.totalSpace * cellPercentageFloat) + kOriginalHorseImageViewLeading;;
if ((_horseImageViewLeading.constant + _horseImageViewWidth.constant) > labelWidth) {
_horseLabelLeading.constant = (_horseImageViewLeading.constant + _horseImageViewWidth.constant) - labelWidth - labelRaceLineBuffer;
} else {
_horseLabelLeading.constant = _horseImageViewLeading.constant;
}
}

UITableViewCell Frame is not according to given width and Height

I am setting the width and height of UITableViewCell in the delegate methods but the when i check the cell frame in cellForRowAtIndexPath method then its totally different. Below is my Code. I am doing every thing programatically.
self.myTable = [[UITableView alloc] initWithFrame:CGRectMake(self.view.frame.size.width * 0.05,self.view.frame.size.height * 0.08,self.view.frame.size.width * 0.90,
(self.view.frame.size.height * 0.90) - tabBarHeight ) style:UITableViewStylePlain];
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
return 1.0;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return 5;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
if(indexPath.row == 0 || indexPath.row == 4){
return self.myTable.frame.size.height * 0.08;
}
return (self.myTable.frame.size.height * 0.28);
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
if(!cell){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
}
if(indexPath.row != 4){
cell.userInteractionEnabled = NO;
}
switch (indexPath.row) {
case 0:{
NSLog(#"0th cell Frame : %#", NSStringFromCGRect(cell.frame));
break;
}
case 1:{
NSLog(#"1st cell Frame : %#", NSStringFromCGRect(cell.frame));
break;
}
case 2:{
NSLog(#"2nd cell Frame : %#", NSStringFromCGRect(cell.frame));
break;
}
case 3:{
NSLog(#"3rd cell Frame : %#", NSStringFromCGRect(cell.frame));
break;
}
case 4:{
NSLog(#"4rth cell Frame : %#", NSStringFromCGRect(cell.frame));
break;
}
default:
break;
}
return cell;
}
The output for the cell frame i am getting is {{0, 0}, {320, 44}}. But this cell frame is not correct. The Height should be 129 and the width should be something like 288. Can some one guide me what i am doing wrong here?
However, if you still need your given (predefined) size for your cell, you can always use,
For width,
With case of a custom cell, you can make a method like this,
- (CGFloat)cellWidth {
return [UIScreen mainScreen].bounds.size.width;
}
Without custom cell, you can always make a function within the same class where you're creating the UITableView,
- (CGFloat)cellWidth {
return yourTableView.frame.size.width;
}
which will return exact size of your table cell. (I'm always prefer second method because sometime you just don't create device width cells!)
For height,
With case of custom cell, you can make a class method like this,
+(CGFloat)myCellHeight {
return 45.f;
}
Without custom cell, you can always make a function within the same class where you're creating the UITableView,
-(CGFloat)myCellHeight {
return 45.f;
}
which you can use in your UIViewController, with cell class name (for custom cell), [cellClassName myCellHeight] or [self myCellHeight] (default cell in UIViewController) in table's - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath datasource method.
These ways you'll have your cells exact width/height, whenever you want ! :)
Nothing, your code is ok. Your cells will have the size you want. The problem is your cells don't know them final size in this methods cellForRowAtIndexPath. In this methods they think, they have the default size. But before they appear they will be resized. If you define your cells by code, you need use de dimension you expect,this is the tableView width and the height of the methods (heightForCell or estimateHeightForCell). You can check this, by comment this line, and pressing in the delegate: (also you can see).
Line to comment to check:
....
if(indexPath.row != 4){
// cell.userInteractionEnabled = NO;
}
.....
Implement to check:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
NSLog(#"cell pressed Frame : %#", NSStringFromCGRect(cell.bounds));
}
- (void)viewDidLoad
{
self.myTable = [[UITableView alloc] initWithFrame:CGRectMake(self.view.frame.size.width * 0.05,self.view.frame.size.height * 0.08,self.view.frame.size.width * 0.90, // set the frame for your tableview
(self.view.frame.size.height * 0.90) - tabBarHeight ) style:UITableViewStylePlain];
self.myTable.delegate = self;
self.myTable.dataSource = self;
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}

Change width of TableView or TableViewCell

Hey everybody :) i try to change the width of my cells so there is a little space between the cells and the tableview border. I tried everything i could found here on stackoverflow, but without success.
I created the tableview with the interface builder, so first i simple tried to set the tableview size to "freeform" and dragged the width to 300.0f, but nothing happenend. Than i tried to do it programmatically in my "viewDidLoad" with:
self.tableView.frame = CGRectMake(10.0f, self.tableView.frame.origin.y, 300.0f, self.tableView.frame.size.height);
but here also nothing happens.... than i tried to change the cells directly with:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
GTNewsCustomCell *newsCell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier1];
newsCell.contentView.frame = CGRectMake(10.0f, 0, 300, newsCell.frame.size.height);
}
but same Problem here....any ideas what I missing?
EDIT: Another Solution for this Problem is to change the frame of the Custom Cell with:
- (void)setFrame:(CGRect)frame {
frame.origin.x += inset;
frame.size.width -= 2 * inset;
[super setFrame:frame];
}
just try this
in your custom cell put a property like
in .h file
#interface GTNewsCustomCell : UITableViewCell
#property (nonatomic, assign)CGRect cellFrame;
in .m file
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
// Initialization code
self.backgroundColor = [UIColor greenColor];//for testing purpose only
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
//automatically called
- (void)layoutSubviews
{
[super layoutSubviews];
CGRect cellRect = self.bounds;
cellRect.size.width = self.cellFrame.size.width;
self.bounds = cellRect;
}
.in .m of viewController
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
GTNewsCustomCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if(cell == nil)
{
cell = [[GTNewsCustomCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
}
cell.cellFrame = CGRectMake(10, 0, tableRect.size.width,40);//hear tableRect is the frame of your tableview
return cell;
}
not sure try this hope this helps u
For this, first of all you can take an UIImageView to cover your full view and set its image as a bordered image. Now add a table view on this imageview with making width so as the borders of this image is visible.
I think you want dynamic Height for the Tableviewcell instead of width.
Delegate method of UITableView will help on this:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
Which will return height of every cell. You can implement it as following sample code. This is showing dynamic height on the basis of dynamic text content.
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
//set width depending on device orientation
self.cellPrototype.frame = CGRectMake(self.cellPrototype.frame.origin.x, self.cellPrototype.frame.origin.y, tableView.frame.size.width, self.cellPrototype.frame.size.height);
CGFloat quotationLabelHeight = [self sizeOfLabel:self.cellPrototype.quotationLabel withText:[self quotationTextForRow:indexPath.row]].height;
CGFloat attributionLabelHeight = [self sizeOfLabel:self.cellPrototype.attributionLabel withText:[self attributionTextForRow:indexPath.row]].height;
CGFloat padding = self.cellPrototype.quotationLabel.frame.origin.y;
CGFloat combinedHeight = padding + quotationLabelHeight + padding/2 + attributionLabelHeight + padding;
CGFloat minHeight = padding + self.cellPrototype.avatarButton.frame.size.height + padding;
return MAX(combinedHeight, minHeight);
}
You can try with this too.
Use this:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;
delegate method of UITableView and return a float value (CellHight+space bw cells).

Retrieve custom prototype cell height from storyboard?

When using "Dynamic Prototypes" for specifying UITableView content on the storyboard, there is a "Row Height" property that can be set to Custom.
When instantiating cells, this custom row height is not taken into account. This makes sense, since which prototype cell I use is decided by my application code at the time when the cell is to be instantiated. To instantiate all cells when calculating layout would introduce a performance penalty, so I understand why that cannot be done.
The question then, can I somehow retrieve the height given a cell reuse identifier, e.g.
[myTableView heightForCellWithReuseIdentifier:#"MyCellPrototype"];
or something along that line? Or do I have to duplicate the explicit row heights in my application code, with the maintenance burden that follows?
Solved, with the help of #TimothyMoose:
The heights are stored in the cells themselves, which means the only way of getting the heights is to instantiate the prototypes. One way of doing this is to pre-dequeue the cells outside of the normal cell callback method. Here is my small POC, which works:
#import "ViewController.h"
#interface ViewController () {
NSDictionary* heights;
}
#end
#implementation ViewController
- (NSString*) _reusableIdentifierForIndexPath:(NSIndexPath *)indexPath
{
return [NSString stringWithFormat:#"C%d", indexPath.row];
}
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(!heights) {
NSMutableDictionary* hts = [NSMutableDictionary dictionary];
for(NSString* reusableIdentifier in [NSArray arrayWithObjects:#"C0", #"C1", #"C2", nil]) {
CGFloat height = [[tableView dequeueReusableCellWithIdentifier:reusableIdentifier] bounds].size.height;
hts[reusableIdentifier] = [NSNumber numberWithFloat:height];
}
heights = [hts copy];
}
NSString* prototype = [self _reusableIdentifierForIndexPath:indexPath];
return [heights[prototype] floatValue];
}
- (NSInteger) tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 3;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString* prototype = [self _reusableIdentifierForIndexPath:indexPath];
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:prototype];
return cell;
}
#end
For static (non-data-driven) height, you can just dequeue the cell once and store the height:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSNumber *height;
if (!height) {
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:#"MyCustomCell"];
height = #(cell.bounds.size.height);
}
return [height floatValue];
}
For dynamic (data-driven) height, you can store a prototype cell in the view controller and add a method to the cell's class that calculates the height, taking into account the default content of the prototype instance, such as subview placement, fonts, etc.:
- (MyCustomCell *)prototypeCell
{
if (!_prototypeCell) {
_prototypeCell = [self.tableView dequeueReusableCellWithIdentifier:#"MyCustomCell"];
}
return _prototypeCell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Data for the cell, e.g. text for label
id myData = [self myDataForIndexPath:indexPath];
// Prototype knows how to calculate its height for the given data
return [self.prototypeCell myHeightForData:myData];
}
Of course, if you're using custom height, you probably have multiple cell prototypes, so you'd store them in a dictionary or something.
As far as I can tell, the table view doesn't attempt to reuse the prototype, presumably because it was dequeued outside of cellForRowAtIndexPath:. This approach has worked very well for us because it allows the designer to modify cells layouts in the storyboard without requiring any code changes.
Edit: clarified the meaning of sample code and added an example for the case of static height.
I created a category for UITableView some time ago that may come helpful for this. It stores 'prototype' cells using asociated objects for reusing the prototypes and provides a convenience method for obtaining the height of the row assigned in storyboard. The prototypes are released when the table view is deallocated.
UITableView+PrototypeCells.h
#import <UIKit/UIKit.h>
#interface UITableView (PrototypeCells)
- (CGFloat)heightForRowWithReuseIdentifier:(NSString*)reuseIdentifier;
- (UITableViewCell*)prototypeCellWithReuseIdentifier:(NSString*)reuseIdentifier;
#end
UITableView+PrototypeCells.m
#import "UITableView+PrototypeCells.h"
#import <objc/runtime.h>
static char const * const key = "prototypeCells";
#implementation UITableView (PrototypeCells)
- (void)setPrototypeCells:(NSMutableDictionary *)prototypeCells {
objc_setAssociatedObject(self, key, prototypeCells, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSMutableDictionary *)prototypeCells {
return objc_getAssociatedObject(self, key);
}
- (CGFloat)heightForRowWithReuseIdentifier:(NSString*)reuseIdentifier {
return [self prototypeCellWithReuseIdentifier:reuseIdentifier].frame.size.height;
}
- (UITableViewCell*)prototypeCellWithReuseIdentifier:(NSString*)reuseIdentifier {
if (self.prototypeCells == nil) {
self.prototypeCells = [[NSMutableDictionary alloc] init];
}
UITableViewCell* cell = self.prototypeCells[reuseIdentifier];
if (cell == nil) {
cell = [self dequeueReusableCellWithIdentifier:reuseIdentifier];
self.prototypeCells[reuseIdentifier] = cell;
}
return cell;
}
#end
Usage
Obtaining the static height set in storyboard is as simple as this:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return [tableView heightForRowWithReuseIdentifier:#"cellIdentifier"];
}
Assuming a multi-section table view:
enum {
kFirstSection = 0,
kSecondSection
};
static NSString* const kFirstSectionRowId = #"section1Id";
static NSString* const kSecondSectionRowId = #"section2Id";
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGFloat height = tableView.rowHeight; // Default UITableView row height
switch (indexPath.section) {
case kFirstSection:
height = [tableView heightForRowWithReuseIdentifier:kFirstSectionRowId];
break;
case kSecondSection:
height = [tableView heightForRowWithReuseIdentifier:kSecondSectionRowId];
}
return height;
}
And finally if the row height is dynamic:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
id thisRowData = self.allData[indexPath.row]; // Obtain the data for this row
// Obtain the prototype cell
MyTableViewCell* cell = (MyTableViewCell*)[self prototypeCellWithReuseIdentifier:#"cellIdentifier"];
// Ask the prototype cell for its own height when showing the specified data
return [cell heightForData:thisRowData];
}

Resources