One of my views freezes up for several seconds when I tap the back button.
In addition, when I tap on one of the items in this view, it shows a popup (custom, not a UIPopoverController). This popup appears quite fast, but when I "flip" the popup to see it's back side, the same long delay occurs.
I suspect the reason has something to do with the complexity of the view. As you can see in the screenshot below, it's a collection view, it has a background and some of the subviews are rotated (UIViewEdgeAntialiasing is on).
I used the Time Profiler in Instruments to figure out what's going on, but I'm stuck.
I don't see anything useful unless I deselect "Hide System Libraries":
If I look at the method names, I think they are related to auto layout. That suggests that it's trying to render something during the segue. But methods such as cellForItemAtIndexPath are not called.
There is also an iPhone version of this app where I don't experience this problem at all. It uses a tableview in stead of a collectionview. It also has a background and rotated pictures.
I took these measurements using the simulator; on my iPad Mini the situation is worse; it can take up to 20 seconds before the animation starts.
Update - Things I've tried thanks to your answers:
turn off UIViewEdgeAntialiasing : no effect on performance
I think this might be due to the UIViewEdgeAntialiasing flag. It seems that your main view (the one with lots of slightly rotated pictures) have lots of antialiased edges and hence is very taxing on the iPad's GPU. The fact that the drawing performance slows down when your popover is spinning (ie when the background is showing again) gives this some credence.
Try turning it off and see if the performance improves. How does it look like?
Rotation was the bad guy here. Each UICollectionViewCell has a UIView as a container view and within that is a UIImageView. I rotate it like this:
container.transform = CGAffineTransformMakeRotation(M_PI * someRandomFloat);
Remove that line and everything is snappy.
I use the same technique on the iPhone, but apparently this kind of rotation has less of a performance impact in UITableViewCell than in UICollectionViewCell.
I tried subclassing UICollectionViewFlowLayout to rotate cell itself in stead of one subview. Unfortunately that causes a similar performance issue.
Related
In storyboard, I have buttons attached to main view as siblings to SCNView(both buttons and SCNView are subviews of main view). At launch time, the buttons appear on screen before the SCNView does. This is quite unfortunate. Do you have any idea how to fix this?
//edit: added button programatically in viedDidLoad: as a subview of SCNView..Still the same problem.
SettingsButton and View appear before SceneView. This happens on iPhone 5,5c and lower and on iPads.(3 for sure, don't know for others)
Code for adding view programatically:
let uv = UIView(frame:myFrame)
uv.backgroundColor = UIColor.blackColor
sceneView.addSubview(uv)// scene view from storyboard
Thats it. How can iOS take 5-10s to load an app, but than rushes with adding views to the screen in an unpredictable order.. Couldn't it just take 0.2s more and present view properly? Really stupid problem
I imagine if you check via debugger breakpoints or logging in viewDidLoad, you'll find that the SCNView instance is indeed in the view hierarchy immediately.
What you're seeing is that said view's SceneKit content does not appear immediately. Depending on what you're doing with SceneKit, it may take some time to load content and prepare for rendering, and during that time the rest of the view hierarchy remains visible. That delay is probably unavoidable — you might be able to minimize it by optimizing asset formats, reducing the amount of stuff in your scene, etc. So it sounds like the key issue here is getting your app launch experience to look the way you want while accounting for the delay.
A good way to do that might be to show a splash image of some sort until SceneKit is ready to display. You could put a UIImageView in your storyboard, set up to obscure everything with a splash screen (possibly the same launch image iOS displays before your app takes over), then hide that image view (with a fade out even?) when SceneKit starts rendering.
To find out when SceneKit starts rendering, make one of your objects the scene renderer delegate and implement renderer:didRenderScene:atTime:. That method gets called on every frame, so you can use it to catch when the first frame has been rendered (and then ignore it for subsequent frames).
Here is the problem :
I am writing an app which displays some pictures, with a treemap layout (for an example, see https://raw.github.com/beckchr/ithaka-treemap/master/Core-API.png)
This layout is displayed in a UIScrollView. Since many pictures can be added to that scrollview, I want to release the ones which are not on currently on screen. I am not using ARC.
At my point, I know which pictures I should release, and how to do it while scrolling (calling some "unload" method). There is no useless call of that method. The problem is that, when pictures are released, the scrolling stops for a little moment (a few ms, but this is enough to be bad looking, making the scroll kind of "jumping" and slow, not smooth at all).
What I've tried (put in the body of my "unload" method) :
imageview.image = nil
performSelectorInBackground:#selector(effectiveUnload) withObject:nil
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0,^(void){
dispatch_sync(dispatch_get_main_queue(), ^(void){
imageview.image=nil}
}
I think this problem is weird, since there is absolutly no slowing effect with memory allocation, but only with memory release.
Thanks for help, don't hesitate to ask for more information.
did you try removeFromSuperview to remove the imageview from scrollview
Don't add more and more UIImageViews - recycle them!
To save as much memory as possible you should follow the UITableView way of recycling views:
1. Once a view has left the visible area, add it to a "views pool" (and remove it from its superview. It's not an expansive operation!)
2. When a new view becomes visible, first check if there's a view in the pool. If not, then create a new view.
I know my answer doesn't answer your question directly, but that's the way to go. Otherwise you'll run out of memory eventually.
Facing a really, really weird problem with an OpenGL View we are using in our app to perform some custom animations. As soon as the Open GL View is added into the project, all native View animations slow down. And by slow down I don't mean a drop in frame rate. The animations are stutter-free, except much much MUCH slower than normal (like someone enabled "Toggle Slow Animations" in the Simulator).
This is affecting only view transitions animations, for example:
a. transitionFromView
b. presentViewController (iOS6, or presentModalViewController on earlier)
While regular UIView Animations, CABasicAnimation etc proceed at the regular pace.
I haven't seen anything like this, and the results honestly have to be seen to be believed. :) But any idea what the problem could be (I'm not sure which piece of the code would help you debug, and I'm unfortunately not in a position to share screenshots or video)
It seems like the animation gets stuck while loading the OpenGL View, debug the
Lifecycle methods like loadView, viewWillLoad. The loadview, etc. happens within the
transition animation, maybe you can solve the slow animation by sending most of the loading
code in to a custom method which you call in viewdidload or viewdidappear.
It does not interrupt any other animation because thats a totally new action in the queue.
Hope this helps!
Found the solution accidentally several days later.
The problem turned out to be much sillier and unrelated. It so happens that if you have a UIView beginAnimation block that is not closed properly, future animations get all wonky. This faultily coded animation happened to be triggered at more or less the same time as the OpenGL view was being initialized, which led to my erroneous belief that the OpenGL View was at the root of this.
Thanks for the help!
We have an out-of-memory crash on the iPad 3 which we traced to the following scenario:
A UIView which uses a CATiledLayer and draws content (say, a PDF) has subviews with their own drawRect methods (which, for example, highlight search results). This makes Core Animation consume tons of memory (100+ MBs in the VM Tracker instrument), and can easily lead to a crash. While this issue exists on all devices, only on the iPad's Retina display does the cache size grow too large.
This can be reproduced with Apple's PhotoScroller example: subclass UIView, uncomment drawRect, and add an instance to the TilingView. The app will crash on iPad 3. Commenting drawRect resolves the memory usage.
Now, we can drop the subviews and do the drawing in the top-most UIView. However, working with subviews is convenient (since we're representing different, independent layers on top of the PDF). Two questions:
What is a good work-around? Preferably one that allows us to continue working with multiple views.
Why is this happening, exactly? I guess the cache mechanism is working overtime, but it would be great to understand the technical details behind it.
Thanks!
EDIT:
I want to elaborate on Kai's answer. The problem was indeed unrelated to CATiledLayer, but to the usage of UIViews that implemented drawRect.
In the case of PhotoScroller, I created a UIView which was of a size of the image - 2000x2000 and more, which creates a huge backing store if drawRect is present.
In the case of our app, the overlay views are full-screen (=~11 MBs on iPad 3) and we have about 5 of them per page. We keep up to three pages in memory while scrolling, and that means more than 150 MBs extra memory. Not fun.
So the solution is to optimize drawRect away, or use less such views. Back to the drawing board it is :-)
To 2.: Whenever you implement drawRect in a UIView subclass and have lots of instances of that class, your memory usage will grow dramatically. Reason is that a lot of optimization tricks in UIKit view/subview handling (e.g. when zooming or scrolling) don't work with such objects, because the framework doesn't know what you're doing/what you are drawing.
So - independent of retina or not - avoid implementing drawRect, especially when having many objects or many layers of subviews.
To 1.: I didn't exactly get what you are trying, but I implemented an PDF-Viewer which is also able to show additional content on top of the PDF. I did it all with normal UIView hierarchies, images etc. and I fear that's the only reliable work around you'll get
My experience:
Never add subviews to a UIView that's backed by a CATiledLayer
Never add sublayers to a CATiledLayer
Unfortunately, that seems to be the only practical answer - Apple's implementation goes horribly wrong in many different ways (not just performance - the rendering itself starts to exhibit visual artifact bugs, some of Apple's rendering code goes weird, etc).
In practice, I always do this:
UIView : view
+-- UIView w/ CATiledLayer : tiledLayerView
+-- UIview : subViewsView
...and safely add views and subviews to "subViewsView". Works fine.
OK, I think it's time to make an official place on the internet for this problem: How to make a UIScrollView photoviewer with paging and zooming. Welcome my fellow UIScrollView hackers.
I have a UIScrollView with paging enabled, and I'm displaying UIImageViews like the built-in photos app. (Does this sound familiar yet?)
I found the following project on github:
https://github.com/andreyvit/ScrollingMadness/wiki
Which shows how to implement zooming in a scroll view while paging is enabled. If anyone else tries this out, I actually had to remove the UIScrollView subclass and use the native class otherwise it doesn't work. I think it's because of changes in the 3.0 SDK relating to how the scroll view intercepts touch events.
So the the idea is to remove all the other views when you start zooming, and move the current view to (0, 0) in the scrollview, updating the contentsize etc. Then when you zoom back to 1.0f it adds the other views back and puts things all back in order.
Anyway, that project works perfectly in the simulator, but on the device there is some nasty movement of the view you are resizing, which looks like it's caused by the fact we are changing the contentsize/offset etc. for the view being resized. You have to do this view moving otherwise you can pan left through the whitespace left by the other views.
I found one interesting note in the "Known Issues" of the 3.0 SDK release notes:
UIScrollView: After zooming, content inset is ignored and content is left in the wrong position.
This kind of sounds like what is happening here. After zooming in, the view will shift offscreen because you have changed the offset etc.
I've spent hours on this already and I'm slowing coming to the sad realization that this just isn't going to work.
Three20's photo viewer is out of the question: it's too heavy weight and there is too much unnecessary UI and other behaviour.
The built in Photo app seems to do some magic. If you zoom in on an image and pan to the far edges, the current photo moves independently of the photo next to it which isn't what you get when trying this with a standard UIScrollView.
I've seen discussion about nesting the UIScrollView's but I really don't want to go there.
Has anybody managed this with the standard UIScrollView (and works in the 2.2 and 3.0 SDK)? I don't fancy rolling my own zoom + bounce + pan + paging code.
UPDATE
I deleted my previous answer because of the news below...
Big news for those who haven't heard. Apple has released the 2010 WWDC session videos to all members of the iphone developer program. One of the topics discussed is how they created the photos app!!! They build a very similar app step by step and have made all the code available for free.
It does not use private api either. Here is a link to the sample code download. You will probably need to login to gain access.
Check This
And, here is a link to the iTunes WWDC page:
Check This
I've written a simple and easy to use photo browser called MWPhotoBrowser. I decided to create it as Three20 was too heavy/bloated as all I needed was a photo viewer.
MWPhotoBrowser can display one or more images by providing either UIImage objects, or URLs to files, web images or library assets. The photo browser handles the downloading and caching of photos from the web seamlessly. Photos can be zoomed and panned, and optional (customisable) captions can be displayed. The browser can also be used to allow the user to select one or more photos using either the grid or main image view.
You say you've seen discussions of nesting UIScrollViews but don't want to go there - but that is the way to go! It works easily and well.
It's essentially what Apple does in its PhotoScroller example (and the 2010 WWDC talk linked to in Jonah's answer). Only in those examples, they've added a whole bunch of complex tiling and other memory management. If you don't need the tiling etc. and if you dont want to wade through those examples and try and remove the bits related to it, the underlying principle of nesting UIScrollViews is actually quite simple:
Create an outer UIScrollView and set its pagingEnabled = true. Add it to your main view and set its width and height to your main view's width and height.
Create as many inner UIScrollViews as you want images. Set their width and height to your main view's width and height. Add them as subviews to your outer UIScrollView, each one next to the other, left to right.
Set the content size of the outer UIScrollView to the total of the widths of all the inner UIScrollViews side by side (which is equal to [your main view's width]*[number of images]).
Add your images' UIImageViews to the inner UIScrollViews, one UIImageView to each inner UIScrollView. Set each UIScrollView's content size to each UIImageView's size.
Set min and max zoom scales for each inner UIScrollView and set each of the inner UIScrollView's delegate to your View Controller. In the delegate's viewForZoomingInScrollView, return the appropriate UIImageView for the UIScrollView that is passed. (To do this, just keep each of the UIImageViews in an NSArray and set the corresponding UIScrollView's tag property to the index of the appropriate UIImageView. You can then read the tag in the UIScrollView passed to viewForZoomingInScrollView and return the appropriate UIImageView from the NSArray).
That's it. Works just like the photo app.
If you have a lot of photos, to save memory you can just have two inner UIScrollViews and two UIImagesViews. You then dynamically flip between them, moving their position within the outer UIScrollView and changing their images as the user scrolls the outer UIScrollView. It's a bit more complex but the same principle.
I did some playing around with the native Photos app, and I think I can say with confidence they are using a single UIScrollView. The giveaway is this: zoom in on an image, and pull to the left or right. You will see the next or previous photo. If you tug hard enough, it will even page to the next photo at 1.0f zoom. Flip back and the previously zoomed photo will be back to 1.0f zoom as well.
Obivously I didn't write Photos.app, but I'll take a wild guess at how they did it:
A single UIScrollView and a single UIScrollViewDelegate
Populate the UIScrollView with UIImageView children
Listen for scrollViewDidScroll:
Do some math and figure out what page you are currently on
Listen for viewForZoomingInScrollView:
Return a different view depending on the page index
Listen for scrollViewDidEndZooming:withView:atScale: and optionally do some anti-aliasing, etc based on the content
If you decide to try that out, let me know how it works out for you. I'd love to know how you finally end up getting this to work. Even better, post it to github.
I did some playing around with the
native Photos app, and I think I can
say with confidence they are using a
single UIScrollView. The giveaway is
this: zoom in on an image, and pull to
the left or right. You will see the
next or previous photo. If you tug
hard enough, it will even page to the
next photo at 1.0f zoom. Flip back and
the previously zoomed photo will be
back to 1.0f zoom as well.
This is wrong. I'm using nested scrollviews, and getting exactly the same effect. If you're using some memory management scheme (which I had to start using... my page number is fairly high ('bout 50 each in 2 scrollViews)), then you can use a mechanism similar to whatever you have triggering your page loads / unloads to trigger a zoom reset for the pages -1 and +1 from the current page.
I suspect that apple sets this off as soon as the previous pic has disappeared.
What I don't understand is how to achieve smooth scrolling between pages - there's always a very short hang at the moment of transition. Do not get it. I've gotten pretty deep into fixing it - NSInvocationOperations were my first stop, then I made a reusable views queue for the page views (which retain their image views)... still this durned hang.
I only have one NSOperationQueue running, and I've tried fiddling with the max number of concurrent operations. My thought was that the main thread was getting clogged by competing Queues, or maybe even one queue trying to do to much... still, the hang.
I even tried creating super low-qual versions of my media, in case that was the problem. With each image weighing in at around 10k (these are jpegs, mind you)... you guessed it. The hang's still there.
I'm pretty much resolved to do what I've done before and use TTPhotoViewController from Three20. I've spent some hours swimming through that code, and it's always a great education. At this point, though, I would really like to know where the heck this hang comes from, if only so I can spend my can't-sleep hours wondering about something less brain boiling.
sure would be nice if apple built an image viewer like the photos app into the SDK for us to use. I'm currently using three20 and it works great. But it is a lot of extra stuff to carry around when all you really want is the photo viewer.
i write a code for that , and can be as reference
load current view scrollview and imageview ..
and for the screen next to the current view , only imageview
remove all view when current page load to save memory , so good for many photo project
use tag to differentiate different scrollview
_xxxxxxx
the download link click here
Take a look at https://github.com/facebook/three20/blob/master/src/Three20UI/Headers/TTPhotoViewController.h Not sure if that's what you are looking for