iOS 8: UITableViewCell detail text not correctly updating
Asked Answered
V

8

18

tl;dr: Can I cause the detailTextLabel to have its size updated by the auto layout system in iOS on a value change?

Has anyone else had issues with the detailText label within an UITableViewCell since iOS 8?

I have a table which both text and detail strings. Initially detail text is an empty string (@""). After performing a storyboard segue to an edit screen, I return with a new value for the detail text and update it. I attempt to reload the table in viewWillAppear so that the value is present immediately upon returning to the previous view.

Once the table view is visible again, the table cell has shifted the text field up to make room for the detail text, but no detail text is displayed. The text does not display until I return to the edit screen, and come back a second time.

What I've done to troubleshoot: It looks as thought the auto layout for the detail text label isn't correctly updating as I think it should, and logging the size and makeup of the detailTextLabel's frame confirms this.

I am able to force the text to update by running [table reloadData] within viewDidAppear, however that leaves me with a flicker effect I don't like, and looks unprofessional.

Edit: Additional things I've done: I've also forced the detailTextLabel to re-size itself using [cell.detailTextLabel sizeToFit]. This causes it to display, but offset in an odd way in the cell. After going to the edit page again, the detailTextLabel fixes it's position.

I've created a simple project as a github repo, to show exactly what I'm dealing with:

https://github.com/acidaris/ios8_table_issue

The main code of the view controller I'm using is also below.

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self.table reloadData]; 
}


- (NSInteger)tableView:(UITableView *)tableView
    numberOfRowsInSection:(NSInteger)section {
    return 1; 
}

- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {  
    UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"CellIdentifier"];
    
    cell.textLabel.text = @"This is a test";
    cell.detailTextLabel.text = self.value;
    
    CGRect frame = cell.detailTextLabel.frame;
    NSLog(@"detailTextLabel x=%f y=%f width=%f height=%f",
        frame.origin.x,frame.origin.y,frame.size.width,frame.size.height);
    return cell; 
}

The cell is prototyped within a StoryBoard, and so the cell selected by dequeueReusableCellWithIdentifier: is always defined. Additionally, the cell type within the storyboard is set to subtitle, and it does display if the initial value is defined.

If anyone could help me figure this out, I would be incredibly grateful.

Partial Solution

If you are sub-classing UITableViewCell, you can modify the frame for the detailTextLabel when the layout is done. This is what I've done, and it seems to have worked, but on the 6 plus, I get a weird dividing line between the textLabel and the detailTextLabel. edit: (I have adjusted for this.) I don't like this solution, but thus far it's the best I've come across. It doesn't update after presenting the view, and is relatively simple. As I said above, I will continue to look for a better solution.

- (void)layoutSubviews {
  [super layoutSubviews]; 
  CGRect textFrame = self.textLabel.frame;
  [self.detailTextLabel sizeToFit];

  CGFloat x = textFrame.origin.x;
  CGFloat y = textFrame.origin.y + textFrame.size.height;

  CGSize detailSize = self.detailTextLabel.frame.size;
  CGRect newFrame = CGRectMake(x, y, detailSize.width, detailSize.height);

  self.detailTextLabel.frame = newFrame;
}

I've also updated my Github project to reflect my current solution. Edit3: This doesn't work perfectly, as it has wrong values for truly auto layout frames, but it works for my uses for the moment.

Edit 4: I've updated my layoutSubviews function to be smarter. It will size to fix the content within the label, and position the label appropriately within the x/y coordinates in relation to the text label.

Virchow answered 23/9, 2014 at 4:53 Comment(2)
See Subtitles of UITableViewCell won't update.Emit
Look at all the work @Virchow put into this! Greatly appreciated.Slocum
H
19

Having the same problem. My solution was to call [cell layoutSubviews] before returning the cell at the end of -tableView:cellFForRowAtIndexPath. This works for me, and I didn't find it necessary to override layoutSubviews in the cell subclass.

Hygrograph answered 18/11, 2014 at 20:42 Comment(4)
Thanks @pixbug! This also worked for me in viewdidload method where I was updating a cell. I think layoutSubViews can be called immediately after updating detailTextLabel.text. code <pre><code> self.dueDate.detailTextLabel.text = [dateFormatter stringFromDate:self.movie.dueDate]; [self.dueDate layoutSubviews]; </code></pre>Municipalize
This should be the accepted solution, in my case I am using storyboard with static cells so I don't have -tableView:cellFForRowAtIndexPath. But calling [cell layoutSubviews] after I modified each cell's content make the cell visible right after view did load.Susceptible
Please DO NOT call layoutSubviews directly. If you need to update the layout of the cell immediately, call setNeedsLayout followed by layoutIfNeeded.Hom
Calling [cell layoutIfNeeded] solves the problem and works perfectly for me. You only need to call setNeedsLayout if you need to force the layout.Selfdeprecating
P
8

I have same problem with cell not updating correctly when using segue in storyboard. Tried

[setNeedsLayout] and [layoutIfNeeded]

on the view and the tableview and it is NOT working.

[self.tableView reloadData] is in viewDidAppear, as it should.

Then I tried the advice from pixbug, and it worked. But one should NOT use [layoutSubviews] directly. So I tried the ones that is adviced by the documents on the cell instead of tableView or the view.

Tried

[cell setNeedsLayout] 

but this did NOT work.

Tried

[cell layoutIfNeeded] 

this WORKED for me.

I put this before returning the cell.

Polypeptide answered 30/12, 2014 at 3:49 Comment(1)
This solved my issue, too ("UITableViewCell blank when reused" #31241749Iconium
D
2

I had the same problem in an iOS 8 storyboard (without Auto Layout enabled).

I had a Table View with a static Table View Cell at the bottom of it. In IB, I added some spaces into that table cell's content area (Style: "Right Detail"). Then in code (ViewDidLoad), I updated that cell's content with...

self.copyrightsCell.detailTextLabel.text = @"<a string>";

WITHOUT the IB spaces, the cell was INVISIBLE in portrait mode.

But WITH the initial spaces, the cell's content later appears correctly.

Hope this helps someone.

Dispose answered 2/12, 2014 at 22:48 Comment(0)
G
1

I have the same issue. I have am certain that the data is available and being assigned to the cell.detailTextLabel.text I have noticed that once a value has been assigned then there is no flicker if the value is changed on the return to the tableView. So it appears to me that there is only an issue on the first assignment of a value to the detailTextLabel.

Griggs answered 28/9, 2014 at 21:2 Comment(6)
Yeah, I'm still unable to find a good solution for this problem. If I perform a reload during viewWillAppear and viewDidAppear I can get the value to appear, but it is set after the view is shown, so the value is blank, then set. (and as you said, it's fine the first time it is displayed).Virchow
I might have a partial solution, see above edit. I don't completely like the solution, but it does update the text as I want it to.Virchow
I have found that when I scroll the table the detailTextLabel.text has been removed from the cell, even after forcing it to be loaded in viewDidAppear.Griggs
Did you try the fix solution I posted above, in the main question body (overriding layoutSubviews in a custom UITableViewCell?) It's not perfect, but it forces the label's frame to an appropriate value. You could use some experimentation to see what values you should set this to, my app only requires a length of about half the frame, so the 150.0 works for me.Virchow
In this case I am using the default UITableViewCell, so I am finding it problematic trying to override the methods.Griggs
This appears to be a duplicate of #25793574 . Workarounds are to never have non-empty text in the label (even in the storyboard), or to drop in the code patch at the above answer, which is a swizzle-fix for Apple's bug.Emit
S
1

So, I followed this tread and many others. This one lead me to what seems to be the correct answer. I hope this helps others, since this has driven me crazy since iOS 7 / 8 made some sort of changes.

My answer was to put the normal processing code in viewWillAppear and add this [self.tableview layoutSubviews] instead of [self.tableView reload data]. I think this has to do with Apple making things much more controllable in iOS 7 / 8. I struck upon that idea when reviewing some info on how cells were working.

Again, I hope this helps others resolve this annoying problem.

Seventy answered 1/3, 2015 at 23:20 Comment(0)
A
1

I had a similar problem with a static UITableView. I change a label's text and it doesn't get updated on the screen unless I clicked on the cell or did anything to force update its views. My workaround was to call after updating the text:

tableView.reloadData()

P.S This doesn't make any sense; because this is a static table view, and I don't know why it worked, but it did!

Albina answered 27/4, 2017 at 15:54 Comment(0)
P
0

I think that if you need to reloadData, that means that the table data isn't loaded when its created.

You'd just need to load your data in the viewdidload (or somewhere else BEFORE the table view gets its data ) where your tableview is, and then create the cells accordingly.

Usually, i just use an array of whatever object i'm using and then use [array objectAtIndex:indexpath.row], which you probably know about.

Also, you ante-last paragraph has an unfinished sentence that looks important.

Purifoy answered 23/9, 2014 at 7:40 Comment(2)
So, If I remove [self.table reloadData] from viewWillAppear, the first load is correct, but the update doesn't happen. If I add the reloadData to viewDidLoad, I get a flicker of old data, then new data. This doesn't seem right.Virchow
Updated the last paragraph, and additional edits to things I've done already to try and fix the issue.Virchow
S
0

Confirming the problem using Xcode 6.3.2 (6D2105) OS X Yosemite 10.10.3 I made sure that the correct value was being assigned, still first time no show, second time show. cell.layoutSubviews() seemed logical to me since it appeared as if the view lacked a refresh and adding layoutSubviews() did the trick.

Scilicet answered 12/6, 2015 at 6:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.