I have a tableView ,each row has four graphs in it with around 20 rows.
As i try to scroll the table i will remove the existing graph and build new graphs for each row.
This operation make the scrolling of the table view very slow.
Any ideas to make the scroll faster as well as load new graphs.
Creating new views is an expensive operation. That's why it's best to reuse them. You can do this by "dequeueing" existing views to recycle.
Also take a look at Table View Programming Guide for iOS
Specifically, look at the section "Subclassing UITableViewCell" that covers optimisation techniques, e.g.
Draw the entire cell only when appropriate. Your subclass of
UITableViewCell could draw all of its content in its drawRect: method,
but you should be aware of the potential drawbacks of this approach.
Custom drawing applies to the cell’s layer, which can be obscured by
any views placed over it. For example, in table views in the grouped
style, the background view (the backgroundView property) obscures any
drawing performed in drawRect:. The blue selection background will
also obscure any drawing. Moreover, custom drawing that occurs during
animation (such as when the table view enters and exits editing mode)
drastically decreases performance.
Slow table scrolling is a common issue, so you'll be able to find plenty of similar questions covered here on Stack Overflow.
The other thing to consider is that your methods for providing data could be the bottleneck. Instruments will be able to help you identify the issues.
Related
In terms of creating an infinite scrolling capability using a UIScrollView (no looping back to the start), for a calendar in fact, will one very large UIView for long scrolling in UIScrollView cause resource issues?
Assumptions:
That is assuming one programmatically adds/removing subviews onto this main backplane view as it is about to need being shown/visible, then removing afterwards.
Don't want to use UICollectionView (to focus question on use of UIScrollView please)
Requirements:
Would want to put UI View onto this background plane as well as drawing to it directly, e.g. adding lines.
So therefore would need to be adding/removing things like Lines drawn as well UILabels. (well haven't delved into Lines and whether they would need to be drawn in their own subview, upon which you're then adding removing these "line" subviews, as opposed to drawing directly onto the main backplane view in drawrect)
Background:
I see some suggestions of having 3 "pages" (views) and you keep moving these as scrolling occurs, however this just seems more complicated.
There should not be any resource issue as far as you use views.
I developed kinda map application with the next view structure.
MapScrollView (UIScrollView)
- TileContainerView (UIView)
- TileContainerSubViews[10..] (UIView)
- TileLayer [] (CALayer)
- UIButton[]
- IconLayer[] (CALayer)
- TrackLineLayer (CALayer)
- .....
All of the Tile**Views in hierarchy and MapScrollView share the same size, which is aligned to the size of the TileContainerSubViews[n], which further holds many TileLayer(s) with size of 256x256. The largest TileContainerSubView[] can hold as many as 10000x10000 TileLayer(s)
which is 2560000x2560000 in view size. (Of course, we can't actually add that many layers due to resource issue.) All of views/layers are added/removed on the way.
Note you can create large UIView but not CALayer.
When I tryed to create huge CALayer, the program terminated with some error message. CATiledLayer doesn't seem to have this constraint but I haven't tested.
I have a table view with custom cells and I am trying to optimize it to make the scrolling as smooth as possible. I've followed most of the advises about table view optimization, for example, using fixed heights, not using AutoLayout etc.
I have achieved in a very optimized state except one thing. The custom cell has one image and several UILabels. With one image & 3 labels, the performance is perfect. However, when I add the 4th label, I start to observe a bit of jerkiness, very small but not as smooth as before.
I have searched the net and found CoreText can help performance. Is CoreText a right direction? Are there any other things that can help to improve UILabel performance when used in a table view?
EDIT:
I only use simple functions for UILabel by setting its font, font color, text and numberOfLines.
Core text is probably the ultimate solution for performance, but even before getting there I like to use a program called PaintCode to draw up the contents of my cells if they are particularly complicated.
Other things you can do however to speed things up a bit before you need to get to that point:
Do all calculations before cellForRow is called. So any number formatting, date calculations etc, should all be done and stored on your model object beforehand. That way you can just plug them straight in to your cells properties and they're ready to be displayed. Any extra time spent in cellForRow will slow down your tableview.
Set all labels and views to opaque. You'll probably have to set their background color when you do this, but it makes drawing the views that much faster. You can see which views have transparency applied by turning on "Color Blended Layers" from the debug menu for the simulator. What you want is to have your cells completely green.
Keep some dummy text in all labels by default. Updating text would not cost memory.
CoreText is pretty expensive and probably not meant for this! I think dummy text tricks should work for you.
Hope this works.
I am trying to create a table to show posts by users with possibly variable lengths. I want each cell in my table to resize to fit the content of the UILabel containing the text of the post. Currently I am doing this with auto-layout, programmatically setting constraints and calculating the height of the table view cell based on the height of the UILabel. Everything looks as I want it to, except when rapidly scrolling through the table the performance is horrendous, with the CPU getting fully maxed out and unable to keep up with setting the constraints for each cell as they are reused and placed with new text.
I was wondering if anyone knows of a better way to do this. Is there a way I can continue to use auto layout to size my cells without sacrificing performance? Or would the best solution be to create different sized cells with different identifiers, and just choose the best fit based on the UILabel text size.
I also read somewhere that UICollectionView has much better performance in displaying variable-sized content, would it be worthwhile/possible to try configuring a UICollectionView to display the messages, and somehow make it look like a UITableView?
Essentially I just need a suggestion on how to display messages of variable sizes (up to about 7 or 8 lines of text max) in a TableView-like manner without causing massive slowdowns as the user rapidly scrolls through the table.
Thank you
The best trick in the book for things like this is cacheing. Add to your model anything that's expensive to compute (not the views themselves, but the computational results, like view bounds sizes). Make your datasource methods all about looking up and assigning, not at all about computing.
The way I had implemented adding the constraints, the constraints were updated whenever [self updateViewConstraints] was called, regardless of whether or not the cell had already had its constraints calculated and applied. I resolved this by adding a BOOL property to my custom TableViewCell called didUpdateConstraints, and setting it to YES as soon as constraints are updated the first time. Then in my [self updateViewConstraints] method I only update the constraints if !self.didUpdateConstraints
Now that constraints are not being needlessly updated when all views have already been correctly constrained, the performance is significantly better, and I don't observe any slowdown upon scrolling through the table.
I would like to create a zoomable timeline in my iOS application for a kind of a todo-list. Zooming in would display days and hours and zooming out would trigger the folding out of days or zooming out to months. There would be a scrolling function.
As an example, I would want it to work like this: http://almende.github.com/chap-links-library/js/timeline/doc/
What kind of basic view would be an appropriate starting point for this, keeping in mind that the memory needed should be as low as possible? Would a UITableView, UIScrollView, or something else work for this?
The UICollectionView / UITableView will not work because the cells are almost always the same width/height. Most importantly the cells always have the same spacing in-between each cell. Because of this it is able to easily calculate what the index range is, and query the dataSource for the cell's it needs based on index.
A timeline view on the other hand is much different than these controls. The spacing between cells is different, with the cells sometimes overlapping with each other. If you had a data source sorted by position, the control would still have to guess where to start looking for the range. So finding the correct index range is going to be more expensive - you just have to find the right algoritm to determine this in a shorter amount of time.
You're going to have to build your own control by subclassing a UIScrollView. You shouldn't have to mess with drawRect at all. An important concept, which is used by UITableView and UICollectionView, is dequeue'ing cells. The iOS 5 version of Apple's PhotoScroller demonstrates this concept with paging (the iOS 6 version replaces the custom paging with UIPageViewController). You'll have to download the old documentation to get the old sample code.
I'm currently building a timeline view, which I will open source at some point. It's somewhat based on the UITableView and works in horizontal or vertical direction. It dequeue's cells just like the UITableView. I'm not focusing on labels or scaling, but the concept of having inconsistent spacing in-between cells. To give you a head start, here are my dataSource methods I settled on:
- (NSInteger)numberOfCellsInTimelineView:(TimelineView *)timelineView;
- (CGRect)timelineView:(TimelineView *)timelineView cellFrameForIndex:(NSInteger)index;
- (TimelineViewCell *)timelineView:(TimelineView *)timelineView cellForIndex:(NSInteger)index;
Two of these calls are identical to what UITableView has, but it has a new call called cellFrameForIndex. What's significant about this call is that the TimelineView can guess an index and lookup the frame of the cell and see where it fits in the visible bounds. If it guesses a cell inside the visible bounds, it can simply iterate forward and backward until it finds the edges.
Currently the algoritm I'm using takes round(count * (CGRectGetMidX(timelineView.bounds) / timelineView.contentSize.width)) (for the horizontal direction). Basically what this does is takes the mid-point of the visible bounds of the UIScrollView and gets the percentage of what has been scrolled. Then it multiplies that by the number of cells. This works fairly well. When testing a random data-set with 100,000 records at random spacing the calls to cellFrameForIndex ranged from 8 to 150. I'm able to pull 52-60 FPS with this. I'm still working on it, and trying to find a better/quicker way to determine the index range. I'm trying to keep it down to visible cell count + 10 (max) iterations.
If you have time to wait, I'll update my answer to include a link to my GitHub project when I'm done. This could be a few days, to a week. I may add scaling. But you'll have to fork it and add labels and anything else you want.
Edit:
Here is my Github project: https://github.com/lukescott/TimelineView
UITableView is definitely not suitable. UIScrollView might be better but it is not very well suited for dynamic or very long content.
I believe the easist approach would be to do it everything by yourself - implement it by a UIView subclass with a drawRect. Of course, you should use UIPanRecognizer in the same manner as UIScrollView uses it.
I've finally finished my first large application, the only problem is that I've focued a lot on design, and I'm using custom nibs as cells with transparent backgrounds. When I tried testing the application on my iPhone, the performance was terrible.
Is there any way to get better scrolling performance while using transparent cells with a ImageView behind the UITableView?
I've read two articles mostly:
blog.atebits.com/2008/12/fast-scrolling-in-tweetie-with-uitableview/
cocoawithlove.com/2009/04/easy-custom-uitableview-drawing.html
Looks good, but what if I want to use transparent cells?
a) Uses solid color.
b) Uses imageview as background.
Any help will be greatly appreciated. I want to get this baby released as soon as possible, but the performance as it is now is terrible!
First off, stop using nibs. Every time a cell is created, you're now hitting the disk in order to unarchive the nib. 3.1 will actually make this better, but until then, please create your cell in code.
Secondly, remove transparency wherever you can. Anything that doesn't need to be transparent, shouldn't be. And anything that isn't transparent should have the opaque property set to YES.
A third suggestion is if you're using a lot of subviews, you will see a performance benefit by using a custom view to draw everything instead of a bunch of subviews. If you choose to go this route, you should consider how it behaves when rotating to landscape mode (e.g. how the stretch action occurs), or if you have any controls that need to handle touches separately from the cell itself.