Update UITableView height when tweet loaded
Asked Answered
P

3

6

I have a UITableViewCell which contains a TWTRTweetView with auto layout. I am loading a tweet like this:

Constraints

Height contraint details

- (void)loadTweetWithId:(NSString *)tweetId {

    if (mTweetId == nil || ![mTweetId isEqualToString:tweetId]) {
        mTweetId = tweetId;
        [[[TWTRAPIClient alloc] init] loadTweetWithID:tweetId completion:^(TWTRTweet *tweet, NSError *error) {
            if (tweet) {
                NSLog(@"Tweet loaded!");
                [mTweetView configureWithTweet:tweet];
                [mTweetView setShowActionButtons:YES];
                //[mTweetView setDelegate:self];
                [mTweetView setPresenterViewController:self.viewController];
                [mTweetView setNeedsLayout];
                [mTweetView layoutIfNeeded];
                [mTweetView layoutSubviews];
                hc.constant = mTweetView.frame.size.height;
                [self updateConstraints];
                [self layoutIfNeeded];
                [self layoutSubviews];
                [self.tableView setNeedsLayout];
                [self.tableView layoutIfNeeded];
                [self.tableView layoutSubviews];
            } else {
                NSLog(@"Tweet load error: %@", [error localizedDescription]);
            }
        }];
    }
}

When tweet loaded cell doesn't resize unless I scroll it out and scroll it to back. I have tried several approaches as you can see in code snippet. But non of these works. My table view uses full auto layout approach which doesn't implement cell height for row function. How can i fix this?

UPDATE:

Using:

[self.tableView beginUpdates];
[self.tableView endUpdates];

is not possible because when I do that all cells being redrawn and very big jumping happens and that is not acceptable. Also I have confirmed that tweet completion block runs in main thread.

Jump video

UPDATE 2:

I have also tried to cache tweet view with tweet id and reload cell for related index path and give the same tweet view for tweet id. The cell height is corrected but it doesn't become visible until scroll out/in.

UPDATE 3:

I give constraints to tweet view in xib of the cell and height constraint is connected. So this is not a main thread issue. I have also mentioned that reloading particular cell at index doesn't work.

While working an other solution I have seen some sample TwitterKit codes that uses TWTRTweetTableViewCell but was preloading tweets to configure the cells. So I have done the same. This is a workaround of course.

Piragua answered 24/6, 2017 at 11:3 Comment(7)
When using automatically sized table view, I've sometimes had luck calling [self.tableView beginUpdates]; and [self.tableView endUpdates]; one right after the other - have you tried that?Nauru
That looks like a whole lot of extra / unneeded "layout" stuff - have you just been adding more and more of them? Anyway, it's a little tough to tell from the code you've posted, but my guess is that your completion block is running on a background thread. All UI updates must be done on the main thread. Search for objective c uitableview update on main thread for examples.Lusk
Late to reply, sorry. Did you check if the mTweetView.frame.size.height has the updated value in the completion callback?Unseasoned
Call tableView's reloadData function after defining the cell height at heightForRowAtIndexPath when the tweet is finished loading.Flanagan
@ayazmon reloadData is not an option.Pandorapandour
@OlcayErtaş Are you sure that you are doing your stuff with autolayout in main thread, and your constraint outlet is connected to your storyboard?, can you put a NSLog in with the value of mTweetView.frame.size.height? another question the mTweetView have intrinsic content size?Macedonian
Do you find a solution to your problem ?Riancho
V
1

Updated Answer:

You're doing a couple of things wrong that are likely to cause (or at least contribute to) the jumping:

  • Never call layoutSubviews yourself. It's a method called by the system to resolve your constraints. It's automatically triggered when calling setNeedsLayout and layoutIfNeeded in a row.

  • The same applies to updateConstraints. It is called by the system during a layout pass. You can manually trigger it by subsequently calling setNeedsUpdateContraints and updateConstraintsIfNeeded. Furthermore, it only has an effect if you actually implemented (overrode) that method in your custom view (or cell).

  • When you call layoutIfNeeded on a view it layouts its subviews. Thus, when you change the constant of a constraint that constrains your mTweetView, it probably won't have any effect (unless the view hierarchy is invalidated during the triggered layout pass). You need to call layoutIfNeeded on mTweetView's superview which is the cell's content view (judging from the screenshot you added to your post):

    [contentView layoutIfNeeded];
    

Furthermore, there is one more thing you need to be aware of that can cause flickering as well:

  • Cells in a table view are being recycled. Each time a cell is reused you load a new tweet. I guess it's from an asynchronous network request? If so, there is the possibility that the completion block from the first tweet you load for that cell instance returns after the completion block from the second tweet you load for that (recycled) cell when you scroll really fast or you internet connection is really slow. Make sure you cancel the request or invalidate it somehow when your cell is reused (prepareForReuse method).

Please make sure you've fixed all these issues and see if animation now works as expected. (My original answer below remains valid.)


Original Answer:

I'm pretty sure that

[self.tableView beginUpdates];
[self.tableView endUpdates];

is the only way to have a cell auto-resize itself while being displayed.

Reason:

For historic and performance reasons a UITableView always works with fixed-height cells (internally). Even when using self-sizing cells by setting an estimatedRowHeight etc. the table view will compute the height of a cell when it's dequeued, i.e. before it appears on screen. It will then add some internal constraints to the cell to give it a fixed width and a fixed height that just match the size computed by Auto Layout.

These internal constraints are only updated when needed, i.e. when a row is reloaded. Now when you add any constraints inside you cell you will "fight" against these internal constraints which have a required priority (aka 1000). In other words: There's no way to win!

The only way to update these internal (fixed) cell constraints is to tell the table view that it should. And as far as I know the only public (documented) API for that is

- (void)beginUpdates;
- (void)endUpdates;

So the only question that remains is:

Why is this approach not an option for you?

I think it's legitimate to redraw a cell after it's been resized. When you expand the cell to show a longer tweet than before the cell needs to be redrawn anyway!

You probably won't (and shouldn't) resize all visible cells all the time. (That would be quite confusing for the user...)

Vanmeter answered 5/7, 2017 at 21:36 Comment(5)
When I use beginUpdates - endUpdates big jumpings happens.Pandorapandour
While this answer might not solve your problem, I believe it still answers your question "why auto layout doesn't update cell content" correctly.Vanmeter
You have to accept the fact that the beginUpdates + endUpdates method is the only documented way to achieve resizing of the cell height while the cell is visible. I've tested that thoroughly and it works great. Thus, if you've fixed all the issues stated above, the error is somewhere else in your code.Vanmeter
I didn't asked for what is the documented way. I have stated that I need to update it without jumps. As you can read I have already tried your suggestion and didn't fit my situation.Pandorapandour
BTW: You might also want to check if the value you set for the hc constant is correct (i.e. if the cell has been laid out correctly at that point). Generally, I would discourage using such a constraint for the content view's heigh at all. If the mTweetView is fully constrained and can layout itself you can simply constrain its fours edges to the contentView's edge and you'll get automatic cell resizing. If not, your cell's layout isn't correct.Vanmeter
T
0

Try reloading that particular cell, after you loaded the tweet using,

- (void)reloadRowsAtIndexPaths:(NSArray<NSIndexPath *> *)indexPaths 
              withRowAnimation:(UITableViewRowAnimation)animation;
Terbia answered 4/7, 2017 at 6:19 Comment(1)
Please read my updates. I have stated that reloading cell doesn't working.Pandorapandour
P
0

I had the similar issue and i got that fixed by adding all my code in dispatch_async to make sure its running on main thread.

 dispatch_async(dispatch_get_main_queue(), ^{ 
      /*CODE HERE*/
 });

So your code should be like this:

- (void)loadTweetWithId:(NSString *)tweetId {

  if (mTweetId == nil || ![mTweetId isEqualToString:tweetId]) {
      mTweetId = tweetId;
    [[[TWTRAPIClient alloc] init] loadTweetWithID:tweetId completion:^(TWTRTweet *tweet, NSError *error) {

        dispatch_async(dispatch_get_main_queue(), ^{ 

        if (tweet) {
            NSLog(@"Tweet loaded!");
            [mTweetView configureWithTweet:tweet];
            [mTweetView setShowActionButtons:YES];
            //[mTweetView setDelegate:self];
            [mTweetView setPresenterViewController:self.viewController];
            [mTweetView setNeedsLayout];
            [mTweetView layoutIfNeeded];
            [mTweetView layoutSubviews];
            hc.constant = mTweetView.frame.size.height;
            [self updateConstraints];
            [self layoutIfNeeded];
            [self layoutSubviews];
            [self.tableView setNeedsLayout];
            [self.tableView layoutIfNeeded];
            [self.tableView layoutSubviews];
        } else {
            NSLog(@"Tweet load error: %@", [error localizedDescription]);
        }

    });

    }];
  }
 }
Preengage answered 4/7, 2017 at 8:3 Comment(1)
I have told that completion block is in main queue. But to be fair before down vote you, I have tested your suggestion and it didn't work.Pandorapandour

© 2022 - 2024 — McMap. All rights reserved.