How to set the width of a cell in a UITableView in grouped style
Asked Answered
M

7

110

I have been working on this for about 2 days, so i thought i share my learnings with you.

The question is: Is it possible to make the width of a cell in a grouped UITableView smaller?

The answer is: No.

But there are two ways you can get around this problem.

Solution #1: A thinner table It is possible to change the frame of the tableView, so that the table will be smaller. This will result in UITableView rendering the cell inside with the reduced width.

A solution for this can look like this:

-(void)viewWillAppear:(BOOL)animated
{
    CGFloat tableBorderLeft = 20;
    CGFloat tableBorderRight = 20;

    CGRect tableRect = self.view.frame;
    tableRect.origin.x += tableBorderLeft; // make the table begin a few pixels right from its origin
    tableRect.size.width -= tableBorderLeft + tableBorderRight; // reduce the width of the table
    tableView.frame = tableRect;
}

Solution #2: Having cells rendered by images

This solution is described here: http://cocoawithlove.com/2009/04/easy-custom-uitableview-drawing.html

I hope this information is helpful to you. It took me about 2 days to try a lot of possibilities. This is what was left.

Marimaria answered 29/3, 2010 at 15:14 Comment(1)
I think this should have been set up as a question with just the second line ("The question is:..."), then Solution #1 and Solution #2 posted as two separate answers, with one of them being accepted. Then if other users add their own answers, and one of them ends up being better, the accepted answer can be changed later.Thurlow
T
230

A better and cleaner way to achieve this is subclassing UITableViewCell and overriding its -setFrame: method like this:

- (void)setFrame:(CGRect)frame {
    frame.origin.x += inset;
    frame.size.width -= 2 * inset;
    [super setFrame:frame];
}

Why is it better? Because the other two are worse.

  1. Adjust table view width in -viewWillAppear:

    First of all, this is unreliable, the superview or parent view controller may adjust table view frame further after -viewWillAppear: is called. Of course, you can subclass and override -setFrame: for your UITableView just like what I do here for UITableViewCells. However, subclassing UITableViewCells is a much common, light, and Apple way.

    Secondly, if your UITableView have backgroundView, you don't want its backgroundView be narrowed down together. Keeping backgroundView width while narrow down UITableView width is not trivial work, not to mention that expanding subviews beyond its superview is not a very elegant thing to do in the first place.

  2. Custom cell rendering to fake a narrower width

    To do this, you have to prepare special background images with horizontal margins, and you have to layout subviews of cells yourself to accommodate the margins. In comparison, if you simply adjust the width of the whole cell, autoresizing will do all the works for you.

Thuja answered 29/3, 2010 at 15:14 Comment(16)
this is a nice and elegant solution, i did it in a category :) I'd also note that the more transparent objects, the more cpu load :)Prefiguration
@misterkoz -- a word of caution: Some iOS controls (UIDatePicker for definite) internally use UITableViewCells and break if you implement this in a category.Narcis
I was getting crazy resizing a static grouped tableview to fit a background! thank you so much!Pneumonoultramicroscopicsilicovolcanoconiosis
This seems to fail when I rotate the device. Is there any way to force cells to maintain their width and be centered inside a variable-width table?Mckinney
@Mckinney I have used this solution with all orientations supported and it doesn't cause issues with autorotation--didn't need to do anything specifically for this as far as I can remember.Narcis
I've found an interesting thing. If you swipe to delete on a row, or switch to edit mode and try do delete, the left side of the cell will be cut. For example: the modified width of the cell is set to 60, but during swipe, the width will be 60 including the delete button too, so left part of the original cell will be hide. How to fix this?Mosa
hey @Mosa with iOS 7, a lot of people are adopting the new swipe to delete you see in mail. i just added this functionality to my app using this: github.com/CEWendel/SWTableViewCell (there are others) and it seems to work with this frame solution if that helpsMccartan
If someone has trouble with this solution when swipe to delete shifts whole section of rows, try to set hardcoded width on third line. It helped me.Matejka
This method does not seem to work in iOS8 when trying to remove a cell in edit mode. The setFrame gets called when you enter/exit edit mode so the cell keeps getting shrunk. Any solution to this?Indict
@JohnBaum this might help (at least in my case it works): https://mcmap.net/q/196361/-ios-8-cell-resizing/…Lindquist
One tiny comment: be aware of the fact that setFrame is called everytime you reload your tableView or reload a part of it. So if your table is constantly reloaded, for example if you fetch data from remote server, setFrame implementation as suggested above will shift and squash your cell width at each table reload.Towney
I tried this and it appeared to work until I started to scroll the table view. The cells that were not visible before scrolling were back to the default width and the top cells that were hidden during scroll down went back to default when I scrolled back up. Is this considered to be reloading?Siret
This solution is truly godsend. In case someone is wondering how to do the same in swift - reddit.com/r/swift/comments/2fr849/…Christychristye
I am facing one problem in this approach when insert the rows or sections in tableview. Right side detail text label and accessory type is changing the origin to the left side.Constantia
This is an excellent solution. For everyone wanting to know what 'inset' is, it is the value of the 'padding' you want each side of the cell. Since I use a base class for all my cells in my app, I implemented this solution and added 'inset' as a property which can be set just after the call to dequeueReusableCellWithIdentifierEllieellinger
I tried this and the cells resized when I added a new row: the amount of empty space on the right increased. Does anyone know why?Seymour
S
17

To do this in Swift, which does not provide methods to set variables, you'll have to override the setter for frame. Originally posted (at least where I found it) here

override var frame: CGRect {
    get {
        return super.frame
    }
    set (newFrame) {
        let inset: CGFloat = 15
        var frame = newFrame
        frame.origin.x += inset
        frame.size.width -= 2 * inset
        super.frame = frame
    }
}
Sling answered 29/3, 2010 at 15:14 Comment(1)
Works for swiftBedroll
M
9

I found the accepted solution didn't work upon rotation. To achieve UITableViewCells with fixed widths & flexible margins I just adapted the above solution to the following:

- (void)setFrame:(CGRect)frame {

    if (self.superview) {
        float cellWidth = 500.0;
        frame.origin.x = (self.superview.frame.size.width - cellWidth) / 2;
        frame.size.width = cellWidth;
    }

    [super setFrame:frame];
}

The method gets called whenever the device rotates, so the cells will always be centered.

Mckinney answered 29/3, 2010 at 15:14 Comment(0)
T
9

If nothing works you can try this

Make the background colour of the cell as clear color and then put an image of the cell with required size. If you want to display some text on that cell put a label above the image. Don't forget to set the background color of the label also to clear color.

Tannin answered 29/3, 2010 at 15:14 Comment(0)
H
0

There is a method that is called when the screen is rotated : viewWillTransitionToSize

This is where you should resize the frame. See example. Change the frame coords as you need to.

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
    [coordinator animateAlongsideTransition:nil completion:^(id<UIViewControllerTransitionCoordinatorContext> context)
    {
        int x = 0;
        int y = 0;
        self.tableView.frame = CGRectMake(x, y, 320, self.tableView.frame.size.height);
    }];
}
Harless answered 29/3, 2010 at 15:14 Comment(0)
K
-2

i do it in

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell;
    CGFloat tableBorderLeft = self.view.frame.origin.x + 10;
    CGFloat tableBorderRight = self.view.frame.size.width - 20;

    CGRect tableRect = self.view.frame;
    tableRect.origin.x = tableBorderLeft; 
    tableRect.size.width = tableBorderRight; 
    tableView.frame = tableRect;
}

And this worked for me

Kummerbund answered 29/3, 2010 at 15:14 Comment(0)
W
-6

In .h file add the delegate 'UITableViewDataSource'

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{ 
    return size;
}
Winy answered 29/3, 2010 at 15:14 Comment(3)
He wants to set the width, not the heightOsterman
oh hoo. sry for inconvenience.Winy
u misunderstand the question and u answer for height and you know what its working for me because i am searching for height not for width..thanks for the answerNadanadab

© 2022 - 2024 — McMap. All rights reserved.