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;
}
}
Related
I have a table with one custom cell with one label and one textfield.Am re-using the same cell with some 5 rows. I want to display one view beneath my textfield only if user enters on the textfield. When am entering data in first textfield view should be hidden for other rows and respectively for all other rows. Default height must be 120 and when am displaying my view row height should increased to 250. Here is my code,
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 120;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"DetailsTableCell";
cell = (NewsDetailsTableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[NewsDetailsTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
}
if (indexPath.row == 0) {
cell.newView.tag = 200;
}
else if (indexPath.row == 1) {
cell.newView.tag = 201;
}
else {
cell.newView.tag = 202;
}
return cell;
}
Here new view is my UIView which am displaying on text change.
- (IBAction)textFieldChanged:(UITextField *)textField replacementString:(NSString *)string {
NSIndexPath *indexPath = [newsTable indexPathForCell:(NewsDetailsTableViewCell*)[[textField superview] superview]];
NSIndexPath *myPath = [NSIndexPath indexPathForRow:indexPath.row inSection:indexPath.section];
NewsDetailsTableViewCell *newsTableCell = (NewsDetailsTableViewCell*)[newsTable cellForRowAtIndexPath:myPath];
if (indexPath.row == 0 && newsTableCell.newView.tag == 100) {
newsTable.rowHeight = 250;
newsTableCell.newView.hidden = false;
} else if (indexPath.row == 1 && newsTableCell.newView.tag.tag == 101) {
newsTable.rowHeight = 250;
newsTableCell.newView.hidden = false;
} else {
newsTable.rowHeight = 250;
newsTableCell.newView.hidden = false;
}
}
Am getting my view in my text change but row height is not getting changed. Also It still showing 120 height.
Also I tried this,
NSArray* rowsTobeReloaded = [NSArray arrayWithObjects:indexPath, nil];
[newsTable reloadRowsAtIndexPaths:rowsTobeReloaded withRowAnimation:UITableViewRowAnimationNone];
But it didn't work. Should I re-load my tableview again in text change? or am I missing something?
You have to modify the logic in the below method:
The selectedRowNum can be modified as per the row in which the textField is to be shown.
Also, cellForRowAtIndexPath is not used for setting the height of rows in a tableview. Everytime, we call the reloadData or reloadIndexes the heightForRowAtIndexPath method is called.
YOu need to change height in
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (usingNewView){
return 250;
}
return 120;
}
And set logic for usingNewView in
- (IBAction)textFieldChanged:(UITextField *)textField replacementString:(NSString *)string
In my app I display a list of items for sale in a UITableView. If the user didn't set a description on an item, I would like to adjust the positioning of some of the elements in the cell to adjust for the blank label.
The following code in my cellForRowAtIndexPath isn't working.
cell.productName.text = product.fields[#"title"];
cell.productPrice.text = product.fields[#"price"];
cell.productDescription.text = vehicle.fields[#"salespersonComments"];
if ([cell.productDescription.text isEqualToString:#""]) {
CGRect f = cell.favoriteButton.frame;
f.origin.y = 10; // new y
cell.favoriteButton.frame = f;
} else {
}
//You have to use the UITableView delegate methods
//Method for displaying the cell
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier];
}
}
//Method for adjusting the cell height
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 80;
}
Rather than physically changing the coordinated/frame of the item, it would be better to have IBOutlets connected to the relevant NSLayoutConstraints.
Then you could do something like:
if ([cell.productDescription.text isEqualToString:#""]) {
cell.favoriteButtonWidthConstraint.constant = 0;
cell.leadingIndentConstraint.constant = 0;
} else {
}
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?
I have a custom table view & cell where a cell is expanded when selected. It is now functioning properly and accordingly. However, When I select cells to expand them, it takes about half a second to respond. The code below is located in the didSelectRowAtIndexPath. My hypothesis is that between beginUpdates and endUpdates, there are too many things going on to increase the height of the original cell and then updating the whole table view. Is there another way I can better implement this?
**[_tableView beginUpdates];**
ReviewTestTableCell *reviewCell1 = (ReviewTestTableCell *)[tableView cellForRowAtIndexPath:indexPath];
CGRect rect = CGRectMake(reviewCell1.review.width, 900, reviewCell1.review.width, 900);
CGRect textRectb = [reviewCell1.review textRectForBounds:rect limitedToNumberOfLines:1000];
float labelHeight = textRectb.size.height;
reviewCell1.review.height = labelHeight;
expandHeight = labelHeight + 75 ;
if ([[userdefaults objectForKey:#"merchantType"] isEqual:#"T"])
{reviewCell1.height = labelHeight + 50;
reviewCell1.bottomRatingView.height = reviewCell1.bottomRatingView.height - 20;
}
else
{
reviewCell1.height = labelHeight + 75;}
reviewCell1.bottomRatingView.hidden = NO;
reviewCell1.bottomRatingView.top = reviewCell1.review.bottom;
**[_tableView endUpdates];**
[_isExpandList replaceObjectAtIndex:indexPath.row withObject:#1];
}
EDIT/ADD:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell3 = [self tableView:tableView cellForRowAtIndexPath:indexPath];
if ([_isExpandList[indexPath.row] isEqual: #1] && [[_dataList objectAtIndex:indexPath.row] valueForKey:#"Item1Score"] != nil) {
return cell3.height + 3 + 65;
}
else if ([_isExpandList[indexPath.row] isEqual: #0])
{
return cell3.height +5;
}
else return cell3.height +3;
}
You should not go through that elaborate dance of trying to manually expand the cell. You should certainly not manually call willDisplayCell.
Using the method described in the answer to this question, you should have a property or something to keep track of which cell was selected and make your heightForRowAtIndexPath: method adjust for that particular indexPath, then just call
[tableView beginUpdates];
[tableView endUpdates];
Which will call heightForRowAtIndexPath for every cell, which your method will give a larger height for when it matches the selected row. The tableView will smoothly adjust the height of your cell.
Something similar to:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([self.selectedIndexPath isEqual:indexPath]) {
return 80.0f;
}
return tableView.rowHeight;
}
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;
}