For this example, we use one image and crop this image multiple times using a category on UIImage. This category essentially crops a certain rectangle within the original image. We then setup our arranged cropped images in the UIImageViews
.
Walk along with the project by cloning the repo.
Step One
First we need to think through a little bit about how we are going to cut an image into multiple pieces. Let’s first start with a static number of views to cut the image. Let’s use 4. In this scenario, we are only going to use static numbers to just showcase how one would crop an image into pieces. First we open storyboard and add 4 UIImageViews in a 2x2 matrix. I created a custom class called SSTapCoveredImageView
that is subclassed from UIImageView, but that’s not important yet (see below, Part 2). Next we need to figure out a cropping method for a given image. I won’t go into too much detail on how we use Core Graphics, but as a preview, let’s walk through a cropping method.
Step Two
To crop an image, we need to know how we are going to crop it. In this case, we will be using a CGRect, because it is the easiest way to know where in the image we are going to cut. Second, we need to know the scale if, say, we are using Retina displays. That said, just a simple conversion if we are using Retina displays:
if (self.scale > 1.0f) {
rect = CGRectMake(rect.origin.x * self.scale,
rect.origin.y * self.scale,
rect.size.width * self.scale,
rect.size.height * self.scale);
}
Now that we have a rectangle, we need to cut the image in that rectangle. We father the reference of the image in core graphics, and create a new reference of that image.
CGImageRef imageRef = CGImageCreateWithImageInRect(self.CGImage, rect);
The next is just a little bit of bookkeeping that involves creating the image from the reference, releasing the reference from memory, and returning. See the full method below:
- (UIImage *)cropImageInRect:(CGRect)rect
{
if (self.scale > 1.0f) {
rect = CGRectMake(rect.origin.x * self.scale,
rect.origin.y * self.scale,
rect.size.width * self.scale,
rect.size.height * self.scale);
}
CGImageRef imageRef = CGImageCreateWithImageInRect(self.CGImage, rect);
UIImage *result = [UIImage imageWithCGImage:imageRef scale:self.scale orientation:self.imageOrientation];
CGImageRelease(imageRef);
return result;
}
Lastly, we put this into a category, namely UIImage+TileImage.m
(don’t forget the public declaration in the header!).
Step Three
Now that we have the ability to crop the image, we just need to decide how we want it cropped. In the storyboard, we created four pixel square image views with a width of 382 pixels. We also left a gap between the views. With a little math, we can then figure out the image sizes and locations. We also used the image that spans itself in this 768-width view (perfect for the iPad Retina’s screen width). We then easily know that we can split the image in quarters (minus two pixels for padding). Got it yet? Just in case you want to do the algebra later, here it is:
UIImage *theImage = [UIImage imageNamed:@"coffee-cup.jpg"];
CGFloat cropWidth = theImage.size.width/2-2;
self.theImageView1.image = [theImage cropImageInRect:CGRectMake(0, 0, cropWidth, cropWidth)];
self.theImageView2.image = [theImage cropImageInRect:CGRectMake(theImage.size.width/2+2, 0, cropWidth, cropWidth)];
self.theImageView3.image = [theImage cropImageInRect:CGRectMake(0, theImage.size.width/2+2, cropWidth, cropWidth)];
self.theImageView4.image = [theImage cropImageInRect:CGRectMake(theImage.size.width/2+2, theImage.size.width/2+2, cropWidth, cropWidth)];
We put that in the viewDidLoad
just to ensure that it runs. That’s it for tiling an image in views! Now onto something interesting…
Part 2
Creating an image in tiles is cool, but what is more fun is using them interactively. I'll show an update on the revised [Conway's Game of Life](https://www.codefellows.org/blog/the-game-of-life), but let's discuss how we can have fun with tiled images...Remember that we subclassed the image views? Well, we wanted to add an extra feature to the class and create a tappable instance to hide and show the image at any time. Since we are using image views, we need to be a little more tricky on how to create a view within. You can of course create a simple view and add layers, but I wanted to show a little more complicated way of doing it in order to give an outside-the-box solution.
Now all we really need to maintain is a layer that is covering the image. For that we create a couple of properties inside the class:
@property (nonatomic) BOOL isCovered;
@property (strong, nonatomic) UIView *coverView;
Next, we need to add some gesture recognition, not only on the image view, but on the cover also, because it will be the topmost layer when we cover it. Here’s some boilerplate code to create a single tap gesture:
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapDetected)];
singleTap.numberOfTapsRequired = 1;
self.userInteractionEnabled = YES;
self.coverView = [[UIView alloc] initWithFrame:self.bounds];
self.coverView.backgroundColor = [UIColor whiteColor];
[self insertSubview:self.coverView aboveSubview:self];
[self.coverView setAlpha:1.f];
self.isCovered = YES;
[self addGestureRecognizer:singleTap];
[self.coverView addGestureRecognizer:coverTap];
You may be wondering about why I am setting the alpha of the cover view and setting isCovered = YES
. We are going to start the image out as covered, and they can release it over time. Lastly, we just need to cover the action of it being tapped by creating a method for the selector tapDetected
.
Now, all we need to do is show the cover when we want the image covered, and hide the cover when we can see the image. That’s a simple if
else
statement to modify the alpha:
-(void)tapDetected{
self.isCovered = !self.isCovered;
if (self.isCovered) {
[self.coverView setAlpha:1.f];
} else {
[self.coverView setAlpha:0.f];
}
}
Not overly complicated right? Small amounts of code to add some cool features is the basis of every great app! Take it, build it up, and create something amazing! Good hunting!
Check out the original post and more resources on Steven’s blog.