I was wondering which would be the best way to scale my Core Graphics drawings as I zoomed in using a UIView subclass within a UIScrollView.
Currently when I zoom in, the drawing becomes slightly fuzzy - I know this is correct functioning, I just want to know how developers override it to keep the image sharp...
Thanks!
(P.s. I have heard about using Core Animation, but I don't know where to start on that huge library with scary names haha!)
The simplest way is to use supersampling. Basically, create your image at a larger resolution than it's displayed at, and scale it down for display (UIImageView will scale it for you). Then when you zoom in, it will simply be scaling back up to the original resolution.
The downside to this approach is you can't scale your image up too large or it will take up too much memory.
Related
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.
Case: I have a simple piece of code for displaying/zooming/scrolling a large image in an UIImageView->UIScrollView. In this UIScrollView content I want to place buttons to create clickable areas.
Issue: When I zoom in the quality remains proper but, when I zoom out to the highest posible level than the image is getting dotted and some lines aren't visible anymore.
Tried: I tried to regenerate the UIImage with Interpolation Quality on kCGInterpolationHigh and tried to change the size of the image after every zoom change. As you might aspect, no results jet.
I suggest you use a CATiledLayer as the backing layer, as demonstrated by Apple's PhotoScroller sample app. This allows you to prebuild the scaled versions, meaning you can precisely control the interpolation quality with Photoshop/ImageMagick/GIMP etc, rather than relying on UIScrollView's built-in scaling.
I would appreciate some advice on an iphone game design. I want to display some backround image and other images on top of it (buildings, characters etc). The backround is going to be large (up to 10 times the size of the screen) so only a piece of the background file will be displayed at once. The idea is to replace this piece when character gets close to screen borders. I need to make this background transition as a smooth animation. Also, I need to have a zoom in/out feature, preferably animated. Some images on the screen will be static (buildings) and some will require some animation (character walking).
What is the best design:
Use Core Graphics combined with
"sprite" classes - displaying
sprite's UIImage with
CGContextDrawImage
Use UIKit -
create UIImageview to hold every
image and add them as subviews in a
single view appplication
Use OpenGL
ES project
Option 1) turned out to be very slow. It seems like CoreGraphics is not meant to display images in a game loop. But maybe there is a way to make it effecient? Maybe combine it with Core Animation somehow?
Option 2) is my current choice. I am hoping the view to cache the image it holds and thus be more effecient than CG. But will the animation provided by UIImageview will be satisfactory? I think the views shouldn't be added all at once, but rather created & added (removed?) dynamically when background moves. Is it a good idea?
Option 3) would probably give the best control over the images but it seems like quite an overhead. I only need to display images, not vector graphics. Plus I'm new to Mac programming and I don't want to get stuck in some complex technology.
I appreciate any advice, thanks :)
I highly recommend Cocos2D as I've done my own development here on my blog. It was really easy to do. I follow Ray Wenderlich's tutorials and he provides great tools for doing everything you describe.
You asked "The backround is going to be large (up to 10 times the size of the screen) so only a piece of the background file will be displayed at once."
The tiled image system is very powerful and fast performance. If you use google maps you will see and example of a tiled image. Scroll off to a new are and blocks appear. In a local app you could take your image that is 10 times the size of the screen and cut in to tiles that are say 100px by 100px and each screen will only load the tiles that are displayed. When the user moves only the needed tiles are loaded. This saves memory and dramatically improves speed. It is the base reason why tables can fly, only the cells one screen are loaded, as is scrolls off the screen it's memory is reused for the next cell.
If option 2 is sufficiently performant for your needs I would stick with that - it's as easy a system as you'll get on the iPhone and fine for very simple graphics. A related option that might buy you a little bit of speed is using CALayers to implement the graphics. CALayers are almost as easy to use, but are a bit more lightweight than UIViews (in some ways you can think of UIViews as just wrappers for CALayers with additional overhead for managing things like touch events, etc.)
If you're interested I would read the Core Animation Programming Guide (I would provide a link but I think my reputation is too low, but Google should track it down for you). Core Animation is a big subject and can be pretty daunting but if you just use layers (i.e. not the animation parts of it) it's not so bad. Here's a quick example to give you a sense of what using layers looks like:
// NOTE: I haven't compiled this code so it may have typos/errors I haven't noticed
UIView* canvasView; // the view that will be the "canvas" for your game
... // initialize the canvas, etc.
CALayer* imageLayer = [CALayer layer];
UIImage* image = [UIImage imageNamed: #"MyImage.png"];
imageLayer.content = (id)image.CGImage;
imageLayer.bounds = CGRectMake(0, 0, image.size.width, image.size.height);
imageLayer.position = CGPointMake(100, 100); // NOTE: Unlike UIViews CALayers have their origin at the center
[canvasView.layer addSublayer:imageLayer];
So basically it looks a lot like working with views but with some added performance (and occasional headache).
P.S. - One thing to keep in mind is that if you make changes to a layer's property that is animatable (e.g. position, opacity, etc.) Core Animation will implicitly animate it (e.g. if you write imageLayer.position = somePoint; the layer animates to that position rather than having it's position set immediately. There's easy ways to work around that but that's a topic for another question/answer.
Pretty new to iPhone / objective-C.
I have an application that has 15-100 small images (16x16 or 8x8 PNG) on the screen. For this example sake, let's assume that I can create these images using CGContext if I needed to.
I would have to assume that iPhone would perform better using that method rather than loading images (PNG's). However, the bitmap version is easier to develop and also has other advantages (like built in touch events) that I need.
If performance is not the ultimate metric for this application, does placing 100 small images degrade performance/memory enough to even consider switching to the CGContext method. My instinct tells me that I will not see that much of a performance difference either way but I am too new to iPhone development to know enough about it to make a difference.
I suppose it depends on the complexity of your image generation algorithm.
I will also depend on you application: will you be drawing this images many times per second, like in an animation? If that's the case, use UIImageViews.
I think using 100 or so UIImageViews should be fine as long as you don't need to rapidly animate them or update them at the same time. You should avoid doing anything that would change the size of the views (like resizing the view that contains them all), and if you use Core Animation to animate them, perform all of the animations inside a single animation block. (Wrap everything with one [UIView beginAnimations:context:], [UIView commitAnimations] - not one for each view)
Good luck!
I'd try the bitmap version first, then CGContext one if bitmap is too slow.
THEN if it's still too slow, I'd put all the icons into a GL texture.
I'm working on a game where I need to let the player look at a plane (e.g., a wall) through a lens (e.g., a magnifying glass). The game is to run on the iPhone, so my choices are Core Animation or OpenGL ES.
My first idea (that I have not yet tried) is to do this using Core Animation.
Create the wall and objects on it using CALayers.
Use CALayer's renderInContext: method to create an image of the wall as a background layer.
Crop the image to the lens shape, scale it up, then draw it over the background.
Draw the lens frame and "shiny glass" layer on top of all that.
Notes:
I am a lot more familiar with Core Animation than OpenGL, so maybe there is a much better way to do this with OpenGL. (Please tell me!)
If I am using CALayers that are not attached to a view, do I have to manage all animations myself? Or is there a straightforward way to run them manually?
3D perspective is not important; I'm just magnifying a flat wall.
I'm concerned that doing all of the above will be too slow for smooth animation.
Before I commit a lot of code to writing this, my question is do you see any pitfalls in the plan above or can you recommend an easier way to do this?
I have implemented a magnifying glass on the iPhone using a UIView. CA was way too slow.
You can draw a CGImage into a UIView using it's drawRect method. Here's the steps in my drawRect:
get the current context
create a path for clipping the view (circle)
scale the current transformation matrix (CTM)
move the current transformation matrix
draw the CGimage
You can have the CGImage prerendered, then it's in the graphics memory.
If you want something dynamic, draw it from scratch instead of drawing a CGImage.
Very fast, looks great.
That is how I'd do it, it sounds like a good plan.
Whether you choose OGL or CA the basic principle is the same so I would stick with what you're more comfortable with.
Identify the region you wish to magnify
Render this region to a separate surface
Render any border/overlay onto of the surface
Render your surface enlarged onto the main scene, clipping appropriately.
In terms of performance you will have to try it and see (just make sure you test on actual hardware, because the simulator is far faster than the hardware). If it IS to slow then you can look at doing steps 2/3 less frequently, e.g every 2-3 frames. This will give some magnification lag but it may be perfectly acceptable.
I suspect that performance between OGL / CA will be roughly equivalent. CA is built ontop of the OGL libraries but your cost is going to be doing the actual rendering, not the time spent in the layers.