CATiledLayer changing images with different sizes when zooming (Map related :) - iphone

I have an UIScrollView with a UIView which in turn contains a CATiledLayer which displays a map.
I have to start with an overview where one can zoom in VERY deep. So I made 4 different images (actually PDFs) with 4 different sizes of the map (the largest has something like 5700 x 6700px)
I add the smallest (the overview) to the CATiledLayer and then swap the images on certain zoom levels.
So far, this kinda works.
The problem is, that when I display the largest map (closest zoom), the tiles get VERY big and it takes ages to draw them (and depending on the levels of detail/bias, the app produces a memory warning an crashes).
I tried to start with the largest map and set the zoom-level to the minimum when the map first shows, but then it takes ages to draw the overview because the tiles become very small (when I don't set it the minimum-zoom, everything works smooth).
I tried to adjust the tile size when I switch the maps - but apparently, this doesn't work.
Any idea how I can solve this? ;)
Thanks for any help...
Edit:
I'm trying now to define two separate CATiledLayers and then swap them when a certain zoom-level is reached - but that makes the app crash?
[myContentView.layer replaceSublayer: tiledLayer with: tiledLayerBig];
oh my... :)
Edit2:
Ok - I got it working - more or less. The only thing that happens now is, that the CATiledLayer behaves strange when I add the largest image. It works fine for a while, but when a certain amount of tiles is drawn, I get a memory warning and the app crashes eventually.
I actually would expect that the invisible tiles would get disposed when they leave the viewport. This doesn't seem to happen, so eventually, all the tiles will be drawn which seems to be too much for the iPhone-memory... :(
Any ideas, how to solve this?

It sounds like the tiles that are no longer being used are not being released from memory. What does your custom override's
- (void)drawRect:(CGRect)rect method
look like?

Related

Finding a free space within current bounds of view on iOS

I have an infinite scrollview in which I add images as the user scrolls. Those images have varying heights and I've been trying to come up with the best way of finding a clear space inside the current bounds of the view that would allow me to add the image view.
Is there anything built-in that would make my search more efficient?
The problem is I want the images to be sort of glued to one another with no blank space between them. Making the search through 320x480 pixels tends to be quite a CPU hog. Does anyone know an efficient method to do it?
Thanks!
It seems that you're scrolling this thing vertically (you mentioned varying image heights).
There's nothing built in to UIScrollView that will do this for you. You'll have to track your UIImageView subviews manually. You could simply maintain the max y coordinate occupied by you images as you add them.
You might consider using UITableView instead, and implementing a very customized tableView:heightForRowAtIndexPath: in your delegate. You would probably need to do something special with the actual cells as well, but it would seem to make your job a little easier.
Also, for what it's worth, you might find a way to avoid making your solution infinite. Be careful about your memory footprint! iOS will shut your app off if things get out of hand.
UPDATE
Ok, now I understand what you're going for. I had imagined that you were presenting photographs or something rectangular like that. If I were trying to cover a scroll view with UILeafs (wah wah) I would take a statistical approach. I would 'paint' leaves randomly along horizontal/vertical strips as the user scrolls. Perhaps that's what you're doing already? Whatever you're doing I think it looks good.
Now I guess that the reason you're asking is to prevent the little random white spots that show through - is that right? If I may suggest a different solution: try to color the background of your scroll view to something earthy that looks good if it shows through here and there.
Also, it occurred to me that you could use a larger template image -- something that already has a nice distribution of leaves -- with transparency all along the outside outline of the leaves but nowhere else. Then you could tile these, but with overlap, so that the alpha just shows through to the leaves below. You could have a number of these images so that it doesn't look obvious. This would take away all of the uncertainty and make your retiling very efficient.
Also, consider learning about CoreAnimation (CALayer in particular) and CoreGraphics/Quartz 2D ). Proper use of these libraries will probably yield great improvements in rendering speed.
UPDATE 2:
If your images are all 150px wide, then split your scrollview into columns and add/remove based on those (as discussed in chat).
Good luck!

ZoomingPDFViewer apple example

I'm trying to understand Apple's example code for the ZoomingPDFViewer. Here are some questions that I have in my understanding of how it works in my mind. I'm not really sure if I understand it correctly. The link for their code is at: http://developer.apple.com/library/ios/#samplecode/ZoomingPDFViewer/Introduction/Intro.html
(1) CATiledLayer is used to represent the PDF at different zoom levels. I'm assuming that's what this class is used for looking at the Class Reference. Would you ever use something else besides this class for a zooming function?
(2) in the initWithFrame for TiledPDFView, they do: tiledLayer.tileSize = CGSizeMake(512.0, 512.0); Is the tileSize the tiles that make up for the whole image? If so, why such a large size?
(3) How does the oldPDFView and pdfView work? Like which one is in front at the different stages of zoom, and when do they get swapped out. I'm having a hard time understanding the flow of the logic. Thanks.
(1) If you don't require the level of detail to vary for different zoom levels, or if the PDF loads fast enough to not warrant drawing a couple of tiles at a time, a regular UIView with a regular CALayer will work fine. For instance, if you were displaying an image instead of a PDF, and the image loads fast enough to not cause a performance snag, you would not need the asynchronous loading that CATiledLayer provides. The PhotoScroller sample uses both the tiled and non-tiled approaches if you want to compare them.
(2) The tileSize attribute changes the size of the blocks the layer should be split into. You can set this to whatever you want. 512x512 really isn't all that large, especially if your PDF dimensions are big. The default is 256x256.
(3) Anytime you start to zoom, oldPDFView is removed and released. Then pdfView is assigned to oldPDFView. When the zooming ends, a new pdfView is created with the change in scale and added on top of the old one. If the new scale is an increase, the new pdfView will be drawn with a higher level of detail. This makes it so you can zoom deeper and deeper into the PDF. The maximumZoomScale and minimumZoomScale only restrict how much you can zoom with an individual gesture.

Two visible CATiledLayers causing problems!

This issue has been encountered by someone else, but I've not seen any solutions - see ( http://openradar.appspot.com/6941930 ).
I have a UIScrollView whose content view's layer contains two CATiledLayers. One tiled layer draws content quite fast (but long enough to make drawInRect: too slow!), the other draws them slowly.
The quicker CATiledLayer doesn't Zoom has has default LOD settings, with a large tile size.
The slower CATiledLayer is zoomable.
Using just one of the CATiledLayers behaves as expected.
The problem is adding both layers to the visible screen causes one or the other to behave slowly. Annoyingly, it seems to make the content that draws normally fast, much slower.
I've not seen a way yet to reliably know when one CATiledLayer has completed its draw.
I think the solution would be to queue the Tiled Layers, so draw the slower tiled layer when the quicker one has finished. But the documentation on CATiledLayer is pretty bad.
I've tried to use a NSTimer, and set it for a 3 second wait before drawing the 2nd Tiled Layer. This however does not seem to solve the problem! I'm lost!
CATiledLayer seems to favor any tiles near the center of the screen. Perhaps one of your layer's is closer to an edge?

How many UIViews can be displayed simultaneously on iOS before running into performance problems?

I'm making an iPhone game and using UIView objects to draw sprites. Most of the time, I have no performance problems. However, once I have around 15 to 20 objects on the screen (and maybe 5 of them moving around), the game becomes considerably slower, especially on the iPhone 3G. The frame rate can drop to as low as a single frame per second.
Is this simply a limitation of using UIView objects, or should iOS be able to handle this many UIView objects on screen at the same time?
In order to isolate the problem, I've made drawing my views very simple — drawing a red rectangle. drawRect is only getting called once per view. The view hierarchy is very simple and shallow. I'm using CADisplayLink to update the UIView locations every frame.
There's very little else going on, so I'd like to hear if anyone else has had success using this number of UIView objects.
The key to my problems ended up being that I had labels on top of my game content. The labels are not opaque, which likely was a large part of the problem, as phix23 suggested.
The first thing that made a big difference was removing a frames per second label that was on top of the content. Having a label that changed content on every frame caused a lot of slowdown.
I also had a large label that displayed on top of much of the game and changed shape when you level up. It turned out that drawing this label on top of everything caused a lot of slowdown as well.
In answer to my original question, I've found that on an iPhone 3G I can support about 30-40 opaque UIViews onscreen at the same time, with 2 or 3 non-opaque views as well. Non-opaque UIViews that change size, shape, or location are by far the worst, and even one of these that covers a significant amount of the screen can quickly cause problems.
If you're setting the opaque property of each view to NO, keep in mind that this seriously affects the speed of drawing the views. If your views aren't transparent, you should leave this set to YES, which is default.
for such type of application you should use CoreGraphics / Quartz / OpenGL but anyway I don't think there is a limitation on such low count. For example if I have a table view with 9 rows and each row has 5 subviews its still displayed acceptable fast. Have you tried using UIView animation to change the position in view?
good luck in learning OpenGL ;)

Infinite maps/scrolling question

I'm using cocos2d for the iPhone to create an infinitely scrolling horizontal tile map. To achieve this, I've generated a library of 'segments', which are basically horizontal chunks of levels that I randomly choose from and append to the end of that particular levels tile map. When tiles scroll off of the left of the screen they are removed from the layer and released. This all works fine.
My question revolves around the legitimacy of the scrolling method I've chosen. Following guidance from this article, I've been scrolling my map by updating the layers position at regular intervals (subtracting from the x axis to move the layer to the left). And while this works, I'm concerned that there's probably some finite limit to the positioning of a layer. Am I going to run into issues after a certain amount of time has passed (when the layers x-axis position is considerably large?)
Any thoughts on my approach would be appreciated.
This is a good question. What I would do is run some tests on how far you can position the layer. I placed a sprite and focused the camera to ccp(1000000000000000, 1000000000000000) with no issues.
Do you really think this would be an issue in real gameplay? Seems like it would take a very long game to reach a position like that.