Quantcast
Channel: Envato Tuts+ Code - Mobile Development
Viewing all 1836 articles
Browse latest View live

Working with UIRefreshControl

$
0
0

When Loren Brichter introduced the idea of “pull to refresh” in Tweetie 2 a few years ago, it wasn’t long before developers started to adopt this ingenious and intuitive concept. Even though Twitter now owns the patent on the “pull to refresh” concept, this hasn’t stopped Apple from introducing the UIRefreshControl class in iOS 6. This new UIControl subclass makes it trivial to add a “pull to refresh” control to any table view controller in iOS 6.


Short and Sweet

The class reference of UIRefreshControl is short, hinting at how easy it is to get started with this addition of the UIKit framework. The UIRefreshControl class directly descends from UIControl, which means that setting up an instance of UIRefreshControl is not much different from creating and configuring any other UIKit control. After instantiating an instance of the UIRefreshControl class, you assign it to the new refreshControl property of a table view controller object (UITableViewController or a subclass of it). The table view controller takes care of properly positioning and displaying the refresh control. As with any other UIControl subclass, you attach a target-action pair to a specific event, UIControlEventValueChanged in the case of UIRefreshControl.

This wouldn’t be a Mobiletuts+ tutorial without an example illustrating how to use the UIRefreshControl class in a project. In the rest of this tutorial, I will show you how to populate a table view with a list of tweets pulled from the Twitter Search API. The request is sent to Twitter’s Search API when the user pulls the table view dow: pull-to-refresh.


Step 1: Setting Up the Project

The application that we are about to build queries the Twitter Search API for tweets about iOS development. The request is sent to Twitter’s Search API when the user pulls the table view down, revealing the refresh control. We will use the fantastic AFNetworking library to send our request to the Twitter Search API. AFNetworking will also help us to asynchronously download and display profile images.

Create a new project in Xcode by selecting the Empty Application template from the list of templates (Figure 1). Name your application Pull-to-Refresh, enter a company identifier, set iPhone for the device family, and check Use Automatic Reference Counting. The rest of the checkboxes can be left unchecked for this project (figure 2). Tell Xcode where you want to save the project and hit the Create button.

New in iOS 6: UIRefreshControl: Choosing a Project Template - Figure 1
New in iOS 6: UIRefreshControl: Configuring the New Project - Figure 2

Step 2: Adding the AFNetworking Library

Installing AFNetworking using Cocoapods is a breeze. However, in this tutorial, I will show you how to manually add the AFNetworking library to an Xcode project to make sure that we are all on the same page. It isn’t that difficult anyway.

Download the latest stable release of the library from its GitHub project page, extract the archive, and drag the folder named AFNetworking to your Xcode project. Make sure that the checkbox labeled Copy items into destination group’s folder (if needed) is checked and double-check that the library is added to the Pull-to-Refresh target (figure 3).

New in iOS 6: UIRefreshControl: Add the AFNetworking Library - Figure 3

The AFNetworking library relies on two frameworks that a new Xcode project isn’t by default linked against, (1) the System Configuration and (2) Mobile Core Services frameworks. Select your project in the Project Navigator, choose the Pull to Refresh target from the list of targets, and open the Build Phases tab at the top. Expand the Link Binary With Libraries drawer and add both frameworks by clicking the plus button (figure 4).

New in iOS 6: UIRefreshControl: Adding the Required Frameworks - Figure 4

To finish things off, add an import statement for both frameworks as well as AFNetworking to the projects precompiled header file as shown in the snippet below. This will make it easier to work with AFNetworking as we don’t need to add an import statement to every class that we want to use the library.

//
// Prefix header for all source files of the 'Pull to Refresh' target in the 'Pull to Refresh' project
//
#import <Availability.h>
#ifndef __IPHONE_3_0
#warning "This project uses features only available in iOS SDK 3.0 and later."
#endif
#ifdef __OBJC__
    #import <UIKit/UIKit.h>
    #import <Foundation/Foundation.h>
    #import <MobileCoreServices/MobileCoreServices.h>
    #import <SystemConfiguration/SystemConfiguration.h>
    #import "AFNetworking.h"
#endif

Step 3: Creating the Table View Controller

The UIRefreshControl is designed to work in conjunction with a table view controller object. Create a new UITableViewController subclass (File > New > File…) by choosing the Objective-C class template from the list of templates (figure 5). Give the new class a name of MTTweetsViewController and double-check that it is a UITableViewController subclass. Tell Xcode that it shouldn’t create a nib file for the new controller class by unchecking the checkbox labeled With XIB for user interface (figure 6). Specify a location to save the new class and click the Create button.

New in iOS 6: UIRefreshControl: Creating the TweetsViewController Class - Figure 5
New in iOS 6: UIRefreshControl: Configuring the TweetsViewController Class - Figure 6

Step 4: Adding the Refresh Control

Before we can add the refresh control to the table view controller, we need to instantiate an instance of the new MTTweetsViewController class. Update the application:didFinishLaunchingWithOptions: method in MTAppDelegate.m as shown below. The implementation shouldn’t hold any surprises. We initialize an instance of the MTTweetsViewController class and set it as the application window’s root view controller. Don’t forget to add an import statement at the top of MTAppDelegate.m to import the header file of the MTTweetsViewController class.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Initialize Tweet View Controller
    MTTweetsViewController *vc = [[MTTweetsViewController alloc] initWithStyle:UITableViewStylePlain];
    // Initialize Window
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Configure Window
    [self.window setBackgroundColor:[UIColor whiteColor]];
    [self.window setRootViewController:vc];
    // Make Key and Visible
    [self.window makeKeyAndVisible];
    return YES;
}
#import "MTTweetsViewController.h"

If you run the application in the iPhone Simulator, you should see an empty table view. The refresh control is added in the viewDidLoad method of the tweets view controller. As I mentioned earlier, adding a refresh control is very easy. Take a look at the implementation of the viewDidLoad method shown below. We initialize the refresh control and add a target and action for the UIControlEventValueChanged event of the refresh control. Finally, the refresh control is assigned to the refreshControl property of the table view controller. Of course, the refreshControl property is also new for iOS 6.

- (void)viewDidLoad {
    [super viewDidLoad];
    // Initialize Refresh Control
    UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
    // Configure Refresh Control
    [refreshControl addTarget:self action:@selector(refresh:) forControlEvents:UIControlEventValueChanged];
    // Configure View Controller
    [self setRefreshControl:refreshControl];
}

Before we build and run the project once more, we need to implement the refresh: action in the view controller’s implementation file. To verify that everything is set up properly, we simply log a message to the console. Build and run the project to see the refresh control in action.

- (void)refresh:(id)sender
{
    NSLog(@"Refreshing");
}

You will notice that the refresh control doesn’t disappear after it has been shown by the table view controller. This is something that you will have to do yourself. The idea behind the refresh control is in some ways similar to UIKit’s activity indicator view (UIActivityIndicatorView), that is, you are responsible for showing and hiding it. Hiding the refresh control is as simple as sending it a message of endRefreshing. Update the refresh: action as shown below and run the application once more in the iPhone Simulator.

- (void)refresh:(id)sender {
    NSLog(@"Refreshing");
    // End Refreshing
    [(UIRefreshControl *)sender endRefreshing];
}

The refresh control immediately disappears after you release the table view. Of course, this makes the refresh control quite useless. What we will do is send a request to the Twitter Search API and hide the refresh control when we have received a response (or when the request times out). AFNetworking makes this very easy to do.


Step 5: Querying the Twitter Search API

We will store the tweets that we get back from the Twitter Search API in an array. Add a private property named tweets to the MTTweetsViewController class as shown below.

#import "MTTweetsViewController.h"
@interface MTTweetsViewController ()
@property (strong, nonatomic) NSArray *tweets;
@end

Next, update the numberOfSectionsInTableView:, tableView:numberOfRowsInSection:, and tableView:cellForRowAtIndexPath: methods as shown below. If you have worked with table views before, this shouldn’t be too difficult to grasp.

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return self.tweets ? 1 : 0;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [self.tweets count] ? [self.tweets count] : 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"Cell Identifier";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
    }
    // Fetch Tweet
    NSDictionary *tweet = [self.tweets objectAtIndex:[indexPath row]];
    // Configure Cell
    [cell.textLabel setText:[tweet objectForKey:@"text"]];
    [cell.detailTextLabel setText:[tweet objectForKey:@"from_user"]];
    // Download Profile Image Asynchronously
    NSURL *url = [NSURL URLWithString:[tweet objectForKey:@"profile_image_url"]];
    [cell.imageView setImageWithURL:url placeholderImage:[UIImage imageNamed:@"placeholder"]];
    return cell;
}

In tableView:cellForRowAtIndexPath:, we create a new cell (or dequeue a reusable cell) and populate it with the contents of a tweet. To make sure that the table view scrolls smoothly, we download the user’s profile image asynchronously. This is very easy to accomplish with AFNetworking as it gives us setImageWithURL:placeholderImage:. What this does, is setting the cell’s image view with the provided placeholder image while requesting the user’s profile image in the background. To make this work, add placeholder.png and placeholder@2x.png to your Xcode project. You can find both files in the source files of this tutorial.

We send our request to the Twitter Search API in the refresh: action. Take a look at the updated implementation below. I won’t go into the details of how the AFJSONRequestOperation class works in this tutorial, but I do want to explain how the flow of the request works. After specifying the request URL (NSURL) and initializing the URL request (NSURLRequest), we create a JSON request operation by passing (1) the URL request, (2) a success block, and (3) a failure block to JSONRequestOperationWithRequest:success:failure:. The succes block is executed if the request was successful and gives us the response of the request as an instance of NSDictionary. We extract the array of tweets that we requested, update the tweets property, reload the table view to show the tweets, and hide the refresh control by sending it a message of endRefreshing.

- (void)refresh:(id)sender {
    // Create URL
    NSURL *url = [NSURL URLWithString:@"http://search.twitter.com/search.json?q=ios%20development&rpp=100&include_entities=true&result_type=mixed/"];
    // Initialize URL Request
    NSURLRequest *urlRequest = [[NSURLRequest alloc] initWithURL:url];
    // JSON Request Operation
    AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:urlRequest success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
        NSArray *results = [(NSDictionary *)JSON objectForKey:@"results"];
        if ([results count]) {
            self.tweets = results;
            // Reload Table View
            [self.tableView reloadData];
            // End Refreshing
            [(UIRefreshControl *)sender endRefreshing];
        }
    } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
        // End Refreshing
        [(UIRefreshControl *)sender endRefreshing];
    }];
    // Start Operation
    [operation start];
}

If the request fails, we only hide the refresh control. Of course, it would be better to inform the user that the request failed, but this will do for our example. We send the request by starting the JSON request operation at the end of the refresh: action.

Build and run the project once more to see the example application in action. If the profile images are not displaying correctly, then double-check that you have added the placeholder images that I mentioned earlier to your project.

New in iOS 6: UIRefreshControl: The Example Application in Action - Figure 7

Conclusion

There are many libraries that try to mimic the original “pull to refresh” functionality, but it is nice to see that Apple has finally embraced this neat concept and included it in the UIKit framework. As you might have noticed, in iOS 6, Apple has already put the UIRefreshControl class to use in some of its own applications, such as the Podcasts application.


Create a 3D Page Folding Animation: Page Sketch & Spine Fold Effect

$
0
0

This two part mini-series will teach you how to create an impressive page-folding effect with Core Animation. In this installment, you’ll first learn how to create a sketch pad view and then how to apply a basic spine folding animation on that view. Read on!


Final Project Demo


Tutorial Overview

Working with the UIView class is central to iOS SDK development. Views have both a visual aspect (i.e. what you see on the screen) and usually a control aspect (user interaction via touches and gestures). The visual aspect is actually handled by a class belonging to the Core Animation Framework, called CALayer (which in turn is implemented via OpenGL, only we don’t care to go down to that level of abstraction here). Layer objects are instances of the CALayer class or one of its subclasses. Without delving too deep into the theory (this is, after all, supposed to be a hands-on tutorial!) we should keep the following points in mind:

  • Every view in iOS is backed by a layer, which is responsible for its visual content. Programmatically, this is accessed as the layer property on the view.
  • There is a parallel layer hierarchy corresponding to the view hierarchy on screen. This means that if (say) a label is a subview to a button, then the label’s layer is the sublayer to the button’s layer. Strictly speaking, this parallelism holds only as long as we don’t add our own sublayers to the mix.
  • Several properties that we set on views (in particular, the views related to appearance) are in reality properties on the underlying layer. However, the layer exposes some properties that aren’t available at the view level. In that sense, layers are more powerful than views.
  • Compared to views, layers are more “lightweight” objects. Therefore, if for some aspect of our app we require visualization without interactivity, then layers are probably the more performant option.
  • A CALayer‘s content consists of a bitmap. While the base class is quite useful as it is, it also has several important subclasses in Core Animation. Notably, there is CAShapeLayer which allows us to represent shapes by means of vector paths.

So, what can we achieve with layers that we can’t easily do with views directly? 3D, for one, which is what we’ll focus on here. CALayer‘s capabilities put fairly sophisticated 3D effects and animations within your reach, without having to descend to the OpenGL level. This is the goal behind this tutorial: to demonstrate an interesting 3D effect that gives a small taste of what we can achieve with CALayers.


Our Objective

We have a drawing app that lets a user draw on the screen with his finger (for which I’ll reuse the code from a previous tutorial of mine). If the user then performs a pinch gesture on the drawing canvas, it folds up along a vertical line running along the center of the canvas (much like the spine of a book). The folded up book even casts a shadow on the background.

The drawing part of the app that I’m borrowing isn’t really important here, and we could very well use any image to demonstrate the folding effect. However, the effect makes for a very nice visual metaphor in the context of a drawing app where the pinching action exposes a book with several leaves (containing our previous drawings) that we can browse. This metaphor is notably seen in the Paper app. While for the purposes of the tutorial our implementation is going to be simpler and less sophisticated, but it isn’t too far off …and of course, you can take what you learn in this tutorial and make it even better!


Layer Geometry In Brief

Recall that in the iOS coordinate system the origin lies at the top-left corner of the screen, with the x-axis increasing towards the right and the y-axis downward. A view’s frame describes its rectangle in its superview’s coordinate system. Layers can be queried for their frame as well, but there’s another (preferred) way of describing a layer’s location and size. We’ll motivate these with a simple example: imagine two layers, A and B, as rectangular pieces of paper. You’d like to make the layer B a sublayer of A, so you fix B atop A using a pin, keeping the straight sides in parallel. The pin passes through two points, one in A and one in B. Knowing the location of these two points gives us an effective way of describing the position of B relative to A. We’ll label the point the pin pierces in A the “anchor point” and the point in B “position”. Take a look at the following figure, for which we’ll do the math:

Layer geometry

The figure looks like it has a lot going on, but not to worry, we’ll examine it bit by bit:

  • The upper graphic shows the hierarchical relationship: the purple layer (A, from our previous discussion) is made a sublayer of the blue layer (B). The green circle with the plus is where A is pinned in B. The position (in A’s coordinate system) is given as {32.5, 62.5}.
  • Now turn your attention to the lower graphic. The anchor point is specified differently. It’s relative to the size of layer B, such that the upper-left corner in {0.0, 0.0} and the bottom-right corner in {1.0, 1.0}. Since our pin is one-fourth the distance across B’s width and one-half the way down, the anchor point is {0.25, 0.5}.
  • Knowing the size of B (50 x 45) we can now compute the coordinate of the upper-left corner. Relative to B’s upper-left corner, the anchor point is 0.25 x 50 = 12.5 points in the x-direction and 0.50 x 45 = 22.5 point in the y-direction. Subtract these from the position’s coordinates, and you get the coordinates of B’s origin in A’s system: {32.5 – 12.5, 62.5 – 22.5} = {20, 40}. Clearly, B’s frame is {20, 40, 50, 45}.

The calculation is quite straightforward, so make sure you understand it thoroughly. It’ll give you a good feel of the relationship between position, anchor point, and frame.

The anchor point is quite significant because when we perform 3D transformations on the layer, these transformations are performed with respect to the anchor point. We’ll talk about that next (and then you’ll see some code, I promise!).


Layer Transformations

I’m sure you’re familiar with the concept of transforms such as scaling, translation, and rotation. In the Photos app in iOS 6, if you pinch with two fingers to zoom in or out on a photo in your album, you’re performing a scale transform. If you do a turning motion with your two fingers, the photo rotates, and if you drag it while keeping the sides parallel, that’s translation. Core animation and CALayer trump UIView by allowing you to perform transforms in 3D instead of just 2D. Of course, in 2013 our iDevice screens are still 2D, so 3D transforms employ some geometric trickery to fool our eyes into interpretting a flat image as a 3D object (the process is no different than depicting a 3D object in a line drawing made with a pencil, really). To deal with the 3rd dimension, we need to use a z-axis that we imagine piercing our device screen and perpendicular to it.

The anchor point is important because the precise result of the same transform applied will usually differ depending on it. This is especially important – and most easily understood – with a rotation transform through the same angle applied with respect to two different anchor points in the rectangle in the figure below (the red dot and the blue dot). Note that the rotation is in the plane of the image (or about the z-axis, if you’d prefer to think of it that way).

Rotation wrt two different anchor points

The Page Fold Animation

So, how do we implement the folding effect we’re after? While layers are really cool, you can’t fold them across the middle! The solution is – as I’m sure you’ve figured out – to use two layers, one for each page on either side of the fold. Based on what we discussed previously, let’s work out the geometric properties of these two layers in advance:

Determining our layers' geometries
  • We’ve chosen a point along the “fold spine” to be our anchor point for both our layers, because that’s where our fold (i.e. the rotation transform) happens. The rotation takes place about a vertical line (i.e. the y-axis) – make sure you visualize this. That’s fine, you might say, but why did I choose the midpoint of the spine (instead of say, a point at the bottom or the top)? Actually, in this particular instance, it doesn’t make a difference as far as rotation is concerned. But we also want to do a scale transform (making the layers slightly smaller as they fold) and keeping the anchor point at the middle means the book will stay nice and centered when folding. That’s because for scaling, the point coinciding with the anchor point remains fixed in position.
  • The anchor point for the first layer is {1.0, 0.5} and for the second layer is {0.0, 0.5}, in their respective coordinate spaces. Make sure you confirm that from the figure before proceeding!
  • The point lying below the anchor point in the superlayer (i.e. the “position”) is the midpoint, so it’s coordinates are {width/2, height/2}. Remember that the position property is in standard coordinates, not normalized.
  • The size of each of the layers is {width/2, height}.

Implementation

We now know enough to write some code!

Create a new Xcode project with the “Empty Application” template, and call it LayerFunTut. Make it an iPad app, and enable Automatic Reference Counting (ARC), but disable the options for Core Data and Unit Tests. Save it.

New project

In the Target > Summary page that shows up, scroll down to “Supported Interface Orientations” and choose the two landscape orientations.

Supported Orientations

Scroll further down until you get to “Linked Frameworks and Libraries”, click on “+” and add the QuartzCore core framework, which is required for Core Animation and CALayers.

Linking the QuartzCore framework

We’ll start by incorporating our drawing app into the project. Create a new Objective-C class called CanvasView, making it a subclass of UIView. Paste the following code into CanvasView.h:

//
//  CanvasView.h
//
#import <UIKit/UIKit.h>
@interface CanvasView : UIView
@property (nonatomic, strong) UIImage *incrementalImage;
@end

And then into CanvasView.m:

//
//  CanvasView.m
//
#import "CanvasView.h"
@implementation CanvasView
{
    UIBezierPath *path;
    CGPoint pts[5];
    uint ctr;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
    if (self = [super initWithCoder:aDecoder])
    {
        self.backgroundColor = [UIColor clearColor];
        [self setMultipleTouchEnabled:NO];
        path = [UIBezierPath bezierPath];
        [path setLineWidth:6.0];
    }
    return self;
}
- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.backgroundColor = [UIColor clearColor];
        [self setMultipleTouchEnabled:NO];
        path = [UIBezierPath bezierPath];
        [path setLineWidth:6.0];
    }
    return self;
}
- (void)drawRect:(CGRect)rect
{
    [self.incrementalImage drawInRect:rect];
    [[UIColor blueColor] setStroke];
    [path stroke];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    ctr = 0;
    UITouch *touch = [touches anyObject];
    pts[0] = [touch locationInView:self];
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *touch = [touches anyObject];
    CGPoint p = [touch locationInView:self];
    ctr++;
    pts[ctr] = p;
    if (ctr == 4)
    {
        pts[3] = CGPointMake((pts[2].x + pts[4].x)/2.0, (pts[2].y + pts[4].y)/2.0);
        [path moveToPoint:pts[0]];
        [path addCurveToPoint:pts[3] controlPoint1:pts[1] controlPoint2:pts[2]];
        [self setNeedsDisplay];
        pts[0] = pts[3];
        pts[1] = pts[4];
        ctr = 1;
    }
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self drawBitmap];
    [self setNeedsDisplay];
    [path removeAllPoints];
    ctr = 0;
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
{
    [self touchesEnded:touches withEvent:event];
}
- (void)drawBitmap
{
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0.0);
    if (!self.incrementalImage)
    {
        UIBezierPath *rectpath = [UIBezierPath bezierPathWithRect:self.bounds];
        [[UIColor clearColor] setFill];
        [rectpath fill];
    }
    [self.incrementalImage drawAtPoint:CGPointZero];
    [[UIColor blueColor] setStroke];
    [path stroke];
    self.incrementalImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
}
@end

As previously mentioned, this is just the code from another tutorial I wrote (with some minor modifications). Be sure to check it out if you’re not sure how the code works. For the purposes of this tutorial though, what is important is that CanvasView allows the user to draw smooth strokes on the screen. We’ve declared a property called incrementalImage which stores a bitmap version of the user’s drawing. This is the image we’ll be “folding up” with CALayers.

Time to write the view controller code and implement the ideas we worked out previously. One thing we haven’t discussed is how we get the drawn image into our CALayer such that half of the image gets drawn into the left page and the other half into the right page. Luckily, that’s only a few lines of code, which I’ll talk about later.

Create a new Objective-C class called ViewController, make it a subclass of UIViewController, and don’t check any of the options that show up.

Paste the following code into ViewController.m

//
//  ViewController.m
//
#import "ViewController.h"
#import "CanvasView.h"
#import "QuartzCore/QuartzCore.h"
#define D2R(x) (x * (M_PI/180.0)) // macro to convert degrees to radians
@interface ViewController ()
@end
@implementation ViewController
{
    CALayer *leftPage;
    CALayer *rightPage;
    UIView *curtainView;
}
- (void)loadView
{
    self.view = [[CanvasView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
}
- (void)viewDidLoad
{
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor blackColor];
}
- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    self.view.backgroundColor = [UIColor whiteColor];
    CGSize size = self.view.bounds.size;
    leftPage = [CALayer layer];
    rightPage = [CALayer layer];
    leftPage.anchorPoint = (CGPoint){1.0, 0.5};
    rightPage.anchorPoint = (CGPoint){0.0, 0.5};
    leftPage.position = (CGPoint){size.width/2.0, size.height/2.0};
    rightPage.position = (CGPoint){size.width/2.0, size.height/2.0};
    leftPage.bounds = (CGRect){0, 0, size.width/2.0, size.height};
    rightPage.bounds = (CGRect){0, 0, size.width/2.0, size.height};
    leftPage.backgroundColor = [UIColor whiteColor].CGColor;
    rightPage.backgroundColor = [UIColor whiteColor].CGColor;
    leftPage.borderWidth = 2.0; // borders added for now, so we can visually distinguish between the left and right pages
    rightPage.borderWidth = 2.0;
    leftPage.borderColor = [UIColor darkGrayColor].CGColor;
    rightPage.borderColor = [UIColor darkGrayColor].CGColor;
    //leftPage.transform = makePerspectiveTransform(); // uncomment later
    //rightPage.transform = makePerspectiveTransform(); // uncomment later
    curtainView = [[UIView alloc] initWithFrame:self.view.bounds];
    curtainView.backgroundColor = [UIColor scrollViewTexturedBackgroundColor];
    [curtainView.layer addSublayer:leftPage];
    [curtainView.layer addSublayer:rightPage];
    UITapGestureRecognizer *foldTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(fold:)];
    [self.view addGestureRecognizer:foldTap];
    UITapGestureRecognizer *unfoldTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(unfold:)];
    unfoldTap.numberOfTouchesRequired = 2;
    [self.view addGestureRecognizer:unfoldTap];
}
- (void)fold:(UITapGestureRecognizer *)gr
{
// drawing the &quot;incrementalImage&quot; bitmap into our layers
    CGImageRef imgRef = ((CanvasView *)self.view).incrementalImage.CGImage;
    leftPage.contents = (__bridge id)imgRef;
    rightPage.contents = (__bridge id)imgRef;
    leftPage.contentsRect = CGRectMake(0.0, 0.0, 0.5, 1.0); // this rectangle represents the left half of the image
    rightPage.contentsRect = CGRectMake(0.5, 0.0, 0.5, 1.0); // this rectangle represents the right half of the image
    leftPage.transform = CATransform3DScale(leftPage.transform, 0.95, 0.95, 0.95);
    rightPage.transform = CATransform3DScale(rightPage.transform, 0.95, 0.95, 0.95);
    leftPage.transform = CATransform3DRotate(leftPage.transform, D2R(7.5), 0.0, 1.0, 0.0);
    rightPage.transform = CATransform3DRotate(rightPage.transform, D2R(-7.5), 0.0, 1.0, 0.0);
    [self.view addSubview:curtainView];
}
- (void)unfold:(UITapGestureRecognizer *)gr
{
    leftPage.transform = CATransform3DIdentity;
    rightPage.transform = CATransform3DIdentity;
    // leftPage.transform = makePerspectiveTransform(); // uncomment later
    // rightPage.transform = makePerspectiveTransform(); // uncomment later
    [curtainView removeFromSuperview];
}
// UNCOMMENT LATER:
/*
 CATransform3D makePerspectiveTransform()
 {
     CATransform3D transform = CATransform3DIdentity;
     transform.m34 = 1.0 / -2000;
     return transform;
 }
*/
@end

Ignoring the commented out code for now, you can see that the layer set up is exactly as we planned out above.

Let’s discuss this code briefly:

  • We decide to override the -viewDidAppear: method instead of -viewDidLoad (which you might be more used to) because when the latter method is called the bounds of the view are still for portrait mode – but our app runs in landscape mode. By the time viewDidAppear: is called, the bounds have been set correctly and so we put our code there (we’ve temporarily added thick borders so we can make out the left and right layers as we apply transformations on them).
  • We added a gesture recognizer that registers a tap, and for each tap it causes the pages to become slightly smaller (95% of their previous size) and makes them turn by 7.5 degrees. The signs are different because one of the pages turns clockwise while the other turns counterclockwise. We would have to go into the math to see which sign corresponds to which direction, but since there are only two options, it’s easier just to write the code and check! By the way, the transform functions accept angles in radians, so we use the macro D2R() to convert from radians to degrees. One important observation is that the functions that take a transform in their argument (such as CATransform3DScale and CATransform3DRotate) “chain together” one transform with another (the current value of the layer transform property). Other functions, such as CATransform3DMakeRotation, CATransform3DMakeScale, CATransform3DIdentity just construct the appropriate transformation matrix. CATransform3DIdentity is the “identity transform” that a layer has when you create one. It’s analagous to the number “1″ in a multiplication in that applying an identity transform to a layer leaves its transform unchanged, much like multiplying a number by one.
  • Regarding the drawing, we set the contents property of our layers to be the image. It’s quite important to note that we set the content rectangle (normalized between 0 and 1 along each dimension) such that each page only displays half of the image corresponding to it. This normalized coordinate system is the same as the one we discussed previously when talking about the anchor point, so you should be able to work out the values we used for each half of the image.
  • The curtainView object simply acts as a container for the page layers (more precisely, they are made sublayers to curtainView’s underlying layer). Remember that we already computed the placement and geometry of the layers, and these are with respect curtainView’s layer. Tapping once adds this view on top of our canvas view and applies the transform on the layer. Double tapping removes it, to reveal the canvas once more, as well as reverts the transform of the layers to the identity transform.
  • Note the use of CGImage here – and also CGColor earlier – instead of UIImage and UIColor. This is because CALayer operates on a level below UIKit, and it works with “opaque” data types (roughly meaning, don’t ask about their underlying implementation!) which are defined in the Core Graphics framework. Objective-C classes like UIColor and UIImage can be thought of as object oriented wrappers around their more primitive CG versions. For convenience sake, many UIKit objects expose their underlying CG type as a property.

In the AppDelegate.m file, replace all the code with the following (the only thing we’ve added is to include the ViewController header file and to make a ViewController instance the root view controller):

//
//  AppDelegate.m
//
#import "AppDelegate.h"
#import "ViewController.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.rootViewController = [[ViewController alloc] init];
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}
@end

Build the project and run it on the simulator or on your device. Scribble on the canvas for a bit, and then tap on the screen with a single finger to trigger the gesture recognizer action (tapping with a two finger causes the 3D effect to end and the drawing canvas to reappear).

Flat

Not quite the effect we’re going for! What’s going on?


Getting Our Perspective Right

First, notice that the pages get smaller with each single tap, so the issue doesn’t lie with the scaling transform, only with the rotation. The problem is that even though the rotation is happening in a (mathematical) 3D space, the result is being projected onto our flat screens pretty much the same way as a 3D object casts its shadow on a wall. To convey depth, we need to use some kind of cue. The most important cue is that of perspective: an object closer to our eyes appears larger than one further away. Shadows are another great cue, and we’ll get to them shortly. So, how do we incorporate perspective into our transform?

Let’s talk a little bit about transforms first. What are they, really? Mathematically speaking, you ought to know that if we represent the points in our shape as mathematical vectors, then geometric transforms such as scale, rotation, and translation are represented as matrix transformations. What this means is that if we take a matrix representing a transformation and multiply with a vector that represents a point in our shape, then the result of the multiplication (also a vector) represents where that point ends up after being transformed. We can’t say much more here without diving into the theory (which is really worth learning about, if you’re not already familiar with it – especially if you intend to incorporate cool 3D effects in your apps!).

What about code? Previously, we set the layer geometry by setting its anchorPoint, position, and bounds. What we see on the screen is the layer’s geometry after it has been transformed by its transform property. Note the function calls that look like layer.transform = // ... That’s where we’re setting the transform, which internally is just a struct representing a 4 x 4 matrix of floating point values. Also note that the functions CATransform3DScale and CATransform3DRotate take the layer’s current transform as a parameter. That’s because we can compose several transforms together (which just means we multiply their matrices together), with the end result being as if you’d performed these transforms one by one. Note that we’re only talking about the final result of the transform, not how Core Animation animates the layer!

Getting back to the perspective problem, what we need to know is that there’s a value in our transform matrix that we can tweak to get the perspective effect that we’re after. That value is a member of the transform structure, called m34 (the numbers indicate its position in the matrix). To get the effect we want, we need to set it to a small, negative number.

Uncomment the two commented sections in the ViewController.m file (the CATransform3D makePerspectiveTransform() function and the lines leftPage.transform = makePerspectiveTransform(); rightPage.transform = makePerspectiveTransform(); and build again. This time round the 3D effect looks more believable.

Fold with perspective

Also note that when we change the transform property of a CALayer, the deal comes with a “free” animation. This is what we want here – as opposed to the layer undergoing its transformation abruptly – but sometimes it’s not.

Of course, perspective only goes as far, when our example becomes more sophisticated, we’ll use shadows too! We might also want to round the corners of our “book” and masking our page layers with a CAShapeLayer can help with that. Plus, we’d like to use a pinch gesture to control the folding/unfolding so that it feels more interactive. All this will be covered in the second part of this tutorial mini-series.

I encourage you to experiment with the code, referring to the API documentation, and try to implement our desired effect independently (you might even end up doing it better!).

Have fun with the tutorial, and thanks for reading!

Create a 3D Page Folding Animation: Polishing the Page Fold

$
0
0

This two part mini-series will teach you how to create an impressive page-folding effect with Core Animation. In this installment, you’ll learn the steps necessary to polish the page fold created previously and you’ll build a more interactive folding experience using pinch gestures.


Introduction

In the first part of this two-part tutorial, we took an existing freehand drawing app which allowed the user to sketch on the screen with his finger and we implemented a 3D folding effect on the drawing canvas that gave the visual impression of a book being folded up along its spine. We achieved this using CALayers and transformations. We hooked up the effect to a tap gesture that caused the folding to happen in increments, animating smoothly between one increment and the next.

In this part of the tutorial, we’ll look at improving both the visual aspects of the effect (by adding curved corners to our pages, and letting our pages cast a shadow on the background) as well as making the folding-unfolding more interactive, by letting the user control it with a pinch gesture. Along the way, we’ll learn about several things: a CALayer subclass called CAShapeLayer, how to mask a layer to give it a non-rectangular shape, how to enable a layer to cast a shadow, how to configure the shadow’s properties, and a bit more about implicit layer animation and how to make these animations play nice when we add user interaction into the mix.

The starting point for this part will be the Xcode project we ended up with at the conclusion of the first tutorial. Let’s proceed!


Shaping the Page Corners

Recall that each of the left and right pages was an instance of CALayer. They were rectangular in shape, placed over the left and right halves of the canvas, abutting along the vertical line running through the center. We drew the contents (i.e. the user’s freehand sketch) of the left and right halves of the canvas into these two pages.

Even though a CAlayer upon creation starts out with rectangular bounds (like UIViews), one of the cool things we can do to layers is clip their shape according to a “mask”, so that they’re no longer restricted to being rectangular! How is this mask defined? CALayers have a mask property which is a CALayer whose content’s alpha channel describes the mask to be used. If we use a “soft” mask (alpha channel has fractional values) we can make the layer partially transparent. If we use a “hard” mask (i.e. with alpha values zero or one) we can “clip” the layer so that it attains a well-defined shape of our choosing.

Effect of masking a layer with a soft vs. a hard mask

We could use an external image to define our mask. However, since our mask has a specific shape (rectangle with some corners rounded), there’s a better way to do it in code. To specify a shape for our mask, we use a subclass of CALayer called CAShapeLayer. CAShapeLayers are layers that can have any shape defined by a vector path of the Core Graphics opaque type CGPathRef. We can either directly create this path using the C-based Core Graphics API, or – more conveniently – we can create a UIBezierPath object with the Objective-C UIKit framework. UIBezierPath exposes the underlying CGPathRef object via its CGPath property which can be assigned to our CAShapeLayer‘s path property, and this shape layer can in turn be assigned to be our CALayer‘s mask. Luckily for us, UIBezierPath can be initialized with many interesting predefined shapes, including a rectangle with rounded corners (where we choose which corner(s) to round).

Add the following code, after, say, the line rightPage.transform = makePerspectiveTransform(); in the ViewController.m viewDidAppear: method:

    // rounding corners
    UIBezierPath *leftPageRoundedCornersPath = [UIBezierPath bezierPathWithRoundedRect:leftPage.bounds byRoundingCorners:UIRectCornerTopLeft|UIRectCornerBottomLeft cornerRadii:CGSizeMake(25., 25.0)];
    UIBezierPath *rightPageRoundedCornersPath = [UIBezierPath bezierPathWithRoundedRect:rightPage.bounds byRoundingCorners:UIRectCornerTopRight|UIRectCornerBottomRight cornerRadii:CGSizeMake(25.0, 25.0)];
    CAShapeLayer *leftPageRoundedCornersMask = [CAShapeLayer layer];
    CAShapeLayer *rightPageRoundedCornersMask = [CAShapeLayer layer];
    leftPageRoundedCornersMask.frame = leftPage.bounds;
    rightPageRoundedCornersMask.frame = rightPage.bounds;
    leftPageRoundedCornersMask.path = leftPageRoundedCornersPath.CGPath;
    rightPageRoundedCornersMask.path = rightPageRoundedCornersPath.CGPath;
    leftPage.mask = leftPageRoundedCornersMask;
    rightPage.mask = rightPageRoundedCornersMask;

The code should be self explanatory. The bezier path is in the shape of a rectangle with the same size as the layer being masked, and has the appropriate corners rounded off (top left and bottom left for the left page, top right and bottom right for the right page).

Build the project and run. The page corners should be rounded now…cool!

Before and after rounding

Applying a Shadow

Applying a shadow is also easy, but there’s a hitch when we want a shadow after we apply a mask (like we just did). We’ll run into this in due course!

A CALayer has a shadowPath which is a (you guessed it) CGPathRef and defines the shape of the shadow. A shadow has several properties we can set: its colour, its offset (basically which way and how far away it falls from the layer), its radius (specifying its extent and blurriness), and its opacity.

Actually it should be mentioned that it is not absolutely essential to set the shadowPath as the drawing system will work out the shadow from the layer’s composited alpha channel. However, this is less efficient and will usually cause performance to suffer, so it is always recommended to set the shadowPath property when possible.

Insert the following block of code immediately after the one we just added:

    leftPage.shadowPath = [UIBezierPath bezierPathWithRect:leftPage.bounds].CGPath;
    rightPage.shadowPath = [UIBezierPath bezierPathWithRect:rightPage.bounds].CGPath;
    leftPage.shadowRadius = 100.0;
    leftPage.shadowColor = [UIColor blackColor].CGColor;
    leftPage.shadowOpacity = 0.9;
    rightPage.shadowRadius = 100.0;
    rightPage.shadowColor = [UIColor blackColor].CGColor;
    rightPage.shadowOpacity = 0.9;

Build and run the code. Unfortunately, no shadow will be cast and the output will look exactly as it did previously. What’s up with that?!

To understand what’s going on, comment out the first block of code we wrote in this tutorial, but leave the shadow code in place. Now build and run. OK, we’ve lost the rounded corners, but now our layers cast a nebulous shadow on the view behind them, enhancing the sense of depth.

What happens is that when we set a CALayer‘s mask property, the layer’s render region is clipped to the mask region. Therefore shadows (which are naturally cast away from the layer) do not get rendered and hence do not appear.

We can't get shadows if we use a mask

Before we attempt to solve this problem, note that the shadow for the right page got cast on top of the left page. This is because the leftPage was added to the view before rightPage, therefore the former is effectively “behind” the latter in the drawing order (even though they’re both sibling layers). Besides switching the order in which the two layers were added to the super layer, we could change the zPosition property of the layers to explicitly specify the drawing order, assigning a smaller float value to the layer we wanted to be drawn first. We’d be in for a more complex implementation if we wanted to eschew this effect altogether, but since it (fortuitiously) lends a nice shaded effect to our page, we’re happy with things the way they are!


Getting Both Shadows and a Masked Shape

To solve this problem, we’ll use two layers to represent each page, one to generate shadows and the other to display the drawn content in a shaped region. In terms of the heirarchy, we’ll add the shadow layers as a direct sublayer to the background view’s layer. Then we’ll add the content layers to the shadow layers. Hence the shadow layers will double as “containers” for the content layer. All our geometric transforms (to do with the page turning effect) will be applied to the shadow layers. Since the content layers will be rendered relative to their containers, we won’t have to apply any transforms to them.

Once you’ve understood this, then writing the code is relatively straightforward. But because of all the changes we’re making it’ll be messy to modify the previous code, therefore I suggest you replace all the code in viewDidAppear: with the following:

- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    self.view.backgroundColor = [UIColor whiteColor];
    leftPageShadowLayer = [CAShapeLayer layer];
    rightPageShadowLayer = [CAShapeLayer layer];
    leftPageShadowLayer.anchorPoint = CGPointMake(1.0, 0.5);
    rightPageShadowLayer.anchorPoint = CGPointMake(0.0, 0.5);
    leftPageShadowLayer.position = CGPointMake(self.view.bounds.size.width/2, self.view.bounds.size.height/2);
    rightPageShadowLayer.position = CGPointMake(self.view.bounds.size.width/2, self.view.bounds.size.height/2);
    leftPageShadowLayer.bounds = CGRectMake(0, 0,
                                            self.view.bounds.size.width/2, self.view.bounds.size.height);
    rightPageShadowLayer.bounds = CGRectMake(0, 0, self.view.bounds.size.width/2, self.view.bounds.size.height);
    UIBezierPath *leftPageRoundedCornersPath = [UIBezierPath bezierPathWithRoundedRect:leftPageShadowLayer.bounds byRoundingCorners:UIRectCornerTopLeft|UIRectCornerBottomLeft cornerRadii:CGSizeMake(25., 25.0)];
    UIBezierPath *rightPageRoundedCornersPath = [UIBezierPath bezierPathWithRoundedRect:rightPageShadowLayer.bounds byRoundingCorners:UIRectCornerTopRight|UIRectCornerBottomRight cornerRadii:CGSizeMake(25.0, 25.0)];
    leftPageShadowLayer.shadowPath = leftPageRoundedCornersPath.CGPath;
    rightPageShadowLayer.shadowPath = rightPageRoundedCornersPath.CGPath;
    leftPageShadowLayer.shadowColor = [UIColor blackColor].CGColor;
    leftPageShadowLayer.shadowRadius = 100.0;
    leftPageShadowLayer.shadowOpacity = 0.9;
    rightPageShadowLayer.shadowColor = [UIColor blackColor].CGColor;
    rightPageShadowLayer.shadowRadius = 100;
    rightPageShadowLayer.shadowOpacity = 0.9;
    leftPage = [CALayer layer];
    rightPage = [CALayer layer];
    leftPage.frame = leftPageShadowLayer.bounds;
    rightPage.frame = rightPageShadowLayer.bounds;
    leftPage.backgroundColor = [UIColor whiteColor].CGColor;
    rightPage.backgroundColor = [UIColor whiteColor].CGColor;
    leftPage.borderColor = [UIColor darkGrayColor].CGColor;
    rightPage.borderColor = [UIColor darkGrayColor].CGColor;
    leftPage.transform = makePerspectiveTransform();
    rightPage.transform = makePerspectiveTransform();
    CAShapeLayer *leftPageRoundedCornersMask = [CAShapeLayer layer];
    CAShapeLayer *rightPageRoundedCornersMask = [CAShapeLayer layer];
    leftPageRoundedCornersMask.frame = leftPage.bounds;
    rightPageRoundedCornersMask.frame = rightPage.bounds;
    leftPageRoundedCornersMask.path = leftPageRoundedCornersPath.CGPath;
    rightPageRoundedCornersMask.path = rightPageRoundedCornersPath.CGPath;
    leftPage.mask = leftPageRoundedCornersMask;
    rightPage.mask = rightPageRoundedCornersMask;
    leftPageShadowLayer.transform = makePerspectiveTransform();
    rightPageShadowLayer.transform = makePerspectiveTransform();
    curtainView = [[UIView alloc] initWithFrame:self.view.bounds];
    curtainView.backgroundColor = [UIColor scrollViewTexturedBackgroundColor];
    [curtainView.layer addSublayer:leftPageShadowLayer];
    [curtainView.layer addSublayer:rightPageShadowLayer];
    [leftPageShadowLayer addSublayer:leftPage];
    [rightPageShadowLayer addSublayer:rightPage];
    UITapGestureRecognizer *foldTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(fold:)];
    [self.view addGestureRecognizer:foldTap];
    UITapGestureRecognizer *unfoldTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(unfold:)];
    unfoldTap.numberOfTouchesRequired = 2;
    [self.view addGestureRecognizer:unfoldTap];
}

You also need to add the two instance variables corresponding to the two shadow layers. Modify the code at the beginning of the @implementation section to read:

@implementation ViewController
{
    CALayer *leftPage;
    CALayer *rightPage;
    UIView *curtainView;
    CAShapeLayer *leftPageShadowLayer;
    CAShapeLayer *rightPageShadowLayer;
}

Based on our previous discussion, you should find the code straightforward to follow.

Recall that I previously mentioned that the layers containing the drawn content would be contained as sublayers to the shadow generating layer. Therefore, we need to modify our fold: and unfold: methods to perform the requisite transformation on the shadow layers.

- (void)fold:(UITapGestureRecognizer *)gr
{
    // drawing the "incrementalImage" bitmap into our layers
    CGImageRef imgRef = ((CanvasView *)self.view).incrementalImage.CGImage;
    leftPage.contents = (__bridge id)imgRef;
    rightPage.contents = (__bridge id)imgRef;
    leftPage.contentsRect = CGRectMake(0.0, 0.0, 0.5, 1.0); // this rectangle represents the left half of the image
    rightPage.contentsRect = CGRectMake(0.5, 0.0, 0.5, 1.0); // this rectangle represents the right half of the image
    leftPageShadowLayer.transform = CATransform3DScale(leftPageShadowLayer.transform, 0.95, 0.95, 0.95);
    rightPageShadowLayer.transform = CATransform3DScale(rightPageShadowLayer.transform, 0.95, 0.95, 0.95);
    leftPageShadowLayer.transform = CATransform3DRotate(leftPageShadowLayer.transform, D2R(7.5), 0.0, 1.0, 0.0);
    rightPageShadowLayer.transform = CATransform3DRotate(rightPageShadowLayer.transform, D2R(-7.5), 0.0, 1.0, 0.0);
    [self.view addSubview:curtainView];
}
- (void)unfold:(UITapGestureRecognizer *)gr
{
    leftPageShadowLayer.transform = CATransform3DIdentity;
    rightPageShadowLayer.transform = CATransform3DIdentity;
    leftPageShadowLayer.transform = makePerspectiveTransform(); // uncomment later
    rightPageShadowLayer.transform = makePerspectiveTransform(); // uncomment later
    [curtainView removeFromSuperview];
}

Build and run the app to check out our pages with both rounded corners and shadow!

Both shadows and rounded corners

As before, one-finger taps cause the book to fold up in increments, while a two finger tap restores removes the effect and restores the app to its normal drawing mode.


Incorporating Pinch-Based Folding

Things are looking good, visually speaking, but the tap isn’t really a realistic gesture for a book folding metaphor. If we think about iPad apps like Paper, the folding and unfolding is driven by a pinch gesture. Let’s implement that now!

Implement the following method in ViewController.m:

- (void)foldWithPinch:(UIPinchGestureRecognizer *)p
{
    if (p.state == UIGestureRecognizerStateBegan) // ............... (A)
    {
        self.view.userInteractionEnabled = NO;
        CGImageRef imgRef = ((CanvasView *)self.view).incrementalImage.CGImage;
        leftPage.contents = (__bridge id)imgRef;
        rightPage.contents = (__bridge id)imgRef;
        leftPage.contentsRect = CGRectMake(0.0, 0.0, 0.5, 1.0);
        rightPage.contentsRect = CGRectMake(0.5, 0.0, 0.5, 1.0);
        leftPageShadowLayer.transform = CATransform3DIdentity;
        rightPageShadowLayer.transform = CATransform3DIdentity;
        leftPageShadowLayer.transform = makePerspectiveTransform();
        rightPageShadowLayer.transform = makePerspectiveTransform();
        [self.view addSubview:curtainView];
    }
    float scale = p.scale > 0.48 ? p.scale : 0.48; // .......................... (B)
    scale = scale < 1.0 ? scale : 1.0;
    // SOME CODE WILL GO HERE (1)
    leftPageShadowLayer.transform = CATransform3DIdentity;
    rightPageShadowLayer.transform = CATransform3DIdentity;
    leftPageShadowLayer.transform = makePerspectiveTransform();
    rightPageShadowLayer.transform = makePerspectiveTransform();
    leftPageShadowLayer.transform = CATransform3DScale(leftPageShadowLayer.transform, scale, scale, scale); // (C)
    rightPageShadowLayer.transform = CATransform3DScale(rightPageShadowLayer.transform, scale, scale, scale);
    leftPageShadowLayer.transform = CATransform3DRotate(leftPageShadowLayer.transform, (1.0 - scale) * M_PI, 0.0, 1.0, 0.0);
    rightPageShadowLayer.transform = CATransform3DRotate(rightPageShadowLayer.transform, -(1.0 - scale) * M_PI, 0.0, 1.0, 0.0);
    // SOME CODE WILL GO HERE (2)
    if (p.state == UIGestureRecognizerStateEnded) // ........................... (C)
    {
        // SOME CODE CHANGES HERE LATER (3)
        self.view.userInteractionEnabled = YES;
        [curtainView removeFromSuperview];
    }
}

A brief explanation regarding the code, with respect to the labels A, B, and C referred in the code:

  1. When the pinch gesture is recognized (indicated by its state property taking the value UIGestureRecognizerStateBegan) we start preparing for the fold animation. The statement self.view.userInteractionEnabled = NO; ensures that the any additional touches that take place during the pinch gesture won’t cause drawing to take place on the canvas view. The remaining code should be familiar to you. We’re just resetting the layer transforms.
  2. The scale property of the pinch determines the ratio of the distance between the fingers with respect to the start of the pinch. I decided to clamp the value we’ll use to calculate our pages’ scaling and rotation transforms between 0.48 < p.scale < 1.0. The condition scale < 1.0 is so that a "reverse pinch" (the user moving his fingers further apart than the start of the pinch, corresponding to p.scale > 1.0) has no effect. The condition p.scale > 0.48 is so that when the inter-finger distance becomes approximately half of what it was at the start of the pinch, our folding animation is completed and any further pinch has no effect. I choose 0.48 instead of 0.50 because of the way I calculate the turning angle of the layer's rotational transform. With a value of 0.48 the rotation angle will be slightly less than 90 degrees, so the book won't completely fold and hence won't become invisible.
  3. After the user ends the pinch, we remove the view presenting our animated layers from the canvas view (as before), and we restore the canvas' interactivity.

Add the code to add a pinch recognizer at the end of viewDidAppear:

    UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(foldWithPinch:)];
    [self.view addGestureRecognizer:pinch];

You may get rid of all the code related to the two tap recognizers from ViewController.m because we won't be needing those anymore.

Build and run. If you're testing on the simulator instead of the device, remember that you need to hold down the option key in order to simulate two finger gestures such as pinching.

In principle, our pinch-based folding-unfolding is working, but you're bound to notice (particularly if you're testing on a physical device) that the animation is slow and lags behind the pinch, therefore weakening the illusion that the pinch is driving the folding-unfolding animation. So what's going on?


Getting The Animations Right

Remember the implicit animation of the transform that we were enjoying "for free" when the animation was controlled by the tap? Well, it turns out that the same implicit animation has become a hindrance now! Generally, when we want to control an animation by a gesture, such as a view being dragged across the screen with a dragging (panning) gesture (or the case with our app here) we want to cancel implicit animations. Let's make sure we understand why, considering the case of a user dragging a view across the screen with his finger:

  1. The view is at position p0 to begin with (i.e. view.position = p0 where p0 = (x0, y0) and is a CGPoint)
  2. When UIKit next samples the user's touch on the screen, he's dragged his finger to another position, say p1.
  3. Implicit animation kicks in, causing the animation system begins to animate the position change of view from p0 to p1 with the "relaxed" duration of 0.25 seconds. However, the animation has barely started, and the user has already dragged his finger to a new position, p2. The animation has to be canceled and a new one begun towards position p2.
  4. ...and so on and so forth!
Implicit animations getting in the way

Since the user's panning gesture (in our hypothetical example) is effectively a continuous one, we just need to change the position of the view in step with the user's gesture to maintain the illusion of a response animation! Exactly the same argument applies for our page fold with pinch situation here. I only mentioned the dragging example because it seemed simpler to explain what was going on.

There are different ways to disable implicit animations. We'll choose the simplest one, which involves wrapping our code in a CATransaction block and invoking the class method [CATransaction setDisableActions:YES]. So, what's a CATransaction anyway? In simplest terms, CATransaction "bundles up" property changes that need to be animated and successively updates their values on the screen, handling all the timing aspects. In effect, it does all the hard work related to rendering our animations onto the screen for us! Even though we haven't explicitly used an animation transaction yet, an implicit one is always present when any animation related code is executed. What we need to do now is to wrap up the animation with a custom CATransaction block.

In the pinchToFold: method, add these lines:

    [CATransaction begin];
    [CATransaction setDisableActions:YES];

At the site of the comment // SOME CODE WILL GO HERE (1),
add the line:

[CATransaction commit];

If you build and run the app now, you'll notice that the folding-unfolding is much more fluid and responsive!

Another animation-related issue that we need to tackle is that our unfolding: method doesn't animate the restoration of the book to its flattened state when the pinch gesture ends. You may complain that in our code we haven't actually bothered reverting the transform in the "then" block of our if (p.state == UIGestureRecognizerStateEnded) statement. But you're welcome to try inserting the statements that reset the layer's transform before the statement [canvasView removeFromSuperview]; to see whether the layers animate this property change (spoiler: they won't!).

The reason the layers won't animate the property change is that any property change that we might carry out in that block of code would be lumped in the same (implicit) CATransaction as the code to remove the layer hosting view (canvasView) from the screen. The view removal would happen immediately - it's not an animated change, after all - and no animation in any subviews (or sublayers added to its layer) would occur.

Again, an explicit CATransaction block comes to our rescue! A CATransaction has a completion block which is only executed after any property changes that appear after it have finished animating.

Change the code following the if (p.state == UIGestureRecognizerStateEnded) clause, so that the if statement reads as follows:

    if (p.state == UIGestureRecognizerStateEnded)
    {
        [CATransaction begin];
        [CATransaction setCompletionBlock:^{
            self.view.userInteractionEnabled = YES;
            [curtainView removeFromSuperview];
        }];
        [CATransaction setAnimationDuration:0.5/scale];
        leftPageShadowLayer.transform = CATransform3DIdentity;
        rightPageShadowLayer.transform = CATransform3DIdentity;
        [CATransaction commit];
    }

Note that I decided to make the animation duration change inversely with respect to the scale so that the greater the degree of the fold at the time the gesture ends the more time the reverting animation should take.

The crucial thing to understand here is that only after the transform changes have animated does the code in the completionBlock execute. The CATransaction class has other properties you can use the configure an animation exactly how you want it. I suggest you take a look at the documentation for more.

Build and run. Finally, our animation not only looks good, but responds properly to user interaction!


Conclusion

I hope this tutorial has convinced you that Core Animation Layers are a realistic choice for achieving fairly sophisticated 3D effects and animations with little effort. With some optimization, you ought to be able to animate a few hundred layers on the screen at the same time if you need to. A great use case for Core Animation is for incorporating a cool 3D transition when switching from one view to another in your app. I also feel that Core Animation might be a viable option for building simple word-based or card-based games.

Even though our tutorial spanned two parts, we've barely scratched the surface of layers and animations (but hopefully that's a good thing!). There are other kinds of interesting CALayer subclasses that we didn't get a chance to look at. Animation is a huge topic in itself. I recommend watching the WWDC talks on these topics, such as "Core Animation in Practice" (Sessions 424 and 424, WWDC 2010), "Core Animation Essentials" (Session 421, WWDC 2011), "iOS App Performance - Graphics and Animations" (Session 238, WWDC 2012) and "Optimizing 2D Graphics and Animation Performance" (Session 506, WWDC 2012), and then digging into the documentation. Happy learning and app writing!

Best of Tuts+ in January 2013

$
0
0

Each month, we bring together a selection of the best tutorials and articles from across the whole Tuts+ network. Whether you’d like to read the top posts from your favourite site, or would like to start learning something completely new, this is the best place to start!


Psdtuts+ — Photoshop Tutorials


Nettuts+ — Web Development Tutorials

  • Visual Studio: Web Dev Bliss

    Visual Studio: Web Dev Bliss

    Developers are a picky bunch, almost to the point of superstition and voodoo magic, when it comes to their tools. If you take into account the countless number of things we use to build apps (Node, Grunt, Fiddler, LESS, EC2 etc.), it’s no wonder why, once we find a nice combo, we guard it like a squirrel with his last nut. And it makes sense, since your development environment is pretty darn important to your success.

    Visit Article

  • Important Considerations When Building Single Page Web Apps

    Important Considerations When Building Single Page Web Apps

    Single page web applications – or SPAs, as they are commonly referred to – are quickly becoming the de facto standard for web app development. The fact that a major part of the app runs inside a single web page makes it very interesting and appealing, and the accelerated growth of browser capabilities pushes us closer to the day, when all apps run entirely in the browser.

    Visit Article

  • Better Workflow in PHP With Composer, Namespacing, and PHPUnit

    Better Workflow in PHP With Composer, Namespacing, and PHPUnit

    In this video tutorial, we’ll again focus on workflow. Specifically, we’ll use Composer’s built-in autoloading capabilities, along with namespacing, to make for an elegant testing experience. We’ll also review a handful of PHPUnit best practices, as we get setup with these tools.

    Visit Article


Vectortuts+ — Illustrator Tutorials


Webdesigntuts+ — Web Design Tutorials

  • Optimizing Content for Search Engines and Users

    Optimizing Content for Search Engines and Users

    So far during this session weve talked about various technical measures that can be taken to optimize your website for search engines. But whats perhaps even more important is the content of your pages. Today we take a look at how we can optimize our content not only for search engines, but also for users.

    Visit Article

  • The Best Way to Learn HTML

    The Best Way to Learn HTML

    Learning a new skill is often intimidating at first; knowing where to start, who to listen to, what to ignore – it can be a difficult process to get moving. That’s what this post is for. It will help you plan out what to learn and in what order, hopefully making what seems like a big hurdle much lower, keeping you interested and encouraging you to carry on learning!

    Visit Article

  • Quick Tip: Using Images as Fullscreen Faux-Gradient Backgrounds

    Quick Tip: Using Images as Fullscreen Faux-Gradient Backgrounds

    In this Quick Tip I’ll show you how to create a nice looking gradient background, using any image and a couple of lines of CSS. What’s more, I’ll talk about performance and explain the background-attachment property as we go along.

    Visit Article


Phototuts+ — Photography Tutorials

  • 7 Tips for Shooting from a Helicopter

    Tips for Shooting from a Helicopter

    I hope you will have the opportunity to shoot photos from a helicopter once in your life. It is a fun and amazing experience, especially for those of us who enjoy flying. While you may have noticed a video by the high-profile photographer Chase Jarvis showing how he got ready for a shoot hanging from the side of a helicopter, chances are your own experience will not be anything like that. It might be a scenic flight over your local city or maybe while on vacation. It might even be for an assignment.

    Visit Article

  • Capturing Portraits, In Flight and Hands On Photos of Birds

    Capturing Portraits, In Flight and Hands On Photos of Birds

    Birds of prey have fascinated me since I was a young boy. When we were out in the countryside my dad would spot them, point them out to me and tell me which bird it was, and I just couldnt get over how beautiful and dangerous they were. This fascination remains with me today, and I still get excited when I see the birds in the wild. Like any wildlife, they can be difficult to photograph, so here are a few tips on how to make your shoot a successful one.

    Visit Article

  • Understanding Color Theory for Digital Photography – Tuts+ Premium

    Understanding Color Theory for Digital Photography – Tuts+ Premium

    We have another Photo Premium tutorial exclusively available to Premium members today. In this tutorial, we’ll be diving into color theory. Learn more after the jump!

    Visit Article


Cgtuts+ — Computer Graphics Tutorials

  • From Maya to Source Engine: Creating a Game Ready Team Fortress 2 Hat

    From Maya to Source Engine: Creating a Game Ready Team Fortress 2 Hat

    Today we’ve got an exciting new tutorial for all you Team Fortress 2 fans and Mod enthusiasts where you’ll learn how to Model, Sculpt, Retopo, UVMap, Texture and Bake your very own ‘Americana’ inspired hat for use in TF2 using Maya, ZBrush, Nex, xNormal and Photoshop.

    Visit Article

  • Designing and Animating a Birch Tree in Maya using Paint Effects

    Designing and Animating a Birch Tree in Maya using Paint Effects

    Trees, plants and other forms of vegetation can go a long way in adding additional detail and a level of believability to any scene. And what was once considered to be an extremely difficult, time consuming task, has been made considerably easier with advances in modeling tools such as Maya’s Paint Effects.

    Visit Article

  • Advanced Poly Modeling – Tassimo T-65 Home Brewing Station: Part 1

    Advanced Poly Modeling – Tassimo T-65 Home Brewing Station: Part 1

    In this new tutorial series focused on product modeling and rendering, we’ll create the Tassimo T-65 Home Brewing Station from Bosch. Starting in 3d Studio Max we’ll use a few simple blueprint images to do a block-out of the base, and then begin the process of breaking down and refining the model using more advanced poly modeling techniques to create the final High-res asset. Later lessons will include UVMapping, Texturing, creating V-Ray materials and finally building a suitable studio lighting rig and rendering out the model with V-Ray.

    Visit Article


Aetuts+ — After Effects Tutorials

  • Create a World Traveler Animation to Show Where in the World You’ve Been

    Create a World Traveler Animation to Show Where in the World You’ve Been

    In today’s tutorial, we’ll be taking a look at mapping layers accurately upon a sphere, then we’ll create a line that travels between the mapped layers using expressions. Because of the 2.5D nature of After Effects, there are some interesting technical challenges to solve along the way.

    Visit Article

  • 8 Ways That You Can Get Better in 2013

    Ways That You Can Get Better in 2013

    The ball has dropped, the confetti has been swept up, and everyone is back at work. I have a feeling that a lot of New Years Resolutions have been broken already. Now Im not a big fan of all the New Years celebrations, but there is one thing I really enjoy doing, and thats putting together my new Demo Reel for the year.

    Visit Article

  • Compositing Fundamentals – Overview of Selections

    Compositing Fundamentals – Overview of Selections

    In this tutorial we’ll take a look at selection methods within a compositing environment and discuss the importance of understanding them. Although its aimed at someone just starting out in the world of compositing, the more experienced artist will also find some valuable information. After a brief overview, and in order to bring greater clarity, we will implement the various selection methods in a test project.

    Visit Article


Audiotuts+ — Audio & Production

  • A Different Approach: Producing “America Dreaming” for Tracii Guns

    A Different Approach: Producing “America Dreaming” for Tracii Guns

    America Dreaming is the title of this great song I produced for Tracii Guns, guitarist and founder of Guns N’ Roses and iconic leader of L.A. Guns. It features the Italian band J27. The single will be released and available, both in US and Italy, in 2013. This article describes my experiences and approach recording and producing the project.

    Visit Article

  • What You Can Learn from the Vocal Production of Katy, Gaga & Ke$ha

    What You Can Learn from the Vocal Production of Katy, Gaga & Ke$ha

    Hyped up, overproduced girl pop. Love it or hate, but you can’t deny the amazing vocal sounds these producers and engineers come up with. It obviously all starts with ’the talent” but there is more to the vocal sound than you think. A quick listen on the radio won’t educate you on the depth of these vocal productions. This article will give you a few examples and tips you can take away from the songs of Katy Perry, Lady Gaga and Ke$ha.

    Visit Article

  • How to Transform A Mono Audio Track to Stereo

    How to Transform A Mono Audio Track to Stereo

    How do you turn a mono recording into stereo? There are many ways, and in this tutorial we’ll cover one traditional technique – virtual double tracking. It avoids stereo enhancement plugins, and I used it effectively in a recent live Jazz recording session.
    In this tutorial we transform a piano from mono to stereo using Logic Pro. But the same technique can be used for any instrument or vocals, and works in most DAWs.

    Visit Article


Wptuts+ — WordPress Tutorials

  • Practical Tips for Aspiring WordPress Developers

    Practical Tips for Aspiring WordPress Developers

    On Wptuts+, we talk a lot about how to achieve certain things using WordPress, how to apply practical tips within WordPress projects themselves, but one overlooked area of working with WordPress is bringing aspiring developers up-to-speed on how to quickly get started with building WordPress-based products.

    Visit Article

  • Improving Your Work-Flow – Separate Your Mark-Up From Your Logic!

    Improving Your Work-Flow – Separate Your Mark-Up From Your Logic!

    In this tutorial I’m going to explain a technique that allows you to utilize a template file for all of your HTML needs! No longer will you need to ‘echo’ strings from inside your functions, or worry about dropping in and out of PHP just to output some mark-up.

    Visit Article

  • Adaptive Blog Theme: Page Templates

    Adaptive Blog Theme: Page Templates

    Not all content on a blog is in posts. We need page templates for archives, a page template that’s full width, and we’re even going to go over a more sophisticated template that deals with a contact form and validation.

    Visit Article


Mobiletuts+ — Mobile Development

  • Learn iOS SDK Development From Scratch!

    Learn iOS SDK Development From Scratch!

    Interested in learning native iOS SDK development? Now is the perfect time to get started! Mobiletuts+ is pleased to announce an in-depth, fully updated, complete session on how to become an iOS SDK developer!

    Visit Article

  • Learn Android SDK Development From Scratch!

    Learn Android SDK Development From Scratch!

    Interested in learning native Android SDK development? Now is the perfect time to get started! Mobiletuts+ is pleased to announce an in-depth, fully up-to-date session on how to become an Android SDK developer.

    Visit Article

  • Debugging in iOS – Essential Tips

    Debugging in iOS – Essential Tips

    Whether you are trying to figure out why your array has 3 objects instead of 5 or why your game plays backward since the new guy started, debugging is an essential part of the development process. At the end of this article, you’ll have an understanding of the most important debugging facilities available to you and how to use them to help squash your bugs in less time.

    Visit Article


Gamedevtuts+ — Game Development


Mactuts+ — Mac & OS X

  • 5 Quick Fixes to Common Mac Problems

    Quick Fixes to Common Mac Problems

    As Mac users were used to not having to frequently troubleshoot our computer problems. However, that doesnt mean that our Macs dont misbehave from time to time. In this first of a two-part tutorial, well detail five quick fixes to your Macs most common problems.

    Visit Article

  • Taming the Elephant: Awesome Evernote Tips and Tricks

    Taming the Elephant: Awesome Evernote Tips and Tricks

    Out of the box, Evernote comes with some pretty robust syncing tools for all your note-taking needs. If you haven’t dug in to all Evernote can do, though, you might not be aware of everything on offer or just how well you can integrate Evernote into your workflow. From automation to advanced searches, we’re going to make Evernote start working harder for you.

    Visit Article

  • 3 Ways to Bring Back Web Sharing in OS X Mountain Lion

    Ways to Bring Back Web Sharing in OS X Mountain Lion

    With the introduction of OS X Mountain Lion, a feature that didnt make the cut was Web Sharing. It may not have been widely used but for developers it served as a quick way to host a website. Lets take a look at three different ways to bring this feature back.

    Visit Article


Crafttuts+ — Craft & Handmade

  • How to Create a Stunning Ombre String Art Word

    How to Create a Stunning Ombre String Art Word

    Forget the brown and orange swirly patterned string art from the ’70s – this string word art is a fun, contemporary and low-cost way to add art to your walls. Random geometric shapes, an ombre effect and the inclusion of type make this string art right on trend. It looks fantastic, can be completed in an afternoon and doesn’t require artistic ability – just follow the step-by-step instructions and you’ll have a gorgeous new art piece ready to hang. This tutorial includes a printable template so there’s no need for you to do the tricky bit of calculating and measuring for the nail placement. It couldn’t be easier!

    Visit Article

  • Create Your Own Stunning Crystallized Bridal Sash

    Create Your Own Stunning Crystallized Bridal Sash

    Congratulations, you’re engaged! And now comes the fun part – creating your dream wedding. There are loads of accessories to think about – from your veil and shoes to your headpiece and earrings. If you’d like to keep costs down and bring a beautiful handcrafted element to your bridal attire, you can make your own bridal sash. It could become a family heirloom in years to come! In this tutorial, you will learn how to create your own crystallized bridal sash that will be “the perfect one” for your wedding day.

    Visit Article

  • How to Carve Your Own Beautiful Stamps

    How to Carve Your Own Beautiful Stamps

    Carving your own stamps and printing by hand is easy and you can achieve lovely results, even if you think you have no artistic ability. Its a simple way to customise and transform almost anything. This tutorial shows you how to carve a stamp from an eraser as well as ideas on how to use your stamps and how to create patterns with them.

    Visit Article


FreelanceSwitch — Freelancing

  • Twitter: To Schedule or Not To Schedule?

    In this article, I look at the cons and pros of scheduling. Admittedly, I favor a mix of scheduled and live tweets. For me, scheduling a tweet is similar to scheduling a blog post. It’s convenient for you, as it saves you time, and it’s convenient for your audience, as it spreads your tweets across the day.

    Visit Article

  • How to Structure a Creative Agency

    The danger of not clearly establishing structure is that you can easily wind up in situations where you need to pay someone for time in which they did no work or did the same work that another person had already completed. Putting a solid structure and clear workflow in place that shows how different tasks pass through your business processes are necessary for an agency to grow.

    Visit Article

  • Top 15 Books for Improving Your Freelance Business

    As a freelancer, I often forget that there are valuable books out there in addition to all of the online reading I do. Recently, though, I’ve taken back up the art of reading a book, rather than just blog posts. And I’ve found a world of knowledge I had been sorely missing.

    Visit Article


Tuts+ Premium – Premium Tutorials

  • Working with Text in Photoshop

    Working with type in Photoshop is never as straight forward as we think, so in this course Howard Pinsky will first teach you the basics of type and then on to more advanced effects that can be accomplished, such as 3D effects, chrome text effect and much much more. So if you have been struggling with how to use type effectively in Photoshop, then struggle no more!

    Visit Article

  • Go Portable with jQuery Mobile

    In this course, we’ll review the widgets that come with jQuery Mobile, and how they can be initialized and configured. We’ll focus on the data-attribute method of using the framework, and learn how we can use it without having to write a single line of JavaScript or CSS.

    Visit Article

  • Create a Misty Landscape Using 3D Renders and Stock Photography

    When working on a matte painting in a production environment, you will likely be provided with either a 3D render or a photo to use as a base for your artwork. In this tutorial, we will combine photo manipulation with digital painting to create a misty landscape. This tutorial includes both written and video content. Let’s get started!

    Visit Article

Build a Multiplayer Minesweeper Game: Server-Side Setup

$
0
0

This two-part mini-series will teach you how to create a multiplayer game called Minesweeper Flags. In this tutorial, you will learn how to implement the server side of the application, the database, and even the Web services. The next tutorial will teach you how to implement the user interface, web server communication, data parsing and interaction. Read on!


Minesweeper Flag

Minesweeper Flag is a multi-player board game that is played between two opponents. Commonly, either blue or red are assigned to each player. The board is composed by 256 equal squares, and each board has 51 mines placed in entirely random positions.

The goal of the game is to uncover all the squares in order to find 26 mines. Touching on the game board will reveal what is hidden underneath the chosen square: a bomb or a number. Every time a player touches on a square that contains a mine he/she is awarded with another move. Moreover, if a number is uncovered, that number represents the number of mines adjacent to the uncovered square.

At the beginning of the game the color of the player is automatically assigned and the board is completely covered. The player with the blue color always moves first.


Project Preview

Figure 5: Blue player
Preview of the Final Effect

System Model

In order to simplify the game and software requirements, a graphical model of all requirements is presented bellow. As you can see, the database, web services, and user interfaces are divided in several distinct layers in order to achieve software independence.

Figure 2: Layer Abstraction

Requirements

In order to complete the tutorial, the reader needs the following requirements:

  • MySQL
  • Netbeans with Web development packages
  • Xcode with iOS development kit.
  • Cocos2d for iOS

Layer Connection

The MySQL component can be seen as the core of the application since it will directly support the game persistence. It will store all data inherent to the Minesweeper Flag game, such as player moves, mine location, and scores.

The database of Minesweeper Flag is composing for only one tablet called games. The following image presents the database in a graphical way.

Figure 3: Entity Relationship Diagram

Database

  • The “games” table is the fundamental table since it preserves the core functions of the game. It contains the game and player identifier, the player turn, game matrix, and the uncovered matrix. The matrix field contains the mines location and their neighbors, while the uncovered matrix initially contains the 256 squares filled with a pre-determined 9 value. This field is very important, since it will be constantly used to check if a given square was already touched and its value. The 9 value means that this single square was not yet touched while the “*”" and “#”" character means that the blue or red player found a mine, correspondingly.

The Netbeans interface development environment is used to provide the web application and its direct web services. This tutorial is based on version 7.0.1 with the Bundled servers installed.


Step 1: Start a Java Web Application

Figure 4: Start a new Java Web Application

Click at File -> New Project


Step 2: Configure the Project Settings

Give a name to the project, its location and folder

Figure 5: Project Name

Step 3: Choose the Web Server Engine

Figure 6: Choose the Web server engine

In this tutorial, the authors used the Glassfish server with Java EE version 6 as a standard.


Step 4: Skip the Framework Option

Figure 7: Frameworks

Do not choose any framework, since we will not use any specification or feature of any of the presented frameworks.

At this point we have the Web server configured. The next step is to launch it in order to verify if everything is correctly configured.


Step 5: Run the Main Project

To run the main project you can click in the Run Main Project option, located at the Run menu or click in the green icon at the top toolbar. If everything is correct a Web page should appear with the message “Hello World!”.

Figure 8: Run the Project

Now it is time to add the web services layer to the web server.


Step 6: Add a new RESTful Web Service

Figure 9: Add Web Service

Right click in the Project name and choose New -> Other. From the left menu options choose Web Services and from the right list RESTful Web Service from Patterns.


Step 8: Select a Pattern

Figure 10: Select a pattern

Choose the first option “Simple Root Reference”.


Step 9: Web Service Configuration

Figure 11: Web Service name

Choose a resource package and define the Path for the root container of all services. Additionally, choose the MIME type. For this tutorial, the authors have selected the JSON data-interchange format.


Step 10: REST Resources Configurations

Figure 12: REST resources

Choose the last option “Create default Jersey REST servlet adaptor in web.xml”.


Step 11: Add the Connection Methods

Additionally, two more methods were added in order to separate the data logic layer with the database layer; CreateConnection() and EndConnection(). The following snippets should be included in the MinesweeperService Java class.

Include this at the import section.

import java.sql.*;
import javax.jws.*;

Define the following class properties.

    private Connection conn = null;
    private Statement statement = null;
    private ResultSet resultSet = null;
    // The authors used the 8889 MySQL port at a localhost host.
    // Change it accordingly.
    private final String url = "jdbc:mysql://localhost:8889/";
    private final String dbName = "minesweeper";
    private final String driver = "com.mysql.jdbc.Driver";
    private final String userName = "root";
    private final String password = "root";

Copy and past the following web methods.

    private Boolean CreateConnection() {
        try {
            Class.forName(driver).newInstance();
            conn = DriverManager.getConnection(url + dbName, userName, password);
            return true;
        } catch (Exception e) {
            return false;
        }
    }
    private Boolean EndConnection() {
        try {
            if (resultSet != null)
                resultSet = null;
            if (statement != null)
                statement.close();
            if (conn != null)
                conn.close();
        } catch (Exception e) {
            return false;
        }
        return true;
    }
    @WebMethod(operationName = "authentication")
    public String authentication(@WebParam(name = "email") String email, @WebParam(name = "password") String pw) {
        CreateConnection();
        String query = "SELECT * FROM players WHERE Email='" + email + "' and Pass='" + pw + "';";
        try {
            statement = (Statement) conn.createStatement();
            resultSet = statement.executeQuery(query);
            if (resultSet.next()) {
                EndConnection();
                return "1";
            } else {
                EndConnection();
                return "0";
            }
        } catch (Exception ex) {
            EndConnection();
            return "0";
        }
    }
}

Step 12: Add the External MySQL JAR

As it is necessary to make a connection to the MySQL database, an external MySQL library is necessary. In order to add it to the project, right click in the project name and go to the Properties options. Then choose Libraries and click at Add JAR / folder.

Figure 13: Add the external MySQL .jar library

Step 13: Web Service Testing

Figure 14: Test the Web Service

Under the main project, click in the Web Services folder and with the second mouse button click in the MinesweeperService object and choose the Test RESTful Web Services option.


Step 14: Select Test File Target

Figure 15: Test files target

Leave the first option selected: “Locally Generated Test Client”.

At this point, you should have a server side application that supports connecting with a database, communication with web servers, and the inherent web methods for game interaction.

All operations related to the game can be made using Step 12, although it is not user friendly to do so.


Next Time

In the second and final installment of this series, we will explain how to create a client-side, native iOS application that will connect with and consume the web server data. Stay tuned for part 2!

Build a Multiplayer Minesweeper Game: Client-Side Creation

$
0
0

This is the second installment of the Minesweeper game mini-series. In this tutorial, you will learn how to fully implement the client side of the application. We will use a native iOS application that connects to the web servers, parses the data, and displays the user interface interaction.


Project Overview

Minesweeper Flag is a multi-player board game that is played between two opponents. Commonly, blue or red is assigned to each player. The board is composed by 256 equal squares, and each board has 51 mines placed in entirely random positions.

Figure 5: Blue player
Preview of the Final Effect

Review of Part 1

Before you start the second part of the series make sure you have the first part fully tested and implemented.


Step 1: Installing Cocos 2D

The first step is to download and install the Cocos 2D game engine. For this tutorial, we used Cocos 2D version 2.

Once downloaded, you need to install it and integrate it with Xcode. Unpack the file downloaded before and you will notice a file called install-templates.sh. Open a terminal window and run that file using the following command

./install-templates -f.

Step 2: Create a New Project

Using Xcode, create a new project: File -> New -> Project.

Figure 1: New cocos2d project

Choose the Cocos2d v2.x on the left side menu and Cocos2d iOS on the right one.


Step 3: Configure Project Settings

Give your project a name and a company identifier. For this tutorial, we use the iPad device family option.

Figure 2: New cocos2d project

Step 4: Verify the Setup

If everything is correct you should see a window similar to the following one:

Figure 3: Project files

Step 5: Add New Classes

Now you should add two new Objective-c classes. To do this, access the menu option File -> New -> File.

Add the following classes:

  • LoadingScene: This class will serve as a loading screen for the user while he / she is waiting for another player to join the game.
  • GameScene: This is the core class of the game; is where the main logic is programmed.

Step 6: Add Resource Files

In order to correctly run the game you should add the resource files to your project. Here you can download the resource files used. Next, copy them to the resources folder.


Step 7: Create the HelloWorldLayer Class

Change your HelloWorldLayer header file to the following:

#import <GameKit/GameKit.h>
#import "cocos2d.h"
@interface HelloWorldLayer : CCLayer {}
@property (nonatomic, retain) NSURLConnection* connectionConnectGame;
@property (nonatomic,retain) NSMutableData* responseData;
@property (nonatomic,retain) NSString* URL;
@property (nonatomic) int playerID;
@property (nonatomic,retain) NSString* playerIDString;
@property (nonatomic) BOOL launchFlag;
@property (nonatomic,retain) NSString* gameID;
@property (nonatomic,retain) NSString* turnID;
+(CCScene *) scene;
-(void) connectTheWebService:(NSString*)id;
@end

Change the HelloWorldLayer implementation file to the following:

#import "HelloWorldLayer.h"
#import "GameScene.h"
#import "LoadingScene.h"
#import "cocos2d.h"
#import "AppDelegate.h"
#pragma mark - HelloWorldLayer
@implementation HelloWorldLayer
@synthesize connectionConnectGame = _connectionConnectGame;
@synthesize URL = _URL;
@synthesize responseData = _responseData;
@synthesize launchFlag = _launchFlag;
@synthesize gameID = _gameID;
@synthesize playerID = _playerID;
@synthesize playerIDString = _playerIDString;
@synthesize turnID = _turnID;
+(CCScene *) scene{
  CCScene *scene = [CCScene node];
  HelloWorldLayer *layer = [HelloWorldLayer node];
  [scene addChild: layer];
  return scene;
}
-(id) init {
  if( (self=[super init]) ) {
        [self setLaunchFlag:FALSE];
        // CHANGE THE LINK accordingly
        [self setURL:@"http://10.0.6.178:8080/Minesweeper_Flag/resources/generic/connectGame/"];
        CGSize size = [[CCDirector sharedDirector] winSize];
        // Background
        CCSprite * background = [CCSprite spriteWithFile:@"background.jpg"];
        background.position = ccp(size.width/2,size.height/2);
        [self addChild:background];
        CCSprite* flags = [CCSprite spriteWithFile:@"bandeiras.png"];
        flags.scale = .30;
        flags.position = ccp(size.width - size.width/1.15,size.height - size.height/1.25);
        [self addChild:flags];
        // Title Label
        CCLabelTTF *label = [CCLabelTTF labelWithString:@"Minesweeper Flags" fontName:@"Marker Felt" fontSize:64];
        label.position =  ccp( size.width /2 , size.height - size.height/4 );
        [self addChild: label];
        // Player ID generation
        NSDateFormatter *date = [[NSDateFormatter alloc] init];
        [date setDateFormat:@"mm"];
        int minute = [[date stringFromDate:[NSDate date] ]intValue];
        [date setDateFormat:@"ss"];
        int seconds = [[date stringFromDate:[NSDate date]] intValue];
        int pID = minute + seconds + (rand() % 1000000);
        [self setPlayerID:pID];
        NSString* playerIDInStringFormat = [NSString stringWithFormat:@"%d", _playerID];
        [self setPlayerIDString:playerIDInStringFormat];
        NSLog(@"ID DO PLAYER %@",_playerIDString);
        [self setResponseData:[NSMutableData data]];
    // Menus
    [CCMenuItemFont setFontSize:38];
    CCMenuItem *itemNewGame = [CCMenuItemFont itemWithString:@"New Game" block:^(id sender) {
            [self connectTheWebService:playerIDInStringFormat];
    }];
        CCMenu *menu = [CCMenu menuWithItems:itemNewGame ,nil];
    [menu alignItemsVerticallyWithPadding:20];
    [menu setPosition:ccp( size.width/2, size.height/2 - 50)];
    [self addChild:menu];
  }
  return self;
}
//connect the Web server method
-(void) connectTheWebService:(NSString*)id{
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[[_URL autorelease] stringByAppendingString:id]                                                             ]];
    _connectionConnectGame = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
//Web server responses.
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    [_responseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [_responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    NSLog(@"Connection failed: %@", [error description]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    NSString* responseString = [[NSString alloc] initWithData:_responseData encoding:NSUTF8StringEncoding];
    NSLog(@"HelloWorld responseString %@",responseString);
    NSArray* resultArray = [[NSArray alloc] init];
    resultArray = [responseString componentsSeparatedByString:@","];
    if ([[resultArray objectAtIndex:1] isEqualToString:@"no"]) {
        [self setGameID:[resultArray objectAtIndex:0]];
        [self setLaunchFlag:TRUE];
    } else {
        [self setGameID:[resultArray objectAtIndex:0]];
        _playerID = [[resultArray objectAtIndex:1] integerValue];
        [self setPlayerID:[[resultArray objectAtIndex:1] integerValue]];
        [self setLaunchFlag:FALSE];
    }
    if (_launchFlag){
        GameScene* scene = [[GameScene alloc] initializeInternalDataWithPlayerID:_playerIDString andWithGameID:_gameID andWithTurnID:@"no"];
        [[CCDirector sharedDirector] replaceScene:scene];
    } else {
        LoadingScene* scene = [LoadingScene initializeWithPlayerID:_playerIDString andWithGameID:_gameID];
        [[CCDirector sharedDirector] replaceScene:scene];
    }
}
- (void) dealloc {
  [super dealloc];
}
@end

Step 8: Create the LoadingScene Class

Change your LoadingScene header file to the following:

#import "cocos2d.h"
@interface LoadingScene : CCLayer <UIGestureRecognizerDelegate>{ }
@property (nonatomic, retain) NSURLConnection *connectionConnectGame;
@property (nonatomic,retain) NSMutableData* responseData;
@property (nonatomic,retain) NSString* gameID;
@property (nonatomic,retain) NSString* playerID;
@property (nonatomic) BOOL launchFlag;
@property (nonatomic,retain) NSString* URL;
+(CCScene *) scene;
+(id) initializeWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid;
-(id) initializeInternalDataWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid;
-(void) connectTheWebService:(NSString*)id;
@end

Change your LoadingScene implementation file to the following:

#import "LoadingScene.h"
#import "GameScene.h"
@implementation LoadingScene
@synthesize responseData = _responseData;
@synthesize connectionConnectGame = _connectionConnectGame;
@synthesize gameID = _gameID;
@synthesize playerID = _playerID;
@synthesize launchFlag = _launchFlag;
@synthesize URL = _URL;
+(CCScene *) scene{
    CCScene *scene = [CCScene node];
    LoadingScene *layer = [LoadingScene node];
    [scene addChild: layer];
    return scene;
}
-(id) init{
    if( (self=[super init] )) {}
    return self;
}
// Custom initializer
+(id) initializeWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid {
    return [[[self alloc] initializeInternalDataWithPlayerID:playerid andWithGameID:gameid] autorelease];
}
-(id) initializeInternalDataWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid {
    self = [super init];
    if (self) {
        CGSize _size = [[CCDirector sharedDirector] winSize];
        _gameID = [[NSString alloc] init];
        [self setLaunchFlag:FALSE];
        [self setPlayerID:playerid];
        [self setGameID:gameid];
        CCSprite* background = [CCSprite spriteWithFile:@"background.jpg"];
        background.position = ccp(_size.width/2,_size.height/2);
        [self addChild:background];
        CCLabelTTF* label = [CCLabelTTF labelWithString:@"Loading..." fontName:@"Marker Felt" fontSize:40];
        label.position = ccp(_size.width/2, _size.height/2);
        [self addChild:label];
        CCLabelTTF* label2 = [CCLabelTTF labelWithString:@"Waiting for another player" fontName:@"Marker Felt" fontSize:25];
        label2.position = ccp(_size.width/2, _size.height - _size.height/1.80);
        [self addChild:label2];
        [self setResponseData:[NSMutableData data]];
        // CHANGE IT ACCORDINGLY
        [self setURL:@"http://10.0.6.178:8080/Minesweeper_Flag/resources/generic/player2Available/"];
        [self schedule:@selector(update:) interval:2];
    }
    return self;
}
-(void) update:(ccTime) dt{
    [self connectTheWebService:_gameID];
}
-(void) connectTheWebService:(NSString*)id{
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[[_URL autorelease] stringByAppendingString:id]                                                             ]];
    _connectionConnectGame = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    [_responseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [_responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    NSLog(@"Connection failed: %@", [error description]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    NSString* responseString = [[NSString alloc] initWithData:_responseData encoding:NSUTF8StringEncoding];
    if ([responseString isEqualToString:@"nulo"]) {
        [self setLaunchFlag:FALSE];
    }else {
        [self setLaunchFlag:TRUE];
    }
    if (_launchFlag){
        GameScene* scene = [[GameScene alloc] initializeInternalDataWithPlayerID:_playerID andWithGameID:_gameID andWithTurnID:_playerID] ;
        [[CCDirector sharedDirector] replaceScene:scene];
    } else { [self schedule:@selector(update:) interval:2];  }
}
-(void)dealloc {
    [super dealloc];
}
@end

Step 9: Create the GameScene Class

Change your GameScene header file to the following:

#import "cocos2d.h"
@interface GameScene : CCLayer <UIGestureRecognizerDelegate> {
    NSString* turn;
}
@property (nonatomic) int gameGridSize, initialSpriteXXPosition, initialSpriteYYPosition, deslocamentoNoEixoXX, deslocamentoNoEixoYY, tamanhoSprite, playerBlueScore, playerRedScore, lastSpriteTouch;
@property (nonatomic, retain) NSMutableArray *spritesMutableArray;
@property (nonatomic,retain) NSMutableArray* touchedLocations;
@property (nonatomic) CGSize size;
@property (nonatomic) CGPoint initialSpritePositon;
@property (nonatomic, retain) CCSprite* sprites;
@property (nonatomic) int spritePositionInGrid;
@property (nonatomic) int webServerResult;
@property (nonatomic, strong) NSMutableData *responseData;
@property (nonatomic, strong) NSString *resourceImage;
@property (nonatomic, retain) CCSprite* playerBlueSprite;
@property (nonatomic, retain) CCSprite* playerRedSprite;
@property (nonatomic, retain) CCSprite* playerTurnSprite;
@property (nonatomic, retain) CCSprite* youArrow;
@property (nonatomic,retain) CCLabelTTF* playerBlueLabel;
@property (nonatomic,retain) CCLabelTTF* playerRedLabel;
@property (nonatomic,retain) NSString* gameID;
@property (nonatomic,retain) NSString* userID;
@property (nonatomic,retain) NSString* colorPlayer;
@property (nonatomic,retain) CCSprite* positionTouchedSprite;
@property (nonatomic,retain) NSString* turn;
@property (nonatomic, retain) NSMutableString* gameBoardInfoS;
+(CCScene *) scene;
-(void)getMinesAtPosition:(int)pos;
-(void)drawGameBoard;
-(void)setInitialAppValues;
-(void)setArrowonTheRightPlayer;
+(id) initializeWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid andWithTurnID:(NSString*) turnid;
-(id) initializeInternalDataWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid andWithTurnID:(NSString*) turnid;
@end

Change your GameScene implementation file to the following:

#import "GameScene.h"
#import "cocos2d.h"
#import "LoadingScene.h"
@implementation GameScene
@synthesize spritesMutableArray = _spritesMutableArray;
@synthesize touchedLocations = _touchedLocations;
@synthesize size = _size;
@synthesize initialSpritePositon = _initialSpritePositon;
@synthesize sprites = _sprites;
@synthesize spritePositionInGrid = _spritePositionInGrid;
@synthesize webServerResult = _webServerResult;
@synthesize gameGridSize = _gameGridSize;
@synthesize initialSpriteXXPosition = _initialSpriteXXPosition;
@synthesize initialSpriteYYPosition = _initialSpriteYYPosition;
@synthesize deslocamentoNoEixoXX = _deslocamentoNoEixoXX;
@synthesize deslocamentoNoEixoYY = _deslocamentoNoEixoYY;
@synthesize tamanhoSprite = _tamanhoSprite;
@synthesize responseData = _responseData;
@synthesize resourceImage = _resourceImage;
@synthesize playerTurnSprite = _playerTurnSprite;
@synthesize youArrow = _youArrow;
@synthesize playerBlueSprite = _playerBlueSprite;
@synthesize playerRedSprite = _playerRedSprite;
@synthesize playerBlueLabel = _playerBlueLabel;
@synthesize playerRedLabel = _playerRedLabel;
@synthesize playerBlueScore = _playerBlueScore;
@synthesize playerRedScore = _playerRedScore;
@synthesize gameID = _gameID;
@synthesize userID = _userID;
@synthesize colorPlayer = _colorPlayer;
@synthesize turn = _turn;
@synthesize gameBoardInfoS;
@synthesize lastSpriteTouch;
@synthesize positionTouchedSprite = _positionTouchedSprite;
+(CCScene *) scene{
    //+(id)scene{
    CCScene *scene = [CCScene node];
    GameScene *layer = [GameScene node];
    [scene addChild: layer];
    return scene;
}
-(id) init{
    if( (self=[super init] )) {
    }
    return self;
}
//new initialize method to recive parameters such as playerID, turnID, and gameID
+(id) initializeWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid andWithTurnID:(NSString*) turnid{
    return [[[self alloc] initializeInternalDataWithPlayerID:playerid andWithGameID:gameid andWithTurnID:turnid] autorelease];
}
//initialize several values and call methods to generate and draw all scene
-(id) initializeInternalDataWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid andWithTurnID:(NSString*) turnid{
    self = [super init];
    if (self) {
        // Activate touch events
        [super setIsTouchEnabled:YES];
        //set the parameters
        [self setGameID:gameid];
        [self setUserID:playerid];
        [self setTurn:turnid];
        [self setInitialAppValues];
        //generate and draw gameBoard
        [self drawGameBoard];
        //call the updater one time for second
        [self schedule:@selector(update:) interval:1];
    }
    return self;
}
// detect touchs and which sprite was touched
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    //verify if the user have the turn
    if(![_turn isEqualToString:_userID]){
        //in case of the user not have the turn
        return;
    }
    UITouch* touch = [touches anyObject];
    CGPoint location = [touch locationInView: [touch view]];
    CGPoint converted = [[CCDirector sharedDirector] convertToGL:location];
    _spritePositionInGrid=0;
    for (CCSprite* spr in _spritesMutableArray){
        CCSprite* currentSprite = [_spritesMutableArray objectAtIndex:_spritePositionInGrid];
        if (CGRectContainsPoint([currentSprite boundingBox], converted)){
            // Verificar se a posição já foi touched
            BOOL jaExiste =FALSE;
            for (int i = 0 ; i < [_touchedLocations count]; i++){
                if (_spritePositionInGrid == [[_touchedLocations objectAtIndex:i] integerValue])
                    jaExiste=TRUE;
            }
            //was touched?
            if([gameBoardInfoS characterAtIndex:_spritePositionInGrid] != '9'){
                return;
            }
            if (!jaExiste){
                [_touchedLocations addObject:[NSNumber numberWithInt:_spritePositionInGrid]];
                lastSpriteTouch = _spritePositionInGrid;
                self.responseData = [NSMutableData data];
                _positionTouchedSprite = [_spritesMutableArray objectAtIndex:_spritePositionInGrid];
                NSMutableString* enderecoReturnValue = [NSMutableString stringWithFormat:@"http://10.0.6.178:8080/Minesweeper_Flag/resources/generic/returnValue/%@/%@/%d",_userID,_gameID,_spritePositionInGrid];
                NSLog(@"%@",enderecoReturnValue);
                NSURLRequest *request = [NSURLRequest requestWithURL: [NSURL URLWithString:enderecoReturnValue]];
                [[NSURLConnection alloc] initWithRequest:request delegate:self];
            }
        }
        _spritePositionInGrid++;
    }
}
-(void) update: (ccTime) dt
{
    if(![_turn isEqualToString:_userID]){
        NSMutableString* returnUpdateURL = [NSMutableString stringWithFormat:@"http://10.0.6.178:8080/Minesweeper_Flag/resources/generic/returnUpdates/%@",_gameID];
        NSURL *url = [NSURL URLWithString:returnUpdateURL];
        NSData *jsonData = [NSData dataWithContentsOfURL:url];
        NSString *value = [[NSString alloc] init];
        NSRange a;
        if(jsonData != nil) {
            NSError *error = nil;
            id result = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
            if(error == nil) {
                NSString* resultString = [result objectForKey:@"Uncover_Matrix"];
                int positionAt, positionDiff=-1;
                for(positionAt= 0; positionAt< resultString.length; positionAt++){
                    a = NSMakeRange(positionAt, 1);
                    if(![[gameBoardInfoS substringWithRange:a] isEqualToString:[resultString substringWithRange:a]]){
                        positionDiff = positionAt;
                        value = [[NSString alloc] initWithString:[resultString substringWithRange:a]];
                        break;
                    }
                }
                if(positionDiff != -1){
                    [self setTurn:[result objectForKey:@"Turn"]];
                    [gameBoardInfoS replaceCharactersInRange:a withString:value];
                    if([value isEqualToString:@"*"]){
                        _playerBlueScore++;
                        NSString *stringBlueScore = [NSString stringWithFormat:@"%d",_playerBlueScore];
                        [_playerBlueLabel setString:stringBlueScore];
                        value = @"9";
                    }
                    if([value isEqualToString:@"#"]){
                        _playerRedScore++;
                        NSString *stringRedScore = [NSString stringWithFormat:@"%d",_playerRedScore];
                        [_playerRedLabel setString:stringRedScore];
                        value = @"10";
                    }
                    [self setArrowonTheRightPlayer];
                    CGPoint coordenates = [self getReversePositionGameBoard:positionDiff];
                    [self getMinesAtPosition:[value integerValue] withXCoord:coordenates.x andYYpos:coordenates.y];
                }
            }
            else
                NSLog(@"error");
        }
    }
}
//recive the position and the value returned from Web Services and replace in the board
-(void)getMinesAtPosition:(int)pos withXCoord:(int)xxpos andYYpos:(int)yypos{
    _webServerResult = pos;
    switch (_webServerResult) {
        case 0:
            [self setResourceImage:@"0.png"];
            break;
        case 1:
            [self setResourceImage:@"1.png"];
            break;
        case 2:
            [self setResourceImage:@"2.png"];
            break;
        case 3:
            [self setResourceImage:@"3.png"];
            break;
        case 4:
            [self setResourceImage:@"4.png"];
            break;
        case 5:
            [self setResourceImage:@"5.png"];
            break;
        case 6:
            [self setResourceImage:@"6.png"];
            break;
        case 7:
            [self setResourceImage:@"7.png"];
            break;
        case 8:
            [self setResourceImage:@"8.png"];
            break;
        case 9:
            [self setResourceImage:@"flag_b.png"];
            break;
        case 10:
            [self setResourceImage:@"flag_r.png"];
            break;
        default:
            _resourceImage = @"flag_r.png";
            break;
    }
    // New sprite substitution after the WS invocation
    CCSprite* newSprite = [CCSprite spriteWithFile:_resourceImage];
    newSprite.scale = .25;
    newSprite.position = ccp(xxpos,yypos);
    [self addChild:newSprite];
}
// communication
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
    [[self responseData] setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
    [[self responseData] appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
    NSLog(@"Connection failed: %@", [error description]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
    NSString* responseString = [[NSString alloc] initWithData:[self responseData] encoding:NSUTF8StringEncoding];
    NSRange range = NSMakeRange(lastSpriteTouch, 1);
    [gameBoardInfoS replaceCharactersInRange:range withString:responseString];
    //assign the turn after the request
    if([responseString isEqualToString:@"*"]){
        _playerBlueScore++;
        NSString *stringBlueScore = [NSString stringWithFormat:@"%d",_playerBlueScore];
        [_playerBlueLabel setString:stringBlueScore];
        _turn = _userID;
        responseString = @"9";
    }
    else if([responseString isEqualToString:@"#"]){
        _playerRedScore++;
        NSString *stringRedScore = [NSString stringWithFormat:@"%d",_playerRedScore];
        [_playerRedLabel setString:stringRedScore];
        _turn = _userID;
        responseString = @"10";
    }
    else{
        _turn = @"is not you";
    }
    [self setArrowonTheRightPlayer];
    [self getMinesAtPosition:[responseString integerValue] withXCoord:_positionTouchedSprite.position.x andYYpos:_positionTouchedSprite.position.y];
}
//put the arrow on the player that holds the turn
-(void)setArrowonTheRightPlayer{
    if([_turn isEqualToString:_userID]) {
        if ([_colorPlayer isEqualToString:@"blue"])
            _playerTurnSprite.position = ccp(_size.width/1.30, _size.height/1.10);
        else
            _playerTurnSprite.position = ccp(_size.width/1.30, _size.height/1.48);
    }
    if(![_turn isEqualToString:_userID]) {
        if ([_colorPlayer isEqualToString:@"blue"])
            _playerTurnSprite.position = ccp(_size.width/1.30, _size.height/1.48);
        else
            _playerTurnSprite.position = ccp(_size.width/1.30, _size.height/1.10);
    }
}
//calculate the reverse position to change the sprite on gameBoard
-(CGPoint)getReversePositionGameBoard:(int)positionTouched{
    NSInteger linha = (int) positionTouched / 16.0;
    NSInteger y = 688 - _tamanhoSprite * linha;
    NSInteger x = ( (int) positionTouched % 16) * 38 + _initialSpriteXXPosition;
    CGPoint position = CGPointMake(x, y);
    return position;
}
//draw the gameBoard (background, sprites blue (water), and the playerss avatar
-(void)drawGameBoard{
    for (int i = 0 ; i < _gameGridSize; i++) {
        _sprites = [CCSprite spriteWithFile:@"water.png"];
        _sprites.scale = .25;
        _initialSpritePositon = CGPointMake(_initialSpriteXXPosition+_deslocamentoNoEixoXX, _size.height-_initialSpriteYYPosition-(_deslocamentoNoEixoYY*_tamanhoSprite));
        _sprites.position = ccp(_initialSpritePositon.x,_initialSpritePositon.y);
        _deslocamentoNoEixoXX+=_tamanhoSprite;
        if (((i+1) % 16) == 0){
            _deslocamentoNoEixoXX=0;
            _deslocamentoNoEixoYY++;
        }
        [_spritesMutableArray addObject:_sprites];
        [self addChild:_sprites];
    }
    [self addChild:_playerTurnSprite z:4];
    [self addChild:_playerBlueSprite];
    [self addChild:_playerRedSprite];
    [self addChild:_youArrow];
    [self addChild:_playerBlueLabel];
    [self addChild:_playerRedLabel];
}
//set initial values
-(void)setInitialAppValues{
    _size = [[CCDirector sharedDirector] winSize];
    _gameGridSize = 256, _tamanhoSprite = 38;
    _initialSpriteXXPosition = 80, _initialSpriteYYPosition = 80;
    _deslocamentoNoEixoXX=0, _deslocamentoNoEixoYY=0;
    _spritesMutableArray = [[NSMutableArray alloc] initWithCapacity:256];
    _touchedLocations = [[NSMutableArray alloc] initWithCapacity:256];
    [self setResourceImage:@""];
    CCSprite * background = [CCSprite spriteWithFile:@"background.jpg"];
    background.position = ccp(_size.width/2,_size.height/2);
    [self addChild:background];
    //init the gameBoard array
    gameBoardInfoS = [[NSMutableString alloc] init];
    for(int i=0;i<256;i++){
        [gameBoardInfoS appendString:@"9"];
    }
    _playerBlueSprite = [CCSprite spriteWithFile:@"player_bue.png"];
    _playerBlueSprite.scale = .5;
    _playerBlueSprite.position = ccp(_size.width/1.30, _size.height/1.25);
    _playerRedSprite = [CCSprite spriteWithFile:@"player_red.png"];
    _playerRedSprite.scale = .5;
    _playerRedSprite.position = ccp(_size.width/1.30, _size.height/1.75);
    //identify the players on field
    if([_turn isEqualToString:_userID]){
        _youArrow = [CCSprite spriteWithFile:@"you.png"];
        _youArrow.scale = .3;
        _youArrow.position = ccp(_size.width/1.30, _size.height/1.30);
        [self setColorPlayer:@"blue"];
    }
    else{
        _youArrow = [CCSprite spriteWithFile:@"you.png"];
        _youArrow.scale = .3;
        _youArrow.position = ccp(_size.width/1.30, _size.height/1.85);
        [self setColorPlayer:@"red"];
    }
    _playerTurnSprite = [CCSprite spriteWithFile:@"arrowTurn.png"];
    _playerTurnSprite.scale = 1;
    _playerTurnSprite.position = ccp(_size.width/1.30, _size.height/1.10);
    _playerBlueLabel = [CCLabelTTF labelWithString:@"0" fontName:@"Marker Felt" fontSize:50.0];
    _playerBlueLabel.position = ccp(_size.width/1.15, _size.height/1.25);
    _playerRedLabel = [CCLabelTTF labelWithString:@"0" fontName:@"Marker Felt" fontSize:50.0];
    _playerRedLabel.position = ccp(_size.width/1.15, _size.height/1.75);
    [self setPlayerBlueScore:0];
    [self setPlayerRedScore:0];
}
-(void) registerWithTouchDispatcher
{
    NSLog(@"registerWithTouchDispatcher");
    [[[CCDirector sharedDirector] touchDispatcher] addStandardDelegate:self priority:0];
}
- (void) dealloc
{
    [turn release];
    [super dealloc];
}
@end

Step 10: Build & Run the Game

After you include the abovementioned code, the next step is to build and test the application. Use the cmd + b shortcut. If everything is fine you should see the iPad simulator up and running the game.

Figure 4: iPad

Step 11: Enjoy the Game

In this final step the objective is to enjoy the game. Since the game is a two-player game the best way to play it is with a friend!

In the following screens we can see the final game board though the vision of the two-players (blue and red). The arrow above the player avatar indicates the current player turn; the player that is currently playing.

Blue player game board:

Figure 5: Blue player

Red player game board:

Figure 6: Red player

Step 12: Conclusions

This concludes the Part II of how to create a Minesweeper Flag game using both server-side and client-side code. By now, you should have enough knowledge to create a simple Cocos2D game using the same game engine. If you have any questions or comments, please feel free to leave them in the comments section here.

iOS 6 and the Social Framework: Twitter Requests

$
0
0

This tutorial will teach you how to use the iOS 6 SDK and the Social Framework to load dynamic content from Twitter. To do so, I’ll teach you how to build a Twitter profile generating application. Read on!


Step 1: Creating the Project

Open Xcode and select “Create a new Xcode project”. Select “Empty Application” and click “next”. Enter a name for your project (I called mine “Twitter Profile”), make sure you select iPhone for the Device, and then select all the checkboxes, except the Use Core Data checkbox. After that, click “next” and choose a place to save your project before clicking “create”.

    Create Project

Step 2: Setting Supported Orientations

We only want to use this application in portrait mode, so go to the Supported Interface Orientations section and deselect the landscape orientations.

    ”Supported

Step 3: Import the Necessary Frameworks

We will need to add three Frameworks to our project: the Social Framework to make the request, the Accounts Framework to use the user’s Twitter account, and the QuartzCore Framework to customize our interface a little.

Go to the “Build Phases” tab and expend the “Link Binary With Libraries” option. Click the “+” button to add a new Framework. Type “Social” into the search box and select the Social.framework option that appears in the list. Click “Add” to include this framework in the linking phase of your project. Do the same for the Accounts and QuartzCore frameworks by typing “Accounts” and “QuartzCore” into the search box.

    ”Frameworks"

Step 4: Create a Storyboard

Go to “File” > “New” > “File…” or press ⌘N to create a new file. Go to the “Use Interface” section, select “Storyboard” and click “Next”. Make sure that the Device Family is set to iPhone and click “Next” again. Name your storyboard MainStoryboard and click “Create”.

    ”Create

Now we have to link the Storyboard to our project. Select the project name in the navigator menu of Xcode and select the current target (in our case “Twitter Profile”). Next, select the “Summery” tab and go to the “iPhone / iPad Deployment Info” section. Once there, select our Storyboard called “MainStoryboard” for the Main Storyboard.

    ”MainStoryboard"

The last thing we need to do to make our storyboard work is changing the application: didFinishLaunchWithOptions: method, so open AppDelegate.m and modify the method as follows:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    return YES;
}

Step 5: Creating the Interface

Open the Storyboard and drag a Navigation Controller from the Object Library to the canvas. Select the Table View controller connected to the Navigation Controller and delete it. Now drag a View Controller from the Object Library to the canvas. CTRL-drag from the Navigation Controller to the View Controller and select “root view controller” from the pop-up menu. Select the Navigation Controller, open the File Inspector, and then deselect “Use Autolayout” since we won’t use this new feature for our app. At last, double-click on the navigation bar of the View Controller we just created and enter “Username” as title.

Now select a Text Field from the Object Library and add it to the View Controller. Select the Text Field and open the Attributes Inspector. Enter “Username” as the Placeholder and make sure to select the “Clear when editing begins” option. Next, grab a Button from the Object Library, add it to the View Controller, and change the title to “Show Info”.

At last, make sure to arrange the Text Field and Button as follows:

    ”Username

We also need a screen to show the Twitter profile, so drag a new View Controller from the Object Library into the canvas. CTRL-drag from the “Show Info” button to the View Controller and select “push” from the pop-up menu. Next, double-click on the navigation bar of the second View Controller and enter “Profile” as title.

We will need a lot of labels and some imageviews on this screen to show the profile information. First, add an Image View to the Profile View Controller and make sure to align it to the top. Next, open the Size Inspector and give it a size of 320 x 160 pixels. Also, modify the autoresizing as follows:

    ”Banner

Add another Image View on top of the previous one. Go to the Size Inspector and modify the size, coordinates, and autoresizing as follows:

    ”Profile

Now we will add some labels to show the name and username. Drag a label on top of the largest image view for the banner. Open the Size Inspector and modify the size, coordinates, and autoresizing as follows:

    ”Name

Next open the Attributes Inspector and Enter “Name” for the label text. Change the color to white, make the font Bold and set the alignment to center. At last we will add a shadow to this label. Set the vertical shadow offset to 1, so the shadow will appear under the text. Click on Shadow color and make the color black with 75% opacity.

    ”Shadow

This label was for the name, but we also need a label for the username, so select the label we just created and press ⌘C ⌘V to copy and paste it. Change the text to “@username”, and change the X and Y coordinates to 20 and 120. We also want to make this font a little bit smaller, so open the Attributes Inspector and change the font size to 14.

We also need six more labels to show the amount of tweets, followers, and people following. Luckily for us, we only need to add two and can copy and paste the other four. Add a new label to the View Controller, make it 74 pixels wide, change the X and Y coordinates to 20 and 168. Enter “0” as text for this label. Next, add a new label, also make it 74 pixels wide, but this time change the X and Y coordinates to 20 and 190. Open the Attributes Inspector and give the label a light gray color. Also change the text to “Tweets”.

Select both labels we just created and ALT drag them to the center. This way we create a copy of the two labels. ALT drag again, but this time align the labels to the right. Now we created these six labels pretty fast. Change the text of the center label to “Following” and the text of the right label to “Followers”.

The last thing we will need to add to our interface is a Text View to show the last tweet of a user, so drag a Text View from the Object Library to the bottom of the View Controller. Open the Size Inspector and modify the size, coordinates, and autoresizing as follows:

    ”TextView

With these options our app will look good on an iPhone 5, but also on an older iPhone 4. You can preview the Storyboard in a 3.5” screen setup by pressing this button at the bottom right:

    ”Button"

Now open the Attributes Inspector and remove the text of the Text View. Also make sure to deselect the Editable option.

The interface should now look as follows:

    ”Finished

Now that we have finished our interface, I think it’s a good time to test our app. Click Build and Run or press ⌘R to test the application. The app should show a simple navigation controller with a textfield and a button. If you tap the button, you should see the profile page we just created. The name and username aren’t easy to read, but we will take care of that later.


Step 6: Passing the Username to the Profile Page

Go to “File” > “New” > “File…” to create a new file. Select “Objective-C class” and click “Next”. Enter “UsernameViewController” for the class and make sure that it’s a subclass of UIViewController and that both the checkboxes are not selected. Click “Next” again and than click “Create”.

Open UsernameViewController.h and modify the code to read as follows:

#import <UIKit/UIKit.h>
@interface UsernameViewController : UIViewController
{
    IBOutlet UITextField *usernameTextfield;
}
@end

Here we just create a simple outlet for our textfield, which we will use to pass the entered username to the profile page.

Next open the Storyboard again and select the username View Controler. Open the Identity Inspector and change the Class to the UsernameViewController we just created. After that, open the Connections Inspector and connect the usernameTextfield outlet with the text field.

Now open UsernameViewController.m and add the following line under #import "UsernameViewController.h":

#import "ProfileViewController.h"

Here we already import ProfileViewController, which we will create later in this tutorial. Next add the following method under the didReceiveMemoryWarning method:

- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    ProfileViewController *profileViewController = [segue destinationViewController];
    [profileViewController setUsername:usernameTextfield.text];
}

Here we pass the username to the ProfileViewController, which we will create in a minute.


Step 7: Making the Profile Page Outlets

To make these outlets we first need to create some new files, so go to “File” > “New” > “File…”. Select “Objective-C class” and click “Next”. Enter “ProfileViewController” for the class and make sure that it’s a subclass of UIViewController and that both the checkboxes are not selected. Click “Next” again and than click “Create”.

Open ProfileViewController.h and modify the code to read as follows:

#import <UIKit/UIKit.h>
#import <Accounts/Accounts.h>
#import <Social/Social.h>
#import <QuartzCore/QuartzCore.h>
@interface ProfileViewController : UIViewController
{
    IBOutlet UIImageView *profileImageView;
    IBOutlet UIImageView *bannerImageView;
    IBOutlet UILabel *nameLabel;
    IBOutlet UILabel *usernameLabel;
    IBOutlet UILabel *tweetsLabel;
    IBOutlet UILabel *followingLabel;
    IBOutlet UILabel *followersLabel;
    IBOutlet UITextView *lastTweetTextView;
    NSString *username;
}
@property (nonatomic, retain) NSString *username;
@end

Here we first import the frameworks we added earlier to our project. After that, we create some outlets for the elements which will show the profile image, and at last we create a string which will contain the entered username. We set this string in the prepareForSegue:sender: method, we created in the previous step.

Open the Storyboard and select the profile View Controller. Open the Identity Inspector and change the Class to ProfileViewController. After that, open the Connections Inspector and connect the outlets with the corresponding interface elements. Just make sure you connect the tweetsLabel, followingLabel, and followersLabel outlets with the labels above the labels with corresponding text. So, don’t connect the tweetsLabel outlet with the label that says “Tweets”, but with the label above that one.

At last, open ProfileViewController.m and add the following line under @implementation:

@synthesize username;

Step 8: Give the Profile Image a Border and Shadow

The ProfileViewController.m file should still be opened so go to the viewDidLoad method and change it to read as follows:

- (void)viewDidLoad
{
    [super viewDidLoad];
    [profileImageView.layer setBorderWidth:4.0f];
    [profileImageView.layer setBorderColor:[[UIColor whiteColor] CGColor]];
    [profileImageView.layer setShadowRadius:3.0];
    [profileImageView.layer setShadowOpacity:0.5];
    [profileImageView.layer setShadowOffset:CGSizeMake(1.0, 0.0)];
    [profileImageView.layer setShadowColor:[[UIColor blackColor] CGColor]];
}

Here we use the QuartzCore Framework to give the profileImageView a border and some shadow.


Step 9: Get the Profile with SLRequest

Add the following method under the didReceiveMemoryWarning method:

- (void) getInfo
{
    // Request access to the Twitter accounts
    ACAccountStore *accountStore = [[ACAccountStore alloc] init];
    ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
    [accountStore requestAccessToAccountsWithType:accountType options:nil completion:^(BOOL granted, NSError *error){
        if (granted) {
            NSArray *accounts = [accountStore accountsWithAccountType:accountType];
            // Check if the users has setup at least one Twitter account
            if (accounts.count > 0)
            {
                ACAccount *twitterAccount = [accounts objectAtIndex:0];
                // Creating a request to get the info about a user on Twitter
                SLRequest *twitterInfoRequest = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodGET URL:[NSURL URLWithString:@"https://api.twitter.com/1.1/users/show.json"] parameters:[NSDictionary dictionaryWithObject:username forKey:@"screen_name"]];
                [twitterInfoRequest setAccount:twitterAccount];
                // Making the request
                [twitterInfoRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        // Check if we reached the reate limit
                        if ([urlResponse statusCode] == 429) {
                            NSLog(@"Rate limit reached");
                            return;
                        }
                        // Check if there was an error
                        if (error) {
                            NSLog(@"Error: %@", error.localizedDescription);
                            return;
                        }
                        // Check if there is some response data
                        if (responseData) {
                            NSError *error = nil;
                            NSArray *TWData = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableLeaves error:&error];
                            // Filter the preferred data
                            NSString *screen_name = [(NSDictionary *)TWData objectForKey:@"screen_name"];
                            NSString *name = [(NSDictionary *)TWData objectForKey:@"name"];
                            int followers = [[(NSDictionary *)TWData objectForKey:@"followers_count"] integerValue];
                            int following = [[(NSDictionary *)TWData objectForKey:@"friends_count"] integerValue];
                            int tweets = [[(NSDictionary *)TWData objectForKey:@"statuses_count"] integerValue];
                            NSString *profileImageStringURL = [(NSDictionary *)TWData objectForKey:@"profile_image_url_https"];
                            NSString *bannerImageStringURL =[(NSDictionary *)TWData objectForKey:@"profile_banner_url"];
                            // Update the interface with the loaded data
                            nameLabel.text = name;
                            usernameLabel.text= [NSString stringWithFormat:@"@%@",screen_name];
                            tweetsLabel.text = [NSString stringWithFormat:@"%i", tweets];
                            followingLabel.text= [NSString stringWithFormat:@"%i", following];
                            followersLabel.text = [NSString stringWithFormat:@"%i", followers];
                            NSString *lastTweet = [[(NSDictionary *)TWData objectForKey:@"status"] objectForKey:@"text"];
                            lastTweetTextView.text= lastTweet;
                            // Get the profile image in the original resolution
                            profileImageStringURL = [profileImageStringURL stringByReplacingOccurrencesOfString:@"_normal" withString:@""];
                            [self getProfileImageForURLString:profileImageStringURL];
                            // Get the banner image, if the user has one
                            if (bannerImageStringURL) {
                                NSString *bannerURLString = [NSString stringWithFormat:@"%@/mobile_retina", bannerImageStringURL];
                                [self getBannerImageForURLString:bannerURLString];
                            } else {
                                bannerImageView.backgroundColor = [UIColor underPageBackgroundColor];
                            }
                        }
                    });
                }];
            }
        } else {
            NSLog(@"No access granted");
        }
    }];
}

That’s a lot of code, but I will explain it step-by-step.

    ACAccountStore *accountStore = [[ACAccountStore alloc] init];
    ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];

Here we first initialize an ACAccountStore object which will allow us to access the user’s accounts. After that, we create an ACAccountType instance, call the accountTypeWithAccountTypeIdentifier method on the ACAccountStore instance we just created, and then set the account type to Twitter. You can use the same method if you want to use Facebook, but then you will have to set the account type identifier to ACAccountTypeIdentifierFacebook.

[accountStore requestAccessToAccountsWithType:accountType options:nil completion:^(BOOL granted, NSError *error){
        if (granted) {
 	}
}];

Here we simply ask the user to give our app access to their Twitter account(s).

NSArray *accounts = [accountStore accountsWithAccountType:accountType];
if (accounts.count > 0)
{
	ACAccount *twitterAccount = [accounts objectAtIndex:0];
}

Here we create an array of all the Twitter accounts of the user. We check if the user has any Twitter accounts. If so, we create an ACAccount instance of the first Twitter account in that array. To keep it simple, we use the first account in this tutorial, but in a real app you should give the user the option to select their preferred Twitter account in case they have more than 1.

   SLRequest *twitterInfoRequest = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodGET URL:[NSURL URLWithString:@"https://api.twitter.com/1.1/users/show.json"] parameters:[NSDictionary dictionaryWithObject:username forKey:@"screen_name"]];
                [twitterInfoRequest setAccount:twitterAccount];

Here we create our request, which will call the Twitter API. We set the service type to Twitter and the request method to GET. We wan’t to get some information about a user on Twitter. You can also use the request methods POST and DELETE. With POST, you can update a profile image or post a tweet. With DELETE, you can delete things from your account. The URL in this tutorial will be: https://api.twitter.com/1.1/users/show.json. This URL will make sure the request returns a variety of information about the specified user in a JSON format. As a parameter we add the entered username for the key screen_name so we will get the information about that user. After that, we set the account for the request to the ACAccount instance.

[twitterInfoRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
                    dispatch_async(dispatch_get_main_queue(), ^{
                        if ([urlResponse statusCode] == 429) {
                            NSLog(@"Rate limit reached");
                            return;
                        }
                        if (error) {
                            NSLog(@"Error: %@", error.localizedDescription);
                            return;
                        }
         });
                }];

Here we actually make the request and make sure it loads asynchronically, so our interface will keep being responsive while loading. We do this with GCD, Grand Central Dispatch. I won’t go into any details, but you can find more info about it here. When the request is finished, we first check if our app hasn’t reached the rate limit. After that, we check if there was an error.

if (responseData) {
                            NSError *error = nil;
                            NSArray *TWData = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingMutableLeaves error:&error];
}

Here we check if the request returned some response data. If so, we create an array from the returned JSON file.

         NSString *screen_name = [(NSDictionary *)TWData objectForKey:@"screen_name"];
                            NSString *name = [(NSDictionary *)TWData objectForKey:@"name"];
                            int followers = [[(NSDictionary *)TWData objectForKey:@"followers_count"] integerValue];
                            int following = [[(NSDictionary *)TWData objectForKey:@"friends_count"] integerValue];
                            int tweets = [[(NSDictionary *)TWData objectForKey:@"statuses_count"] integerValue];
                            NSString *profileImageStringURL = [(NSDictionary *)TWData objectForKey:@"profile_image_url_https"];
                            NSString *bannerImageStringURL =[(NSDictionary *)TWData objectForKey:@"profile_banner_url"];

Here we filter the preferred data, we get the user’s name, screen_name (username), the number of tweets, followers, and people following. We also get the URLs for the profile image and the banner.

        nameLabel.text = name;
                            usernameLabel.text= [NSString stringWithFormat:@"@%@",screen_name];
                            tweetsLabel.text = [NSString stringWithFormat:@"%i", tweets];
                            followingLabel.text= [NSString stringWithFormat:@"%i", following];
                            followersLabel.text = [NSString stringWithFormat:@"%i", followers];
                            NSString *lastTweet = [[(NSDictionary *)TWData objectForKey:@"status"] objectForKey:@"text"];
                            lastTweetTextView.text= lastTweet;

Here we simply update the text properties of our labels and textview with the data we just received and filtered.

                            profileImageStringURL = [profileImageStringURL stringByReplacingOccurrencesOfString:@"_normal" withString:@""];
                            [self getProfileImageForURLString:profileImageStringURL];

Because Twitter gives use the url for the normal sized profile image, which is only 48 by 48 pixels, we remove the string “_normal”, so we can get the original profile image. After that, we call the getProfileImageForURLString: method to download the profile image. We will create that method in a few minutes.

if (bannerImageStringURL) {
                                NSString *bannerURLString = [NSString stringWithFormat:@"%@/mobile_retina", bannerImageStringURL];
                                [self getBannerImageForURLString:bannerURLString];
                            } else {
                                bannerImageView.backgroundColor = [UIColor underPageBackgroundColor];
                            }

Here we check if the user has a profile banner. If so, we update the URL so we get the mobile retina version of the banner, which has as size of 640×320 pixels. After that, we call the getBannerImageForURLString: method to download the banner. If the user doesn’t have a profile banner, we give the bannerImageView a background color.

Now add the following methods under the getInfo method to download the profile and banner image:

- (void) getProfileImageForURLString:(NSString *)urlString;
{
    NSURL *url = [NSURL URLWithString:urlString];
    NSData *data = [NSData dataWithContentsOfURL:url];
    profileImageView.image = [UIImage imageWithData:data];
}
- (void) getBannerImageForURLString:(NSString *)urlString;
{
    NSURL *url = [NSURL URLWithString:urlString];
    NSData *data = [NSData dataWithContentsOfURL:url];
    bannerImageView.image = [UIImage imageWithData:data];
}

The last thing we need to do to finish our app is to call the getInfo method in the viewDidLoad method, so go to the viewDidLoad method and add the following line at the bottom of that method:

[self getInfo];

Now that we have finished our app, click Build and Run or press ⌘R to test the app. If you enter a username from someone on Twitter, you should see a mini profile with information about that user.

    Final  App

Wrap Up

Thanks for reading this tutorial about making a Twitter request with the new Social framework. If you want to learn more about the Twitter API, I suggest you take a look at the official documentaion. I hope you liked this tutorial, and if you have questions or comment on this tutorial, leave them in the comments section below!

Android SDK: Receiving Data from the Send Intent

$
0
0

On the Android platform, the Intent system allows users to share content between apps. You can send and receive multiple types of data on Android but in this tutorial we will implement receiving either a text String or an image, displaying the received content in the app’s user interface!


Step 1: Start a New Android Project

Start by creating a new Android project in Eclipse. Choose “File”, “New”, “Project” – select “Android Application Project” and then click “Next”. In the New Android Application window, enter your chosen Application, Project, and Package names and then choose “Next”.

New Android Project

In the project configuration window, choose your options, letting Eclipse create an Activity for you. In the next screens, configure your launcher icon if appropriate, and then select “Blank Activity”. In the Configure Activity screen, enter an Activity name and then choose “Finish”.

Blank Activity

Eclipse should create Activity and layout files for your app.


Step 2: Edit the Project Manifest

In order for your app to appear in the chooser list presented when the user attempts to share data from another app, you need to alter the Project Manifest file. Open it in Eclipse and switch to the XML editor tab. Find the section containing your app’s main Activity – you should see an Intent Filter element there already, indicating MAIN action and LAUNCHER category.

After the existing Intent Filter element, inside the main Activity element, add another Intent Filter:

<intent-filter>
</intent-filter>

This is where we define the Actions that can launch the app – the existing Intent Filter specifying the normal options for when the user launches from the homescreen or device application menu. By adding another Intent Filter element, we are specifying additional options for launching the app. Inside the new element, add an Action element specifying the SEND Intent Action:

<action android:name="android.intent.action.SEND" />

This tells Android that when other apps launch a SEND Intent this app may receive what is being shared. Now give the Intent Filter a category:

<category android:name="android.intent.category.DEFAULT" />

Next we need to indicate the MIME types the app is going to receive:

<data android:mimeType="image/*" />
<data android:mimeType="text/*" />

This indicates that the app will receive any text or image type. You can optionally specify particular text or image types. There are lots of other types you can receive, including audio, video, message, and application. If you need your app to receive multiple data items of one of your MIME types, you can include the SEND_MULTIPLE Action as well as/instead of the SEND Action alone.

Here is the “DataReceiver” app appearing in the share list for the Gallery application, through which various image types can be shared.

The App in the Share List

Step 3: Define the App Layout

Let’s design the app to display the received text String or image. Open the main layout file and switch to the XML tab to edit the code. Replace any existing code in the layout file with the following Relative Layout outline:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:tools="http://schemas.android.com/tools"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	android:padding="10dp"
	tools:context=".DataReceiverActivity" >
</RelativeLayout>

Alter the “tools:context” attribute if you chose a different Activity class name. Inside the layout, add the following to display a received image:

<ImageView
	android:id="@+id/picture"
	android:layout_width="fill_parent"
	android:layout_height="fill_parent"
	android:contentDescription="@string/pic"
/>

We will use the ID to refer to the Image View in our Java Activity, updating it to display any image received from the SEND Intent. Add the specified String to your app’s “res/values/strings.xml” file:

<string name="pic">Received Picture</string>

Back in the layout file, add a Text View to display any text Strings received via the SEND Intent:

<TextView
	android:id="@+id/txt"
	android:layout_width="fill_parent"
	android:layout_height="fill_parent"
/>

Again, we have an ID attribute we can use in the Java code.


Step 4: Receive the Intent

Open your app’s Activity class file. Your class will need the following import statements, some of which may already be present:

import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.view.Menu;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

Eclipse should have created a basic onCreate method applying your layout. After this line, retrieve the Views we want to update with any received data:

//get the image view
ImageView picView = (ImageView)findViewById(R.id.picture);
//get the text view
TextView txtView = (TextView)findViewById(R.id.txt);

These use the ID attributes we included in the layout file. Now that we have a reference to the Views, we can update their appearance with data received from other applications. Within an Activity class, you can retrieve the Intent that was responsible for the Activity starting. Add the following line in your onCreate method:

//get the received intent
Intent receivedIntent = getIntent();

Now we can use this to also retrieve the Action that was used when starting the Activity:

//get the action
String receivedAction = receivedIntent.getAction();

This will let us determine whether the app has launched from the device menu or from the share list. If the app has been launched with the SEND Action, there will also be a MIME type indicated within the Intent that started it. Retrieve this as follows:

//find out what we are dealing with
String receivedType = receivedIntent.getType();

If the app was launched, for example, from the main device menu rather than from a share list, this will be null. If it was launched with the SEND Action, we will be able to use this to handle the received data, tailoring the app response to the type of data received.

Users can share text from a variety of applications, including the Web browser – if a user chooses to share a Web page, this data is contained in a text String, so our app will be able to receive it:

The Browser Menu

Here is the app listed in the browser share list:

The App in the Browser Share List

Step 5: Handle Alternative Actions

Now we have two possibilities: the app has been launched from the device in the default fashion and is not receiving any incoming data; the app has been launched to share content, in which case there is either a text String or image incoming. Add a conditional statement to onCreate to handle these two scenarios:

//make sure it's an action and type we can handle
if(receivedAction.equals(Intent.ACTION_SEND)){
	//content is being shared
}
else if(receivedAction.equals(Intent.ACTION_MAIN)){
	//app has been launched directly, not from share list
}

If you look back at your Manifest file you will see that these are the Actions included in our two Intent Filter elements for the Activity. Let’s complete the ACTION_MAIN else if statement first as it’s simpler. In here you need to include whatever you want your app to do when nothing has been shared to it. For the purposes of this tutorial we will simply display a text String in the Text View:

txtView.setText("Nothing has been shared!");

Step 6: Determine MIME Type

Now to the ACTION_SEND if statement, in which we need to handle received data of the MIME types we listed in the Manifest. First add a conditional to handle the two types we listed, text and images:

if(receivedType.startsWith("text/")){
	//handle sent text
}
else if(receivedType.startsWith("image/")){
	//handle sent image
}

If you set your app up to receive other types, simply add conditional statements here for each of them.


Step 7: Receive Shared Text Data

Let’s receive text first. In the if statement for text data, first hide the Image View as we only need the Text View in this case:

//hide the other ui item
picView.setVisibility(View.GONE);

Now retrieve the String as an Extra from the Intent:

//get the received text
String receivedText = receivedIntent.getStringExtra(Intent.EXTRA_TEXT);

We use the getStringExtra method for text data, passing the EXTRA_TEXT Intent constant to receive any character sequence passed using the SEND Action. Now let’s make sure we have some data and display it in the app UI:

//check we have a string
if (receivedText != null) {
	//set the text
	txtView.setText(receivedText);
}

You can of course carry out further processing on the text if you need to – this is the text URL shared from the browser:

Shared Text in the App UI

Step 8: Receive Shared Image Data

Now let’s turn to the else if statement for image data. As before, first set the alternative UI item to disappear as we don’t need it:

//hide the other ui item
txtView.setVisibility(View.GONE);

Now we need to get the image URI, as this is what the Intent will be sharing:

//get the uri of the received image
Uri receivedUri = (Uri)receivedIntent.getParcelableExtra(Intent.EXTRA_STREAM);

This time we use the getParcelableExtra method – there are lots of other Intent methods for the different data types you can receive in your apps, including number types, arrays and lists. We pass the EXTRA_STREAM Intent constant this time, which is used to receive data other than text. Now we can use the image URI within the app – the sample code below simply displays it in the Image View but if you plan on receiving images in your own apps you should first resample the image data to avoid memory issues:

//check we have a uri
if (receivedUri != null) {
	//set the picture
	//RESAMPLE YOUR IMAGE DATA BEFORE DISPLAYING
	picView.setImageURI(receivedUri);//just for demonstration
}

Tip: For guidance on avoiding memory problems when importing images see the Android Developers guide: Displaying Bitmaps Efficiently

Here is the image shared from the Gallery app:

Shared Image in the App UI

Conclusion

That’s our basic receiver app complete. This is of course just the bare bones of a functional app in which data is received from the SEND Action, but the principle of setting your app up to appear in the share list and handling the incoming content remains. In general, it’s worth thinking of the possible ways in which users may want to launch your apps, as the main menu launcher is really just the beginning.


iOS Quick Tip: Create a Dynamic Action Sheet

$
0
0

Welcome to this entry-level quick tip about working with action sheets. In this tutorial, you will learn how to create a simple action sheet dynamically using content from an array. This technique will allow you to determine how many buttons to place on the action sheet at run-time as opposed to compile-time. A handy technique to have!

We will create a simple application in this tutorial that will show us a list of fruits in an action sheet. If you select a fruit, a label’s text will be updated with the selected fruit.


Step 1: Creating the Project

Open Xcode and select “Create a new Xcode project”. Select “Single View Application” and click “next”. Enter a name for your project (I called mine “Fruits”), make sure you select iPhone for the Devices and select the “Use Storyboards” and “Use Automatic Reference Counting” checkboxes. After that click, “next” and choose a place to save your project before clicking “create”.

    Create Project

Step 2: Setting Supported Orientations

We only want to use this application in portrait mode, so go to the Supported Interface Orientations section and deselect the landscape orientations.

    Supported Orientations

Step 3: Creating the Interface

Open the Storyboard and drag a label from the Object Library to the View Controller. Place it at the top of the View Controller and make sure it’s centered and it’s 280 pixels wide. Open the Attributes Inspector and change the Alignment to center. At last, delete the text.

Next, drag a Button from the Object Library to the View Controller and place it just below the label. Double click on its title and change it to “Fruits”.

    Interface

Step 4: Making the IBOutlet Connections

Open ViewController.m and modify the code to read as follows:

#import "ViewController.h"
@interface ViewController () <UIActionSheetDelegate>
@property(nonatomic, weak)   IBOutlet UILabel *fruitLabel;
@property(nonatomic, strong) NSMutableArray *fruits;
- (IBAction)showFruits:(id)sender;
@end

Here we create an outlet for our label, a mutable array to store our fruits, and we create an action to show the action sheet. We also added the UIActionSheetDelegate, so we can update the label when you select a fruit from the action sheet. Note that all of this was done in the class extension because none of these properties or methods need to be exposed to an outside class.

Now that we have created the outlet and action, we only need to connect them with the corresponding elements. Open the Storyboard and connect the fruitsLabel outlet with the label and the showFruits: action with the button. Select Touch Up Inside as the control event for the button.


Step 5: Create a List of Fruits

Open ViewController.m and create the following initializer method:

-(id) initWithCoder:(NSCoder *)aDecoder{
    if (self = [super initWithCoder:aDecoder]) {
        self.fruits = [[NSMutableArray alloc] initWithObjects:@"Apple", @"Orange", @"Banana", @"Strawberry", @"Peach",nil];
    }
    return self;
}

Here we create our fruits array and store some fruits within it.


Step 6: Show the List

Add the following code below the didReceiveMemoryWarning method:

- (IBAction)showFruits:(id)sender
{
    UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@"Select a fruit"
                                                             delegate:self
                                                    cancelButtonTitle:nil
                                               destructiveButtonTitle:nil
                                                    otherButtonTitles:nil];
    for (NSString *fruit in self.fruits) {
        [actionSheet addButtonWithTitle:fruit];
    }
    actionSheet.cancelButtonIndex = [actionSheet addButtonWithTitle:@"Cancel"];
    [actionSheet showInView:self.view];
}

We first create an action sheet, like you would normally do. We give it a title and a delegate, but we don’t add any buttons, not even a cancel button. If we were to add a cancel button here, and all the other buttons later, the cancel button would be on top of the list, instead of at the bottom.

Next, we use a fast enumeration loop to go through all the fruits stored in the fruits array created earlier. In that for loop, we add buttons for all the fruits to the action sheet. After the for loop, we add a cancel button to the action sheet. As you can see, we do that by adding a button with the title “Cancel” to the cancelButtonIndex. This way the action sheet knows that the Cancel button should be at the bottom of the list. At last, we show the action sheet the normal way.


Step 7: Update the Fruits Label

Add the following action sheet delegate protocol method below the showFruits: action:

- (void) actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
    if (buttonIndex != actionSheet.cancelButtonIndex) {
        self.fruitLabel.text = [self.fruits objectAtIndex:buttonIndex];
    }
}

This delegate method gets called when you press a button from the action sheet. First, we check if the button pressed is for a fruit or the cancel button. We do that by comparing the selected button index with the cancel button index. If the selected button is for a fruit, we update the label with the selected fruit.


Wrap Up

Thanks for reading this quick tip about creating an action sheet with items from an array. I hope you found it useful! If you have questions or comment on this quick tip, leave them in the comments section below.

Create a Hundreds-Like Game – Interface Creation

$
0
0

In this tutorial series, you’ll learn how to create a game like the popular Hundreds. The objective of the game is to reach 100 points without the circles touching while you raise the score. Read on!


Step 1: Application Overview

Using pre made graphics we will code an entertaining game using Lua and the Corona SDK API’s.

The player will be able to tap on the circles on the screen in order to increase the score. You can modify the parameters in the code to customize the game.


Step 2: Target Device

The first thing we have to do is select the platform we want to run our app within, this way we’ll be able to choose the size for the images we will use.

The iOS platform has these characteristics:

  • iPad 1/2/Mini: 1024x768px, 132 ppi
  • iPad Retina: 2048×1536, 264 ppi
  • iPhone/iPod Touch: 320x480px, 163 ppi
  • iPhone/iPod Retina: 960x640px, 326 ppi
  • iPhone 5/iPod Touch: 1136×640, 326 ppi

Because Android is an open platform, there are many different devices and resolutions. A few of the more common screen characteristics are:

  • Asus Nexus 7 Tablet: 800x1280px, 216 ppi
  • Motorola Droid X: 854x480px, 228 ppi
  • Samsung Galaxy SIII: 720x1280px, 306 ppi

In this tutorial we’ll be focusing on the iOS platform with the graphic design, specifically developing for distribution to an iPhone/iPod touch, but the code presented here should apply to Android development with the Corona SDK as well.


Step 3: Interface

A simple and friendly interface will be used, this involves multiple shapes, buttons, bitmaps and more.

The interface graphic resources necessary for this tutorial can be found in the attached download.


Step 4: Export Graphics

Depending on the device you have selected, you may need to export the graphics in the recommended PPI, you can do that in your favorite image editor.

I used the Adjust Size… function in the Preview app on Mac OS X.

Remember to give the images a descriptive name and save them in your project folder.


Step 5: App Configuration

An external file will be used to make the application go fullscreen across devices, the config.lua file. This file shows the original screen size and the method used to scale that content in case the app is run in a different screen resolution.

application =
{
    content =
    {
        width = 320,
        height = 480,
        scale = "letterbox"
    },
}

Step 6: Main.lua

Let’s write the application!

Open your prefered Lua editor (any Text Editor will work, but may not have syntax highlighting) and prepare to write your awesome app. Remember to save the file as main.lua in your project folder.


Step 7: Code Structure

We’ll structure our code as if it were a Class. If you know ActionScript or Java, you should find the structure familiar.

Necessary Classes
Variables and Constants
Declare Functions
    contructor (Main function)
    class methods (other functions)
call Main function

Step 8: Hide Status Bar

display.setStatusBar(display.HiddenStatusBar)

This code hides the status bar. The status bar is the bar on top of the device screen that shows the time, signal, and other indicators.


Step 9: Import Physics

We’ll use the Physics library to handle collisions. Use this code to import it:

local physics = require('physics')
physics.start()
physics.setGravity(0,0)

Step 10: Background

A simple graphic is used as the background for the application interface, the next line of code stores it.

-- Graphics
-- [Background]
local bg = display.newImage('bg.png')

Step 11: Title View

This is the Title View, it will be the first interactive screen to appear in our game, these variables store its components.

-- [Title View]
local titleBg
local playBtn
local creditsBtn
local titleView

Step 12: Credits View

This view will show the credits and copyright of the game, this variable will be used to store it.

-- [CreditsView]
local creditsView

Step 13: Game Background

This image will replace our previous background. This will be the game background.

-- Game Background
local gameBg

Step 14: Circles

The circles group will store the five circles that will be created dynamically later in the game. We’ll be using the Corona SDK drawing API.

-- Circles Group
local circles

Step 15: Walls

Physics walls are created to make the circles bounce on the screen.

-- Walls
local left
local right
local top
local bottom

Step 16: Score

The score value is handled by this variable.

-- Score TextField
local score

Step 17: Alert

This is the alert that will be displayed when you win the game. It will complete the level and end the game. There is also a ‘You Lost’ alert that will behave in a similar way.

-- Alert
local alertView

Step 18: Variables

This are the variables we’ll use, read the comments in the code to know more about them.

-- Variables
local lastY --Used to reposition the credits view
local lastX --used to reposition the title view

Step 19: Declare Functions

Declare all functions as local at the start.

-- Functions
local Main = {}
local startButtonListeners = {}
local showCredits = {}
local hideCredits = {}
local showGameView = {}
local gameListeners = {}
local onTouch = {}
local onCollision = {}
local alert = {}

Step 20: Constructor

Next we’ll create the function that will initialize all the game logic:

function Main()
	-- code...
end

Step 21: Add Title View

Now we place the TitleView in the stage and call a function that will add the tap listeners to the buttons.

function Main()
	titleBg = display.newImage('title.png', 10, 20)
	playBtn = display.newImage('playBtn.png', 100, 220)
	creditsBtn = display.newImage('creditsBtn.png', 100, 290)
	titleView = display.newGroup(titleBg, playBtn, creditsBtn)
	startButtonListeners('add')
end

Next Time…

In this part of the series, you’ve learned the interface and the basic setup of the game. In the next and final part of the series, we’ll handle the level creation, collision detection, and the final steps to take prior to release, like app testing, creating a start screen, adding an icon and, finally, building the app. Stay tuned for the final part!

Create a Hundreds-Like Game – Adding Interaction

$
0
0

This is the second installment in our Corona SDK Hundreds game tutorial. In today’s tutorial, we’ll add to our interface and the game interaction. Read on!


Where We Left Off. . .

Please be sure to check part 1 of the series to fully understand and prepare for this tutorial.


Step 1: Start Button Listeners

This function adds the necesary listeners to the TitleView buttons.

function startButtonListeners(action)
	if(action == 'add') then
		playBtn:addEventListener('tap', showGameView)
		creditsBtn:addEventListener('tap', showCredits)
	else
		playBtn:removeEventListener('tap', showGameView)
		creditsBtn:removeEventListener('tap', showCredits)
	end
end

Step 2: Show Credits

The credits screen is shown when the user taps the about button, a tap listener is added to the credits view to remove it.

function showCredits:tap(e)
	playBtn.isVisible = false
	creditsBtn.isVisible = false
	creditsView = display.newImage('credits.png', 0, display.contentHeight)
	lastY = titleBg.y
	lastX = titleBg.x
	transition.to(titleBg, {time = 300, y = (display.contentHeight * 0.5) - (titleBg.height + 20), x = (display.contentWidth * 0.5) - (titleBg.width * 0.5) - 10})
	transition.to(creditsView, {time = 300, y = (display.contentHeight * 0.5) + 35, onComplete = function() creditsView:addEventListener('tap', hideCredits) end})
end

Step 3: Hide Credits

When the credits screen is tapped, it’ll be tweened out of the stage and removed.

function hideCredits:tap(e)
	transition.to(creditsView, {time = 300, y = display.contentHeight + 25, onComplete = function() creditsBtn.isVisible = true playBtn.isVisible = true creditsView:removeEventListener('tap', hideCredits) display.remove(creditsView) creditsView = nil end})
	transition.to(titleBg, {time = 300, y = lastY, x = lastX});
end

Step 4: Show Game View

When the Start button is tapped, the title view is tweened and removed revealing the game view. There are many parts involved in this view, so we’ll split them in the next steps.

function showGameView:tap(e)
	transition.to(titleView, {time = 300, x = -titleView.height, onComplete = function() startButtonListeners('rmv') display.remove(titleView) titleView = nil end})

Step 5: Game Background

This code places the game background image in the stage.

-- Game Background
display.remove(bg)
gameBg = display.newImage('gameBg.png')

Step 6: Walls

Next we add the walls to the stage. They are created using the drawing API.

-- Walls
left = display.newLine(0, 240, 0, 720)
right = display.newLine(320, 240, 320, 720)
top = display.newLine(160, 0, 480, 0)
bottom = display.newLine(160, 480, 480, 480)

Step 7: Circles

This part creates five circles in the screen at a random position, the i variable is used to multiply a constant that will define the circles color making it lighter each time. A textfield is also created and both are held within a group to move them together.

-- Circles
circles = display.newGroup()
local color = 0
for i = 1, 5 do
	local rx = 21 + math.floor(math.random() * (display.contentWidth - 42))
	local ry = 21 + math.floor(math.random() * (display.contentHeight - 42))
	local cg = display.newCircle(rx, ry, 21)
	local label = display.newText('0',cg.x-4.2, cg.y-12.2, native.systemFontBold, 13)
	cg.fillColor = color + (i*40)
	cg:setFillColor(cg.fillColor)
	local c = display.newGroup(cg, label)
	c.pressed = false
	c.name = 'c'
	c.radius = 21

Step 8: Circle Physics

Here we add physics to our new circle.

	-- Circle Physics
	physics.addBody(c, 'dynamic', {radius = 21, bounce = 1})
	c:setLinearVelocity(100, 100)
	circles:insert(c)
end

Step 9: Wall Physics

And do the same with our walls.

-- Walls Physics
physics.addBody(left, 'static')
physics.addBody(right, 'static')
physics.addBody(top, 'static')
physics.addBody(bottom, 'static')

Step 10: Score TextField

Now we add the score textfields.

	-- Score TextField
	score = display.newText('0', 257, 4, native.systemFont, 15)
	score:setTextColor(150, 150, 150)
	local total = display.newText(' / 100', 267, 4, native.systemFont, 15)
	total:setTextColor(150, 150, 150)
	gameListeners('add')
end

Step 11: Game Listeners

This function adds the necessary listeners to start the game logic.

function gameListeners(action)
	if(action == 'add') then
		for i = 1, 5 do
			circles[i]:addEventListener('touch', onTouch)
			circles[i]:addEventListener('collision', onCollision)
		end
	else
		for i = 1, 5 do
			circles[i]:removeEventListener('touch', onTouch)
			circles[i]:removeEventListener('collision', onCollision)
		end
	end
end

Step 12: Increase Score

This code runs when the circle is touched.

First we increase the score.

function onTouch(e)
	if(e.phase == 'began') then
		e.target.pressed = true
		-- Increase Counter
		score.text = tostring(tonumber(score.text) + 2)

Step 13: Increase Size

Then the size of the circle. The circle will grow when the finger is no longer on it.

-- Increase size
e.target.radius = e.target.radius + 2

Step 14: Change Color

Changes the color from black/gray to red.

-- Change Color
e.target[1]:setFillColor(224, 11, 0)

Step 15: Update Circle

We recreate the circle and its properties when the touch event has ended.

	if(e.phase == 'ended') then
		e.target.pressed = false
		-- Update physics
		local number = tostring(tonumber(e.target[2].text)+2)
		local r = e.target.radius
		local cg = display.newCircle(e.target.x, e.target.y, r)
		local label = display.newText(number ,cg.x-4.2, cg.y-12.2, native.systemFontBold, 13)
		cg:setFillColor(e.target[1].fillColor)
		cg.fillColor = e.target[1].fillColor
		local c = display.newGroup(cg, label)
		c.pressed = false
		c.name = 'c'
		c.radius = r
		circles:remove(e.target)
		physics.addBody(c, 'dynamic', {radius = r, bounce = 1})
		c:setLinearVelocity(100, 100)
		c:addEventListener('touch', onTouch)
		c:addEventListener('collision', onCollision)
		circles:insert(c)
		-- Move Textfield when number is 2 digit
		if(tonumber(number) > 9) then
			label.x = label.x - 5
		end

Step 16: Level Complete

Here we check if the score has reached 100 points and call a win alert if true.

		-- Check if score has reached 100
		if(tonumber(score.text) >= 100) then
			local bg = display.newImage('gameBg.png')
			transition.from(bg, {time = 500, alpha = 0, onComplete = alert('win')})
		end
	end
end

Step 17: Collisions

The game is lost when a pressed circle collides with another one. The listeners are removed and a lost alert is called.

function onCollision(e)
	if(e.target.pressed and e.other.name == 'c') then
		-- Wait 0.1 seconds to stop physics
		timer.performWithDelay(100, function() physics.stop() end, 1)
		local r = e.target.radius
		local c = display.newCircle(e.target.x, e.target.y, r)
		c:setFillColor(224, 11, 0)
		gameListeners('rmv')
		transition.to(c, {time = 700, xScale = 25, yScale = 25, onComplete = alert('lost')})
	end
end

Step 18: Alert

The alert function displays a message according to the type of alert called.

function alert(action)
	if(action == 'win') then
		local alertView = display.newImage('won.png', 10, display.contentHeight * 0.5 - 41)
		transition.from(alertView, {time = 300, y = -82, delay = 500})
	else
		local alertView = display.newImage('lost.png', 10, display.contentHeight * 0.5 - 41)
		transition.from(alertView, {time = 300, y = -82, delay = 500})
	end
end

Step 19: Call Main Function

In order to start the game, the Main function needs to be called. With the above code in place, we’ll do that here:

Main()

Step 20: Loading Screen

The Default.png file is an image that will be displayed right when you start the application while the iOS loads the basic data to show the Main Screen. Add this image to your project source folder, it will be automatically added by the Corona compliler.


Step 21: Icon

Using the graphics you created before you can now create a nice and good looking icon. The icon size for the non-retina iPhone icon is 57x57px, but the retina version is 114x114px and the iTunes store requires a 512x512px version. I suggest creating the 512×512 version first and then scaling down for the other sizes.

It doesn’t need to have the rounded corners or the transparent glare, iTunes and the iPhone will do that for you.


Step 22: Testing in Simulator

It’s time to do the final test. Open the Corona Simulator, browse to your project folder, and then click open. If everything works as expected, you are ready for the final step!


Step 23: Build

In the Corona Simulator go to File > Build and select your target device. Fill the required data and click build. Wait a few seconds and your app will be ready for device testing and/or submission for distribution!


Conclusion

Experiment with the final result and try to make your custom version of the game!

I hope you liked this tutorial series and find it helpful. Thank you for reading!

Android SDK: Creating a Simple Tween Animation

$
0
0

The Android platform facilitates three different types of animation you can define in your application resources: property, tween and frame. In this tutorial, we will create a simple tween animation using shape drawables displayed within Image Views. The end result will be an animation in which the sun rises, increasing in opacity and expanding as the tween elapses. We will also include a simple clock animation indicating the passing of time.


Step 1: Open or Start an Android Project

You can either create a new app or add the animation to an existing app. You will need an Activity class plus a layout for it. In the demo code, the app is named “AnimatedApp” with “AnimatedActivity” as the main Activity class and “activity_animated” for the layout XML file. We will be editing the Activity Java and the layout XML as well as creating various additional resource files.


Step 2: Create the Sky Shape Drawable

We will add the visual elements starting from the back and working towards the front, so let’s create a shape drawable for the sky first. In your application drawable folder(s), create a new file by selecting the folder, choosing “File”, “New”, “File” (or right-click, “New”, “File”) and entering “sky.xml” as the file name.

Inside your new drawables XML file, enter the following code to represent the sky:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:dither="true"
    android:shape="rectangle" >
    <gradient
        android:angle="90"
        android:endColor="#ff000033"
        android:startColor="#ff0000ff" />
</shape>

We define a rectangle shape with a blue gradient running from bottom to top. Dithering is enabled for the shape so that the gradient does not appear with banding (although sometimes it still will on the emulator).

Tip: Each time you create and complete a shape drawable file, remember to copy it into all drawables folders you plan on using for your app, amending the dimensions if you wish to tailor the shape to different device screen densities.


Step 3: Create the Sun Shape Drawable

Next create another drawable file, this time naming it “sun.xml”. Inside it, define the sun shape as follows:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:dither="true"
    android:shape="oval" >
    <gradient
        android:endColor="#ffff6600"
        android:gradientRadius="150"
        android:startColor="#ffffcc00"
        android:type="radial"
        android:useLevel="false" />
    <size
        android:height="150dp"
        android:width="150dp" />
</shape>

This time, the shape is an oval, again with dithering. The fill is a radial gradient running from a deeper yellow to a lighter one. In this case we define an explicit size, whereas with the sky rectangle we are just going to let the shape fill the available space when we add it to the layout.


Step 4: Create the Grass Shape Drawable

Create another drawables file, this time named “grass.xml”. Enter the following code to represent the grass:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:dither="true"
    android:shape="rectangle" >
    <gradient
        android:angle="90"
        android:endColor="#ff003300"
        android:startColor="#ff009900" />
</shape>

This is another rectangle, this time with a vertical green gradient. We will define its dimensions and position when we add it to the layout file.


Step 5: Include the Drawables in the Layout

Now we can include our shapes in the Activity layout. We will be using Image Views, which require the Content Description attribute – a string value describing the image content. Before we start with the layout, open your app’s “res/values/strings.xml” file and add the following:

<string name="sun">Sun</string>
<string name="grass">Grass</string>
<string name="sky">Sky</string>
<string name="clock">Clock</string>
<string name="hour">Hour Hand</string>

This includes values for the three shapes we have already defined plus two we will use later.

Now open your layout XML file. Replace any existing content with the following Relative Layout outline:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".AnimatedActivity" >
</RelativeLayout>

Alter the context attribute if you are using a different Activity class name. Inside this layout, add an Image View for the sky shape:

<ImageView
    android:id="@+id/sky"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:contentDescription="@string/sky"
    android:src="@drawable/sky" />

We use an ID so that we can refer to the View in Java and apply the animation to it. The shape will fill the available screen space using the drawable we defined as its source src attribute.

Next add the sun shape, as we want it to appear in front of the sky but behind the grass:

<ImageView
    android:id="@+id/sun"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerHorizontal="true"
    android:contentDescription="@string/sun"
    android:scaleType="fitCenter"
    android:src="@drawable/sun" />

This time we wrap the content because we have defined the size explicitly in the shape drawable. Using a scale type allows us to instruct Android to fit the shape into the available screen size if it is too big. We center the sun shape horizontally within the layout.

Now add the grass:

<ImageView
    android:id="@+id/grass"
    android:layout_width="fill_parent"
    android:layout_height="150dp"
    android:layout_alignParentBottom="true"
    android:contentDescription="@string/grass"
    android:src="@drawable/grass" />

The grass shape will fill this View, which is defined to be a portion of the screen 150dp in height and aligned to the bottom of its container.

This is the layout initial appearance. We will be adding a couple more elements and altering the initial position of the sun.

Layout Initial Appearance

This is what the Activity will look like when the animation begins on an actual device – we will add the clock later and the start position for the sun (at the bottom, behind the grass) will be determined as part of the animation code:

Animation Initial Appearance

Step 6: Define the Sun Rise Animation

Now we can define the animation via which the sun shape will rise up the Activity screen. Create a new folder in your application’s resources directory by selecting “res” and choosing “File”, “New”, “Folder” (or right-clicking and choosing “New”, “Folder”). Name the folder “anim” – this is where our animation definitions will be stored.

Create a new file in the “anim” folder and name it “sun_rise.xml”. In the new file, enter the following outline:

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="false"
    android:duration="5000"
    android:fillAfter="true"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator" >
</set>

We will specify the animation details inside the set element. Notice that the opening tag indicates the duration of the animation. We specify fillAfter so that the animation does not jump back to the beginning but rather retains its appearance at the end. We indicate the accelerate decelerate interpolator, which means that the animation will ease in and out, speeding up in the middle.

Inside the animation set, we can define the changes we want to take place. Let’s make the sun expand in size as it rises – add the following scale element to resize the shape:

<scale
    android:fromXScale="1.0"
    android:toXScale="1.5"
    android:fromYScale="1.0"
    android:toYScale="1.5"
    android:pivotX="50%"
    android:pivotY="50%"
    />

The shape will begin at its normal size and end at 1.5 times that on both axes. The central point will remain the same, as we apply it as a pivot point for the scale.

Next add a translate element to move the sun up the screen:

<translate
    android:fromYDelta="80%p"
    android:toYDelta="10%p"
    />

The translate is purely vertical, so we only define the Y start and end points. We define the translation points relative to the parent element using the “p” suffix. The sun will start 80% along its parent element’s Y axis and end 10% along it, moving it upwards as the animation elapses.

Let’s also increase the opacity of the sun as it rises by adding an alpha element:

<alpha
    android:fromAlpha="0.3"
    android:toAlpha="1.0"
    />

The sun will start mostly transparent and end at full opacity.

Here is how the animation will appear mid-way:

Animation Midway

Step 7: Apply the Animation

Now open your Java Activity class. First add the following import statements:

import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;

Inside your onCreate method, after the existing code, retrieve a reference to the sun Image View:

//get the sun View
ImageView sun = (ImageView) findViewById(R.id.sun);

Now load the sunrise animation you defined:

//get the sunrise animation
Animation sunRise = AnimationUtils.loadAnimation(this, R.anim.sun_rise);

We refer to the animation using its resource identifier. Now begin the animation, applying it to the sun View:

//apply the animation to the View
sun.startAnimation(sunRise);

Run your app to see the sun rise, becoming brighter and expanding in size.

Here is the animation a little further along (including the clock which we will add next):

Animation Midway

Step 8: Add a Clock Animation

To reinforce what we’ve learned, let’s go through the process again to add a simple clock animation to the app. Start by creating a new drawable file called “clock.xml”. This time we will use a Layer List to include more than one shape:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
</layer-list>

Inside the Layer List add the first item, which is a circle shape:

<item>
    <shape
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:dither="true"
        android:shape="oval" >
        <gradient
            android:endColor="#ffffffff"
            android:gradientRadius="100"
            android:startColor="#66ffffff"
            android:type="radial"
            android:useLevel="false" />
        <size
            android:height="100dp"
            android:width="100dp" />
        <stroke
            android:width="2dp"
            android:color="#99000000" />
    </shape>
</item>

The shape is an oval, with a radial gradient, explicit size and stroke. Next, still inside the Layer List, add a clock hand shape:

<item
    android:bottom="44dp"
    android:left="48dp"
    android:right="48dp"
        android:top="5dp">
    <shape
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle" >
        <solid android:color="#99000000" />
    </shape>
</item>

This time we define the shape as a rectangle, with its properties relative to the circle shape it will appear on top of. We use the dimensions of the circle shape to make the rectangle appear like a minute hand on a circular clock face.

Now create a new file in your “anim” folder, naming it “clock_turn.xml”. Define the animation inside it:

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="5000"
    android:fillAfter="true"
    android:interpolator="@android:anim/linear_interpolator"
    android:shareInterpolator="false" >
    <rotate
        android:fromDegrees="0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toDegrees="720" />
</set>

This animation will elapse over the same period as the sun rise animation, as we give it the same duration. This time the interpolator is linear, so that the change will be applied at a constant rate throughout. The animation this time is a rotation, starting at the shape’s initial position and rotating in two full circles (720 degrees). The circle center point will remain the same due to the pivot attributes. Although the entire circle shape will rotate, it will appear to the user as though the hand is turning around the clock face.

Add the clock to your layout XML file after the existing Image Views:

<ImageView
    android:id="@+id/clock"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_alignParentRight="true"
    android:contentDescription="@string/clock"
    android:padding="10dp"
    android:src="@drawable/clock" />

It will appear in the bottom-right corner.

In your Activity class, after the existing sunrise animation code, get a reference to the clock:

//get the clock View
ImageView clock = (ImageView) findViewById(R.id.clock);

Next load the animation:

//get the clock turn animation
Animation clockTurn = AnimationUtils.loadAnimation(this, R.anim.clock_turn);

Now apply it to the clock shape:

//apply the animation to the View
clock.startAnimation(clockTurn);

You can run your app again to see it in action.

Let’s add an hour hand to the clock as well, so that the user can see the time advance two hours to match the two rotations of the minute hand. Create another drawable named “hour_hand.xml” and use the previous clock drawable as a template:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    <item>
        <shape
            xmlns:android="http://schemas.android.com/apk/res/android"
            android:dither="true"
            android:shape="oval" >
            <solid android:color="#00000000" />
            <size
                android:height="100dp"
                android:width="100dp" />
        </shape>
    </item>
    <item
        android:bottom="44dp"
        android:left="48dp"
        android:right="48dp"
        android:top="15dp">
        <shape
            xmlns:android="http://schemas.android.com/apk/res/android"
            android:shape="rectangle" >
            <solid android:color="#99000000" />
        </shape>
    </item>
</layer-list>

The only differences between this and the previous shape are the circle, which has a solid transparent fill in this case, and the hand rectangle, which is shorter. We don’t bother with the stroke for the circle this time as it won’t be visible anyway. By using the original clock shape as a guide, we can be sure this hand will rotate directly on top of the existing one.

Add the new shape to the layout XML after the existing items so that it appears on top:

<ImageView
    android:id="@+id/hour"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_alignParentRight="true"
    android:contentDescription="@string/hour"
    android:padding="10dp"
    android:src="@drawable/hour_hand" />

It sits in exactly the same position as the previous shape.

Now create a new animation resource in your “anim” folder, naming it “hour_turn.xml”. Define the animation again using the previous one as a guide:

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="5000"
    android:fillAfter="true"
    android:interpolator="@android:anim/linear_interpolator"
    android:shareInterpolator="false" >
    <rotate
        android:fromDegrees="180"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toDegrees="240" />
</set>

This time the shape will begin already rotated to 180 degrees, so that it will appear to be 6 o’clock. Over the same duration as the other animations it will move 60 degrees, so that at the end it will appear to be 8 o’clock, reflecting the two complete 360 degree rotations of the minute hand.

Open your Java Activity and apply the new animation:

//get the hour hand View
ImageView hour = (ImageView) findViewById(R.id.hour);
//get the hour turn animation
Animation hourTurn = AnimationUtils.loadAnimation(this, R.anim.hour_turn);
//apply the animation to the View
hour.startAnimation(hourTurn);

Here is the animation at the end-point:

Animation End

Conclusion

That’s our simple tween animation complete! We have experimented with the set of elements you can use within a tween animation on a View element: translate, alpha, scale and rotate. You can alternatively use property animation to alter particular properties of an item over time, such as its color, or frame animation, in which you display an image sequence. The animation elements we used above have lots of additional attributes you can try, so experiment with your apps to see what’s possible.

Windows Phone: Connecting with Facebook

$
0
0

In this tutorial, we will talk about how to interact with the Facebook API, and all the tools you need in order to connect with it. Specifically, the app that we are going to make will be able to connect with the user’s Facebook account and update their status. Let’s get started!


Step 1: Visual Studio Project Creation

First of all, we need to create a new project with Visual Studio. We’ll just be building a simple app, so select the “Windows Phone App” option:

If you are using Visual Studio 2012 with the new WP8 SDK, you are going to be asked the Target Windows Phone OS Version. If that’s the case, then just select the 7.1 OS.

Tutorial-02

Step 2: Adding the UI

With the project already created, open the “MainPage.xaml” file if it isn’t already open and change the default application and page name text box:

<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="WP7 Tutorial" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="PageTitle" Text="Facebook" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>

Now in our ContentPanel Grid, we will add two Rows, one for a TextBox where the user will input the new status, and the other one for the button to submit the status:

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Grid.RowDefinitions>
    <RowDefinition Height="auto"/>
    <RowDefinition Height="auto"/>
</Grid.RowDefinitions>
</Grid>

And then just add a TextBox on the first Row with the name of “Message”, and a button on the second:

<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Grid.RowDefinitions>
    <RowDefinition Height="auto"/>
    <RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Height="150" Name="Message"/>
<Button Grid.Row="1" Content="Post"/>
</Grid>

At the end you should have this:

Tutorial-08

Step 3: Creating Developer Accounts

Facebook has a very comprehensive API for enabling interaction between apps and the site. The API allows your app to connect and interact with the user’s FB account.

In order to connect our app to facebook, we’ll need to register as a Facebook Developer. To create a Facebook developer account, go to the Facebook Developer Site,
then login with your facebook account or create one if you don’t have one already. Once you’ve already logged in, click on the “Register Button” and follow the instructions.


Step 4: Registering a New App

Now, create a new App by going to your Apps menu, and then select the “Create New App” button.

After you have created your app, you will see your App settings page, and on it an App ID/API Key number.

Copy this number, return to the project, and inside of the “MainPage.xaml.cs” file, create a new global constant string on top of your constructor:

    private const string FBApi = "YOUR API KEY GOES HERE";
    // Constructor
    public MainPage()
    {
        InitializeComponent();
    }

Step 5: Choosing the Facebook C# SDK

Facebook has some great SDK’s for iOS, and Android, but sadly none for WP7, so in order to connect with Facebook from a WP7 App, we have two options: (1) create all the calls manually, or (2) use the Facebook C# SDK, a non-official SDK made specifically for C# apps.

For this tutorial, we’ll use the C# SDK. It has every method from the Facebook API already integrated, so it will make our task a lot easier!


Step 6: Downloading the SDK

This SDK is only available through NuGet, so in case your Visual Studio doesn’t include the NugGet Package manager,
you will need to download it from the NuGet hompage.
In order to download the package, open the Package Manager Console on Visual Studio (Tools>Library Package Manager>Package Manager Console), and enter the following command:Install-Package Facebook . In case you are having troubles with the downloaded version, then try using this command: Install-Package Facebook -version 6.0.24


Step 7: Add the FB SDK to Your App

Now that we have the SDK, we will add it to our project. Add a new import on the “MainPage.xaml.cs” file:

    using Facebook;

Step 8: Adding a Browser

In order for a user to connect to Facebook, he has to first give us access and permission to his FB account. This is done through the Facebook webpage, and therefore we need to add a web browser into our application. The browser should cover most of the page, so initially it will be collapsed, and then it will change to be visible just when the user needs to login. On the “MainPage.xaml” file, add a new WebBrowser just below the ContentPanel:

   <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Grid.RowDefinitions>
    <RowDefinition Height="auto"/>
    <RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Height="150" Name="Message"/>
<Button Grid.Row="1" Content="Post"/>
</Grid>
<phone:WebBrowser Name="Browser" Grid.Row="1"  Background="Aqua" Width="450" Height="600" Visibility="Collapsed"/>

Step 9: Connecting with Facebook

With everything correctly set, we can now start to code our application. Create a new FacebookClient variable, and name it just client. This is where all the connections will be made. Also, initiate the variable inside of the constructor:

    private FacebookClient client;
    // Constructor
    public MainPage()
    {
        InitializeComponent();
        client = new FacebookClient();
    }

Step 10: Adding the Click Event

In order to actually post something, the user has to click on the “Post” button. Let’s go and add a click event to that button:

<Button Grid.Row="1" Content="Post" Click="PostClicked"/>

On the code side, when the user clicks the button, he has to login with Facebook and authorize accept our App. For this process, we need to make the browser visible and navigate to a url that the client will give us, but before that we need to send some initial parameters:

private void PostClicked(object sender, RoutedEventArgs e)
{
//Client Parameters
var parameters = new Dictionary<string, object>();
parameters["client_id"] = FBApi;
parameters["redirect_uri"] = "https://www.facebook.com/connect/login_success.html";
parameters["response_type"] = "token";
parameters["display"] = "touch";
//The scope is what give us the access to the users data, in this case
//we just want to publish on his wall
parameters["scope"] = "publish_stream";
Browser.Visibility = System.Windows.Visibility.Visible;
Browser.Navigate(client.GetLoginUrl(parameters));
}

If you run your code right now and click on the Post button, the browser should appear, showing the Facebook login page:

Tutorial-07

Step 11: Adding a Navigation Event

After the user has logged in on Facebook, the browser will be navigated to a URL which will contain our access token for API calls. Once we retrieve it, we just have to assign it to our client. One thing to take into consideration is that there are many pages to which the browser could navigate (wrong password, user rejected our app, etc.), so we need to try to get the token just when we are sure that we are on the correct page.

Add the navigated event to the web browser:

   <phone:WebBrowser Name="Browser" Grid.Row="1"  Background="Aqua" Width="450" Height="600" Visibility="Collapsed" Navigated="BrowserNavitaged"/>

Then add the following lines to the event handler:

private void BrowserNavitaged(object sender, System.Windows.Navigation.NavigationEventArgs e)
{
FacebookOAuthResult oauthResult;
//Making sure that the url actually has the access token
if (!client.TryParseOAuthCallbackUrl(e.Uri, out oauthResult))
{
    return;
}
//Checking that the user successfully accepted our app, otherwise just show the error
if (oauthResult.IsSuccess)
{
    //Process result
    client.AccessToken = oauthResult.AccessToken;
    //Hide the browser
    Browser.Visibility = System.Windows.Visibility.Collapsed;
    PostToWall();
}
else
{
    //Process Error
    MessageBox.Show(oauthResult.ErrorDescription);
    Browser.Visibility = System.Windows.Visibility.Collapsed;
}
}

Step 12: Adding a Post Method

Now that we have access, we can go on and actually post to the user’s wall. Create a new private void method called postToWall, and add the following lines:

private void PostToWall()
{
var parameters = new Dictionary<string, object>();
parameters["message"] = Message.Text;
client.PostAsync("me/feed", parameters);
}

The only parameter that we need to send to this call is the message that we are going to post on the user’s wall. The message that we will send will be the text from our TextBox called “Message”. The message will be posted asynchronously, so the PostCompleted event will be called once the task is finished, therefore we need no add an event handler for it.


Step 13: PostCompleted Event Handler

Since we just want to add the event handler once, we will add it on the constructor, just after our client is initialized. Inside the handler, check if the post was successfully completed or if there were errors during the operations, then notify the user:

// Constructor
public MainPage()
{
InitializeComponent();
client = new FacebookClient();
client.PostCompleted += (o, args) =>
{
    //Checking for errors
    if (args.Error != null)
    {
        Dispatcher.BeginInvoke(() => MessageBox.Show(args.Error.Message));
    }
    else
    {
        Dispatcher.BeginInvoke(() =>MessageBox.Show("Message posted successfully"));
    }
};
}

Step 14: Testing the Code

With this code, our App should already be able to post a message through the user’s Facebook account.

Run the App in the emulator, try to post any test message that you want, and at the end you should receive a message telling you: “Message successfully posted”.

Tutorial-06

Now open the Facebook account on a web browser, and you should see the message that you just posted:

Tutorial-05

Congratulations! You now have an app that is able to connect to Facebook, but there’s still some things that we could improve. For example, we could try saving the access token, so users don’t have to login every time they open the app.


Step 15: Saving the Acces Token

We are going to save the token for the application settings, but in order to do this, we must first import the IsolatedStorage library:

   using System.IO.IsolatedStorage;

With this library we can now just go on and create the method:

private void SaveToken(String token)
{
//If it is the first save, create the key on ApplicationSettings and save the token, otherwise just modify the key
if (!IsolatedStorageSettings.ApplicationSettings.Contains("token"))
    IsolatedStorageSettings.ApplicationSettings.Add("token", token);
else
    IsolatedStorageSettings.ApplicationSettings["token"] = token;
IsolatedStorageSettings.ApplicationSettings.Save();
}

Step 16: Retrieving with the Saved Token

Now we need to get the token from IsolatedStorage:

private string GetToken()
{
//If there's no Token on memory, just return null, otherwise return the token as string
if (!IsolatedStorageSettings.ApplicationSettings.Contains("token"))
    return null;
else
    return IsolatedStorageSettings.ApplicationSettings["token"] as string;
}

Step 17: Logging with the Saved Token

With these two methods, we can now retrieve the token and assign it to our client each time the app is opened:

// Constructor
public MainPage()
{
InitializeComponent();
client = new FacebookClient();
client.PostCompleted += (o, args) =>
{
    //Checking for errors
    if (args.Error != null)
    {
        Dispatcher.BeginInvoke(() => MessageBox.Show(args.Error.Message));
    }
    else
    {
        Dispatcher.BeginInvoke(() =>MessageBox.Show("Message posted successfully"));
    }
};
//Checking for saved token
if (GetToken() != null)
    client.AccessToken = GetToken();
}

Step 18: Checking Expired Tokens

Another thing to take into account is that the user may reject the permissions of our App, so we need to detect this and ask for permissions again. This detection should be done on our PostCompleted handler, since that’s where Facebook will notify us of a problem with our post. Add the following lines to our PostCompleted handler:

client.PostCompleted += (o, args) =>
{
    //Checking for errors
    if (args.Error != null)
    {
        //Authorization error
        if (args.Error is FacebookOAuthException)
        {
            Dispatcher.BeginInvoke(() => MessageBox.Show("Authorization Error"));
            //Remove the actual token since it doesn't work anymore.
            SaveToken(null);
            client.AccessToken = null;
        }
        else
        {
            Dispatcher.BeginInvoke(() => MessageBox.Show(args.Error.Message));
        }
    }
    else
    {
        Dispatcher.BeginInvoke(() =>MessageBox.Show("Message posted successfully"));
    }
};

Step 19: Modify the Back Button

Just as a last step, we must give the user the option to close the browser whenever desired.
This action should be given to the back button, so we just need to modify the event handler to achieve it.

Add the following method to your code:

protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
{
//If browser is visible, hide it and cancel the navigation event
if (Browser.Visibility == System.Windows.Visibility.Visible)
{
    Browser.Visibility = System.Windows.Visibility.Collapsed;
    e.Cancel = true;
}
base.OnBackKeyPress(e);
}

Step 20: The Final Product

Test your app once again, now you have a fully operational Facebook app!


Where to Go From Here

Facebook isn’t just about updating your status. There’s many other thing that you could add to your App, like sharing Photos, sending app recommendations to friends, etc. The Facebook C# SDK offers many possibilities for Facebook integration. To learn more about it, go and check out their web page and start working on making your app more social!

Android SDK Quick Tip: Creating Frame Animations

$
0
0

In this Quick Tip article, we will run through the necessary steps required to implement a frame animation in an Android application.

Of the three types of animation you can implement in Android apps, frame animations, also known as drawable animations, are by far the simplest to code. Property and tween animations both require more code and setup, although with frame animations you do need to have a series of images prepared for the frames you want to display. Like tween animations, frame animation falls under the category of View animation, with the result displayed within a View in the app’s UI.

Unlike property and tween animations, with frame animations you do not need to prepare an animation resource, just a drawable resource in which the animation frames are listed together with duration and other details.


Prepare the Images

Frame animations display sequences of images one after another. If you already have a sequence of images comprising your animation, copy them into your application’s drawables folders, remembering to include them in the folder for each screen size you plan on supporting. You can alternatively create your own drawables in XML, such as shape drawables, to use within an animation. If so, create one shape drawable for each frame you want to appear within the animation, again making sure you have them all copied into each required drawable folder in your app’s resources directory. When giving your images filenames, consider including a numeric indicator of the order as in the code excerpts below.

In the source download folder, the animation uses ten frames of a rotating gyroscope sequence as JPEG images:

Gyroscope Animation

The result is very short, but is purely for demonstration.


Include an Image View in Your Layout

Open the layout file for your Activity and enter an Image View to hold the animation drawable, as in the following excerpt from the source download:

<ImageView
	android:id="@+id/gyro"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:layout_centerInParent="true"
	android:contentDescription="@string/gyro"
	/>

Include an ID attribute so that you can set the animation to appear within the View in your Activity Java code. Choose ID and content description attributes as appropriate for your own animation. We don’t bother setting a source attribute in the layout XML as we will do this when instantiating the animation from the Activity code.


Create a Drawable Animation List

In your app’s drawables resource folder(s), create a new XML file to represent your frame animation. In the download the file is named “gyro_animation.xml”. Inside the new file, first enter an Animation List element as follows:

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
	android:oneshot="false" >
</animation-list>

In the oneshot attribute, enter a value of “true” if you only want the animation to iterate once, or “false” if you want it to repeat. Inside the Animation List element add an item element for each frame image you want to appear:

<item
	android:drawable="@drawable/gyroscope1"
	android:duration="100"/>
<item
	android:drawable="@drawable/gyroscope2"
	android:duration="100"/>
<item
	android:drawable="@drawable/gyroscope3"
	android:duration="100"/>
<item
	android:drawable="@drawable/gyroscope4"
	android:duration="100"/>
<item
	android:drawable="@drawable/gyroscope5"
	android:duration="100"/>
<item
	android:drawable="@drawable/gyroscope6"
	android:duration="100"/>
<item
	android:drawable="@drawable/gyroscope7"
	android:duration="100"/>
<item
	android:drawable="@drawable/gyroscope8"
	android:duration="100"/>
<item
	android:drawable="@drawable/gyroscope9"
	android:duration="100"/>
<item
	android:drawable="@drawable/gyroscope10"
	android:duration="100"/>

The duration values are integers indicating how long each item should be displayed. Alter the duration attributes if you want each frame to appear for a different period of time. The items will appear in the order listed, so you can see why it helps if your image or drawable files have a numeric suffix indicating where each one sits in the running order. The image files in the download are named “gyroscope1.jpg”, “gyroscope2.jpg”, and so on.


Apply and Start the Animation

In your Activity class file, you can now apply and start the animation. Add the following import statements:

import android.graphics.drawable.AnimationDrawable;
import android.widget.ImageView;

Inside the onCreate method, get a reference to the Image View you included in your layout:

ImageView gyroView = (ImageView) findViewById(R.id.gyro);

Set the animation drawable you created as the background resource for the View:

gyroView.setBackgroundResource(R.drawable.gyro_animation);

Create an Animation Drawable object based on this background:

AnimationDrawable gyroAnimation = (AnimationDrawable) gyroView.getBackground();

Start the animation:

gyroAnimation.start();

The animation will start as soon as the Activity starts. If you want to start your animation on user interaction, simply place the start call inside an event listener method. Doing so would allow you to run the animation when the user touches a particular View item.


Conclusion

That’s all you need to include a frame animation in an Android app. This type of animation is of course only suited to cases where you either have the frame images already or are planning to implement them as drawables. For more detailed control over animations, look at either property or tween animation instead, both of which are a little more complex but achievable even for beginners. It goes without saying that in many cases an animation will not be intended as a standalone component, as in the source download example, but to enhance user interaction with UI items such as buttons.

iOS SDK: Previewing and Opening Documents

$
0
0

Sandboxing on iOS makes the platform much more secure and this ultimately benefits every user of the platform. However, because of the strict rules inherent to sandboxing, sharing data between applications is not trivial. An often overlooked class that helps with sharing documents between applications is the UIDocumentInteractionController class. In this quick tip, I will show you how you can use this class to preview documents as well as opening documents in other applications installed on the device.


Interacting with Documents

I must admit that I was a little surprised to discover that UIDocumentInteractionController has been with us since iOS 3.2. The UIDocumentInteractionController class is flexible in its use and more powerful than most of us realize. Not only does it allow developers to open documents in other applications installed on the device, it also supports previewing, printing, emailing, and copying of documents.

Using the UIDocumentInteractionController class is straightforward. The first step is to initialize an instance of the class by invoking its sole class method, interactionControllerWithURL:, and passing the URL (NSURL) of the document that you intend to work with. After setting the document interaction controller’s delegate property, you implement the appropriate delegate methods.

Keep in mind that the UIDocumentInteractionController is not a subclass of UIViewController. In other words, even though previewing a document is mediated by the UIDocumentInteractionController, you have to tell the document interaction controller which view controller to use for previewing the document. What this exactly means will become clear when we build the sample application. In the sample application, I will show you how to (1) preview a document and (2) open a document in another application that supports the document’s file type.


Step 1: Setting Up the Project

As I mentioned a moment ago, our sample application will allow users to preview a document and open it in another application installed on the device. The latter is often very useful if you want to give your users more flexibility in terms of what they can do with the data stored in your application. A common example is opening a photo in a photo editing application, such as iPhoto.

Create a new project in Xcode by selecting the Single View Application template from the list of templates (figure 1). Name your application Documents, enter a company identifier, set iPhone for the device family, and check Use Automatic Reference Counting. The rest of the checkboxes can be left unchecked for this project (figure 2). Tell Xcode where you want to save the project and hit the Create button.

Previewing and Opening Documents with UIDocumentInteractionController: Choosing a Project Template - Figure 1
Figure 1
Previewing and Opening Documents with UIDocumentInteractionController: Configuring the New Project - Figure 2
Figure 2

Step 2: Creating the User Interface

The user interface of our application will contain two buttons, one for previewing a PDF document and one for opening a PDF document in another application. Before we create the user interface, create an action for each button in the view controller’s implementation file as shown below.

- (IBAction)previewDocument:(id)sender {
}
- (IBAction)openDocument:(id)sender {
}

Select MTViewController.xib and drag two UIButton instances from the Object Library on the right in the view controller’s view (figure 3). Select the File’s Owner object on the left, open the Connections Inspector, and connect the actions that we created a moment ago with the buttons (figure 4). That is all we need to do in Interface Builder.

Previewing and Opening Documents with UIDocumentInteractionController: Creating the User Interface - Figure 3
Figure 3
Previewing and Opening Documents with UIDocumentInteractionController: Connecting the Actions with the Buttons - Figure 4
Figure 4

Step 3: Previewing a Document

The document that we will be working with is a PDF document. You can use any PDF document, but I have included a sample PDF document with the source files of this quick tip. It is Apple’s iOS Programming Guide, which you can also find online. Drag the document into your project and make sure to check the checkbox labeled Copy items into destination group’s folder (if needed) when you are prompted (figure 5). Also make sure that the document is added to the Documents target (figure 5).

Previewing and Opening Documents with UIDocumentInteractionController: Adding the Sample PDF Document to the Project - Figure 5
Figure 5

When using the UIDocumentInteractionController class, it is important to keep two things in mind, (1) you need to hold a reference to the document interaction controller and (2) the UIDocumentInteractionControllerDelegate protocol needs to be implemented. Start by updating the view controller’s header file as shown below to inform the compiler that the MTViewController class conforms to the UIDocumentInteractionControllerDelegate protocol.

#import <UIKit/UIKit.h>
@interface MTViewController : UIViewController <UIDocumentInteractionControllerDelegate>
@end

In the view controller’s implementation file, add a private property of type UIDocumentInteractionController and name it documentInteractionController. This property will store the reference to the document interaction controller that we will be using.

Let’s now take a look at the implementation of the previewDocument: action. We start by obtaining the URL (NSURL) of the document. Because the document is part of the application bundle, obtaining the document’s URL is very easy thanks to a convenience method of the NSBundle class (see below).

- (IBAction)previewDocument:(id)sender {
    NSURL *URL = [[NSBundle mainBundle] URLForResource:@"sample" withExtension:@"pdf"];
    if (URL) {
        // Initialize Document Interaction Controller
        self.documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:URL];
        // Configure Document Interaction Controller
        [self.documentInteractionController setDelegate:self];
        // Preview PDF
        [self.documentInteractionController presentPreviewAnimated:YES];
    }
}

If a valid URL is returned to us, we initialize an instance of the UIDocumentInteractionController class and pass the URL of the document. We store a reference to the document interaction controller in the documentInteractionController property we created a minute ago. Our view controller will serve as the delegate of the document interaction controller. Presenting a preview of the PDF document is as easy as calling presentPreviewAnimated: on the document interaction controller.

If you were to build and run the application now, you would notice that nothing happens when the button labeled Preview is tapped. There is one delegate method that we need to implement first. A few minutes ago, I told you that it is important to realize that the UIDocumentInteractionController class is a subclass of NSObject, not UIViewController. Even though it will take care of displaying the document, we need to tell the document interaction controller which view controller to use for previewing the document. One of the delegate methods of the UIDocumentInteractionControllerDelegate protocol asks the delegate for a view controller that it can use to preview the document. That delegate method is appropriately named documentInteractionControllerViewControllerForPreview:. Because we want to display the preview in our main view controller, we can simple return self as shown in the implementation below. What this means is that the document interaction controller will use our view controller to preview the PDF document. It will present a modal view in which it presents the document.

- (UIViewController *) documentInteractionControllerViewControllerForPreview: (UIDocumentInteractionController *) controller {
    return self;
}

Of course, you can modify the implementation of documentInteractionControllerViewControllerForPreview: to fit your needs. With the delegate method implemented, it is time to build and run our application for the first time and try it out (figure 6). Notice that you can even share the sample document via email, print it, or copy it to the clipboard. In addition, it is possible to open the document in any other application that supports the document’s file type. Tap the button in the top right to see what I mean (figure 7).

Previewing and Opening Documents with UIDocumentInteractionController: Previewing a Document - Figure 6
Figure 6
Previewing and Opening Documents with UIDocumentInteractionController: Sharing a Document - Figure 7
Figure 7

Step 4: Opening a Document

In many situations, however, it is more appropriate to allow users to open a document in another application without first showing a preview of the document. To make this possible in our sample application, we need to implement the openDocument: action. As in the previewDocument: action, we obtain the URL of the sample PDF document in the application bundle and use it to initialize an instance of the UIDocumentInteractionController class. After setting the delegate of the document interaction controller, we present a menu by calling presentOpenInMenuFromRect:inView: on the document interaction controller. The CGRect that we pass as the first argument is the frame of the button as you can see in the code snippet below.

- (IBAction)openDocument:(id)sender {
    UIButton *button = (UIButton *)sender;
    NSURL *URL = [[NSBundle mainBundle] URLForResource:@"sample" withExtension:@"pdf"];
    if (URL) {
        // Initialize Document Interaction Controller
        self.documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:URL];
        // Configure Document Interaction Controller
        [self.documentInteractionController setDelegate:self];
        // Present Open In Menu
        [self.documentInteractionController presentOpenInMenuFromRect:[button frame] inView:self.view animated:YES];
    }
}

To test the openDocument: action, it is important that you run the sample application on a physical device. The reason is simple. The operating system checks which applications on the device support the file type (UTI) that we want to open. If it cannot find any applications that support the corresponding file type, then it will not show an Open In menu and that is what will happen in the iOS Simulator.

To test this feature out, make sure that you have an application installed on your physical device that accepts PDF documents, such as Dropbox or Amazon’s Kindle application.

Previewing and Opening Documents with UIDocumentInteractionController: Opening a Document - Figure 8
Figure 8

Conclusion

As you can see, it is very easy to preview and open documents using the UIDocumentInteractionController class. I recommend that you explore its class reference as well as the UIDocumentInteractionControllerDelegate protocol. There are many more delegate methods that might come in handy especially when working with larger documents or complex workflows.


BlackBerry: Working with XML

$
0
0

XML is one of the most commonly used data-interchange formats on the web. With the proliferation of smartphones, it has become a common requirement to parse and display XML documents on mobile devices. In this article, I’ll teach you how to parse an XML document on BlackBerry and also display an XML document on BlackBerry.


Installing the BlackBerry Eclipse Plugin

First, we need to install the BlackBerry Java Plug-in for Eclipse, which may be installed with one of the following two methods.

  1. From the BlackBerry Java Plugin Update site
  2. From the plugin .exe file BlackBerry_JDE_PluginFull_1.5.0_helios.exe.

We shall discuss each of these methods in this section. To install from the update site, select Help>Install New Software in Eclipse Java IDE. In the Install window, click on Add underneath Available Software. In the Add Repository window, specify the Name as BlackBerry Java Plug-in Update Site and the Location as http://www.blackberry.com/go/eclipseUpdate/3.6/java as shown in the following illustration.



Specifying BlackBerry Plugin Update Site Location

The update website gets added to Available Software and the available software for the website gets listed including the different versions. Select BlackBerry Java Plug-in Category Version 7.0.0.28. Click on Next.



Selecting Software to Install

In the Install Details section, the BlackBerry Java SD 7.0.0.28 gets listed. Click on Next.



BlackBerry Plugin Install Details

Accept the license agreement and click on Finish. The Installing Software dialog gets displayed. After the software gets installed a dialog to indicate the Restart requirement gets displayed. Click on Restart Now. The BlackBerry Java Plug-in for Eclipse gets installed.

In the second method, click on the BlackBerry_JDE_PluginFull_1.5.0_helios.exe or an earlier version exe file. The BlackBerry Java Plug-in for Eclipse wizard gets started. Click on Next in Introduction.



The BlackBerry Java Plug-in for Eclipse wizard

Accept the license terms and click on Next. In Choose Installation Folder specify the directory in which to install the plugin, including the Eclipse IDE. Click on Next.



Selecting Installation Folder

In Select Additional Tasks select the default shortcut tasks and click on Next. In Pre-Installation Summary click on Install.



Installing BlackBerry Plugin

The BlackBerry Java Plug-in for Eclipse gets installed. Click on Done. Next, we need to open the BlackBerry perspective. Select Window>Open Perspective>Other. In Open Perspective, select BlackBerry Application Development and click on OK.


Opening the BlackBerry Application Development Perspective

Creating a BlackBerry Project

In this section we shall create a BlackBerry project in Eclipse using the BlackBerry Application Development perspective features. Select File>New. In New window select BlackBerry>BlackBerry Project and click on Next.



Creating a BlackBerry Project

In the New BlackBerry Project window, specify Project Name (ParsingXML for example). Select JRE as BlackBerry JRE 7.0.0. Click on Next.



Specifying BlackBerry Project Name

In Java Settings select the default build settings, which include src and res folders for project source code and the bin folder for the output. Click on Next.



Specifying BlackBerry Project Java Settings

In the Templates section, select the BlackBerry Application template and click on Next.



Selecting Project Template

In the UI Application window, specify the Application Details. Specify a Package Name (blackberry.xml), Application Class Name (ParsingXML), Screen Class Name (Parsing_XML), and a Screen Title. Click on Finish.



Specifying Application Details

A BlackBerry Project gets created. The BlackBerry project consists of the following
artifacts:

  1. A class (ParsingXML) that extends the UiApplication class.
  2. A class (Parsing_XML) that extends the MainScreen class.
  3. A BlackBerry application descriptor file BlackBerry_App_Descriptor.xml.

The directory structure of the BlackBerry project is shown below.



BlackBerry Project Structure

Configuring the BlackBerry Descriptor

The properties of a BlackBerry project are specified in the BlackBerry_App_Descriptor.xml file, shown below in GUI mode.



BlackBerry_App_Descriptor.xml file

The General Information includes the following property fields/checkboxes.

FieldDescriptionValue
TitleThe application titleParsing XML
VersionThe application version1.0.0
VendorThe vendor nameBlackBerry Developer
Application Type“BlackBerry Application” for a BlackBerry applicationBlackBerry Application
BlackBerry ApplicationRuntime arguments to the application
Auto-run on StartupSelect checkbox to auto-run application on startuptrue
Startup TierThe startup priority7 (default) is the lowest priority
Do not display the application iconSelect checkbox to not display application icon

The Application Descriptor may also include Locale Resources and Application Icons. Alternate entry points may be used to run an application in background or foreground. The BlackBerry_App_Descriptor.xml is listed below.

<!-- This file has been generated by the BlackBerry Plugin for Eclipse v3.6.0. -->
<Properties ModelVersion="1.1.2">
  <General Title="Parsing XML" Version="1.0.0" Vendor="BlackBerry Developer" Description=""/>
  <Application Type="BlackBerry Application" MainMIDletName="" MainArgs="" HomeScreenPosition="0" StartupTier="7" IsSystemModule="false" IsAutostartup="true"/>
  <Resources hasTitleResource="false" TitleResourceBundleName="" TitleResourceBundleRelativePath="" TitleResourceBundleClassName="" TitleResourceBundleKey="" DescriptionId="">
    <Icons>
      <Icon CanonicalFileName="res\img\icon.png" IsFocus="false"/>
    </Icons>
  </Resources>
  <KeywordResources KeywordResourceBundleName="" KeywordResourceBundleRelativePath="" KeywordResourceBundleClassName="" KeywordResourceBundleKey=""/>
  <Compile OutputCompilerMessages="false" ConvertImages="true" CreateWarningForNoExportedRoutine="true" CompressResources="false" AliasList="">
    <PreprocessorDefines/>
  </Compile>
  <Packaging PreBuildStep="" PostBuildStep="" CleanStep="" OutputFileName="ParsingXML" OutputFolder="deliverables" GenerateALXFile="true">
    <AlxFiles/>
  </Packaging>
  <HiddenProperties>
    <ClassProtection/>
    <PackageProtection/></HiddenProperties>
  <AlternateEntryPoints/>
</Properties>

Creating the Screen class

In this section we shall create a screen for the BlackBerry application. At the minimum a screen should have a title. A screen is a MainScreen class and provides features common to RIM device applications. Create a class that extends the MainScreen class and in the class constructor set the screen title using the setTitle method. The screen class Parsing_XML is listed below.

package blackberry.xml;
import net.rim.device.api.ui.container.MainScreen;
/**
 * A class extending the MainScreen class, which provides default standard
 * behavior for BlackBerry GUI applications.
 */
public final class Parsing_XML extends MainScreen
{
    /**
     * Creates a new Parsing_XML object
     */
    public Parsing_XML()
    {
        // Set the displayed title of the screen
        setTitle("Parsing XML");
    }
}

Parsing an XML Document

In the ParsingXML class we shall parse an XML document. The catalog.xml document has a catalog for a journal.

<?xml version = '1.0' encoding = 'UTF-8'?>
<catalog journal="Oracle Magazine" publisher="Oracle Publishing">
    <article>
        <edition>Sept-Oct 2005</edition>
        <title>Creating Search Pages</title>
        <author>Steve Muench</author>
    </article>
    <article>
        <edition>November - December 2010</edition>
        <title>Agile Enterprise Architecture</title>
        <author>Bob Rhubart</author>
    </article>
</catalog>

Copy the XML document to the res folder in the src folder, or directly in the src folder. Next, we shall develop the ParsingXML class, which extends the UiApplication class, the base class for all device applications that provide a user interface. Create an instance variable for the MainScreen. Create a String variable for element values. We shall use a Thread to parse the XML document. Create an instance variable for the thread class.

Parsing_XML screen = new Parsing_XML();
String element;
Connection connection;

In the class constructor set the screen title, and add the MainScreen screen onto the UI stack for rendering. Create a Thread instance and start the thread with the start() method.

screen.setTitle("Parsing XML");
pushScreen(screen);
connection = new Connection();
connection.start();

In the main method, create an instance of the ParsingXML class and invoke the event dispatcher thread using the enterEventDispatcher method.

All BlackBerry framework UI applications contain one event dispatcher thread. After a screen has been pushed onto the display stack all modifications must be made either on the event dispatcher thread or a background thread. We shall use a background thread Connection to parse the XML document catalog.xml.

ParsingXML theApp = new ParsingXML();
theApp.enterEventDispatcher();

Create a DocumentBuilderFactory factory instance using static method newInstance() to create a DOM parser from and subsequently create a DOM tree from the XML document.

DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();

Create a DocumentBuilder parser instance using the newDocumentBuilder method to subsequently parse the XML document and create a DOM document object.

DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();

Set the parser to be validating using the isValidating() method.

docBuilder.isValidating();

Create an InputStream object from the catalog.xml document. Include the ‘/’ in the catalog.xml path.

InputStream inputStream = getClass().getResourceAsStream("/catalog.xml");

Alternatively, the InputStream may be created using a String literal for the XML, and a ByteArrayInputStream.

/**   str = "<?xml version = '1.0' encoding = 'UTF-8'?>" +
                  "<catalog journal='Oracle Magazine' publisher='Oracle Publishing'>"
                  + "<article>" + "<edition>Sept-Oct 2005</edition>"+
                  "<title>Creating Search Pages</title>"+
                  "<author>Steve Muench</author>" + "</article>" + "<article>"
                  + "<edition>November - December 2010</edition>" +
                  "<title>Agile Enterprise Architecture</title>"+
                  "<author>Bob Rhubart</author>"+ "</article>"+ "</catalog>";
                // convert String into InputStream
InputStream inputStream =new ByteArrayInputStream(str.getBytes());**

Next, obtain a DOM Document object from the InputStream using the parse(InputStream) method.

Document document = docBuilder.parse(inputStream);

Get the Document element, which is the child node of the XML document, also the root element, using the getDocumentElement() method. Normalize the XML document using the normalize() method, which removes adjacent and empty Text nodes.

document.getDocumentElement().normalize();

We shall output the <title> element values to the screen. Create a NodeList of the title elements using the getElementsByTagName() method.

NodeList list = document.getElementsByTagName("title");

The first child node of an element is the text node in the element. The text value in a Text node is obtained using the getNodeValue() method. Iterate over the NodeList and obtain the text node values for the title elements. To display the title values on the screen first, we need to obtain the event lock using the getEventLock() method.

synchronized (UiApplication.getEventLock()) {
}

The event lock is required for performance and concurrency issues. Output the title values to the screen using the add() method.

screen.add(new RichTextField("Title : " + element));
screen.add(new SeparatorField());

If using the String literal for the XML document output the String to the screen.

screen.add(new RichTextField(str));

In the catch block output exception message if any.

screen.add(new RichTextField("Error : " + e.toString()));

The ParsingXML.java class is listed below.

package blackberry.xml;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import javax.microedition.io.*;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.system.*;
import net.rim.device.api.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.*;
public class ParsingXML extends UiApplication {
    // creating a member variable for the MainScreen
    Parsing_XML screen = new Parsing_XML();
    // string variable to store the value of the XML document element/s
    String element;
    Connection connection;
    public static void main(String arg[]) {
        ParsingXML theApp = new ParsingXML();
        theApp.enterEventDispatcher();
    }
    public ParsingXML() {
        screen.setTitle("Parsing XML");// setting title
        screen.add(new RichTextField("Requesting Catalog titles....."));
        screen.add(new SeparatorField());
        // Push a screen onto the UI stack for rendering.
        pushScreen(screen);
        connection = new Connection();
        connection.start();//
    }
    private class Connection extends Thread {
        public Connection() {
            super();
        }
        public void run() {
            StreamConnection conn;
            String str=null;
            try {
                DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory
                        .newInstance();
                DocumentBuilder docBuilder = docBuilderFactory
                        .newDocumentBuilder();
                docBuilder.isValidating();
                InputStream inputStream = getClass().getResourceAsStream("/catalog.xml");
                /**   str = "<?xml version = '1.0' encoding = 'UTF-8'?>" +
                  "<catalog journal='Oracle Magazine' publisher='Oracle Publishing'>"
                  + "<article>" + "<edition>Sept-Oct 2005</edition>"+
                  "<title>Creating Search Pages</title>"+
                  "<author>Steve Muench</author>" + "</article>" + "<article>"
                  + "<edition>November - December 2010</edition>" +
                  "<title>Agile Enterprise Architecture</title>"+
                  "<author>Bob Rhubart</author>"+ "</article>"+ "</catalog>";
                // convert String into InputStream
                  InputStream inputStream =new ByteArrayInputStream(str.getBytes());**/
                Document document = docBuilder.parse(inputStream);
                document.getDocumentElement().normalize();
                NodeList list = document.getElementsByTagName("title");
                element = new String();
                // this "for" loop is used to parse through the
                // XML document and extract all elements and their
                // value, so they can be displayed on the device
                for (int i = 0; i < list.getLength(); i++) {
                    Node value = list.item(i).getChildNodes().item(0);
                    element = value.getNodeValue();
                    synchronized (UiApplication.getEventLock()) {
                        screen.add(new RichTextField("Title : " + element));
                        screen.add(new SeparatorField());
                    }
                }// end for
                                          //      screen.add(new RichTextField(str));
            }// end try
                // will catch any exception thrown by the XML parser
            catch (Exception e) {
                screen.add(new RichTextField("Error : " + e.toString()));
            }
        }// end connection function
    }// end connection class
}

Running the BlackBerry Project

Next, we shall run the ParsingXML application in Eclipse IDE. Right-click on ParsingXML and select Run As>BlackBerry Simulator.



Running BlackBerry Application

The BlackBerry Simulator gets started. The application gets installed on the BlackBerry Simulator.



Installed BlackBerry Application

Click on the application to run the application.



Running BlackBerry Application

The titles from the catalog.xml document get displayed.



Parsing XML Document

The directory structure of the ParsingXML application is shown below.



Directory Structure of ParsingXML Application

If the XML document is created from a String literal, the XML may be output to the screen as well.



Displaying XML Document



Summary

In this article we learned about parsing XML on a BlackBerry using the BlackBerry JDE. We used the BlackBerry Java Plug-in for Eclipse to add the BlackBerry Application Development perspective to Eclipse IDE. We used the MainScreen class to create a screen for BlackBerry device. We used the UiApplication class to create an event dispatcher thread, which a BlackBerry framework application is required to have. We created a background thread to parse an XML document and obtained an event lock to display the parsed catalog titles on the BlackBerry screen. We also output the XML document to the screen.

Android SDK: Creating a Simple Property Animation

$
0
0

With Android you can include various types of animation in your apps. In this tutorial we will create a basic property animation using Android’s Object Animator and Value Animator classes. The result will be simple but the techniques involved will apply in more complex animated effects. We will create an animation in which a steering wheel turns and the background scene moves accordingly.

With property animation, you have a few more options than with View animation, for example you can animate UI items other than Views and can animate more properties. Property animation can also have more consistent results in some cases, as unlike View animation, it alters the UI objects themselves, rather than just drawing them in particular ways. The downside to these advantages is that property animation is a little more complex – but it’s still accessible for beginners.


Step 1: Start an Android Project

Start or open an Android project in Eclipse. You will need a blank Activity and a layout for it. You will also need to choose a minimum SDK of 11 in order to use the methods in this tutorial, so make sure your project Manifest indicates an appropriate level as in the following excerpt:

<uses-sdk
	android:minSdkVersion="11"
	android:targetSdkVersion="16"
/>

In the tutorial and source code, the app is named “PropertyAnimatedApp”, the Activity is “PropertyAnimatedActivity” and the layout is “activity_property_animated.xml”. You can choose your own names as long as you make changes to the below code where necessary.


Step 2: Add Images to the Project

We will be creating a few drawable files in XML for the animation, but will also be using a couple of PNG images. We will use the cloud and steering wheel images below within Image Views in the app layout. Download them from the copies displayed here or copy them from the source folder download. Make sure you copy them into each drawables folder in your app – you may wish to make adjustments to any of the drawable files we use if you plan on targeting particular screen sizes.

Cloud
Steering Wheel

Step 3: Create the Drawables

Now let’s define the XML drawables for the remaining animation elements. In your application drawables folder(s), create the first new file by selecting the folder and choosing “File”, “New”, “File”. Enter “sun.xml” as the file name. In the new file, enter the following code to define a sun shape:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
	android:dither="true"
	android:shape="oval" >
	<gradient
		android:endColor="#ffff6600"
		android:gradientRadius="150"
		android:startColor="#ffffcc00"
		android:type="radial"
		android:useLevel="false" />
	<size
		android:height="100dp"
		android:width="100dp" />
</shape>

The drawable is an oval shape, with a radial gradient fill and specified size. When you finish entering code for each of the drawables files, save them and copy them into each drawables folder your app is using.

Create another new file in your app’s drawables resources, this time naming it “ground.xml”. Enter the following shape drawable:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
	android:dither="true"
	android:shape="rectangle" >
	<solid android:color="#339933" />
</shape>

The ground will be represented by a green rectangle.

Create another drawables file, naming it “window.xml” and entering the following shape:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
	android:shape="rectangle" >
	<solid android:color="#00000000" />
	<stroke
		android:width="40dp"
		android:color="#cccccc" />
</shape>

This shape will represent a window frame around the edge of the screen, with a transparent fill so that only the stroke appears.

You can see each of the shapes as they will appear at the start of the animation below:

Animation Start

Step 4: Design the Layout

Now we can include our drawables in the layout. We will be using Image Views, with which we need to supply a content description String describing the image in each case. In preparation for this, open your app’s “res/values/strings.xml” file and add the following values:

<string name="wheel">Steering Wheel</string>
<string name="ground">The Ground</string>
<string name="window">Window Frame</string>
<string name="sun">The Sun</string>
<string name="cloud">A Cloud</string>

We need a String for each of the images/drawables we will be using.

Now open your layout file. Replace the contents with the following Relative Layout:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:tools="http://schemas.android.com/tools"
	android:id="@+id/car_layout"
	android:layout_width="match_parent"
	android:layout_height="match_parent"
	android:background="#66ccff"
	tools:context=".PropertyAnimatedActivity" >
</RelativeLayout>

Alter the context attribute if your Activity class has a different name. Notice that we apply a background color to the layout – we will be animating this later, which is why we also include an ID attribute for referring to the layout in Java.

First inside the layout, add an Image View to display the sun shape we created:

<ImageView
	android:id="@+id/sun"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:contentDescription="@string/sun"
	android:paddingLeft="100dp"
	android:paddingTop="45dp"
	android:src="@drawable/sun" />

We include an ID attribute so that we can refer to the View when animating it. We also refer to one of the content description Strings we created and list the name of the drawable file as source src attribute. We also position the View within the layout.

Next add two more Image Views for the clouds:

<ImageView
	android:id="@+id/cloud1"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:contentDescription="@string/cloud"
	android:paddingLeft="170dp"
	android:paddingTop="70dp"
	android:src="@drawable/cloud" />
<ImageView
	android:id="@+id/cloud2"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:contentDescription="@string/cloud"
	android:paddingLeft="200dp"
	android:paddingTop="90dp"
	android:src="@drawable/cloud" />

These two are identical apart from the the positioning and ID attributes.

Next add the ground:

<ImageView
	android:id="@+id/ground"
	android:layout_width="fill_parent"
	android:layout_height="200dp"
	android:layout_alignParentBottom="true"
	android:contentDescription="@string/ground"
	android:padding="40dp"
	android:src="@drawable/ground" />

The ground is aligned to the bottom of the parent View. Notice that we also include padding on the bottom – this is to accommodate the window frame we will add next:

<ImageView
	android:id="@+id/window"
	android:layout_width="fill_parent"
	android:layout_height="fill_parent"
	android:contentDescription="@string/window"
	android:src="@drawable/window" />

The window shape uses a 40dp wide stroke, which is why we added 40dp of padding on the ground shape.

Each Image View added will be displayed on top of previous items in terms of the z-index so we add the steering wheel last:

<ImageView
	android:id="@+id/wheel"
	android:layout_width="wrap_content"
	android:layout_height="wrap_content"
	android:layout_alignParentBottom="true"
	android:layout_centerHorizontal="true"
	android:contentDescription="@string/wheel"
	android:padding="3dp"
	android:src="@drawable/steering_wheel" />

The layout will initially appear as follows:

Layout

Step 5: Define the Wheel Turning Animation

Let’s get stuck into some animation now. In your app’s “res” folder, create a new sub-folder by selecting “res” and choosing “File”, “New”, “Folder”. Enter “animator” as the folder name. We will be adding two XML files to this folder, defining the wheel turning and sun moving animations. Start by creating a new file in “animator” and naming it “wheel_spin.xml”.

Begin by adding a set element:

<set xmlns:android="http://schemas.android.com/apk/res/android"
	android:interpolator="@android:anim/accelerate_decelerate_interpolator"
	android:ordering="sequentially" >
</set>

Inside the set we will define the details of the animation. In the opening set tag we specify an interpolator, in this case the accelerate decelarate interpolator so that the animation will speed up at the start and slow down at the end. We also specify ordering, which will not actually have an effect in this case as we are only going to define one animator within the set. If you have more than one, you can use this attribute to carry out the animations at the same time or sequentially.

Inside the set, include an animator to make the wheel turn:

<objectAnimator
	android:duration="3000"
        android:propertyName="rotation"
        android:repeatCount="infinite"
        android:repeatMode="reverse"
        android:valueTo="180"
        android:valueType="floatType" />

This is an Object Animator, which first defines a duration and property to animate over this period. The valueTo attribute indicates 180 degrees, which is how much the wheel will turn over the duration. When the animation completes, we set it to reverse and then repeat continuously.


Step 6: Apply the Wheel Turning Animation

Now let’s turn to the Activity class. First add the following import statements at the top:

import android.animation.AnimatorInflater;
import android.animation.AnimatorSet;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.graphics.Color;
import android.view.Menu;
import android.widget.ImageView;

Inside the onCreate method, after the existing code, first get a reference to the wheel shape using the ID we gave its Image View:

ImageView wheel = (ImageView)findViewById(R.id.wheel);

Now create an Animator Set to load the animation we defined:

AnimatorSet wheelSet = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.wheel_spin);

Now set the View item as target for the animation:

wheelSet.setTarget(wheel);

Start the animation:

wheelSet.start();

You can run the app if you wish at this stage to see the wheel turning back and forth. The remaining animations will add to the effect, creating the impression that we are moving according to the steering, with the world outside the window moving back and forth at the same time.


Step 7: Define the Sun Moving Animation

Let’s make the sun move to create the impression that we are moving as a result of the steering. Create a new file in your “animator” folder, this time named “sun_swing.xml”. Enter the following code:

<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:interpolator="@android:anim/accelerate_decelerate_interpolator"
    android:ordering="sequentially" >
    <objectAnimator
        android:duration="3000"
        android:propertyName="x"
        android:repeatCount="infinite"
        android:repeatMode="reverse"
        android:valueTo="-400"
        android:valueType="floatType" />
</set>

This time the Object Animator animates the x property, moving the object to the left, as the steering wheel turns us to the right. The duration and repeat properties are the same as the wheel turn animation, as this effect is intended to coincide with it.


Step 8: Apply the Sun Moving Animation

Back in the Activity class onCreate method, apply this new animation to the sun View using the same technique as before:

//get the sun view
ImageView sun = (ImageView)findViewById(R.id.sun);
//load the sun movement animation
AnimatorSet sunSet = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.animator.sun_swing);
//set the view as target
sunSet.setTarget(sun);
//start the animation
sunSet.start();

Run the app again if you wish to see the sun appearing to move out of view and back.


Step 9: Implement a Sky Darkening Animation

So far we have implemented a couple of animations by defining animation resources. Let’s now explore creating animations in Java from the Activity class. We will make the sky go slightly darker when the sun moves out of view. Still inside onCreate, instantiate a Value Animator:

ValueAnimator skyAnim = ObjectAnimator.ofInt
	(findViewById(R.id.car_layout), "backgroundColor",
	Color.rgb(0x66, 0xcc, 0xff), Color.rgb(0x00, 0x66, 0x99));

We create the Value Animator by calling the ofInt method of the Object Animator class, as we are dealing with integer values for the colors. We pass the ID of the layout element which has the background color applied to it, also specifying “backgroundColor” as the property we wish to animate. Finally, we specify colors to animate from and to, which are lighter and darker shades of blue.

Set the duration and repeat properties to match the existing animations:

skyAnim.setDuration(3000);
skyAnim.setRepeatCount(ValueAnimator.INFINITE);
skyAnim.setRepeatMode(ValueAnimator.REVERSE);

Now set an evaluator to instruct the animator how to evaluate the passed color values:

skyAnim.setEvaluator(new ArgbEvaluator());

Start the animation:

skyAnim.start();

You can run the app to see the effect.


Step 10: Implement Two Cloud Moving Animations

Let’s finish this animation off by also moving the two clouds. Still in onCreate, instantiate an Object Animator for the first cloud View, specifying the x property and a value to move it to:

ObjectAnimator cloudAnim = ObjectAnimator.ofFloat(findViewById(R.id.cloud1), "x", -350);

Set the duration and repeat properties as before, then start the animation:

cloudAnim.setDuration(3000);
cloudAnim.setRepeatCount(ValueAnimator.INFINITE);
cloudAnim.setRepeatMode(ValueAnimator.REVERSE);
cloudAnim.start();

Do the same for the second cloud, passing a slightly different value to move it along the x axis:

ObjectAnimator cloudAnim2 = ObjectAnimator.ofFloat(findViewById(R.id.cloud2), "x", -300);
cloudAnim2.setDuration(3000);
cloudAnim2.setRepeatCount(ValueAnimator.INFINITE);
cloudAnim2.setRepeatMode(ValueAnimator.REVERSE);
cloudAnim2.start();

The difference in x value with an animation over the same duration will create the impression that the clouds are moving at slightly different speeds, one being closer than the other.

Animation Finish

Conclusion

That’s the app complete – run it and experiment with the various properties involved to see the effects. In this tutorial we have created a simple property animation. In the app we have built, the animation begins at launch and runs continuously. In your own apps you may of course wish to run animations on user interaction with elements such as buttons. The other two main types of animation on Android are tween and frame, both of which are View animations. Each type is suited to different needs, depending on both the image resources you are working with and the animation details.

iOS 6 SDK: Displaying App Store Products In-App

$
0
0

Have you ever had the need to send a customer from your iOS application to the App Store? Maybe you wanted her to rate your application in the App Store or you just wanted to promote one of your other apps. Prior to iOS 6, the only viable option was to send the customer to the App Store application. In iOS 6, however, Apple introduced the SKStoreProductViewController class, which allows an application to show a product in the App Store without leaving the application. In this quick tip, I will show you how this works.


Store Kit

As its class prefix indicates, the SKStoreProductViewController class is a member of the Store Kit framework. Using SKStoreProductViewController is incredibly easy. Before we take a look at an example application, it is useful to understand how everything fits together.

The SKStoreProductViewController class is a subclass of UIViewController, which means that it is easy to use if you are familiar with view controllers. Whenever you want to show a customer a product in the App Store, you (1) instantiate an instance of the SKStoreProductViewController class, (2) set its delegate, and (3) present the store product view controller to the customer. The operating system takes care of the rest. Keep in mind that an instance of the SKStoreProductViewController class can only be presented modally.

The SKStoreProductViewControllerDelegate delegate protocol defines a single method, productViewControllerDidFinish:. This method is called when the customer leaves the App Store, usually by tapping the cancel button in the top left of the view. By sending the delegate the message of productViewControllerDidFinish:, the operating system gives control back to your application. Let me show you how to use the SKStoreProductViewController class by creating a sample application.


Step 1: Setting Up the Project

The application that we are about to build is not very functional as it only has one button, which takes the user to the App Store and shows the user Drizzle, a simple weather application that I released a few weeks ago. However, it shows you how the different pieces fit together and how to use the SKStoreProductViewController class in your projects.

Create a new project in Xcode by selecting the Single View Application template from the list of templates (figure 1). Name your application App Store, enter a company identifier, set iPhone for the device family, and check Use Automatic Reference Counting. The rest of the checkboxes can be left unchecked for this project (figure 2). Tell Xcode where you want to save the project and hit the Create button.

New in iOS 6: SKStoreProductViewController: Choosing a Project Template - Figure 1
New in iOS 6: SKStoreProductViewController: Configuring the New Project - Figure 2

Step 2: Adding the Store Kit Framework

Because the SKStoreProductViewController class is part of the Store Kit framework, we need to link our project against the Store Kit framework. Select the project in the Project Navigator and choose the target in the list of targets. At the top, choose the Build Phases tab and open the Link Binary With Libraries drawer. Click the button with the plus sign and choose StoreKit.framework form the list that appears (figure 3). You have now successfully linked your project against the Store Kit framework.

New in iOS 6: SKStoreProductViewController: Linking the Project Against the Store Kit Framework - Figure 3

To use the Store Kit framework in the MTViewController class, we need to import the framework’s header files. Open MTViewController.h and add the following import statement at the top.

#import <StoreKit/StoreKit.h>

Step 3: Using the SKStoreProductViewController Class

In the view controller’s viewDidLoad method, create a new button as shown in the snippet below. The button is of type UIButtonTypeRoundedRect and we position it at the center of the view controller’s view. We also give it a descriptive title and add a target-action pair to the UIControlEventTouchUpInside event. This means that whenever the user taps the button, the view controller receives a message of openAppStore:. This is the method where the magic happens.

- (void)viewDidLoad {
    [super viewDidLoad];
    // Initialize Button
    UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [button setTitle:@"Go to App Store" forState:UIControlStateNormal];
    [button setFrame:CGRectMake(0.0, 0.0, 200.0, 44.0)];
    [button setCenter:self.view.center];
    [self.view addSubview:button];
    // Add Target-Action Pair
    [button addTarget:self action:@selector(openAppStore:) forControlEvents:UIControlEventTouchUpInside];
}

In the openAppStore: method, we initialize an instance of the SKStoreProductViewController class, set the delegate to self, and send it a message of loadProductWithParameters:completionBlock:. The loadProductWithParameters:completionBlock: accepts two arguments, (1) a dictionary with a key specifying the application identifier of the application that we want to show to the user and (2) a completion block. The completion block is executed when the request to the App Store is finished. In the completion block, we verify if no error was thrown and present the store product view controller to the user. Keep in mind that even though the user doesn’t leave your application, the operating system does connect to the App Store under the hood. It is also important to note that the request to the App Store can take a non-trivial amount of time. In other words, it is good practice to show an activity indicator to the user as long as the request has not returned a response. The completion block will allow us to dismiss the activity indicator once the request has finished, successfully or unsuccessfully.

- (void)openAppStore:(id)sender {
    // Initialize Product View Controller
    SKStoreProductViewController *storeProductViewController = [[SKStoreProductViewController alloc] init];
    // Configure View Controller
    [storeProductViewController setDelegate:self];
    [storeProductViewController loadProductWithParameters:@{SKStoreProductParameterITunesItemIdentifier : @"594467299"} completionBlock:^(BOOL result, NSError *error) {
        if (error) {
            NSLog(@"Error %@ with User Info %@.", error, [error userInfo]);
        } else {
            // Present Store Product View Controller
            [self presentViewController:storeProductViewController animated:YES completion:nil];
        }
    }];
}

You can find the unique identifier of an application in iTunes Connect. Every application in the App Store is given a unique identifier or Apple ID. Note that you need to pass the Apple ID in the parameters dictionary as a string.

Before building and running the application, we need to conform the MTViewController class to the SKStoreProductViewControllerDelegate protocol by implementing the productViewControllerDidFinish: method. We start by updating the view controller’s interface file by telling the compiler that the MTViewController class conforms to the SKStoreProductViewControllerDelegate protocol (see below).

#import <UIKit/UIKit.h>
#import <StoreKit/StoreKit.h>
@interface MTViewController : UIViewController <SKStoreProductViewControllerDelegate>
@end

In the view controller’s implementation file, implement the productViewControllerDidFinish: method as shown below. Remember that the store product view controller is presented modally when we invoke the loadProductWithParameters:completionBlock: method. It is our responsibility to dismiss the store product view controller when the customer decides to leave the App Store.

- (void)productViewControllerDidFinish:(SKStoreProductViewController *)viewController {
    [self dismissViewControllerAnimated:YES completion:nil];
}

Step 4: Build and Run

Build and run the application to try it out. Even though Apple advertises the SKStoreProductViewController class as a way to show other apps to your users, it is also an ideal way to give users the chance to rate or review an app in the App Store without the hassle of leaving your application.

New in iOS 6: SKStoreProductViewController: The Store Product View Controller in Action - Figure 4

Conclusion

The SKStoreProductViewController class is a welcome addition to the Store Kit framework and I have already taken advantage of this new addition in some of my applications. I hope that I have convinced you of its usefulness with this quick tip.

Best of Tuts+ in February 2013

$
0
0

Each month, we bring together a selection of the best tutorials and articles from across the whole Tuts+ network. Whether you’d like to read the top posts from your favourite site, or would like to start learning something completely new, this is the best place to start!


Psdtuts+ — Photoshop Tutorials

  • Create a Surreal “Parting of the Sea” Photo Manipulation

    Create a Surreal “Parting of the Sea” Photo Manipulation

    Water, by its very nature is difficult to convincingly incorporate into a composition. In this tutorial, you will discover how to combine advanced masking techniques, blending modes, adjustment layers and clever use of filters to part the sea and create a surreal photo manipulation. Let’s get started!

    Visit Article

  • How to Replace a Woman’s Lipstick With Artwork Using Creative Retouching Techniques

    How to Replace a Woman’s Lipstick With Artwork Using Creative Retouching Techniques

    Creative retouching techniques are often used to enhance the photography that is being used for a particular project. In this tutorial, Stefka Pavlova will show you how to replace a woman’s lipstick with artwork using some creative and effective photo retouching techniques. Let’s get started!

    Visit Article

  • 11 Ways to Make Your Portfolio Friendlier

    Ways to Make Your Portfolio Friendlier

    As the editor of Psdtuts+, I look through a lot of portfolios. A big part of my job is to find talented artists and recruit them to become authors for the site. Over the years, I have developed some strong opinions about the ways in which artists present their work online. In this article, I wanted to share a few easy ways that you can make your online portfolio a bit friendlier to people that hire artists. Let’s take a look!

    Visit Article


Nettuts+ — Web Development

  • Speaking With the Ember.js Core Team

    Speaking With the Ember.js Core Team

    Single page apps are the new hotness; everyone’s trying to figure out the easiest way to build them. But it’s more than just finding a couple of controls to slap together and sprinkling Ajax pixie dust on it. Building scalable and maintainable apps is serious business, which requires serious tools.

    Visit Article

  • Testing Like a Boss in Laravel: Models

    Testing Like a Boss in Laravel: Models

    If you’re hoping to learn why tests are beneficial, this is not the article for you. Over the course of this tutorial, I will assume that you already understand the advantages, and are hoping to learn how best to write and organize your tests in Laravel 4.

    Visit Article

  • How to Write Code That Embraces Change

    How to Write Code That Embraces Change

    Writing code, which is easy to change is the Holy Grail of programming. Welcome to programming nirvana! But things are much more difficult in reality: source code is difficult to understand, dependencies point in countless directions, coupling is annoying, and you soon feel the heat of programming hell. In this tutorial, we will discuss a few principles, techniques and ideas that will help you write code that is easy to change.

    Visit Article


Vectortuts+ — Illustrator Tutorials


Webdesigntuts+ — Web Design Tutorials

  • A Web Designer’s SEO Checklist (Including Portable Formats)

    A Web Designer’s SEO Checklist (Including Portable Formats)

    As you might have noticed from our previous articles; there are many factors which need to be influenced to optimize a website for search engines. After a while you might not see the woods for the trees. To help you bring some order to this chaos, weve compiled an SEO checklist.

    Visit Article

  • Making the Most of Photoshop Layers

    Making the Most of Photoshop Layers

    The way web designers are using Photoshop is changing; pixel perfect comps are less relevant than they were a year or two ago. However, Photoshop is still an invaluable design tool and using its features properly is as important as ever. The following tips will help you master working with layers.

    Visit Article

  • Designing for Performance

    Designing for Performance

    Designers have a tremendous impact on site performance based on how they design for the web, before a single line of code is written. This article will cover how performance can be designed and what it takes to make it happen.

    Visit Article


Phototuts+ — Photography Tutorials

  • Comparing Effects in Lightroom and Photoshop

    Comparing Effects in Lightroom and Photoshop

    Do you need Photoshop if you own Lightroom? This is a question that is often asked, and which there is no definite answer. In this video, we take an existing image manipulated in Photoshop and set out to attempt to create a similar style of image using only Lightroom 3. Let’s see how it turns out.

    Visit Article

  • How to Shoot a Mysterious Levitation Photo

    How to Shoot a Mysterious Levitation Photo

    If you’re looking to bring some magic to your next photograph, today’s tutorial is going to help you do just that. With some creative planning and easy post-production, we’ll create a levitation photo that seems to break the laws of physics.

    Visit Article

  • Quick Tip: How Does Shutter Speed Affect Video?

    Quick Tip: How Does Shutter Speed Affect Video?

    When talking about video, many people refer the “cinematic” or “videoish” looks. Cinematic is in. Everyone wants to make sure their videos look like they came from a Hollywood backlot. One of the most basic methods of changing the look is by controlling the shutter speed.

    Visit Article


Cgtuts+ — Computer Graphics Tutorials

  • Create a Stylish Earth Cross-Section Animation with Cinema 4D – Part 1

    Create a Stylish Earth Cross-Section Animation with Cinema 4D – Part 1

    In our latest Cinema 4D tutorial, Aleksey Voznesenski will walk you through the process of Modeling, Texturing, Lighting and Animating a cross section of planet Earth. You’ll learn a whole bunch of techniques for adding materials and using alpha channels to modify only certain parts of the texture maps. You’ll also learn how to animate the cross section using booles and make everything look sexy with materials and lighting.

    Visit Article

  • Create a Classic He-Man Action Figure using Maya: Part 1 – Tuts+ Premium

    Create a Classic He-Man Action Figure using Maya: Part 1 – Tuts+ Premium

    Today we’re kicking off a brand new tutorial series covering the creation of an awesome He-Man Action Figure using Maya. This unique series will walk you through the entire process of modeling your very own action figure inspired by the iconic 80′s character.

    Visit Article

  • Global Illumination vs Photometric Lighting in Maya

    Global Illumination vs Photometric Lighting in Maya

    When working with Mental-Ray, many times we would use final gather almost exclusively. In this tutorial I would like to explore two less commonly used methods of illuminating our scenes, Global Illumination and photometric lighting.Throughout this tutorial we will explore these two methods, and compare their positive versus negative virtues.

    Visit Article


Aetuts+ — After Effects Tutorials

  • Advanced Spill Suppression Methods

    Advanced Spill Suppression Methods

    This tutorial shows how to use sophisticated channel operations to achieve a more accurate spill suppression result than is attainable with AE’s default plug-ins.

    Visit Article

  • Keyframing Tips for Adding Weight to Basic Shapes

    Keyframing Tips for Adding Weight to Basic Shapes

    There are some tiny tricks when animating moving graphics and in this tutorial we will go through some of them. We’ll be examining the movements of some basic shapes as they are repositioned from point A to B. I will show you how they should react the force based on their given weight, both in rotation and position. And of course, as a bonus we’ll be creating bouncing ball.

    Visit Article

  • Introduction to the New Features in Newton 2

    Introduction to the New Features in Newton 2

    This is an introduction to the new features of Newton 2. Newton is a 2D physics simulator that allows users to apply various simulations like gravity, collision, and bounce to their layers in After Effects. We’re going to work through a few scenarios to see how to set up and execute a simulation.

    Visit Article


Audiotuts+ — Audio & Production Tutorials


Wptuts+ — WordPress Tutorials


Mobiletuts+ — Mobile Development Tutorials

  • iOS SDK: Previewing and Opening Documents

    iOS SDK: Previewing and Opening Documents

    Sandboxing on iOS makes the platform much more secure and this ultimately benefits every user of the platform. However, because of the strict rules inherent to sandboxing, sharing data between applications is not trivial. An often overlooked class that helps with sharing documents between applications is the UIDocumentInteractionController class. In this quick tip, I will show you how you can use this class to preview documents as well as opening documents in other applications installed on the device.

    Visit Article

  • Create a Hundreds-Like Game – Interface Creation

    Create a Hundreds-Like Game – Interface Creation

    In this tutorial series, you’ll learn how to create a game like the popular Hundreds. The objective of the game is to reach 100 points without the circles touching while you raise the score. Read on!

    Visit Article

  • Windows Phone: Connecting with Facebook

    Windows Phone: Connecting with Facebook

    In this tutorial, we will talk about how to interact with the Facebook API, and all the tools you need in order to connect with it. Specifically, the app that we are going to make will be able to connect with the user’s Facebook account and update their status. Lets get started!

    Visit Article


Gamedevtuts+ — Game Development

  • Making Your First Game: A Walkthrough for Game Developers

    Making Your First Game: A Walkthrough for Game Developers

    When you get stuck in a video game, you might head to GameFAQs to find a walkthrough to help you get through it. When you get stuck making your first video game, what can you do? That’s where this article comes in: think of this as a walkthrough for developing your first game.

    Visit Article

  • Portal 2 Level Design: Creating Puzzles to Challenge Your Players

    Portal 2 Level Design: Creating Puzzles to Challenge Your Players

    Portal was one of the most distinctive puzzle games of the past few years, and its sequel Portal 2 showed us that lightning can strike twice. A little less than a year ago, Valve made waves once again by introducing a simple yet powerful level editing tool in Portal 2 itself. In this article I’m going to discuss how to design great puzzles that will challenge your players.

    Visit Article

  • How We Dealt With Cheaters in Our Turn-Based MMO Game

    How We Dealt With Cheaters in Our Turn-Based MMO Game

    Cheaters, griefers and trolls can ruin your game. The bigger your game, the more likely you are to face them. Although they are just trying to have fun like everybody else, they do that by negatively affecting the whole game. Suddenly your creation becomes a living nightmare. Read on to find out how we dealt with this problem in our turn-based MMO.

    Visit Article


Mactuts+ — Mac & OS X


Crafttuts+ — Craft & Handmade

  • Make Your Own Perpetual Calendar With Blackboard Paint

    Make Your Own Perpetual Calendar With Blackboard Paint

    A perpetual calendar is a nice spin on a traditional calendar, as it’s super-practical and means you don’t have to shell out for a new one every year. In this tutorial, we’ve combined the practicality of a perpetual calendar with the awesome functionality of chalkboard paint, to create a stunning organisational tool that can be updated time and again – with little fuss, or mess.

    Visit Article

  • Make a Beautiful Paper Polyhedron Mobile

    Make a Beautiful Paper Polyhedron Mobile

    If you thought mobiles were just for kids’ rooms, think again. This mobile of polyhedron paper shapes is a beautiful sculptural addition for any room of your home. Choose colours to suit your space and hang it from the ceiling – or even on a wall.

    Visit Article

  • Create a Stunning Combination Coptic Long-stitch Archival Book

    Create a Stunning Combination Coptic Long-stitch Archival Book

    This tutorial will show you how to make a good quality, archival book from beginning to end. Book binding requires a lot of patience and practice, but the result is a beautiful work of art that you can give as a gift, or fill with your own drawings, notes and photos. Let’s begin!

    Visit Article


FreelanceSwitch — Freelance Jobs & Information

  • How to Find Freelance Clients on Twitter

    I’ve often heard freelancers say “I don’t use Twitter for marketing”. That’s like saying you don’t use networking events to meet people (Well, perhaps you enjoy the free food, but really?).
    Like it or not, if you’re on Twitter, you use it for marketing. Twitter is a public platform, and potential clients will look at your Twitter account when they’re deciding whether to hire you.

    Visit Article

  • The Differences Between Marketing Yourself Solo and Marketing an Agency

    When you’re first marketing yourself as a freelancer, you have to make sure that your clients trust you. The entire point of your marketing is to convince each prospective client that you, personally, will do an awesome job on their project. But that approach doesn’t work when you won’t be personally doing every piece of work that passes through your agency.

    Visit Article

  • 10 Best Mobile Apps for Easier Social Media Marketing on the Go

    I have found that one of the best ways to take advantage of my time – while waiting in the drive-thru line, waiting for an appt., or watching my kids at sports events – is to get some social media marketing done. This helps with efficiency when I am sitting down at my computer trying to get some writing done.

    Visit Article


Tuts+ Premium — Creative & Technical Skills

  • dev

    What’s New in Laravel 4

    The coding world moves pretty fast. Since my last coverage of Laravel, Laravel Essentials, much has changed for the better. While plenty of that course still applies, a variety of adjustments and additions have been made to the framework. This course will get you up to speed on those changes as quickly as possible.

    Please note that this is a live course. Check back each week for new lessons!

    Visit Article

  • ppsd

    PSD to HTML for Designers

    Have you ever tried to take a nice PSD design for a web site and tried to translate it to HTML? Not always easy, is it! Here is Adi Purdila to help us learn this sometimes tricky art, where you will be taking an already designed web page layout and developing it into a functional, working web page using intermediate and current techniques.

    Visit Article

  • draw

    Digital Drawing Fundamentals

    Whether you’re an ambitious illustrator or an experienced designer, almost everyone wants to improve their traditional drawing skills. Kirk Nelson is here to do just that! As an experienced digital painter and designer, Kirk walks you through the building blocks of digital drawing, shapes and shading, composing and perspective, and much more. Grab your tablet or stylus and let’s get started.

    Visit Article

Create a Bubble Popping Game – Tuts+ Premium

$
0
0

In this tutorial series, you’ll learn how to create a bubble popping game. The objective of the game is to pop the required bubbles using a single tap. Read on!


Tutorial Preview


Get the Full Series!

This tutorial series is available to Tuts+ Premium members only. Read a preview of this tutorial on the Tuts+ Premium web site or login to Tuts+ Premium to access the full content.


Joining Tuts+ Premium. . .

For those unfamiliar, the family of Tuts+ sites runs a premium membership service called Tuts+ Premium. For $19 per month, you gain access to exclusive premium tutorials, screencasts, and freebies from Mobiletuts+, Nettuts+, Aetuts+, Audiotuts+, Vectortuts+, and CgTuts+. You’ll learn from some of the best minds in the business. Become a premium member to access this tutorial, as well as hundreds of other advanced tutorials and screencasts.

Viewing all 1836 articles
Browse latest View live