Friday, May 25, 2012

How To Use UIScrollView to Scroll and Zoom Content

UIScrollViews are one of the most useful controls in iOS. They are a great way to present content larger than a single screen, and there’s a lot of tips and tricks about using them you should know!
In this tutorial you’ll learn all about UIScrollViews, from beginning to advanced. You’ll learn:
  • How to use a scroll view to view a very large image.
  • How to keep the scroll view’s content centered while zooming.
  • How to embed a complex view hierarchy inside a UIScrollView.
  • How to use UIScrollView’s paging feature in conjunction with the UIPageControl, to allow scrolling through multiple pages of content.
  • How to make a “peeking” scroll view that gives a glimpse of the previous/next page as well as the current page.
  • And much more!
This tutorial assumes some familiarity with Objective-C and iOS programming. If you are a complete beginner, you may wish to check out some of the other tutorials on this site first.
This tutorial also assumes that you know how to use Interface Builder to add new objects to a view and connect outlets, so make sure that you do. Further, this tutorial uses a storyboard. You’ll want to get familiar with them if you aren’t already, perhaps by reading the Storyboards tutorial on this site.
Lastly, I’ll be using Xcode 4 in this tutorial, so make sure you’re fully updated to the latest version available through the Mac App Store.

Getting Started

Fire up Xcode and create a new project with the iOS\Application\Single View Application template. Enter ScrollViews for the product name, enter the company identifier you used when creating your App ID, leave the class prefix blank, set device family to iPhone, and make sure that Use Storyboards and Use Automatic Reference Counting are checked (but leave the other checkboxes.

To build the tableview menu, do the following:
  1. Open MainStoryboard.storyboard and delete the scene that’s already in there by selecting the view controller (click on it on the story board) and then deleting it.
  2. Then, add a table view controller by dragging one from the Object Library on to the story board.
  3. Now select the table you added and then click Editor\Embed In\Navigation Controller.
  4. Select the table view within the table view controller, and set the content type to Static Cells in the attributes inspector (as shown in image below).
  5. Finally, set the number of rows in the table view section (if you don’t see it, tap on the arrow next to “Table View” in the left sidebar showing the storyboard hierarchy and then select “Table View Section”) to 4, and for each row in the table view, set its style to basic and edit the labels to read:
    • Image scroll
    • Custom view scroll
    • Paged
    • Paged with peeking

    Scrolling and Zooming a Large Image

    The first thing you’re going to learn is how to set up a scroll view that allows the user to zoom into an image and pan around.
    First, you need to set up the view controller. Select ViewController.h, add an outlet for a UIScrollView called scrollView and declare that the view controller is going to be a UIScrollView delegate, like so:
    #import 
     
    @interface ViewController : UIViewController 
     
    @property (nonatomic, strong) IBOutlet UIScrollView *scrollView;
     
    @end
    Then, synthesize scrollView at the top of ViewController.m, just below the @implementation line:
    @synthesize scrollView = _scrollView;
    Back in the storyboard, drag a view controller from the objects list onto the storyboard and set its class to ViewController.

    Now you’re going to get down and dirty with ViewController.m. First you need to declare some properties and methods in the class continuation category at the top of the file (above the @implementation line).
    @interface ViewController ()
    @property (nonatomic, strong) UIImageView *imageView;
     
    - (void)centerScrollViewContents;
    - (void)scrollViewDoubleTapped:(UITapGestureRecognizer*)recognizer;
    - (void)scrollViewTwoFingerTapped:(UITapGestureRecognizer*)recognizer;
    @end
     
    That should give you a sneak peek of what you’re going to be doing in a moment. But first, synthesize the property you just added.
    @synthesize imageView = _imageView;
    And now it’s time to get into the most interesting part of setting up the scroll view. Replace viewDidLoad and viewWillAppear: with the following code:
    - (void)viewDidLoad {
        [super viewDidLoad];
     
        // 1
        UIImage *image = [UIImage imageNamed:@"photo1.png"];
        self.imageView = [[UIImageView alloc] initWithImage:image];
        self.imageView.frame = (CGRect){.origin=CGPointMake(0.0f, 0.0f), .size=image.size};
        [self.scrollView addSubview:self.imageView];
     
        // 2
        self.scrollView.contentSize = image.size;
     
        // 3
        UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(scrollViewDoubleTapped:)];
        doubleTapRecognizer.numberOfTapsRequired = 2;
        doubleTapRecognizer.numberOfTouchesRequired = 1;
        [self.scrollView addGestureRecognizer:doubleTapRecognizer];
     
        UITapGestureRecognizer *twoFingerTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(scrollViewTwoFingerTapped:)];
        twoFingerTapRecognizer.numberOfTapsRequired = 1;
        twoFingerTapRecognizer.numberOfTouchesRequired = 2;
        [self.scrollView addGestureRecognizer:twoFingerTapRecognizer];
    }
     
    - (void)viewWillAppear:(BOOL)animated {
        [super viewWillAppear:animated];
     
        // 4
        CGRect scrollViewFrame = self.scrollView.frame;
        CGFloat scaleWidth = scrollViewFrame.size.width / self.scrollView.contentSize.width;
        CGFloat scaleHeight = scrollViewFrame.size.height / self.scrollView.contentSize.height;
        CGFloat minScale = MIN(scaleWidth, scaleHeight);
        self.scrollView.minimumZoomScale = minScale;
     
        // 5
        self.scrollView.maximumZoomScale = 1.0f;
        self.scrollView.zoomScale = minScale;
     
        // 6
        [self centerScrollViewContents];
    }
    This might look complicated, so let’s break it down step-by-step. You’ll see it’s really not too bad.
  6. First, you need to create an image view with the photo1.png image you added to your project and you set the image view frame (it’s size and position) so it’s the size of the image and sits at point 0,0 within the parent. Finally, the image view gets added as a subview of your scroll view.
  7. You have to tell your scroll view the size of the content contained within it, so that it knows how far it can scroll horizontally and vertically. In this case, it’s the size of the image.
  8. Here you’re setting up two gesture recognizers: one for the double-tap to zoom in, and one for the two-finger-tap to zoom out. If you’re unfamiliar with how these work, then I suggest reading this tutorial.
  9. Next, you need to work out the minimum zoom scale for the scroll view. A zoom scale of one means that the content is displayed at normal size. A zoom scale below one shows the content zoomed out, while a zoom scale of greater than one shows the content zoomed in. To get the minimum zoom scale, you calculate how far you’d need to zoom out so that the image fits snugly in your scroll view’s bounds based on its width. Then you do the same based upon the image’s height. The minimum of those two resulting zoom scales will be the scroll view’s minimum zoom scale. That gives you a zoom scale where you can see the entire image when fully zoomed out.
  10. You set the maximum zoom scale as 1, because zooming in more than the image’s resolution can support will cause it to look blurry. You set the initial zoom scale to be the minimum, so that the image starts fully zoomed out.
  11. We’ll come back to this in a bit. For now, just understand that this will center the image within the scroll view.
You might ask why we don’t do all of the above in viewDidLoad, and you’d be right to ask. The reason you can’t is that the view size isn’t definitely known until viewWillAppear:, and since you use the size of scrollView when calculating the minimum zoom, things might go wrong if we do it in viewDidLoad.
Now let’s look at those methods that I said you’d implement – centerScrollViewContents, scrollViewDoubleTapped: and scrollViewTwoFingerTapped:. Add the following code above viewDidLoad:
- (void)centerScrollViewContents {
    CGSize boundsSize = self.scrollView.bounds.size;
    CGRect contentsFrame = self.imageView.frame;
 
    if (contentsFrame.size.width < boundsSize.width) {
        contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0f;
    } else {
        contentsFrame.origin.x = 0.0f;
    }
 
    if (contentsFrame.size.height < boundsSize.height) {
        contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0f;
    } else {
        contentsFrame.origin.y = 0.0f;
    }
 
    self.imageView.frame = contentsFrame;
}
The point of this method is to get around a slight annoyance with UIScrollView, which is: if the scroll view content size is smaller than its bounds, then it sits at the top-left rather than in the center. Since you’ll be allowing the user to zoom out fully, you’d rather like the image to sit in the center of the view, wouldn’t you? :] This method accomplishes that by positioning the image view such that it is always in the center of the scroll view’s bounds.
Next up is scrollViewDoubleTapped:. Add this just above viewDidLoad again:
- (void)scrollViewDoubleTapped:(UITapGestureRecognizer*)recognizer {
    // 1
    CGPoint pointInView = [recognizer locationInView:self.imageView];
 
    // 2
    CGFloat newZoomScale = self.scrollView.zoomScale * 1.5f;
    newZoomScale = MIN(newZoomScale, self.scrollView.maximumZoomScale);
 
    // 3
    CGSize scrollViewSize = self.scrollView.bounds.size;
 
    CGFloat w = scrollViewSize.width / newZoomScale;
    CGFloat h = scrollViewSize.height / newZoomScale;
    CGFloat x = pointInView.x - (w / 2.0f);
    CGFloat y = pointInView.y - (h / 2.0f);
 
    CGRect rectToZoomTo = CGRectMake(x, y, w, h);
 
    // 4
    [self.scrollView zoomToRect:rectToZoomTo animated:YES];
}
This method is called when the tap gesture recognizer fires. Remember, you set that up to recognize double-tap events. Here’s a step-by-step guide to what scrollViewDoubleTapped: does:
  1. First, you need to work out where the tap occurred within the image view. You’ll use this to zoom in directly on that point, which is probably what you’d expect as a user.
  2. Next, you calculate a zoom scale that’s zoomed in 150%, but capped at the maximum zoom scale you specified in viewDidLoad.
  3. Then you use the location from step #1 to calculate a CGRect rectangle that you want to zoom in on.
  4. Finally, you need to tell the scroll view to zoom in, and here you animate it, as that will look pretty.
The final method to implement is scrollViewTwoFingerTapped:. Once again, add this above viewDidLoad:
- (void)scrollViewTwoFingerTapped:(UITapGestureRecognizer*)recognizer {
    // Zoom out slightly, capping at the minimum zoom scale specified by the scroll view
    CGFloat newZoomScale = self.scrollView.zoomScale / 1.5f;
    newZoomScale = MAX(newZoomScale, self.scrollView.minimumZoomScale);
    [self.scrollView setZoomScale:newZoomScale animated:YES];
}
This is similar to the way you zoomed in, except you don’t bother calculating anything about where the user tapped in the view, because it doesn’t particularly matter. Trust me, it’ll look good if you just do it like this. :]
Now, remember how you set up ViewController as a UIScrollView delegate? Well, now you’re going to implement a couple of needed methods for a UIScrollView delegate. The first is viewForZoomingInScrollView. Add the following above viewDidLoad:
- (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView {
    // Return the view that you want to zoom
    return self.imageView;
}
This is the heart and soul of the scroll view’s zooming mechanism. You’re telling it which view should be made bigger and smaller when the scroll view is pinched. So, you tell it that it’s your imageView.
The second method you’ll need to implement is scrollViewDidZoom:, which is a notification when the scroll view has been zoomed. Here you need to re-center the view – if you don’t, the scroll view won’t appear to zoom naturally, instead, it will sort of stick to the top-left. Add this above viewDidLoad:
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
    // The scroll view has zoomed, so you need to re-center the contents
    [self centerScrollViewContents];
}
Now take a deep breath, give yourself a pat on the back and build and run your project. Tap on Image scroll and if everything went smoothly, you’ll end up with a lovely image that you can zoom, pan and tap.

Scrolling and Zooming a View Hierarchy

What if you want more than an image in your scroll view? What if you’ve got some complex view hierarchy which you want to be able to zoom and pan around? Well, there’s a scroll view for that! What’s more, it’s just a small step beyond what you’ve done already.
Create a new file with the iOS\Cocoa Touch\UIViewController subclass template. Name the class CustomScrollViewController and make sure that Targeted for iPad and With XIB for user interface are not checked. Click Next and save it with the rest of the project.
Open CustomScrollViewController.h and replace the contents with this:
#import 
 
@interface CustomScrollViewController : UIViewController 
 
@property (nonatomic, strong) IBOutlet UIScrollView *scrollView;
 
@end
Next, go to MainStoryboard.storyboard and just as before, add a view controller that’s wired up with a push segue from the 2nd row of the table. This time, set the view controller’s class to be the class just created, CustomScrollViewController.
Also add a scroll view and connect it to the outlet created and set the view controller as its delegate, just as before.
Then, open CustomScrollViewController.m and set up the class continuation category at the top (above the @implementation line) like this:
@interface CustomScrollViewController ()
@property (nonatomic, strong) UIView *containerView;
 
- (void)centerScrollViewContents;
@end
And add the property synthesizers below the @implementation line:
@synthesize scrollView = _scrollView;
@synthesize containerView = _containerView;
You’ll probably notice the lack of gesture recognizer callbacks. That is simply to make this part of the tutorial more straightforward. Feel free to add them in afterwards as an additional exercise.
The only other difference compared to the previous view controller is that instead of a UIImageView, we’ve got a UIView and it’s called containerView. That should be a little hint as to how this is all going to work.
Now, implement viewDidLoad and viewWillAppear: like so.
- (void)viewDidLoad {
    [super viewDidLoad];
 
    // Set up the container view to hold your custom view hierarchy
    CGSize containerSize = CGSizeMake(640.0f, 640.0f);
    self.containerView = [[UIView alloc] initWithFrame:(CGRect){.origin=CGPointMake(0.0f, 0.0f), .size=containerSize}];
    [self.scrollView addSubview:self.containerView];
 
    // Set up your custom view hierarchy
    UIView *redView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 640.0f, 80.0f)];
    redView.backgroundColor = [UIColor redColor];
    [self.containerView addSubview:redView];
 
    UIView *blueView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 560.0f, 640.0f, 80.0f)];
    blueView.backgroundColor = [UIColor blueColor];
    [self.containerView addSubview:blueView];
 
    UIView *greenView = [[UIView alloc] initWithFrame:CGRectMake(160.0f, 160.0f, 320.0f, 320.0f)];
    greenView.backgroundColor = [UIColor greenColor];
    [self.containerView addSubview:greenView];
 
    UIImageView *imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"slow.png"]];
    imageView.center = CGPointMake(320.0f, 320.0f);
    [self.containerView addSubview:imageView];
 
    // Tell the scroll view the size of the contents
    self.scrollView.contentSize = containerSize;
}
 
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
 
    // Set up the minimum & maximum zoom scales
    CGRect scrollViewFrame = self.scrollView.frame;
    CGFloat scaleWidth = scrollViewFrame.size.width / self.scrollView.contentSize.width;
    CGFloat scaleHeight = scrollViewFrame.size.height / self.scrollView.contentSize.height;
    CGFloat minScale = MIN(scaleWidth, scaleHeight);
 
    self.scrollView.minimumZoomScale = minScale;
    self.scrollView.maximumZoomScale = 1.0f;
    self.scrollView.zoomScale = 1.0f;
 
    [self centerScrollViewContents];
}
You might be feeling a sense of deja-vu here, as it’s very familiar code. In fact, viewWillAppear: is almost identical to the previous code, except for the line where you set the zoomScale. Here, we set the zoomScale to 1 instead of minScale so that we’d have the content view at normal size instead of it fitting the screen. Since we aren’t going to implement the zoom handlers, if the view fits the screen, you will not be able to test the scroll view by panning the view around.
viewDidLoad, however, sets up a view hierarchy with a single root view, which is your instance variable, containerView. Then that single view is added to the scroll view. That is the key here – just one view can be added to the scroll view if you’re going to be zooming in, because as you’ll recall, you can only return one view in the delegate callback, viewForZoomingInScrollView:.
Again, implement centerScrollViewContents and the two UIScrollView delegate methods, substituting imageView with containerView from the original versions. (You can add the code above viewDidLoad, as before.)
- (void)centerScrollViewContents {
    CGSize boundsSize = self.scrollView.bounds.size;
    CGRect contentsFrame = self.containerView.frame;
 
    if (contentsFrame.size.width < boundsSize.width) {
        contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0f;
    } else {
        contentsFrame.origin.x = 0.0f;
    }
 
    if (contentsFrame.size.height < boundsSize.height) {
        contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0f;
    } else {
        contentsFrame.origin.y = 0.0f;
    }
 
    self.containerView.frame = contentsFrame;
}
 
- (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView {
    // Return the view that we want to zoom
    return self.containerView;
}
 
- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
    // The scroll view has zoomed, so we need to re-center the contents
    [self centerScrollViewContents];
}
Now build and run your project. This time, select Custom view scroll and watch in amazement as you can pan around a beautifully hand-crafted scene. (If you add in the gesture recognizers from the previous code, you’ll even be able to zoom in and out.
Read rest of entry

Beginning Twitter in iOS 5 Tutorial

These days, social networks are a huge part of our daily lives. Not only do we access social networks via their dedicated websites like twitter.com or facebook.com, but we also find social features in apps, websites, blogs, video games, and more.

Adding some social features into your apps can really help you increase the virality of your app, help you identify and retain customers, and can boost the polish and added value of your app.

Until now, adding social features into apps has been a pain. Not only do you have to use a different API for each social network, but users have to constantly log into each one of them for every app they use.

I can’t remember how many times I’ve had to log into Facebook or Twitter within a game or app. It can get quite tedious both as a developer and as a user to repeat the same thing over and over again for every application, up to the point where users won’t even bother because they don’t want to have to log on again.
Thankfully for us Apple has taken a huge step forward in this regard by having Twitter natively incorporated in iOS 5! Now all a user needs to do is log into Twitter once and each app can make use of your accounts stored on the device.

How Does It Work?

iOS 5 includes several ways to interact with Twitter. The simplest, and possibly the one you will most likely implement, is the TWTweetComposeViewController. That name is quite a handful so we will affectionately call it “Tweet Sheet” just as Apple does.
In this tutorial you will see that the tweet sheet is very easy to implement. With literally just a couple of lines you can have a full tweet composer within your app! You don’t have to worry about contacting the Twitter backend, handling user logins, or anything like that.
Here is a code snippet for creating and presenting the TWTweetComposeViewController:
if ([TWTweetComposeViewController canSendTweet])
{
    TWTweetComposeViewController *tweetSheet = 
        [[TWTweetComposeViewController alloc] init];
    [tweetSheet setInitialText:@"Initial Tweet Text!"];
    [self presentModalViewController:tweetSheet animated:YES];
}
All you do is determine whether the device can send tweets, create an instance of the tweet sheet, attach any links or images, put some initial text and present it modally, that’s it! All within Xcode and through the use of Objective-C.
In fact it’s so easy, that if you’re an advanced developer you can just skip the rest of this tutorial and implement it yourself! But if you want to see an example of using “simple tweet” capability in a simple project, keep reading!

The advantage of using the tweet sheet is that it’s built right onto iOS, this means lots of things:
  • Standard interface throughout the OS
  • Automatic use of the user’s system Twitter account
  • Automatic check for a tweet less than 140 characters long
  • Easy image and link attachments
  • Easy to program, no need to implement OAuth or connect to the Twitter backend
As you can see we have lots of advantages and incentives to use this and, being that it’s so simple, there’s no excuse not to include Twitter functionality in your applications!

Getting Started

Alright fellow programmers, it’s time to get our fingers typing and creating an awesome Twitter enabled app. In this example we’re going to create a simple app that will allow the user to tweet whatever text they like, and even include images or links within their tweet.
Create a new project with Xcode with the iOS\Application\Single View Application template. Enter SimpleTweet for the product name, set the device family to iPhone, and make sure that Use Storyboard and Use Automatic Reference Counting are checked (leave the other checkbox unchecked).

Making Some Connections

Next we need to make some connections from our storyboard to the view controller. Open MainStoryboard.storyboard file and make sure the Assistant Editor is visible, and displaying ViewController.h.
Control-drag from each of the four labels to below the @interface, and connect them to Outlets named button1Label, button2Label, button3Label, and button4Label.
Similarly, control-drag from each of the four buttons to below the @interface, and connect them to Actions named button1Tapped, button2Tapped, button3Tapped, and button4Tapped.
Finally, control-drag from the “Tweet” button to below the @interface, and connect it to an action named tweetTapped.
When you are done, your header file should like this:
#import 
 
@interface ViewController : UIViewController
 
@property (weak, nonatomic) IBOutlet UILabel *button1Label;
@property (weak, nonatomic) IBOutlet UILabel *button2Label;
@property (weak, nonatomic) IBOutlet UILabel *button3Label;
@property (weak, nonatomic) IBOutlet UILabel *button4Label;
 
- (IBAction)button1Tapped:(id)sender;
- (IBAction)button2Tapped:(id)sender;
- (IBAction)button3Tapped:(id)sender;
- (IBAction)button4Tapped:(id)sender;
- (IBAction)tweetTapped:(id)sender;
 
@end
Awesome! Now let’s implement these methods to send out some tweets!
In order for us to use the TWTweetComposeViewController we need to add the Twitter framework to our project. Select your project in the project navigator and then the SimpleTweet target. Go to the Build Phases tab and click on the + button inside the Link Binary With Libraries section, on the window that appears navigate to the Twitter.framework file and click Add:

Next, open ViewController.m and add the following import at the top of the file:
#import 
That’s all we need in order for our file to see the Twitter API, let’s now add some code so we display the tweet sheet when the user taps the Tweet button. Go to your tweetTapped method add the following code:
- (IBAction)tweetTapped:(id)sender { 
{
    if ([TWTweetComposeViewController canSendTweet])
    {
        TWTweetComposeViewController *tweetSheet = 
            [[TWTweetComposeViewController alloc] init];
        [tweetSheet setInitialText:
            @"Tweeting from iOS 5 By Tutorials! :)"];
     [self presentModalViewController:tweetSheet animated:YES];
    }
    else
    {
        UIAlertView *alertView = [[UIAlertView alloc] 
            initWithTitle:@"Sorry"                                                             
            message:@"You can't send a tweet right now, make sure  
                your device has an internet connection and you have 
                at least one Twitter account setup"                                                          
            delegate:self                                              
            cancelButtonTitle:@"OK"                                                   
            otherButtonTitles:nil];
        [alertView show];
    }
}
Yes, believe it or not that’s all we need to do in order to send a tweet (mind you we haven’t included any links or images), it doesn’t get any easier than this! Let’s go over the code we added to our tweetTapped method.
First thing we do is check to see if we can send a tweet, we accomplish this by calling the canSendTweet class method on TWTweetComposeViewController. This method will return NO if the device cannot access the network or if the user hasn’t setup a Twitter account yet.
If our application can send a tweet then all we do is create an instance of the TWTweetComposeViewController, use the setInitialText: method to load up the tweet sheet with some default text and present it modally. If we cannot send a tweet then we just show a simple alert view to provide the user with feedback.

Adding Images and Links

Now let’s implement the logic to allow the user to select one of the tutorials and have its image and link added to the tweet. Add the following category before the implementation inside ViewController.m:
@interface ViewController ()
@property (strong, nonatomic) NSString *imageString;
@property (strong, nonatomic) NSString *urlString;
- (void)clearLabels;
@end
All we are doing here is creating two private string properties to store the image’s name and the link to the tutorial website as well as a private method to set our labels’ text color back to white.
Then add the synthesize statements for these variables:
@synthesize imageString = _imageString;
@synthesize urlString = _urlString;
And set the properties to nil inside viewDidUnload:
self.imageString = nil;
self.urlString = nil;
Next, implement the clearLabels method to set the text color of each label to white:
- (void)clearLabels
{
    self.button1Label.textColor = [UIColor whiteColor];
    self.button2Label.textColor = [UIColor whiteColor];
    self.button3Label.textColor = [UIColor whiteColor];
    self.button4Label.textColor = [UIColor whiteColor];
}
Yup, that’s all this private method will do for us.
The way we are going to implement this is as follows: when the user selects a tutorial we store its image name and link within our private properties and we set the label’s color to red in order to indicate the current selection.
If the user selects another tutorial then we just set all the labels back to white, store the new image and url and set it’s label to red. Add the following code for your button tapped methods:
- (IBAction)button1Tapped:(id)sender {
 
    [self clearLabels];
 
    self.imageString = @"CheatSheetButton.png";
    self.urlString = @"http://www.raywenderlich.com/4872/
        objective-c-cheat-sheet-and-quick-reference";
 
    self.button1Label.textColor = [UIColor redColor];
}
 
- (IBAction)button2Tapped :(id)sender {
 
    [self clearLabels];
 
    self.imageString = @"HorizontalTablesButton.png";
    self.urlString = @"http://www.raywenderlich.com/4723/
        how-to-make-an-interface-with-horizontal-tables-like-the-
        pulse-news-app-part-2";
 
    self.button2Label.textColor = [UIColor redColor];
}
 
- (IBAction)button3Tapped:(id)sender { 
 
    [self clearLabels];
 
    self.imageString = @"PathfindingButton.png";
    self.urlString = @"http://www.raywenderlich.com/4946/
        introduction-to-a-pathfinding";
 
    self.button3Label.textColor = [UIColor redColor];
}
 
- (IBAction)button4Tapped:(id)sender {
 
    [self clearLabels];
 
    self.imageString = @"UIKitButton.png";
    self.urlString = @"http://www.raywenderlich.com/4817/
        how-to-integrate-cocos2d-and-uikit";
 
    self.button4Label.textColor = [UIColor redColor];
}
In each of the methods we just clear the labels, set the image string to the appropriate image, set the URL to the corresponding tutorial and change the label’s text color to red. We could have implemented things a bit differently in order to avoid writing the same code in all 4 methods but since this example is very simple, we’ll leave it at that.
Now go over to the tweetTapped method and add the following code right before the call to [self presentModalViewController:...]:
if (self.imageString)
{
    [tweetSheet addImage:[UIImage imageNamed:self.imageString]];
}
 
if (self.urlString)
{
    [tweetSheet addURL:[NSURL URLWithString:self.urlString]];
}
We just added two if statements to check whether we have an image and url, if we do then we just add them to our tweet sheet by using the addImage: and addURL: methods respectively.




Read rest of entry

Working with JSON in iOS 5 Tutorial

iOS 5 has some new built-in APIs to make it really easy to read and write JSON.
If you don’t know what JSON is, it’s a simple human readable format that is often used to send data over a network connection.
For example, if you have an array of three strings, the JSON representation would simply be:
["test1", "test2", "test3"]
If you have a Pet object with member variables name, breed, and age, the JSON representation would simply be:
{"name" : "Dusty", "breed": "Poodle", "age": 7}
It’s that simple, which is why it’s so easy and popular to use. For the full spec, which can be read in just a couple minutes, check out www.json.org.
The reason JSON is important is that many third parties such as Google, Yahoo, or Kiva make web services that return JSON formatted data when you visit a URL with a specified query string. If you write your own web service, you’ll also probably find it really easy to convert your data to JSON when sending to another party.
In this tutorial, we’ll cover how you can work with JSON in iOS 5. Keep reading to see how simple it is!

JSON and iOS 5

If you’ve had to parse JSON in your iOS apps in the past, you’ve probably used a third party library such as JSON Framework.
Well with iOS 5, needing to use a third party library for JSON parsing is a thing of the past. Apple has finally added a JSON library in Cocoa and I must say I personally like it very much!
You can turn objects like NSString, NSNumber, NSArray and NSDictionary into JSON data and vice versa super easily. And of course no need to include external libraries – everything is done natively and super fast.
In this chapter we’re going to get hands-on experience with the new native JSON support. We’re going to build a simple application which will connect to the Internet and consume JSON service from the Kiva.org web site. It will parse the data, show it in human readable format on the screen, and then build back a different JSON.

Later on in the chapter we’re going to create a class category which will give you ideas how to integrate JSON support more tightly into Cocoa and your own classes. Having the possibility to turn your own classes data into JSON could really help you persist data structures online, exchange data between applications, or anything that requires your classes to be able to serialize and persist data, which can be sent over http, email, etc.

Getting Started

Open up Xcode and from the main menu choose File\New\New Project. Choose the iOS\Application\Single View Application template, and click Next. Name the product KivaJSONDemo, select iPhone for the Device family, and make sure just the Use Automatic Reference Counting checkbox is checked, click Next and save the project by clicking Create.
Let’s do a little bit of clean up first – open up ViewController.m file and replace everything inside with this :
#define kBgQueue dispatch_get_global_queue(
 DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) //1
#define kLatestKivaLoansURL [NSURL URLWithString: 
 @"http://api.kivaws.org/v1/loans/search.json?status=fundraising"] //2
 
#import "ViewController.h"
 
@implementation ViewController
 
@end
In the first line of code we define a macro that gives us back a background queue – I like having a kBgQueue shortcut for that, so I can keep my code tighter.

In the second line of code we create a macro named kLatestKivaLoansURL which returns us an NSURL pointing to this URL [http://api.kivaws.org/v1/loans/search.json?status=fundraising].
Go ahead and visit this URL in your browser if you want – you’ll see Kiva.org’s list of currently fundraising loans in JSON format. We’re going to use this API to read the list of loans, take the latest one and show the information on the screen.

Let’s make a little detour and design the application’s UI in Interface Builder real quick.
Open ViewController.xib in the Project Navigator. This app is supposed to be positive and life-changing, so we need to do something about that background! Select it and from the Utilities bar (1), make sure you have the Attributes Inspector open (2), and set the Background to a nice green color (3).

Parsing JSON from the Web

The first thing we need to do is download the JSON data from the web. Luckily, with GCD we can do this in one line of code! Add the following to ViewController.m:
- (void)viewDidLoad
{
    [super viewDidLoad];
 
    dispatch_async(kBgQueue, ^{
        NSData* data = [NSData dataWithContentsOfURL: 
          kLatestKivaLoansURL];
        [self performSelectorOnMainThread:@selector(fetchedData:) 
          withObject:data waitUntilDone:YES];
    });
}
Remember how earlier we defined kBGQueue as a macro which gives us a background queue?
Well this bit of code makes it so that when viewDidLoad is called, we run a block of code in this background queue to download the contents at the Kiva loans URL.

When NSData has finished fetching data from the Internet we call performSelectorOnMainThread:withObject:waitUntilDone: so we can update the application’s UI. We haven’t written fetchedData: yet but will do so shortly.
Remember it is only OK to run a synchronous method such as dataWithContentsOfURL in a background thread, otherwise the GUI will seem unresponsive to the user.
Also, remember that you can only access UIKit objects from the main thread, which is why we had to run fetchedData: on the main thread.

Note: You might wonder why I preferred to use performSelectorOnMainThread:withObject:waitUntilDone: over dispatching a block on the main thread? It’s a personal preference really and I have two reasons:
I’m all for the greatest readability of a piece of code. For me, [self performSelectorOnMainThread:...] makes it easier to spot what’s going on in that piece of code.

I’m a symmetry freak! I find that Xcode doesn’t handle text indentation well when you use dispatch_async(), so purely visually the code is not so pleasant to look at.
You might have other preferences, so yes – if you prefer dispatch_async(dispatch_get_main_queue(), ^(){…}); go for it!

So, when the data has arrived the method fetchedData: will be called and the NSData instance will be passed to it. In our case the JSON file is relatively small so we’re going to do the parsing inside fetchedData: on the main thread. If you’re parsing large JSON feeds (which is often the case), be sure to do that in the background.

Read rest of entry

User Interface Customization in iOS 5

To be successful on the App Store, your app needs to stand out. The vanilla user-interface “look and feel” provided by Apple just doesn’t cut it any more in a crowded market.
Many of the most popular apps on the App Store present standard iOS UI elements in a non-standard fashion:
  • Twitter employs a custom UITabBar
  • Instagram uses both a custom UITabBar and a custom UINavigationBar
  • Epicurious for iPad customizes elements of the standard split-view interface
Prior to iOS 5, many developers had to take somewhat unconventional approaches to achieve these results. Although subclassing and overriding drawRect: was the recommended approach, many resorted to the dreaded “method swizzling”.
But with iOS 5, those dark days are over! iOS 5 has included many new APIs you can use to easily customize the appearance of various UIKit controls.
To illustrate some of these new APIs, in this tutorial we’re going to take a “plain vanilla” app about surfing trips and customize the UI to get a more “beach-themed” look-and-feel.
To get the most out of this tutorial, you need to know the basics of iOS development first. If you are new to iOS development, you should check out some of the other tutorials on this site first.

Getting Started

To begin, download the starter project. I’ve created a simple app for you to start with so we can focus on the meat of this tutorial – UIKit customization.
Once you open up the project, take a look around the code and XIBs. You’ll see that the primary view presents a list of our surfing trips, and the detail view allows us to capture more information about each trip individually.
With that context, Build & Run the app (Cmd-R) to see what we have to start with.

Huh. Yes, this app is functional, but it’s hardly representative of the fun one would expect to have on a surfing trip. Let’s survey the scene in more detail.
Let’s start with the detail page. Things look pretty standard there, eh?
A plain UIBarButtonItem on the UINavigationBar at the top, stock UITabBar elements at the bottom, and the following “standard” data entry components including the following:
  • UILabels with “System” Helvetica fonts
  • UITextField
  • UISlider
  • UISwitch
  • UISegmentedControl
In this tutorial, we’ll completely customize the detail screen to give it some style, using the new APIs available in iOS 5. So with an idea of what’s in store, let’s convert this app from “zero” to “hero”.

Adding a Background Image

If you open up the Images folder in your project, you’ll see that we already have some images we can use to customize the UI included in the project – we just need to modify the code to make use of them.
Inside the images folder is a file called bg_sand.png. We’re going to start our UI customization by making this the background image in the detail view.
Open DetailViewController.m and create a viewDidLoad method like this:
- (void)viewDidLoad {    
    [super viewDidLoad];
 
    [[self view] setBackgroundColor:[UIColor colorWithPatternImage:[UIImage imageNamed:@"bg_sand"]]];    
}
If you aren’t familiar with this technique, you can actually make a “color” based on an image like you see here. This is an easy way to set the background of a view, because there’s no “backgroundImage” property, but there is a “backgroundColor” property!

Compile and run to verify that it worked:

Customizing UINavigationBar

If you look inside the images folder, you’ll see two images that we want to use to customize the navigation bar: surf_gradient_textured_32.png and surf_gradient_textured_44.png.
We want to repeat these from left to right across the navigation bar. there are two different heights because the height of the navigation bar shrinks when the phone goes into landscape.
iOS 5 offers two new APIs that can help us with this:
  • UINavigationBar has a new backgroundImage property we can use to set a custom background image like this.
  • UIImage has a new resizableImageWithCapInsets method we can use to create a resizable image. The cap insets allow you to specify the portions of the image that should not be repeated, such as if you have rounded corners for a button on the edges that shouldn’t be repeated.
We could go into the detail view and use these new APIs to set the navigation bar’s background image directly. But then we’d have to go and do the same thing inside our list view, and any other views we might have in our app!
Obviously this would get old quick. Recognizing this, iOS 5 offers a cool new feature that allows us to customize user interface elements once, allowing it to “stand in” for other elements within the same level in the containment hierarchy.
So starting with the navigation bar, we’re going to use this concept of the “appearance proxy” to customize some elements that will be repeated throughout the app.
Let’s see how it looks. Inside SurfsUpAppDelegate.m, create a new method right above application:didFinishLaunchingWithOptions:
- (void)customizeAppearance
{
    // Create resizable images
    UIImage *gradientImage44 = [[UIImage imageNamed:@"surf_gradient_textured_44"] 
        resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0)];
    UIImage *gradientImage32 = [[UIImage imageNamed:@"surf_gradient_textured_32"] 
        resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0)];
 
    // Set the background image for *all* UINavigationBars
    [[UINavigationBar appearance] setBackgroundImage:gradientImage44 
        forBarMetrics:UIBarMetricsDefault];
    [[UINavigationBar appearance] setBackgroundImage:gradientImage32 
        forBarMetrics:UIBarMetricsLandscapePhone];
 
    // Customize the title text for *all* UINavigationBars
    [[UINavigationBar appearance] setTitleTextAttributes:
        [NSDictionary dictionaryWithObjectsAndKeys:
            [UIColor colorWithRed:255.0/255.0 green:255.0/255.0 blue:255.0/255.0 alpha:1.0], 
            UITextAttributeTextColor, 
            [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.8], 
            UITextAttributeTextShadowColor, 
            [NSValue valueWithUIOffset:UIOffsetMake(0, -1)], 
            UITextAttributeTextShadowOffset, 
            [UIFont fontWithName:@"Arial-Bold" size:0.0], 
            UITextAttributeFont, 
            nil]];
 
}
The first two lines create stretchable images using the resizableImageWithCapInsets method discussed earlier. Note that this method replaces stretchableImageWithLeftCapWidth:topCapHeight:, which is now deprecated.
For the cap insets, you basically specify the fixed region of a given image in top, left, bottom, right. What’s left is stretched over the remainder of the region to which the image is applied. In this particular image we want the whole thing stretched, so we pass 0 for all of the fixed caps.
The next two lines invoke the appearance proxy, designating these stretchable images as background images, for the bar metrics specified.
The last line stylizes the title that appears in our detail view. To do so, we pass a dictionary of title text attributes. The available keys include the following:
  • UITextAttributeFont
  • UITextAttributeTextColor
  • UITextAttributeTextShadowColor
  • UITextAttributeTextShadowOffset
Almost done – just add the line to call this method at the top of application:didFinishLaunchingWithOptions:
[self customizeAppearance];
Compile and run, and now you should see the navigation bar has the teal background image applied in both orientations, with stylized title text as well!

Customizing UIBarButtonItem

Open up the Images directory and look at button_textured_24.png and button_textured_30.png. We want to use these to customize the look and feel of the buttons that appear in the UINavigationBar.
Notice that we’re going to set up these button images as resizable images. It’s important to make them resizable because the button widths will vary depending on what text is inside.
For these buttons, we don’t want the 5 leftmost pixels to stretch, nor the 5 rightmost pixels, so we’ll set the left and right cap insets to 5. The pixels in between will repeat as much as is needed for the width of the button.
Let’s try this out! We’ll use the appearance proxy to customize all the UIBarButtonItems at once, like we did last time. Add the following code to the end of customizeAppearance:
UIImage *button30 = [[UIImage imageNamed:@"button_textured_30"] 
    resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 5)];
UIImage *button24 = [[UIImage imageNamed:@"button_textured_24"] 
    resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 5)];
[[UIBarButtonItem appearance] setBackgroundImage:button30 forState:UIControlStateNormal 
    barMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearance] setBackgroundImage:button24 forState:UIControlStateNormal 
    barMetrics:UIBarMetricsLandscapePhone];
 
[[UIBarButtonItem appearance] setTitleTextAttributes:
    [NSDictionary dictionaryWithObjectsAndKeys:
        [UIColor colorWithRed:220.0/255.0 green:104.0/255.0 blue:1.0/255.0 alpha:1.0], 
        UITextAttributeTextColor, 
        [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0], 
        UITextAttributeTextShadowColor, 
        [NSValue valueWithUIOffset:UIOffsetMake(0, 1)], 
        UITextAttributeTextShadowOffset, 
        [UIFont fontWithName:@"AmericanTypewriter" size:0.0], 
        UITextAttributeFont, 
        nil] 
    forState:UIControlStateNormal];
This looks familiar. We create the stretchable images for the buttons and set them as the background for both display in both portrait & landscape orientation. We then format the text to match the typewriter-style font you saw at the outset of the tutorial.
The “back” bar button item needs special customization, because it should look different – like it’s pointing backwards. Take a look at the images we’re going to use to see what I mean: Images\button_back_textured_24.png and Images\button_back_textured_30.png.
Add the following code at the bottom of customizeAppearance to take care of the back bar button item:

UIImage *buttonBack30 = [[UIImage imageNamed:@"button_back_textured_30"] 
    resizableImageWithCapInsets:UIEdgeInsetsMake(0, 13, 0, 5)];
UIImage *buttonBack24 = [[UIImage imageNamed:@"button_back_textured_24"] 
    resizableImageWithCapInsets:UIEdgeInsetsMake(0, 12, 0, 5)];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:buttonBack30 
    forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[[UIBarButtonItem appearance] setBackButtonBackgroundImage:buttonBack24 
    forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone];
 
Note that we use different cap inset values because the back image has a wider left hand side that shouldn’t stretch. Also note that there is a separate property on UIBarButtonItem for “backButtonBackgroundImage” that we use here.

Customizing UITabBar

To customize a UITabBar, iOS 5 offers an API to let you change the background image of the toolbar, and the image to indicate the selected item. Take a look at Images\tab_bg.png and Images\tab_select_indicator.png to see the images we’ll use for these.
Although our mockups only depict one UITabBar, these will in all likelihood have the same appearance if others appear, so we’ll use the appearance proxy to customize this as well.
Add the following code to the bottom of customizeAppearance:
UIImage *tabBackground = [[UIImage imageNamed:@"tab_bg"] 
    resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0)];
[[UITabBar appearance] setBackgroundImage:tabBackground];
[[UITabBar appearance] setSelectionIndicatorImage:
        [UIImage imageNamed:@"tab_select_indicator"]];
 
Compile and run again…nice! The background and selected image are nice touches.

Compile and run, and you should now see some cool customized UIBarButtonItems in your UINavigationBar!


Customizing UISlider

Open up Images\slider_minimum.png, Images\slider_maximum.png, and Images\thumb.png to see the images that we’re going to use to customize the UISlider.
iOS 5 makes it ridiculously easy to customize the UISlider by just setting the “maximumTrackImage”, “minimumTrackImage”, and “thumbImage” properties of a UISlider.
Let’s try it out. Add the following code to the bottom of customizeAppearance:
UIImage *minImage = [[UIImage imageNamed:@"slider_minimum.png"] 
    resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 0)];
UIImage *maxImage = [[UIImage imageNamed:@"slider_maximum.png"] 
    resizableImageWithCapInsets:UIEdgeInsetsMake(0, 5, 0, 0)];
UIImage *thumbImage = [UIImage imageNamed:@"thumb.png"];
 
[[UISlider appearance] setMaximumTrackImage:maxImage 
    forState:UIControlStateNormal];
[[UISlider appearance] setMinimumTrackImage:minImage 
    forState:UIControlStateNormal];
[[UISlider appearance] setThumbImage:thumbImage 
    forState:UIControlStateNormal];
Compile and run, and check out your cool and stylish UISlider!

Customizing UISegmentedControl

Now we’ll customize our segmented control. This component is a little bit more complicated, as we have both selected & unselected backgrounds, as well as varying states for the adjacent regions (e.g., selected on left, unselected on right; unselected on the left & selected on the right; unselected on both sides).
Take a look at the images we’ll use for this to see what I mean: Images\segcontrol_sel.png, Images\segcontrol_uns.png, Images\segcontrol_sel-uns.png, and Images\segcontrol_uns-uns.png.
Then add the code to make use of these to the bottom of customizeAppearance:
UIImage *segmentSelected = 
    [[UIImage imageNamed:@"segcontrol_sel.png"] 
        resizableImageWithCapInsets:UIEdgeInsetsMake(0, 15, 0, 15)];
UIImage *segmentUnselected = 
    [[UIImage imageNamed:@"segcontrol_uns.png"] 
        resizableImageWithCapInsets:UIEdgeInsetsMake(0, 15, 0, 15)];
UIImage *segmentSelectedUnselected = 
    [UIImage imageNamed:@"segcontrol_sel-uns.png"];
UIImage *segUnselectedSelected = 
    [UIImage imageNamed:@"segcontrol_uns-sel.png"];
UIImage *segmentUnselectedUnselected = 
    [UIImage imageNamed:@"segcontrol_uns-uns.png"];
 
[[UISegmentedControl appearance] setBackgroundImage:segmentUnselected 
    forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[[UISegmentedControl appearance] setBackgroundImage:segmentSelected 
    forState:UIControlStateSelected barMetrics:UIBarMetricsDefault];
 
[[UISegmentedControl appearance] setDividerImage:segmentUnselectedUnselected 
    forLeftSegmentState:UIControlStateNormal 
    rightSegmentState:UIControlStateNormal 
    barMetrics:UIBarMetricsDefault];
[[UISegmentedControl appearance] setDividerImage:segmentSelectedUnselected 
    forLeftSegmentState:UIControlStateSelected 
    rightSegmentState:UIControlStateNormal 
    barMetrics:UIBarMetricsDefault];
[[UISegmentedControl appearance] 
    setDividerImage:segUnselectedSelected 
    forLeftSegmentState:UIControlStateNormal 
    rightSegmentState:UIControlStateSelected 
    barMetrics:UIBarMetricsDefault];
Compile and run, and now our UISegmentedControl has a completely different look!

Customizing UISwitch

At the time of writing this tutorial, there is no easy way to customize the artwork on a UISwitch. However (like many other controls) it is extremely easy to change its color via the tintColor property.
To ensure that we touch on a few different approaches to customization, we’ll customize the “onTintColor” parameter in code. You’ll note that in DetailViewController, we already have an IBOutlet – rentSwitch – wired to our switch in DetailView.xib.
So, add the following code to set the tint color in the viewDidLoad method of DetailViewController:

[rentSwitch setOnTintColor:[UIColor colorWithRed:0 green:175.0/255.0 blue:176.0/255.0 alpha:1.0]];

Compile and run, and check out your newly colored switch!


Read rest of entry
 

sitemeter