157

I feel like this might be a common issue and was wondering if there was any common solution to it.

Basically, my UITableView has dynamic cell heights for every cell. If I am not at the top of the UITableView and I tableView.reloadData(), scrolling up becomes jumpy.

I believe this is due to the fact that because I reloaded data, as I'm scrolling up, the UITableView is recalculating the height for each cell coming into visibility. How do I mitigate that, or how do I only reloadData from a certain IndexPath to the end of the UITableView?

Further, when I do manage to scroll all the way to the top, I can scroll back down and then up, no problem with no jumping. This is most likely because the UITableViewCell heights were already calculated.

Spidy
  • 1,129
  • 3
  • 30
  • 48
David
  • 6,582
  • 10
  • 44
  • 91
  • A couple things... (1) Yes you can definitely reload certain rows using `reloadRowsAtIndexPaths`. But (2) what do you mean by "jumpy" and (3) have you set an estimated row height? (Just trying to figure out if there's a better solution that would allow you to update the table dynamically.) – Lyndsey Scott Jan 30 '15 at 21:17
  • 1
    @LyndseyScott, yes, I have set an estimated row height. By jumpy I mean that as I scroll up, the rows are shifting upwards. I believe this is because I set an estimated row height of 128, and then as I scroll up, all my posts above in the UITableView are smaller, so it shrinks the height, causing my table to jump. I'm thinking of doing reloadRowsAtIndexPaths from row `x` to the last row in my TableView... but because I'm inserting new rows, it won't work, I can't know what the end of my tableview will be before I reloaded the data. – David Jan 30 '15 at 21:26
  • *how do I only reloadData from a certain IndexPath to the end of the UITableView?* -- You do that by reading the documentation. – Hot Licks Jan 30 '15 at 21:59
  • Note for future answer seekers: It seems as if the issue comes from using `tableview.rowHeight = UITableViewAutomaticDimension`. The issue was also mentioned in this answer: http://stackoverflow.com/q/25999880/2274694 – Lyndsey Scott Jan 31 '15 at 00:29
  • 2
    @LyndseyScott still i can't solve problem, is there any good solution? – rad Mar 10 '15 at 23:06
  • 1
    Did you ever find a solution for this problem? I am experiencing the exact same problem as seen in your video. – user3344977 Apr 23 '15 at 05:54
  • Did anyone find the solution to this issue? I am too facing this issue shown in the video – z22 Nov 26 '15 at 11:16
  • I was using exact cell heights but was supposedly hit by this, or a very similar problem. Setting the exact size as estimated size (as suggested in several answers, but I could do it directly in my storyboard) fixed that problem - the table view would jump around a bit strangely after deleting some source data and then doing `reloadData()`. – Jonny Nov 13 '17 at 07:47
  • 1
    None of the answers below worked for me. – Srujan Simha Jun 26 '18 at 16:36
  • Can you please re-upload the video? Link appears broken. – tboyce12 Aug 28 '18 at 21:59
  • There seems to be a **bug** for when you use `UITableViewAutomaticDimension `. See [this answer](https://stackoverflow.com/questions/8640409/how-to-keep-uitableview-contentoffset-after-calling-reloaddata/31324129#31324129) and other answers to the question. @SrujanSimha – mfaani Oct 10 '18 at 03:55

22 Answers22

252

To prevent jumping you should save heights of cells when they loads and give exact value in tableView:estimatedHeightForRowAtIndexPath:

Swift:

var cellHeights = [IndexPath: CGFloat]()

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    cellHeights[indexPath] = cell.frame.size.height
}

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    return cellHeights[indexPath] ?? UITableView.automaticDimension
}

Objective C:

// declare cellHeightsDictionary
NSMutableDictionary *cellHeightsDictionary = @{}.mutableCopy;

// declare table dynamic row height and create correct constraints in cells
tableView.rowHeight = UITableViewAutomaticDimension;

// save height
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    [cellHeightsDictionary setObject:@(cell.frame.size.height) forKey:indexPath];
}

// give exact height value
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath {
    NSNumber *height = [cellHeightsDictionary objectForKey:indexPath];
    if (height) return height.doubleValue;
    return UITableViewAutomaticDimension;
}
Igor
  • 11,785
  • 4
  • 54
  • 72
  • 1
    Thanks, u really save my day :) Works in objc too – Artem Z. Aug 15 '16 at 12:02
  • 3
    Don't forget to initialize `cellHeightsDictionary`: `cellHeightsDictionary = [NSMutableDictionary dictionary];` – Gerharbo Nov 05 '16 at 20:08
  • 1
    `estimatedHeightForRowAtIndexPath:` returns a double value may cause a `*** Assertion failure in -[UISectionRowData refreshWithSection:tableView:tableViewRowData:]` error. To fix it, `return floorf(height.floatValue);` instead. – liushuaikobe May 26 '17 at 06:05
  • Hi @lgor, I'm having the same issue & trying to implement your solution. The issue i'm getting is estimatedHeightForRowAtIndexPath gets called prior to willDisplayCell, so cell's height is not calculated when estimatedHeightForRowAtIndexPath is called. Any Help? – Madhuri Jul 25 '17 at 09:15
  • 1
    @Madhuri effective heights should be calculated in "heightForRowAtIndexPath", that is called for every cell on the screen just before willDisplayCell, which will set the height in the dictionary for later use in estimatedRowHeight (on table reload). – Donnit Jul 26 '17 at 15:16
  • @Madhuri, this solution for "Dynamic cell heights". First time height of cell defined by autolayout. Check your constraints and set `tableView.rowHeight = UITableViewAutomaticDimension;` – Igor Jul 26 '17 at 21:30
  • I've always underestimated estimatedHeightForRowAtIndexPath before the day i shaw you answer... wow man works like a charm. – tryKuldeepTanwar Jun 20 '18 at 07:17
  • Hello @Igor i'm having same tableview jumping problem after reload every time, i'm trying many solutions to fix it but same problem is there any solution. – Gaganpreet Aug 08 '18 at 09:22
  • 1
    How should you deal with row insertion/deletion using this solution? TableView jumps, as the dictionary data isn't actual. – mfaani Oct 10 '18 at 03:22
  • For me this worked with combination of setting estimatedRowHeight, estimatedSectionHeaderHeight, estimatedSectionFooterHeight to 1. – rgreso Oct 17 '18 at 00:12
  • Works like a charm. Do you have any idea why this happens? I'd suggest that is due to the `UITableView` algorithm used to find the height for cell given an estimated height "considerably far" from the correct/desired/necessary height. – henrique Jun 18 '19 at 14:10
  • yeah, I guess the same way – Igor Jun 20 '19 at 00:15
  • WOW. Such little concept can made a huge difference! I wonder if there's something for collectionview too – Raj D May 12 '20 at 06:55
  • What if your screen has, let's say, pull-to-refresh and you got new elements inserted at the top? At this moment `[IndexPath: CGFloat]` would be invalidated. – Legonaftik Dec 19 '20 at 08:27
  • That actually works. However, the question now is: how come `estimatedHeightForRowAtIndexPath` but the regular `heightForRowAtIndexPath` doesn't??? – ekashking Jun 04 '22 at 00:04
112

Swift 3 version of accepted answer.

var cellHeights: [IndexPath : CGFloat] = [:]


func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    cellHeights[indexPath] = cell.frame.size.height
}

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    return cellHeights[indexPath] ?? 70.0 
}
ScottyBlades
  • 9,795
  • 4
  • 62
  • 71
Casey Wagner
  • 1,518
  • 1
  • 10
  • 12
  • Thanks this worked great! in fact I was able to remove my implementation of `func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {`, this handles all the height calculation I need. – Natalia Mar 30 '17 at 17:33
  • After struggling many hours with persistant jumping I figured out that I forgot adding `UITableViewDelegate` to my class. Conforming to that protocol is neccessary because it contains the above shown `willDisplay` function. I hope I can save someone the same struggle. – MJQZ1347 Sep 25 '17 at 16:25
  • Thank you for the Swift answer. In my case I was having some SUPER weird behavior of cells going out of order on reload when the table view was scrolled to/near the bottom. I'll be using this from now on whenever I have self-sizing cells. – Trev14 Jan 31 '19 at 22:51
  • Works perfectly in Swift 4.2 – Adam S. May 14 '19 at 04:01
  • A life saver. So helpful when trying to add more items in the datasource. Prevents jumping of newly added cells to the center of the screen. – Philip Borbon Sep 17 '19 at 03:56
  • 1
    This is a great answer - my only suggestion would be to replace your default value in the `estimatedHeightForRowAt:` method with `UITableView.automaticDimension`. This way it will fallback to the (often imprecise but hopefully close) automatically determined value from Apple rather than 70. – Zack Apr 15 '20 at 17:59
42

The jump is because of a bad estimated height. The more the estimatedRowHeight differs from the actual height the more the table may jump when it is reloaded especially the further down it has been scrolled. This is because the table's estimated size radically differs from its actual size, forcing the table to adjust its content size and offset. So the estimated height shouldn't be a random value but close to what you think the height is going to be. I have also experienced when i set UITableViewAutomaticDimension if your cells are same type then

func viewDidLoad() {
     super.viewDidLoad()
     tableView.estimatedRowHeight = 100//close to your cell height
}

if you have variety of cells in different sections then I think the better place is

func tableView(tableView: UITableView, estimatedHeightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
     //return different sizes for different cells if you need to
     return 100
}
Alex Nolasco
  • 17,947
  • 9
  • 79
  • 80
Krishna Kishore
  • 904
  • 8
  • 4
  • 2
    thank you, it's exactly why my tableView was so jumpy. – Louis de Decker Jun 19 '18 at 12:36
  • 1
    An old answer, but it is still actual as of 2018. Unlike all other answers, this one suggests setting estimatedRowHeigh once in viewDidLoad, which helps when cells are of same or very similar height. Thanx. BTW, alternatively esimatedRowHeight can be set via Interface Builder in Size Inspector > Table View > Estimate. – Vitalii Nov 20 '18 at 12:34
  • provided a more accurate estimated height helped me. I also had a multi-section grouped table view style, and had to implement `tableView(_:estimatedHeightForHeaderInSection:)` – nteissler Sep 22 '19 at 03:09
28

@Igor answer is working fine in this case, Swift-4 code of it.

// declaration & initialization  
var cellHeightsDictionary: [IndexPath: CGFloat] = [:]  

in following methods of UITableViewDelegate

func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
  // print("Cell height: \(cell.frame.size.height)")
  self.cellHeightsDictionary[indexPath] = cell.frame.size.height
}

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
  if let height =  self.cellHeightsDictionary[indexPath] {
    return height
  }
  return UITableView.automaticDimension
}
Kiran Jasvanee
  • 5,894
  • 1
  • 32
  • 48
21

I have tried all the workarounds above, but nothing worked.

After spending hours and going through all the possible frustrations, figured out a way to fix this. This solution is a life savior! Worked like a charm!

Swift 4

let lastContentOffset = tableView.contentOffset
tableView.beginUpdates()
tableView.endUpdates()
tableView.layer.removeAllAnimations()
tableView.setContentOffset(lastContentOffset, animated: false)

I added it as an extension, to make the code look cleaner and avoid writing all these lines every time I want to reload.

extension UITableView {

    func reloadWithoutAnimation() {
        let lastScrollOffset = contentOffset
        beginUpdates()
        endUpdates()
        layer.removeAllAnimations()
        setContentOffset(lastScrollOffset, animated: false)
    }
}

finally ..

tableView.reloadWithoutAnimation()

OR you could actually add these line in your UITableViewCell awakeFromNib() method

layer.shouldRasterize = true
layer.rasterizationScale = UIScreen.main.scale

and do normal reloadData()

Srujan Simha
  • 3,527
  • 7
  • 40
  • 56
  • 1
    How does this do any reloading? You _call_ it `reloadWithoutAnimation` but where's the `reload` part? – matt Oct 13 '18 at 16:01
  • @matt you could call `tableView.reloadData()` first and then `tableView.reloadWithoutAnimation()`, it still works. – Srujan Simha Nov 12 '18 at 17:35
  • Great! None of above didn't work for me neither. Even all heights and estimated heights are totally the same. Interesting. – T.Y. Kucuk Dec 25 '18 at 19:25
  • 1
    Don't work for me. It is crash at tableView.endUpdates(). Can someone help me! – Kakashi Feb 06 '19 at 04:31
13

I use more ways how to fix it:

For view controller:

var cellHeights: [IndexPath : CGFloat] = [:]


func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
    cellHeights[indexPath] = cell.frame.size.height
}

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    return cellHeights[indexPath] ?? 70.0 
}

as the extension for UITableView

extension UITableView {
  func reloadSectionWithoutAnimation(section: Int) {
      UIView.performWithoutAnimation {
          let offset = self.contentOffset
          self.reloadSections(IndexSet(integer: section), with: .none)
          self.contentOffset = offset
      }
  }
}

The result is

tableView.reloadSectionWithoutAnimation(section: indexPath.section)
rastislv
  • 270
  • 2
  • 6
  • 1
    The key for me was implementing his UITableView extension here. Very clever. Thanks rastislv – BennyTheNerd May 21 '19 at 17:01
  • Works perfectly but it has only one drawback, you lose the animation when inserting header, footer or row. – Soufian Hossam May 22 '19 at 09:45
  • 1
    Where would reloadSectionWithouAnimation be called? So for example, users can post an image in my app (like Instagram); I can get the images to resize, but in most cases I have to scroll the table cell off scree for that to happen. I want the cell to be the correct size once the table goes through reloadData. – Luke Irvin May 30 '19 at 03:08
11

I ran into this today and observed:

  1. It's iOS 8 only, indeed.
  2. Overridding cellForRowAtIndexPath doesn't help.

The fix was actually pretty simple:

Override estimatedHeightForRowAtIndexPath and make sure it returns the correct values.

With this, all weird jittering and jumping around in my UITableViews has stopped.

NOTE: I actually know the size of my cells. There are only two possible values. If your cells are truly variable-sized, then you might want to cache the cell.bounds.size.height from tableView:willDisplayCell:forRowAtIndexPath:

MarcWan
  • 2,933
  • 3
  • 26
  • 41
  • 2
    Fixed it wen overriding the estimatedHeightForRowAtIndexPath method with an high value, for example 300f – Flappy May 11 '16 at 08:50
  • 1
    @Flappy it is interesting how solution provided by you works and is shorter than other suggested techniques. Do consider posting it as an answer. – Rohan Sanap Mar 20 '19 at 06:45
9

You can in fact reload only certain rows by using reloadRowsAtIndexPaths, ex:

tableView.reloadRowsAtIndexPaths(indexPathArray, withRowAnimation: UITableViewRowAnimation.None)

But, in general, you could also animate table cell height changes like so:

tableView.beginUpdates()
tableView.endUpdates()
Lyndsey Scott
  • 36,595
  • 10
  • 91
  • 126
  • I've tried the beginUpdates/endUpdates method, but that only affects the visible rows of my table. I still have the issue when I scroll up. – David Jan 30 '15 at 21:27
  • @David Probably because you're using estimated row heights. – Lyndsey Scott Jan 30 '15 at 21:28
  • Should I get rid of my EstimatedRowHeights, and instead replace it with the beginUpdates and endUpdates? – David Jan 30 '15 at 21:29
  • @David You wouldn't be "replacing" anything, but it really depends on the desired behavior... If you want to use estimated rows height and just reload the indexes below the current visible portion of the table, you can do that like I said using reloadRowsAtIndexPaths – Lyndsey Scott Jan 30 '15 at 21:30
  • One of my issues with trying the reladRowsAtIndexPaths method is that I'm implementing infinite scrolling, so when I'm reloadingData it is becauseI just added 15 more rows to the dataSource. This means that the indexPaths for those rows don't yet exist in the UITableView – David Jan 30 '15 at 21:39
  • @David Oh well if you're inserting rows, why not use `insertRowsAtIndexPaths:` instead – Lyndsey Scott Jan 30 '15 at 21:56
  • Interesting. I suppose I'm too used to Android Dev where you just notify the list of changes and it does the rest. I'm still a bit perplexed with how I would go about inserting rows to the end of the table. Would I have to create 15 indexPaths and give each their own row number if I'm going to insert 15 items to the end of the UITableView? – David Jan 30 '15 at 22:07
  • @David Yeah, you could create an array of the 15 index paths. – Lyndsey Scott Jan 30 '15 at 22:09
  • I was under the impression IndexPaths were for the UITableView to handle and fetch from, not to create on ones own. I see. So even if my UITableView currently only had 30 items, and I'm appending 15, I'd use indexPaths with row 30 - 44 (since its 0-indexed)? – David Jan 30 '15 at 22:10
  • @David I'd recommend trying it yourself and seeing if it work for your particular case. – Lyndsey Scott Jan 30 '15 at 22:12
  • I'm using the insert method, and the rows are being properly inserted, but the problem still persists and for some reason the heights for the rows not above the screen (not visible) are still being recalculated and thus causing the jumpy behavior – David Jan 30 '15 at 22:25
  • @David And, just curious, if you remove the estimated row height does the issue persist? – Lyndsey Scott Jan 30 '15 at 22:26
  • Yeah, it still persists. I included a video in the Question above so you can visually see the issue. – David Jan 30 '15 at 22:35
  • @David Oh, I see. It looks like your table height of the elements changes below the visible table thus shrinking the content size of the scrollview. – Lyndsey Scott Jan 30 '15 at 22:39
  • If it changes below the visible table, wouldn't it just change once and I'd see one bump. Instead, it seems like it continues to be changed (if that is the case) – David Jan 30 '15 at 22:41
  • @David, depends on how often you're reloading/inserting? – Lyndsey Scott Jan 30 '15 at 22:47
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/69928/discussion-between-lyndsey-scott-and-david). – Lyndsey Scott Jan 30 '15 at 22:48
4

Overriding the estimatedHeightForRowAtIndexPath method with an high value, for example 300f

This should fix the problem :)

Flappy
  • 759
  • 13
  • 29
3

Here's a bit shorter version:

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    return self.cellHeightsDictionary[indexPath] ?? UITableViewAutomaticDimension
}
jake1981
  • 193
  • 2
  • 11
2

There is a bug which I believe was introduced in iOS11.

That is when you do a reload the tableView contentOffSet gets unexpectedly altered. In fact contentOffset should not change after a reload. It tends to happen due to miscalculations of UITableViewAutomaticDimension

You have to save your contentOffSet and set it back to your saved value after your reload is finished.

func reloadTableOnMain(with offset: CGPoint = CGPoint.zero){

    DispatchQueue.main.async { [weak self] () in

        self?.tableView.reloadData()
        self?.tableView.layoutIfNeeded()
        self?.tableView.contentOffset = offset
    }
}

How you use it?

someFunctionThatMakesChangesToYourDatasource()
let offset = tableview.contentOffset
reloadTableOnMain(with: offset)

This answer was derived from here

mfaani
  • 28,843
  • 15
  • 145
  • 252
2

This one worked for me in Swift4:

extension UITableView {

    func reloadWithoutAnimation() {
        let lastScrollOffset = contentOffset
        reloadData()
        layoutIfNeeded()
        setContentOffset(lastScrollOffset, animated: false)
    }
}
2

One of the approach to solve this problem that I found is

CATransaction.begin()
UIView.setAnimationsEnabled(false)
CATransaction.setCompletionBlock {
   UIView.setAnimationsEnabled(true)
}
tableView.reloadSections([indexPath.section], with: .none)
CATransaction.commit()
ShaileshAher
  • 477
  • 4
  • 7
1

None of these solutions worked for me. Here's what I did with Swift 4 & Xcode 10.1...

In viewDidLoad(), declare table dynamic row height and create correct constraints in cells...

tableView.rowHeight = UITableView.automaticDimension

Also in viewDidLoad(), register all your tableView cell nibs to tableview like this:

tableView.register(UINib(nibName: "YourTableViewCell", bundle: nil), forCellReuseIdentifier: "YourTableViewCell")
tableView.register(UINib(nibName: "YourSecondTableViewCell", bundle: nil), forCellReuseIdentifier: "YourSecondTableViewCell")
tableView.register(UINib(nibName: "YourThirdTableViewCell", bundle: nil), forCellReuseIdentifier: "YourThirdTableViewCell")

In tableView heightForRowAt, return height equal to each cell's height at indexPath.row...

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {

    if indexPath.row == 0 {
        let cell = Bundle.main.loadNibNamed("YourTableViewCell", owner: self, options: nil)?.first as! YourTableViewCell
        return cell.layer.frame.height
    } else if indexPath.row == 1 {
        let cell = Bundle.main.loadNibNamed("YourSecondTableViewCell", owner: self, options: nil)?.first as! YourSecondTableViewCell
        return cell.layer.frame.height
    } else {
        let cell = Bundle.main.loadNibNamed("YourThirdTableViewCell", owner: self, options: nil)?.first as! YourThirdTableViewCell
        return cell.layer.frame.height
    } 

}

Now give an estimated row height for each cell in tableView estimatedHeightForRowAt. Be accurate as you can...

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {

    if indexPath.row == 0 {
        return 400 // or whatever YourTableViewCell's height is
    } else if indexPath.row == 1 {
        return 231 // or whatever YourSecondTableViewCell's height is
    } else {
        return 216 // or whatever YourThirdTableViewCell's height is
    } 

}

That should work...

I didn't need to save and set contentOffset when calling tableView.reloadData()

1

I have 2 different cell heights.

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        let cellHeight = CGFloat(checkIsCleanResultSection(index: indexPath.row) ? 130 : 160)
        return Helper.makeDeviceSpecificCommonSize(cellHeight)
    }

After I added estimatedHeightForRowAt, there was no more jumping.

func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
    let cellHeight = CGFloat(checkIsCleanResultSection(index: indexPath.row) ? 130 : 160)
    return Helper.makeDeviceSpecificCommonSize(cellHeight)
}
sabiland
  • 2,426
  • 1
  • 23
  • 23
0

Try to call cell.layoutSubviews() before returning cell in func cellForRowAtIndexPath(_ indexPath: NSIndexPath) -> UITableViewCell?. It's known bug in iOS8.

CrimeZone
  • 230
  • 2
  • 13
0

You can use the following in ViewDidLoad()

tableView.estimatedRowHeight = 0     // if have just tableViewCells <br/>

// use this if you have tableview Header/footer <br/>
tableView.estimatedSectionFooterHeight = 0 <br/>
tableView.estimatedSectionHeaderHeight = 0
mastisa
  • 1,697
  • 2
  • 17
  • 37
Vid
  • 29
  • 1
0

I had this jumping behavior and I initially was able to mitigate it by setting the exact estimated header height (because I only had 1 possible header view), however the jumps then started to happen inside the headers specifically, not affecting the whole table anymore.

Following the answers here, I had the clue that it was related to animations, so I found that the table view was inside a stack view, and sometimes we'd call stackView.layoutIfNeeded() inside an animation block. My final solution was to make sure this call doesn't happen unless "really" needed, because layout "if needed" had visual behaviors in that context even when "not needed".

Gobe
  • 2,169
  • 1
  • 19
  • 24
0

I had the same issue. I had pagination and reloading data without animation but it did not help the scroll to prevent jumping. I have different size of IPhones, the scroll was not jumpy on iphone8 but it was jumpy on iphone7+

I applied following changes on viewDidLoad function:

    self.myTableView.estimatedRowHeight = 0.0
    self.myTableView.estimatedSectionFooterHeight = 0
    self.myTableView.estimatedSectionHeaderHeight = 0

and my problem solved. I hope it helps you too.

Burcu Kutluay
  • 382
  • 5
  • 12
0

For me, it worked with "heightForRowAt"

extension APICallURLSessionViewController: UITableViewDelegate {

func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
    print("Inside heightForRowAt")
    return 130.50
}
}
0

For me the working solution is

UIView.setAnimationsEnabled(false)
    tableView.performBatchUpdates { [weak self] in
    self?.tableView.reloadRows(at: [indexPath], with: .none)
} completion: { [weak self] _ in
    UIView.setAnimationsEnabled(true)
    self?.tableView.scrollToRow(at: indexPath, at: .top, animated: true) // remove if you don't need to scroll
}

I have expandable cells.

Dmitry
  • 2,815
  • 2
  • 18
  • 37
-1

Actually I found if you use reloadRows causing a jump problem. Then you should try to use reloadSections like this:

UIView.performWithoutAnimation {
    tableView.reloadSections(NSIndexSet(index: indexPath.section) as IndexSet, with: .none)
}
Michael
  • 1
  • 6