Self Sizing Cells make UITableView jump - ios

I've got a UITableView, each cell has an image and two labels, as you can see on the first picture
So I am trying ti use self-sizing cells and everything is great, except 2 things:
1) First and Second cell don't show content properly, but after I scroll down and then return to them everything is ok. I tried to use [tableview reloadData] in viewDidAppear, but it doesn't help. Watch the labels at first they don't fit. You can watch it on this video https://www.youtube.com/watch?v=I9DGBl1c5vg
Look at the labels on the first cell.
2) It's a tough one. I'm scrolling the table and everything is great, but the problem happens, when I select the row. It's pushing me to detail view, but when I press "back" and return to master view, the tableview jumps,so I'm coming back not to the selected row, also if I scroll the table it doesn't scroll smooth, but jumps. Here are the links for two videos, first one shows that scroll is smooth https://www.youtube.com/watch?v=9wAICcZwfO4 if i don't go to detail view, and the second shows the jumping problem https://www.youtube.com/watch?v=fRcQ3Za1wDM .
It's absolutely sure connected with self sizing cells, because if I don't use it, none of this problem happens.

Okay, I solved both of the problems.
Question 1
I've added these two lines
[cell.contentView setNeedsLayout];
[cell.contentView layoutIfNeeded];
in
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath before return cell
Question 2
The solution appeared to be very simple. I've just needed to implement viewWillDissapear and reload data in it, so the code looks like this
- (void)viewWillDisappear:(BOOL)animated;{
[super viewWillDisappear:animated];
[self.tableView reloadData];
}
Solution for me was simple, if it doesn't work for someone, here I found some useful links.
1) UITableView layout messing up on push segue and return. (iOS 8, Xcode beta 5, Swift)
2) http://useyourloaf.com/blog/2014/08/07/self-sizing-table-view-cells.html

Unfortunately I believe both of the questions that you ask about are IOS bugs.
Question (1)
A easy fix is suggested by this blog.
When the table view is first displayed, you may find some of the cells are not sized properly. But when you scroll the table view, the new cells are displayed with correct row height. To workaround this issue, you can force a reload after the view appears:
Simply add the following to your viewDidAppear method. I have tested it and it works very well.
[self.tableView reloadData];
Question (2)
This second question is a duplicate of this following question:
IOS 8 UITableView self-sizing cells jump/shift visually when a new view controller is pushed
A workaround is suggested by the author of the question himself, which seems okay. However, I have not tried out this one yet.
Okay, I solved it by caching my cell heights in sizeThatFits, and returning that value for estimated cell heights within the delegate. Works beautifully.
Feel free to head over to that question for other proposed solutions.

Related

Why is there a floating "2" in my UITableView?

In the image below you can see a 2 appearing to the right of my UITableViewCell's accessory buttons. This only appears when I am using section headers (which is what I'm currently adding to my existing UITableView. If I fetch the data with sectionNameKeyPath:nil then the 2 doesn't appear.
My guess is that this 2 is appearing instead of the scrollIndicator. The 2 always remains in the center of the UITableView. When I don't use sections I see the scrollIndicator as expected. Does anyone know how to fix this?
Edit:
This shows that the floating 2 is a part of the scroll indicator.
hahah, that's great :) Butt! i think a way to find out where it's coming from is using the Debug View Hierarchy. Run your app and once you see the two in your table view:
once xcode has loaded the view from the Simulator, look for the two and click on it. at the top you'll see where this object is located.
Then i would try to back track on how it was added to this view. maybe it's part of a cell? etc.
I hope this helped getting closer to the answer :)
Well I finally found the problem. My superclass had the following code:
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView
{
return [self.fetchedResultsController sectionIndexTitles];
}
I must have picked up that code from an example somewhere when I setup my superclass for UITableViewController. In this case I don't want index titles, just the scroll indicator, so I removed that method.

UICollectionView stops loading cells after moveItemAtIndexPath:toIndexPath:

I have a UICollectionView with UICollectionViewFlowLayout which contains many items. I want to reorder a cell due to the user input.
[self.collectionView moveItemAtIndexPath:indexPath toIndexPath:newIndexPath];
After execution of this code, the collection view doesn't update it's contents when scrolling. It doesn't show next cells. It doesn't even call cellForItemAtIndexPath: but it's content size is correct (the layout "updates" it's state).
Therefore we can only see the cells which were distributed on the collection view before the reorder.
Any clues what might be the issue ? Are there any known workarounds for this issue? The app is iOS 8 only and the collection view is used with the UICollectionViewFlowLayout.
EDIT: When I call [self.collectionView reloadData]; the datasource is not even asked for anything, too.
The issue was with the UICollectionViewCell. High level description is that the cell is sent a method actionForKey from my code during the move animation. It caused the cell to stay in internal being animated state. I couldn't find reasonable workaround and refactored some bits in order to avoid actionForKey call. Now everything works as expected.

iOS 8: UITableViewCell detail text not correctly updating

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.
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.
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.
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.
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.
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.
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!
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.
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.

Table View: replace whole data on click

I've been searching for a really long time but I found no answer for my question(s).
In my app I have a segmented control with two buttons.
When I toggle between those buttons I do:
Show a loading screen as a subview
do an url request to get the content to show in my tableview
fill my dataArray for my TableView with the new data
call reload data
remove loading screen subview.
In most cases everything works fine, BUT..
Sometimes, really not often, the table view "crashes", after this 5 steps. It means. The view only show one or two cells. And when I scroll to top or bottom I only scroll into white. In my current solution I can't reproduce this error on my iPhone(IOS6), only in simulator(IOS7). I think it has something to do with scrolling while changing the data of the table view. It only occurs while scrolling and pressing the segmented control button shortly after started scrolling, but only sometimes. But it could have another reason for sure..
My Question is, what could cause the table view to layout wrong (only displaying some cells correctly and scrolling into white), any Idea? And how can I recover/redraw the whole tableview after this happened? Please help..
I read a lot about [tableview beginUpdates],[tableview endUpdates] and insertRows, but I think this is not the right solution in this case. Or tell me if I'm wrong.
Here is the main code snipped for steps 2-4. If another part of code could help, maybe the cellForRowIndexPath, let me know.
NSMutableArray *newResults = [[NSMutableArray alloc] initWithArray:[self getResultsForRequest:url]];
[resultsLock lock];
results = newResults; //results is the data source for my tableView
[resultsLock unlock];
[self.tableView reloadData];
Thanks to all responders.

UITableView stop updating UI without crashing after adding data

I have a problem that is driving me crazy.
I have an UITableView that is always in editing mode (it has to be).
The user can add new rows to it.
The navigationItem.leftbarButton of the tableViewController pushes a new controller just to do it, let's call it "newRowsVC".
Before the push the tableViewController set itself as the delegate of the newRowsVC, the protocol has only a method:
-(void) aNewRowHasBeenCreated
{
[self.tableView reloadData];
}
I start adding rows and everything works fine, each now row is immediately displayed in the tableViewController until the last new row will force the tableView to scroll because there won't be anymore screen real estate for it. Here, I have no idea why, the tableView, only it, is as frozen, with no scrollbar and doesn't respond to input. The app continues to run without a crash and I can even dismiss the tableViewController by tapping the navigationItem.rightbarButtonItem.
I can keep creating new rows, they are added to the array, the number of row in the tableView data source is computed correctly. But the table is like dead.
If I dismiss the tableViewController and then I come back to it, I see that all the rows previously created, also the ones not shown as soon as they were created are there!
I really do not have idea of how I can fix this.
The first thing I tried was to force the scroll after the reload of the table but it didn't fix it.
-(void) aNewRowHasBeenCreated
{
[self.tableView reloadData];
[self.tableView setScrollEnabled:YES];
}
I also tried forcing the tableView to scroll to the last row but it didn't fix it.
-(void) aNewRowHasBeenCreated
{
[self.tableView reloadData];
[self.tableView setScrollEnabled: YES];
[self.tableView scrollToRowAtIndexPosition: [self.tableView indexPathsForVisibleRows]last object] atScrollPosition: UITableViewScrollPositionBottom animated:YES];
}
I check the number of rows each time the table is reloaded. No error, the number is perfect and the new rows data are correctly in the array, also the data for the cells not shown.
I thought it could be because the tableView is always in editing mode so I tried setting it to NO but nothing changes, id din't fix the problem.
Two notes:
1)the tableView has to be the delegate of each one of it's custom cells. They have an UITextField and an UIStepper, handled by the tableViewController. I already tried to not set the tableViewController as the delegate of its custom cells but nothing changes so the problem is not this.
2) self.tableView.bounces = NO but this has nothing to do with the scrolling issue, the scroll is enabled.
Update: After more tests I found that if I remove the dequeue of the reusable custom cell everything works fine, so the problem should be about the reuse.
static NSString *CellIdentifier=#"MyCell"
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier: CellIdentifier];
Nicola
After hours I got it. As pointed out by Amit the problem was hidden in cellForRowAtIndexPath... It was hard to catch because it didn't happened all the times and the first times I enabled/disabled the cell reusing everything seemed the same so I did not link the problem to it. I got back on it after I tried about all the other options I had been able to think about.
The problem was in the reuse of the cells and the fact that the custom cell has the tableView as its delegate to handle its textView and the stepper without exposing them.
I got rid of all the delagion stuff and exposed the textView and the stepper as public properties of the view. By doing this I was able to set the tableViewController directly to be the delegate of the cell.textView and to add directly a target/action for the stepper.
Everything works flawslessly now.
Thanks to everyone who tried to help me!
Nicola

Resources