© 2011 Eric Knapp

Home Simplied Scrolling with Tiled Images

Simplified Scrolling with Tiled Images

We're going to start as simply as we can to get into displaying tiled images in iOS apps. We still need a UIScrollView but that's just the beginning.

Our Goal

  • Here's a nice sized image. This image has over 81 million pixels and is 162.5 MB in size. The iPad 1 only has 256 MB of RAM and an app trying to show this image would not run, it would be terminated by iOS. Almost all desktop apps that can open images could open this one. Here's a look at a smaller version:

    • This image was create by NASA from the Hubble telescope. It is licensed for free and unrestricted use as long as I acknowledge the source.
  • Here's a link to the source of the image: NASA Orion Nebula Image Site
  • Here's a link to the image I used if you would like to download it. Orion Nebula PNG (162.5 MB)
  • If you could see it on an iPad, it would look like this:

  • Is that really possible on an iOS device?
  • Yes!

Here's the finished project source code.

Start the Project

  • This will be a mostly code-based project.
  • Create a new Window-based Application project in Xcode. (I'm using Xcode 4.)

  • Name the project. I'm doing this project for the iPad.

  • I'm going to pick some settings that will simplify the example. Here I'm setting the app for use in Landscape mode only.

  • Add the Quartz 2D Library.

  • We need to make three subclasses of Cocoa Touch classes. Here's a list of the classes I created and their superclasses.
    • DCSimplifiedTilesViewController : UIViewController
    • DCSimplifiedTilesView : UIView
    • DCSimplifiedTilesLayer : CATiledLayer

Let's look at the code.

Here's a detailed look at the relevant parts of each of these classes. The line numbers correspond to the line numbers in the actual source code.


  • Here's the DCSimplifiedTilesViewController.h file
    • We might as well declare this to be a UIScrollViewDelegate in case we need it someday.
  • In the DCSimplifiedTilesViewController.m implementation file, I set this view controller to only be in landscape mode.
  • The important code in this class is to set up the view in the loadView method.
  • Let's look at the code in detail. The line numbers in the following snippets match the method above except for the comments //... before and after the line we are focused on.
    • To make the code simpler, I am hard-coding the rect for the views. I also have fixed the app in landscape mode so this will work. This rect will become the frame of the first UIView we add to the controller.
    • The first value in the rect is the x position with respect to the underlying window. It is 0 because the view is all the way to the left.
    • The second value is the y position and is shifted 20 points to account for the top bar on the iPad.
    • The third value, 1024, is the width of the view, which is also the width of the screen.
    • The fourth value, 748, is the height of the view, which is the height of the screen in landscape mode minus 20 for the top bar.
  • Create the first view.
    • Initialize the view object using the frame into a temporary local variable.
    • Then set its backgroundColor to black.
  • Set the new view as the view controller's view.
    • Assign the new view to the controller's view.
    • Release the temporary variable.
  • Variables for the image size.
    • I'm hard-coding these values because I'm using one image.
    • (Wow, that's a big image.)
  • Create the Rect for the scroll view.
    • Make the rect for the scroll view. Since it will be added to the first view the origin of the rect is 0, 0.
  • Create the scroll view.
    • Line 54: Create the UIScrollView instance with the scroll rect.
    • Line 56: Make its contentSize the size of the image.
    • Line 57: Set the delegate for the scroll view to be our main view controller.
    • Line 58: Set the background color to be clear (transparent).
    • List 59: Set the deceleration rate. This is optional.
  • Create the rect for the view that will show the image.
    • The rect is made based on the image size.
  • Create the custom view with the big rect.
  • Add and Release the custom view.
    • Line 65: Add the new custom view to the scroll view's subviews.
    • Line 66: Release the local custom view pointer.
  • Add and Release the scroll view.
    • Line 68: Add the scroll view to the view controller's view's subviews.
    • Line 69: Release the local scroll view pointer.


  • The DCSimplifiedTilesView.h file.
    • Line 16: We'll create one method in the view, tileAtRow:column:
  • Here's the entire DCSimplifiedTilesView.m implementation file.
  • The detailed breakdown. First, we have to import the custom layer we will be making.
  • Make a constant for the tile size. 256 is a good number for the iPad since the screen dimensions are multiples of 256.
  • Override this class method.
    • The layerClass is part of the UIView class. All UIView subclass instances are backed by a Core Animation Layer (CALayer). This method is used when the layer is being created.
    • Line 16: We need to return our custom layer class.
  • The tileAtRow:column: method.
    • This method returns the image for a specific row and column. The tiles are named with a pattern list this: x1y1.png, x1y2.png, etc.
    • Lines 22-23: Generate the file name.
    • Lines 25-27: Create the file path.
    • Line 29: Use the method initWithContentsOfFile: to load the image. This method does not cache the image, it just loads it.
  • Draw each Tile.
    • Now for some magic. The drawInRect: usually is called to draw the entire view's frame. However, when the view has a CATiledLayer instead of the default CALayer, then the behavior changes. With a tiled layer, the drawInRect: method gets called once for each row and column combination that is showing on the screen. The real magic is that the method is called for only what is showing on the screen. The view can be huge, as in the example, but the app will always draw just what you see on the screen plus any tiles that extend off the screen.
    • Lines 38-39: Calculate the current row and column. We need this to generate the name of the tile image.
    • Line 41: Get the image that will be drawn into the current rect.
    • Lines 43-46: Create a rect that is the size of the image, and positioned at the correct place in the view frame. Some of the images are smaller than a full tile. This occurs at the right and bottom edges of the full original image.
    • Line 48: The CGRectIntersection() function will return the intersection of two rects. In our case the smaller tiles at the bottom and right edges of the full image will be fully inside the rect that passed to the method. With this function call, we don't have to know the sizes of the smaller tiles.
    • Line 50: Now draw the image tile on the screen.
    • Line 52: The tile UIImage was created with an alloc, so you have to release it.


  • The DCSimplifiedTilesLayer.h header file.
    • Line 9: We need to import the Quartz 2D library.
    • Line 11: Subclass CATiledLayer
  • The DCSimplifiedTilesLayer.m implementation file.
    • Yup, that's it.
    • The fadeDuration method is, "The time, in seconds, that newly added images take to "fade-in" to the rendered representation of the tiled layer." The default value is 0.25 seconds. We want that value to be 0.0 seconds. But the fadeDuration is a class method, we can only change the value in a subclass of CATiledLayer. For this single reason, we have to create our own subclass.
  • That's all the code! We're now scrolling through a very large file.

How does it work?