Is it possible to create a custom UITableViewCell that resizes depending on the length of the text that I want to put into? If possible, how do I do this?
#EquinoX yes it is possible through heightForRowAtIndexPath delegate....Please have a look on this and Dynamic Height UITableViewCell they have same thing you are asking.
Good Luck!
Absolutely,
I like to set all the data on my cell by passing it a model (just an NSObject with data in it). Then I create a custom cell that can have its data set by the model, and I add a class level function to return the size. It looks like this.
TableViewDelegate:
-(float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
XTableViewCellModel *model = [self modelForIndexPath:indexPath];
return [XTableViewCell cellHeight:model];
}
XTableViewCell:
+(CGFloat)cellHeight:(XTableViewCellModel*)model {
CGSize titleSize = [model.title sizeWithFont:model.titleFont
constrainedToSize:CGSizeMake(280, 9999)
lineBreakMode:UILineBreakModeTailTruncation];
return titleSize.height;
}
Note that setting lineBreakMode to UILineBreakModelTailTruncation only takes effect after the height of 9999 points is reached. Until then the text wraps normally.
Check out my open source framework around this stuff. It has a lot of resizable cell types built by default: https://github.com/andrewzimmer906/XCell
Look at this Sample Code it uses exactly what you looking for.
Related
If you want to to pack the cells neatly together even if some of the other content in another cell is bigger, how do one do that?
If you look at the picture below, the first string is the biggest, but if I want to pack the other cells as neatly and tightly together as the first one, regardless of the size, how is this done?
I want to do this regardless of the size both from the sides and up and down.
I want the cells to be laid out as the arrows in this picture are pointing.
UPDATE: the first line of text is two cells packed togheter.
You have to use UICollectionViewFlowLayout to achieve this
Note: Below information's are taken from Raywenderlich blog
You can subclass UICollectionViewLayout to create your own custom layouts , but Apple has graciously provided developers with a basic “flow-based” layout called UICollectionViewLayout. It lays elements out one after another based on their size, quite like a grid view. You can use this layout class out of the box, or subclass it to get some interesting behavior and visual effects.
You can see a good example in Raywenderlich blog
Example code:
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
NSString *searchTerm = self.searches[indexPath.section]; FlickrPhoto *photo =
self.searchResults[searchTerm][indexPath.row];
CGSize retval = photo.thumbnail.size.width > 0 ? photo.thumbnail.size : CGSizeMake(100, 100);
retval.height += 35; retval.width += 35; return retval;
}
- (UIEdgeInsets)collectionView:
(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section {
return UIEdgeInsetsMake(50, 20, 50, 20);
}
There are more delegate methods you can implement than this. Examples are given below
collectionView:layout:sizeForItemAtIndexPath
collectionView:layout:insetForSectionAtIndex:
Two UITableViewCell related questions:
In my custom UITableViewCell I loop through an array (of which I do not know how many objects it holds) and add a UILabel displaying some text for each object in that array.
This means I have to adjust the height of the cell so that these labels fit in. How can I do this?
When going into edit mode, I have the cells indent, however I do not want this. I have tried the following:
cell.shouldIndentWhileEditing = NO;
and
-(BOOL)tableView:(UITableView *)tableView shouldIndentWhileEditingRowAtIndexPath:(NSIndexPath *)indexPath
{
return NO;
}
Both sadly failed, I have I no idea why. How could I possible remedy this?
Any help is much appreciated with either of these issues, thanks.
You can specify the height for every row with the delegate method [UITableViewDelegate tableView:heightForRowAtIndexPath:].
Just change what the method returns and the reload your table.
This method actually has nothing to do with the indendation of cell content:
Asks the delegate whether the background of the specified row should be indented while the table view is in editing mode.
You can try to set indentationWidth but I never managed to make it work.
Fortunately, it's easy to change everything you want in [UITableView layoutSubviews] method.
Example:
- (void)layoutSubviews {
[super layoutSubviews];
self.contentView.frame = self.bounds;
}
You may also need to set
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath;
{
return UITableViewCellEditingStyleNone;
}
For the dynamic height part there are plenty of other answers and a quick google found a tutorial here
I have a custom cell for my UITableView. I have put a swipe left right/left observer on my cell. In which that if a user swipes on a cell it will call a function. This function basically creates a UIView that needs to be added to the cell.
I would like my code to use a MVC best practice. So I think what is the appropriate thing to do is to pass in the UIView to the custom cell and let my implementation of my custom cell add it to the cell. In my custom cell I have a property of UIView as well.
The issue is that I will need to adjust the height of the cell as well. Now the method that I have is:
+ (CGFloat)tableView:(UITableView *)tableView rowHeightForObject:(id)item {
}
this is similar to what a regular heightForRowAtIndexPath, instead of index path it's object. In this method I need to determine whether there is a UIView that needs to be added or not. If there is, the height needs to be adjusted based on that. I can't seem to get my head around this. In this method I can't do self.optionsView, or access any of the property in my custom cell subclass. So how do I check whether the options is added or not?
You can add a rowHight property to your custom cell class, and add another height var to your table view datasource data.
Every time you change the custom cell, make sure to set the height property, and assign it to your data source data in the right position.
Then, inside heightForRowAtIndexPath just ask for the height data.
Hope it's clear.
As Idan pointed out it's good to just ask cell for it's content size. I would just use the standard heightForRowAtIndexPath method like this:
//Assuming myCustomCell contains a placeholder for my additional view called innerView.
//If the place holder is nil I don't need to adjust cells height.
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
//Get the cellPath and myCustomCell at this path
NSIndexPath *rowPath = [NSIndexPath indexPathForRow:row inSection:0];
MyCustomTableCell *myCustomCell = (MyCustomTableCell*)[self.table cellForRowAtIndexPath:rowPath];
if(myCustomCell.innerView != nil)
return myCustomCell.innerView.frame.size.height + someDefaultCellHeight;
else
return defaultCellHeight;
}
The standard Grouped UITableView style allows UITableViewCells to be drawn with rounded corners at the top and bottom of each section. How is this accomplished? How does the cell know its own location within its section, and how does it know when to change its rounded edges?
I want to make my own rounded cells, and I have images to use, but don't know when to show which image
Note: I already know how the UITableView works, and I know how to use it. I just thought that since a UITableView is able to automatically draw rounded corners at the correct places, I should be able to as well, without needing to add anything to my data source or delegate.
NSIndexPath *indexPath = [(UITableView *)self.superview indexPathForCell: self];
int rows = [(UITableView *)self.superview numberOfRowsInSection:indexPath.section];
if (indexPath.row == 0 && rows == 1) {
// the one and only cell in the section
}
else if (indexPath.row == 0) {
//top
}
else if (indexPath.row != rows - 1) {
//middle
}
else {
//bottom
}
It's very simple. suppose cell is the object, whose position is to be found out.
UITableView* table = (UITableView *)[cell superview];
NSIndexPath* pathOfTheCell = [table indexPathForCell:cell];
NSInteger sectionOfTheCell = [pathOfTheCell section];
NSInteger rowOfTheCell = [pathOfTheCell row];
There is sectionLocation method of UITableViewCell that returns integer telling you what you need:
1 - middle cell
2 - top cell
3 - bottom cell
4 - single cell
I had no issues using this in several production apps since 2010.
UPDATE: one of our binaries was automatically rejected recently (end of 2018) because we were using 'sectionLocation' property, so it's not a good option anymore.
Add something like this into your header files and you can use it:
typedef NS_ENUM(NSInteger, MMMTableViewCellLocation) {
MMMTableViewCellLocationUndefined = 0,
MMMTableViewCellLocationMiddle = 1,
MMMTableViewCellLocationTop = 2,
MMMTableViewCellLocationBottom = 3,
MMMTableViewCellLocationSingle = 4
};
#interface UITableViewCell ()
/** Undocumented method of UITableViewCell which allows to know where within section the cell is located,
* so the cell can draw its borders properly. */
- (MMMTableViewCellLocation)sectionLocation;
/** Override this one to know when the value of sectionLocation changes. */
- (void)setSectionLocation:(MMMTableViewCellLocation)sectionLocation animated:(BOOL)animated;
#end
You can use
- (NSIndexPath *)indexPathForCell:(UITableViewCell *)cell
for this issue. In my example I am using this to scroll the cell (with custom content) to the top of the view.
If you need more robust and general stuff, take a look at http://cocoawithlove.com/2009/04/easy-custom-uitableview-drawing.html - Matt Gallagher shows what you need, pretty effectively. He basically recreates UITableViewController from UIViewController, while adding ability to use your own custom graphics. I'm just working on applying this to one my projects, so far it looks it would do the job.
Unfortunately, I have found no solution to this problem, and have resorted to subclassing UITableViewController and UITableViewCell into a generic solution that I can extend as necessary.
You don't do this in cell. Rounded corners are drawn in [tableView viewForHeaderInSection] and viewForFooterInSection.
The way I do it is to use Plain tableview style, then use these two views for roundness and cells are normal, no rounds.
Without getting into who draws what, you can know which cell is the last cell in its section inside of cellForRowAtIndexPath very easily.
You're passed in the indexPath of the cell you need to provide, right? You're also passed the tableView.
call [tableView numberofRowsInSection:indexPath.section] and if it's == ([indexPath.row]-1) you know you're being asked to supply the last cell in that section.
At the time that cellForRowAtIndexPath is being called, the cell is guaranteed to be at the indexPath passed in.
To expand upon Darren's answer (which I found most useful, thanks Darren!), what you can do is to iterate through all of the superviews' until you find the parent UITableView. This should be future proof since you do not rely on a fixed hierarchy of views.
I use a recursive method that will return the UITableView if it finds one or return nil if there is none.
- (UITableView *)parentTableViewOf:(UIView *)view {
Class class = [view.superview class];
NSLog(#"Class : %#", NSStringFromClass(class));
if([view.superview isKindOfClass:[UITableView class]]) {
return (UITableView *)view.superview;
} else {
return [self parentTableViewOf:view.superview];
}
return nil;
}
So far I've used this one and it seems to work without hiccups. Hope it helps! :)
The cells dont know where they go...The table view has cells, You are the one telling the table view WHAT goes in the cell. You do this in the DataSource where you implement cellForRowAtIndexPath...The way this works :
An index path has a row and a section
For a grouped table view
A section pertains to a group, and a row pertains to 1 entry in that section,
the way UITableView knows how many rows are in a section and how many sections there are is the DataSources methods numberOfSectionInTableView and the method numberOfRowsInSection, this will make the right calls to cellForRowAtIndexPath, it is up to you to recognize which section and row is being queried and you need to build your cell according to these specifications.
A good way to do this i s you can have a Dictionary with keys of section names and values of NSArray with the values that go in that section.
So you implementation for numberOfSectionsInRows would look like
return [[dictionary allKeys] count]
And the implmentation of numberOfRowsInSection would look like
NSString* key=[[dictionary allKeys] objectAtIndex:sectionNumber]
return [[dictionary objectForKey:key] count]
You can always refer to the UITableView programming guide at http://developer.apple.com/iphone/library/documentation/UserExperience/Conceptual/TableView_iPhone/Introduction/Introduction.html
Hope that helps
Simply add a property to your custom UITableViewCell (depending on implementation) class that contains an int, NSNumber, or an NSIndexPath specifying which one it is. In you're using a data structure instead, then put it in you element in that data structure. Then you simply set the property when you create the data structure, something like elt.id=i, and then you access it in the cellForRowAtIndexPath, something like if (elt.id == 0 || elt.id == n-1) where n is the number of rows in your section.
I might have totally missed your question, but if I did, just comment and I'll post again.
I am setting the row height of my UITableView using following code
[tableView setRowHeight: 100.00];
I am using the single line as separator in the UITableView.
Eventhough setting the height above, height of row does not get change.
You should implement
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
delegate method. and return 100.0 there.
You should avoid the heightForRowAtIndexPath if all your rows are of similar height and use the rowHeight property. According to the documentation:
There are performance implications to using tableView:heightForRowAtIndexPath: instead of rowHeight. Every time a table view is displayed, it calls tableView:heightForRowAtIndexPath: on the delegate for each of its rows, which can result in a significant performance problem with table views having a large number of rows (approximately 1000 or more).
In the UITableViewController subclass it could be done, for instance, in the viewDidAppear method (the UITableViewController has a reference to the tableView):
self.tableView.rowHeight = 79.f;
The row height is baked into the cells when they are first displayed.
Did you set UITableView#rowHeight before setting the data source?
If not, do so.
If for whatever reason you can't, your other option is to call UITableView#reloadData after setting the row height.
I did like this, here tableobj is nothing but UITableView object in my application.
- (void)viewDidLoad
{
[super viewDidLoad];
[tableObj setRowHeight:100.0f];
}
Or handle it in numberOfRowsInSection: like:
- (NSInteger)tableView:(UITableView *)tblView numberOfRowsInSection:(NSInteger)section {
[tableObj setRowHeight:100.0f];
return [soandso count]; // soandso is my object
}
Because set the rowHeight before setting the data source. It worked for me (for equal row heights).
The better and cleaner solution is to implement the delegate function (Maybe not the best one if your UITableView has many rows ...).
Also, think that UITableVieCell are UIView so you could change their height firstly...
Delegates are very powerful in iPhone dev, here the UITableViewDelage:
http://developer.apple.com/iphone/library/documentation/uikit/reference/UITableViewDelegate_Protocol/Reference/Reference.html
You can also change the indentForRow, displayCellForRow, heightForRow,edit stuff .....
I dont think there is such a method in UITableView...
Instead you can use the property rowHeight...
Try,
tableView.rowHeight =100;