I am displaying an array of movable cards (UIViews) in viewController.
This cards have each set a pan Gesture recogniser that allows them to be moved.
When I move a card up self.removeFromSuperview() is called and the view is removed from the viewController's view.
The problem is that since I have displayed many cards one in top of the other, the first one I move around lags a lot and drops some frames.
After some views are removed tho, the user experience improves and It doesn't drop frames anymore.
I was wondering If there was a limit in how many panGestureRecongizers there may be concurrently.
To investigate more what was causing the issue, I tried to use the Time Profiler tool to see what may cause this traffic on the main thread and this is what I found out:
To be honest I'm pretty new to time profiler tool but what I think I see it's causing the problem is indeed the panGesturerecogniser method.
Is this possible or may I search on another path?
Related
I'm creating a project which uses a UICollectionView. As the user selects a cell this slides them to another UICollectionView of similar nature. Ive been noticing that no matter how I go about this I wind up with mountains of memory usage.
I've been experimenting with placing UICollectionView's in full page UICollectionViewCell's so as to take advantage of the reusability of UICollectionViews. The downside of this approach has been memory retention as the CollectionViews are never fully deallocated. Ive also heard that it is not the best practice to put a UICollectionView in a UICollectionView.
I've experimented with UIPageViewController's containing UIViewController's with the UICollectionView inside. This is more efficient as the UIViewController's can be deallocated as the user swipes back however as long as the user continues to select cells and create new view controllers the memory grows unbounded looking like a mountain.
As a bit of a hybrid I attempted as well to put ViewControllers containing UICollectionView's on UICollectionViewCell's. This method seemed to work best but I had to deallocate the view controllers manually as the user swiped.
Are there any strategies or libraries anyone could recommend that would fit this problem. How can I keep the memory down. I understand I'll need some kind of reusable views.
Ive been looking into this Library so far thank you in advance for all of your advise
Parchment Library
I think I understand what you're saying. You have a UICollectionView that can drill down to another UICollectionView, leaving the first one and its backing data retained until you come back and pop it off. Drilling down further and further allocates more and more memory until you back out.
I'd keep things as simple as possible. Solutions like putting a UICollectionView inside UICollectionViewCells can cause your code to get unnecessarily complicated, resulting in new issues and code that's programmer-hostile. If the user experience that works best is a collection view UI that you can drill down into infinitely, then go with that paradigm.
Your issue is not with UICollectionViews, it's in managing your backing data's memory use. That could be done a few ways. It would help to know what kind of data you have that's so large, and what "large" means, but here are a few approaches that come to mind.
One idea would be to unload any large data when you go to the next screen. For example, if your datasource uses an array with a bunch of large images, clear them out when the next view is pushed. Reload the data when your view appears again, or do it lazily when the view's cells need it, whichever works best for you. This would be the easiest approach and probably take care of your memory concerns.
A second approach would be to use one UICollectionView and use custom animations so it looks like a new collection view is pushing/popping from an old one, when in fact you're just changing the data for the collection view and reloading. You could even provide animations that are more interesting than pushing/popping.
On top of either of these approaches, you could implement the UICollectionView prefetch API calls to load data just before you need it. That will reduce your memory footprint even further.
All of these approaches assume that you can load the data to display from storage-- that it's not just in memory from recent webservice requests. Your users are guaranteed a miserable experience if your app has to keep requesting the same large data from the web over and over. So, if you don't have the data stored locally already, set up a cache.
Regardless of the approach, this is something you should be able to handle without adopting a library. UICollectionViews are designed to be memory friendly. Your issue is really in determining the best way to manage your backing data's memory use.
I have a scenario where I overlapped a view(pageViewController inside the view) with a uitableview with an offset of 130, and that uitableview is Transparent, only cells will be scrolled and visible.
My requirement is within this offset place of 130, I want to pass touch events to the view(pageViewController) which is below the uitableview and that view is not a superview(It is a sibling view) of table view but I overlapped it with tableview.
So is it possible to pass touch (Horizontal Swiping Event) to the view which is below the uitableview in the transparent area (Offset Area of 130); if yes, what I have to do for this?
There are lots of options for controlling how touch events and scrolling interact in iOS. You can override hitTest, you can use delegate messages sent to gesture recognizers to say which combinations of recognizers should fire when, etc. Without seeing your problem in detail it's hard to say which is best... and even then, you might say it's partly a matter of style. (At first blush, though, I'd guess that overriding hitTest to return different views at different times might be it.)
I'd recommend watching the WWDC 2014 video on Advanced Scrollviews and Touch Handling Techniques. That talk works through solving some problem that sound almost exactly like yours, so you'll almost certainly find a good solution in there. And lots of other tips that might be helpful elsewhere in your app. (Actually, if you have the time, I'd recommend watching the UIScrollView-themed sessions from the past several years' WWDCs... the so-called "Josh & Eliza Show" has always been full of cool demos and useful insights.)
So I have an app, in which I use custom UIView. There are other UIView, which are subviews of that view. All of them have pan gesture recognizers, so that the user can pan them in the specific fashion. When I test my app using one such view all is well and good, it is all smooth and fps is around 55-60. However, when I add the second one(somewhat smaller then the first one, and kinda inside first one, but not as a subview). The FPS of the outer views drops to 20-30, while the inner one(which is smaller) works in 60 fps. This two views are ABSOLUTELY independent, meaning that interacting with one doesn't affect the other one. In addition to that, I checked running time on the interval of one second, the results are the following:
For 1 view: 153ms
For 2 views 145ms
For 3 views 150ms
The calculations that are performed on each view are ABSOLUTELY identical in complexity. Why does one work smoothly, but when I add the second one it starts to lag, while the execution time stays the same ? I would appreciate if somebody could hint me at how I should debug it.
The most consuming operation according to the Time Profiler is repositioning one of the views:
view.center = newCenter
I simply can't understand why FPS drops 2-3 times, while the running time stays the same. What should I look for ?
UPDATE:
So I found the source of the problem, when one view is inside the frame of the other the whole thing is being constantly redrawn, even though no changes were made to the layout of the inner view. So when I positioned a few of them in different parts of the screen everything was fine, but when I put one "inside" the other things get messed up. Inner one works quickly, while the outer one lags. "Flash updated regions" flag in core animation show that when I interact with outer view, inner view gets updated as well. Any suggestions on what I might need to do, because I need that layout when they are one inside each other. So how can I prevent(lock) other view from redrawing ?
Thank You.
Ok. I HAVE seen a LOT of topics which are similar to what I'm trying to do here, but nothing that quite matches. Maybe my approach is way off base. Here's the deal.
I'm drawing a chart to my UIView subclass in drawRect:. I then use a UIPanGestureRecognizer to translate the chart's offset (internal, not in the view hierarchy) to the right/left. This works great, but my problem is that the recognizer's action gets fired with relatively low frequency. Specifically, the resultant panning is MUCH choppier than, say, in a scrollview or tableview. Particularly when the number of points in my chart goes up. Now, what I'd LIKE to do is not actually trigger drawing from the pan gesture's action, but have a timer firing at something like 30 times per second, interpolating the offset from the moment the pan fired to the current moment. However, this is getting more and more complicated.
Anybody have suggestions? Should I really just be doing this with a UIScrollView? The reason I'd prefer not to is that the data may extend quite far in either direction, and I'd like to not have to draw (or at least store) the whole path at once, but just do the section which is currently displayed. Should I be using CAShapeLayer? I'd like some input before I spend another day playing around with these possibilities...
Any input / experiences would by most welcome.
So, in the end, I DID end up putting these things in a UIScrollView. I had to do quite a bit of work to get this up and running, the complicated part being the creation of a series of PANES which would be added to the scrollview. Each pane asks a delegate for the data it needs to draw, and then they're all stitched together. Fortunately, everything worked great once I put these scroll views containing chart panes into a table view. Still haven't tackled scaling, but I've seen a few posts on here which should make it relatively trivial.
So that's the answer to my original question. DON'T try to pan and draw on each frame, it WILL be choppy, and baring some real hack, there's no good way I could find to smooth it out. Scroll view handles all these problems for you, as long as you can split your view up nicely. I guess I could have used one long view, but then when the data set gets large I'm in trouble.
Now I've got a related problem though -- when offscreen charts scroll into view (they're in UITableViewCells), there's a big hiccup as the new chart gets drawn. I think I will try drawing them into an image asynchronously, so that when the new row scrolls into view, I've already got the image.
So, I currently have a GridView class, which is essentially what it sounds like. It is a subclass of UIView that has a staging area that has a bunch of subviews placed in a grid-like manner. Each cell of the GridView is a custom UIView subclass which calls drawRect.
I implemented lazy loading by following the UITableView pattern. I only loaded and added the grid cells which were visible (+/- 2 cells on each side). As the view is scrolled through, I cache cells that are no longer visible in a NSMutableSet and also re-use cells from that Set to build the cells that are now visible. Everything about that seems to work fine, as I have tested and profiled it, with no memory leaks.
The problem is that scrolling is too slow / jerky. The faster the user scrolls, the jerkier it is. Sometimes, if you scroll very fast, it throws a memory warning. After some debugging work, I found when it starts freezing and jerking, the subviews in the gridview don't get removed, like they should
I also noticed that the scrolling is significantly slower on the iPad 1 vs. the iPad 2.
My questions is, does anyone have any ideas on how to handle this? Any tricks or optimizations to stop the jerky-ness of the scrolling? Anything would help at this point, as I have been trying to figure this out for 2 days. Thanks in advance
Possible slowdown causes : 1) drawRect 2) cacheing during user enabled active interface
choice a) preload, and then present smooth interface, or b) smooth scroll while loading stuff in background, blank areas where load is in progress?
Debugging: have you tried doing NSLog during the process, for example for each cache call, seeing if it is doing a lot of unnecessary work?