AWS S3 Image upload using AWS SDK for iOS v2

Hi there and welcome. Today we will be talking about AWS S3 Image upload using AWS SDK for iOS v2 (WOW thats a lot of acronyms!). Last week I had to do this in a project and it took me and I was disappointed with the documentation, and sample project they had available. So in this tutorial I’m going to be showing a real world example where you would select an image from the gallery or take a photo on a device and upload that image to your bucket. If you want to follow along I have the projects available on github for both objective-c and swift! I also have a video of setting up S3 and Amazon Cognito at the end of the tutorial, but I have to apologize for the quality. I am terrible at making videos, and I was very tired when I made it (also my voice is very annoying).

Github Links

Also don’t forget to follow me on twitter if you ever want to chat about iOS or mobile development! .

Set up credentials

First thing we need to do is set up amazons credentials your app delegate’s didFinishLaunchingWithOptions method. In this demo we are going to use Cognito to manage access to our S3 bucket. If you want a full in depth look at how to do this watch the video at the end of the tutorial.

Objective-C

AppDelegate.m

//
//  AppDelegate.m
//  s3-objectiveC
//
//  Created by Barrett Breshears on 12/5/14.
//  Copyright (c) 2014 Barrett Breshears. All rights reserved.
//

#import "AppDelegate.h"
#import "AWSCore.h"

@interface AppDelegate ()

@end

@implementation AppDelegate


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    
    AWSCognitoCredentialsProvider *credentialsProvider = [AWSCognitoCredentialsProvider
                                                          credentialsWithRegionType:AWSRegionUSEast1
                                                          accountId:@"#######"
                                                          identityPoolId:@"######"
                                                          unauthRoleArn:@"#####"
                                                          authRoleArn:@"######"];
    
    AWSServiceConfiguration *configuration = [AWSServiceConfiguration configurationWithRegion:AWSRegionUSEast1
                                                                          credentialsProvider:credentialsProvider];
    
    [AWSServiceManager defaultServiceManager].defaultServiceConfiguration = configuration;
    return YES;
}

- (void)applicationWillResignActive:(UIApplication *)application {
}

- (void)applicationDidEnterBackground:(UIApplication *)application {
}

- (void)applicationWillEnterForeground:(UIApplication *)application {
}

- (void)applicationDidBecomeActive:(UIApplication *)application {
}

- (void)applicationWillTerminate:(UIApplication *)application {
}

@end

Swift

AppDelegate.swift

//
//  AppDelegate.swift
//  s3-swift
//
//  Created by Barrett Breshears on 12/6/14.
//  Copyright (c) 2014 Barrett Breshears. All rights reserved.
//

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?


    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // Override point for customization after application launch.
        var credentialsProvider:AWSCognitoCredentialsProvider = AWSCognitoCredentialsProvider.credentialsWithRegionType(AWSRegionType.USEast1, accountId:"##########", identityPoolId:"#######", unauthRoleArn:"#######", authRoleArn:"########")
        
        var configuration:AWSServiceConfiguration = AWSServiceConfiguration(region: AWSRegionType.USEast1, credentialsProvider: credentialsProvider)
        
        AWSServiceManager.defaultServiceManager().setDefaultServiceConfiguration(configuration)
        
        
        
        return true
    }

    func applicationWillResignActive(application: UIApplication) {
    }

    func applicationDidEnterBackground(application: UIApplication) {
    }

    func applicationWillEnterForeground(application: UIApplication) {
    }

    func applicationDidBecomeActive(application: UIApplication) {
    }

    func applicationWillTerminate(application: UIApplication) {
    }


}

This is a pretty simple set up here, and if you configure your credentials in the Cognito console it will actually give you the code to set up your configuration.

Upload an image to S3

So the main difference between v1 and v2 is that you used to be able to set the upload body request to NSData or a NSURL. In v2 you can only set the upload request to a NSURL. So what we have to do is save the image to the app’s local directory and then we can create a local file url to send to amazon.

Objective-C
- (void)uploadToS3{
    // get the image from a UIImageView that is displaying the selected Image
    UIImage *img = _selectedImage.image;
    
    // create a local image that we can use to upload to s3
    NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:@"image.png"];
    NSData *imageData = UIImagePNGRepresentation(img);
    [imageData writeToFile:path atomically:YES];
    
    // once the image is saved we can use the path to create a local fileurl
    NSURL *url = [[NSURL alloc] initFileURLWithPath:path];
    
    // next we set up the S3 upload request manager
    _uploadRequest = [AWSS3TransferManagerUploadRequest new];
    // set the bucket
    _uploadRequest.bucket = @"s3-demo-objectivec";
    // I want this image to be public to anyone to view it so I'm setting it to Public Read
    _uploadRequest.ACL = AWSS3ObjectCannedACLPublicRead;
    // set the image's name that will be used on the s3 server. I am also creating a folder to place the image in
    _uploadRequest.key = @"foldername/image.png";
    // set the content type
    _uploadRequest.contentType = @"image/png";
    // we will track progress through an AWSNetworkingUploadProgressBlock
    _uploadRequest.body = url;
    
    __weak ViewController *weakSelf = self;
    
    _uploadRequest.uploadProgress =^(int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend){
        dispatch_sync(dispatch_get_main_queue(), ^{
            weakSelf.amountUploaded = totalBytesSent;
            weakSelf.filesize = totalBytesExpectedToSend;
            [weakSelf update];
            
        });
    };
    
    // now the upload request is set up we can creat the transfermanger, the credentials are already set up in the app delegate
    AWSS3TransferManager *transferManager = [AWSS3TransferManager defaultS3TransferManager];
    // start the upload
    [[transferManager upload:_uploadRequest] continueWithExecutor:[BFExecutor mainThreadExecutor] withBlock:^id(BFTask *task) {
        
        // once the uploadmanager finishes check if there were any errors
        if (task.error) {
            NSLog(@"%@", task.error);
        }else{// if there aren't any then the image is uploaded!
            // this is the url of the image we just uploaded
            NSLog(@"https://s3.amazonaws.com/s3-demo-objectivec/foldername/image.png");
        }
        
        return nil;
    }];
    
}
Swift
 func uploadToS3(){
        
        // get the image from a UIImageView that is displaying the selected Image
        var img:UIImage = selectedImage!.image!
        
        // create a local image that we can use to upload to s3
        var path:NSString = NSTemporaryDirectory().stringByAppendingPathComponent("image.png")
        var imageData:NSData = UIImagePNGRepresentation(img)
        imageData.writeToFile(path, atomically: true)
        
        // once the image is saved we can use the path to create a local fileurl
        var url:NSURL = NSURL(fileURLWithPath: path)!
        
        // next we set up the S3 upload request manager
        uploadRequest = AWSS3TransferManagerUploadRequest()
        // set the bucket
        uploadRequest?.bucket = "s3-demo-swift"
        // I want this image to be public to anyone to view it so I'm setting it to Public Read
        uploadRequest?.ACL = AWSS3ObjectCannedACL.PublicRead
        // set the image's name that will be used on the s3 server. I am also creating a folder to place the image in
        uploadRequest?.key = "foldername/image.png"
        // set the content type
        uploadRequest?.contentType = "image/png"
        // and finally set the body to the local file path
        uploadRequest?.body = url;
        
        // we will track progress through an AWSNetworkingUploadProgressBlock
        uploadRequest?.uploadProgress = {[unowned self](bytesSent:Int64, totalBytesSent:Int64, totalBytesExpectedToSend:Int64) in
            
            dispatch_sync(dispatch_get_main_queue(), { () -> Void in
                self.amountUploaded = totalBytesSent
                self.filesize = totalBytesExpectedToSend;
                self.update()

            })
        }
        
        // now the upload request is set up we can creat the transfermanger, the credentials are already set up in the app delegate
        var transferManager:AWSS3TransferManager = AWSS3TransferManager.defaultS3TransferManager()
        // start the upload
        transferManager.upload(uploadRequest).continueWithExecutor(BFExecutor.mainThreadExecutor(), withBlock:{ [unowned self]
            task -> AnyObject in
            
            // once the uploadmanager finishes check if there were any errors
            if(task.error != nil){
                NSLog("%@", task.error);
            }else{ // if there aren't any then the image is uploaded!
                // this is the url of the image we just uploaded
                NSLog("https://s3.amazonaws.com/s3-demo-swift/foldername/image.png");
            }
            
            self.removeLoadingView()
            return "all done";
        })
        
    }

Once you understand how to configure your credentials and how to get the image from an image view to local file to url to amazon the process is pretty simple. Please let me know if you have any questions in the comments below or on send me a tweet
.

Setting up credentials and project walk through video

Create Custom Delegate and Protocol iOS | Swift & Objective-C

One of the most powerful tools an iOS developer has is the ability to create protocols and implement delegates that let your classes notify when an event occurs. I use them all the time in my projects and wanted to show how easy it is to implement your iOS Project. I will demonstrate them in both Objective-C and Swift so you can see a side by side comparison. In this example we will be implementing timer that fires a timerFinished method to let the UIViewController know the timer finished and start the timer over.

If you want to follow allow you can download the projects from GitHub here:

Objective-C  Project
Swift Project

Protocols

A protocol is a list of methods that specify an interface that your delegate will implement. There are two kinds of delegates we can use: Option and Required. They are pretty self explanatory but the difference is Required will throw an error letting you know your class is not conforming to the protocol. Also protocol methods are required by default so if you want it optional don’t forget that optional keyword. If you are using swift you will also need to add the @objc prefix if you want an optional method.

Swift
//
//  MyTimer.swift
//  SwiftProtocol
//
//  Created by Barrett Breshears on 10/11/14.
//  Copyright (c) 2014 Sledge Dev. All rights reserved.
//

import UIKit

// set up the MyTimerDelegate protocol with a single option timer finished function
@objc protocol MyTimerDelegate{
    optional func timerFinished()
}


class MyTimer: UIViewController {
    
    // this is where we declare our protocol
    var delegate:MyTimerDelegate?
    
    // set up timer variables and labels
    var timer:NSTimer! = NSTimer()
    var labelTimer:NSTimer! = NSTimer()
    var timerLabel:UILabel! = UILabel()
    var timerCount = 0
    var duration = 0
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Do any additional setup after loading the view.
        timerLabel = UILabel(frame: self.view.frame)
        timerLabel.textAlignment = NSTextAlignment.Center
        self.view.addSubview(timerLabel)
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    func startTimer(timerDuration:Double){
        self.duration = Int(timerDuration)
        timerLabel.text = String(format: "%d", duration)
        
        timer = NSTimer.scheduledTimerWithTimeInterval(timerDuration, target: self, selector: Selector("timerFired:"), userInfo: nil, repeats: false)
        
        labelTimer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("updateLabel:"), userInfo: nil, repeats: true)
        
        
    }
    
    func timerFired(timer:NSTimer){
        if(timer.valid){
            timer.invalidate()
        }
        if(labelTimer.valid){
            labelTimer.invalidate()
        }
        // ************************************** \\
        // ************************************** \\
        // This is the important part right here
        // we want to call our protocol method
        // so the class implementing this delegate will know
        // when the timer has finished
        // ************************************** \\
        // ************************************** \\
        delegate?.timerFinished!()
        
    }
    
    func updateLabel(timer:NSTimer){
        duration = duration - 1
        timerLabel.text = String(format: "%d", duration)
    }
    
    
    /*
    // MARK: - Navigation
    
    // In a storyboard-based application, you will often want to do a little preparation before navigation
    override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
    // Get the new view controller using segue.destinationViewController.
    // Pass the selected object to the new view controller.
    }
    */
    
}

This is a pretty simple example but here is what is going on. The UIViewController has a start timer method that sets up the two timers: one that will fire when the overall time is complete and one that fires every second to update the timer label. When the total duration timer is finished the timerFired method is called, and thats where we run the delegate’s timerFinished method. Now lets do the same thing but with Objective-C.

Objective-C
//
//  MyTimer.h
//  ObjectIveCProtocol
//
//  Created by Barrett Breshears on 10/11/14.
//  Copyright (c) 2014 Sledge Dev. All rights reserved.
//

#import 

// set up the MyTimerDelegate protocol with a single option timer finished function
@protocol MyTimerDelegate 

@optional
-(void)timerFinished;

@end

@interface MyTimer : UIViewController
// this is where we declare our protocol
@property (nonatomic, strong) id delegate;
// set up timer variables and labels
@property (nonatomic, strong) NSTimer *timer;
@property (nonatomic, strong) NSTimer *labelTimer;
@property (nonatomic, strong) UILabel *timerLabel;
@property (nonatomic, assign) int timerCount;
@property (nonatomic, assign) int duration;

- (void)startTimer:(float)duration;

@end

//
//  MyTimer.m
//  ObjectIveCProtocol
//
//  Created by Barrett Breshears on 10/11/14.
//  Copyright (c) 2014 Sledge Dev. All rights reserved.
//

#import "MyTimer.h"



@interface MyTimer ()

@end

@implementation MyTimer

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    _timer = [[NSTimer alloc] init];
    _labelTimer = [[NSTimer alloc] init];
    _timerCount = 0;
    _duration = 0;
    
    
     _timerLabel = [[UILabel alloc] initWithFrame:self.view.frame];
    [self.view addSubview:_timerLabel];
    [_timerLabel setTextAlignment:NSTextAlignmentCenter];
    
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)startTimer:(float)duration{
    _duration = (int)duration;
    _timerLabel.text = [NSString stringWithFormat:@"%d", _duration];
    
    _timer = [NSTimer scheduledTimerWithTimeInterval:duration
                                              target:self
                                            selector:@selector(timerFired:)
                                            userInfo:nil
                                             repeats:NO];
    
    _labelTimer = [NSTimer scheduledTimerWithTimeInterval:1.0
                                                   target:self
                                                 selector:@selector(updateLabel:)
                                                 userInfo:nil
                                                  repeats:YES];
    
    
}

- (void)timerFired:(NSTimer *)timer {
    if ([_timer isValid]) {
        [_timer invalidate];
    }
    _timer = nil;
    if ([_labelTimer isValid]) {
        [_labelTimer invalidate];
    }
    _labelTimer = nil;
    // ************************************** \\
    // ************************************** \\
    // This is the important part right here
    // we want to call our protocol method here
    // so the class implementing this delegate will know
    // when the timer has finished
    // ************************************** \\
    // ************************************** \\
    [_delegate timerFinished];
}

- (void)updateLabel:(NSTimer *)timer{
    _duration = _duration - 1;
    _timerLabel.text = [NSString stringWithFormat:@"%d", _duration];
}

@end

So same thing just different syntax. The UIViewController has a start timer method that sets up the two timers: one that will fire when the overall time is complete and one that fires every second to update the timer label. When the total duration timer is finished the timerFired method is called, and thats where we run the delegate’s timerFinished method.

Delegates

Now that we have our protocols all set up all we have to do is implement them. We are going to do this by creating a delegate. A delegate is a variable that complies to a protocol, which a class typically uses to notify of events, in this case the timer finishing. To do this we add our protocol to our class declaration, to let our class know it must comply with the delegate. Then we add our delegate method to our class.

Swift
//
//  ViewController.swift
//  Swift-Protocol
//
//  Created by Barrett Breshears on 10/11/14.
//  Copyright (c) 2014 Sledge Dev. All rights reserved.
//

import UIKit

// add our MyTimerDelegate to our class
class ViewController: UIViewController, MyTimerDelegate {
    
    var timer:MyTimer = MyTimer()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        timer.view.frame = self.view.frame
        // ************************ \\
        // This is where we let the delegate know 
        // we are listening for the timerFinished method
        // ************************ \\
        timer.delegate = self
        self.view.addSubview(timer.view)
        timer.startTimer(10.0)
        
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    // ************************ \\
    // This is where our delegate method is fired
    // ************************ \\
    func timerFinished(){
        timer.startTimer(10.0)
        println("Hey my delegate is working")
    }

}

So the important thing here is we set the timer.delegate to self so the ViewController’s method timerFinished() class is called.

Objective-C
//
//  ViewController.h
//  ObjectIveCProtocol
//
//  Created by Barrett Breshears on 10/10/14.
//  Copyright (c) 2014 Sledge Dev. All rights reserved.
//

#import 
#import "MyTimer.h"

// add our MyTimerDelegate to our class
@interface ViewController : UIViewController 

@property (nonatomic, strong) MyTimer *timer;

@end
//
//  ViewController.m
//  ObjectIveCProtocol
//
//  Created by Barrett Breshears on 10/10/14.
//  Copyright (c) 2014 Sledge Dev. All rights reserved.
//

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    _timer = [[MyTimer alloc] init];
    _timer.view.frame = self.view.frame;
    _timer.delegate = self;
    [self.view addSubview:_timer.view];
    [_timer startTimer:10.0];
    
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

-(void)timerFinished{
    [_timer startTimer:10.0];
    NSLog(@"Hey my delegate is working!");
}

@end

When we run the code we see the timer label is added and set to a 10 second timer. It counts down, and when it reaches 0 it notifies the viewcontroller, restarts the timer and prints the “Hey my delegate is working in the console”.

If you have any questions about the code or found this tutorial helpful let me know in the comments below! I appreciate feedback. Also don’t forget to follow me on twitter. I am always looking for iOS developers to tweet with.

iOS Custom UIActionSheet

customActionSheetThe UIActionSheet is a great tool to easily let users select between multiple options. I use it all the time when developing apps. Just like all great and easy to use components, designers like to customize them and make developer’s lives harder than they have to be. In this tutorial I will show you how to make an action sheet that is easy to customize and allow you to control every aspect of its look and feel. If you would like to follow along the project is on git hub. Also before we get started, if you would like to follow me on twitter I am always looking for iOS developer friends!


Download this sample project on git hub

Quick! To the documentation

If you head over to Apple’s documentation we will quickly see that the UIActionSheet isn’t supposed to be subclassed.

UIActionSheet is not designed to be subclassed, nor should you add views to its hierarchy. If you need to present a sheet with more customization than provided by the UIActionSheet API, you can create your own and present it modally with presentViewController:animated:completion:.

So what will we do? We will create a UIView that looks and acts like an UIActionSheet. To get started lets create the class in Xcode. Select new objective-c class and name it CustomActionSheet and set it’s subclass to UIView. Next, lets also create a custom button that the action sheet will use. Select new objective-c class and name it ActionButton and make it a subclass of UIButton.

Action Button

Before we get started on the action sheet view lets finish up the button that the view will use. You don’t have to do this and could just insert a UIButton, but I like to subclass things like buttons because they will most likely be used all over the app. Here is what the ActionButton class looks like.

ActionButton.h

//
//  ActionButton.h
//  CustomUIActionSheet
//
//  Created by Barrett Breshears on 8/8/14.
//  Copyright (c) 2014 Barrett Breshears. All rights reserved.
//

#import 

@interface ActionButton : UIButton

// class method that will be used when allocating button
+ (ActionButton *)buttonWithText:(NSString *)text cancel:(BOOL)cancel;

// instance method to set the button label and let the button know if it is a cancel button
- (id)initWithText:(NSString *)text cancel:(BOOL)cancel;

@property (nonatomic, retain) UILabel *label;

@end

ActionButton.m

//
//  ActionButton.m
//  CustomUIActionSheet
//
//  Created by Barrett Breshears on 8/8/14.
//  Copyright (c) 2014 Barrett Breshears. All rights reserved.
//

#import "ActionButton.h"

@implementation ActionButton

+ (ActionButton *)buttonWithText:(NSString *)text cancel:(BOOL)cancel
{
    // return the initialized button
    return [[self alloc] initWithText:text cancel:(BOOL)cancel];
}

- (id)initWithText:(NSString *)text cancel:(BOOL)cancel
{
    // initialize
    self = [super init];
    if (self != nil) {
        
        // we are only using a single image for the demo, but this project is set up for
        // an image with a highlighted state
        UIImage *mainImage;
        UIImage *tappedImage;
        
        // check if the image is a cancel button, if it is  we will use a special image
        if (cancel) {
            mainImage = [UIImage imageNamed:@"red_button.png"];
            tappedImage = [UIImage imageNamed:@"red_button.png"];
        } else { // otherwise the default button image will be a green button
            mainImage = [UIImage imageNamed:@"green_button.png"];
            tappedImage = [UIImage imageNamed:@"green_button.png"];
        }
        
        // create the buttons frame based on the image size
        CGRect frame;
        frame.size = mainImage.size;
        self.frame = frame;
        
        // set button images
        [self setImage:mainImage forState:UIControlStateNormal];
        [self setImage:tappedImage forState:UIControlStateHighlighted];
        
        // set up button label
        _label = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
        _label.backgroundColor = [UIColor clearColor];
        _label.textColor = [UIColor whiteColor];
        _label.textAlignment = NSTextAlignmentCenter;
        _label.text = text;
        [self addSubview:_label];
    }
    return self;
}

@end

As you can see, this is a simple button that will give us the look of both UIActionSheet buttons. This type of subclassing has always worked great for me and if I needed more variety I would change the cancel param to a button type with a string parameter with a switch or if statement to decide what image to use for the button.

CustomActionSheet Class

Now lets move on to the custom action sheet. We will create our view to behave just like an action sheet, initializing it with a title, button titles, and a cancel button title. It will also fire a custom delegate method to let the implementing object know the action sheet has been dismissed and a button was selected.

CustomActionSheet.h

The header file is nothing special, but has one little snippet of code that I always get excited to use which allows you to pass in as many strings as you need.

otherButtonTitles:(NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION

I always get excited when I get to use this. Anyway here is the CustomAction.h file.

//
//  CustomActionSheet.h
//  CustomUIActionSheet
//
//  Created by Barrett Breshears on 8/7/14.
//  Copyright (c) 2014 Barrett Breshears. All rights reserved.
//

#import 

@class CustomActionSheet;

// define the custom actionview delegate
@protocol CustomActionViewDelegate 

@optional
// declare the option delegate method which passed in the alert and which button was selected
- (void)modalAlertPressed:(CustomActionSheet *)alert withButtonIndex:(NSInteger)buttonIndex;

@end

@interface CustomActionSheet : UIView

@property (assign) id  delegate;
@property (nonatomic, strong) UIView *backgroundView;
@property (nonatomic, assign) float yPosition;
@property (nonatomic, assign) int index;

- (void)animateOn;
- (void)animateOff;
- (id)initWithTitle:(NSString *)title delegate:(id)delegate cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION;
- (void)buttonPressedWithIndex:(id)sender;
- (void)showAlert;

@end

CustomActionSheet.m

Here is a little overview of what we will be doing here:

–The view will need to be initialized and create the background, and all the buttons, adding and adjusting the views’ size depending on the number of buttons needed.

–Create an action method the buttons will use when they are clicked.

–We will need to create a showAlert method that adds the view to the window.

–Create methods to animate the view on and off the window.

Just a quick note I decided to call the delegate method [_delegate modalAlertPressed:self withButtonIndex:index] inside of the animateOff method instead of the – (void)buttonPressedWithIndex:(id)sender since there is a delay with animation. If you need to change this to fire immediately after the button is pressed you can stick it in the – (void)buttonPressedWithIndex:(id)sender method instead.

//
//  CustomActionSheet.m
//  CustomUIActionSheet
//
//  Created by Barrett Breshears on 8/7/14.
//  Copyright (c) 2014 Barrett Breshears. All rights reserved.
//

#import "CustomActionSheet.h"
#import "ActionButton.h"

@interface CustomActionSheet ()

@end

@implementation CustomActionSheet
@synthesize backgroundView;
@synthesize yPosition;
@synthesize index;

- (id)initWithTitle:(NSString *)title delegate:(id)delegate cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ...
{
    self = [super init];
    if (self) {
        
        // set the delegate
        _delegate = delegate;
        
        // create a frame this will take up the full screen size
        self.frame = CGRectMake(0.0, 0.0, [[UIScreen mainScreen] bounds].size.width, [[UIScreen mainScreen] bounds].size.height);
        self.backgroundColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.7];
        
        // set up the background view
        backgroundView = [[UIView alloc] init];
        backgroundView.backgroundColor = [UIColor colorWithRed:7.0/255.0f green:45.0/255.0f blue:58.0/255.0 alpha:1];
        backgroundView.userInteractionEnabled = YES;
        CGRect frame = backgroundView.frame;
        frame.size = CGSizeMake(320, 380);
        frame.origin = CGPointMake(0, self.frame.size.height);
        backgroundView.frame = frame;
        
        
        // get our button array from the otherButtonTittles parameter
        NSMutableArray *buttonArray = [[NSMutableArray alloc]init];
        va_list args;
        va_start(args, otherButtonTitles);
        for (NSString *arg = otherButtonTitles; arg != nil; arg = va_arg(args, NSString*))
        {
            [buttonArray addObject:arg];
        }
        va_end(args);
        
        
        // this will track the current position of where the elements will be placed
        yPosition = 15;
        
        // check if there is a title
        if (title != nil) {
            // create a the title and position it in the view
            UILabel *titleField = [[UILabel alloc] init];
            titleField.textColor = [UIColor whiteColor];
            titleField.shadowColor = [UIColor blackColor];
            [titleField setTextAlignment:NSTextAlignmentCenter];
            titleField.text = title;
            frame = titleField.frame;
            frame.size.width = self.frame.size.width ;
            frame.size.height = 19;
            frame.origin.x = self.frame.size.width/2 - frame.size.width/2;
            frame.origin.y = yPosition;
            titleField.frame = frame;
            [backgroundView addSubview:titleField];
            yPosition += titleField.frame.size.height + 10;
        }
        
        // i will be used to set the button's tag and is returned to delegate method
        int i;
        // loop through the buttons and create an ActionButton for each
        for (i = 0; i < buttonArray.count; i++) {
            ActionButton *alertButton = [ActionButton buttonWithText:[buttonArray objectAtIndex:i] cancel:NO];
            frame = alertButton.frame;
            frame.origin.x = backgroundView.frame.size.width/2 - frame.size.width/2;
            frame.origin.y = yPosition;
            alertButton.frame = frame;
            alertButton.tag = i;
            [alertButton addTarget:self action:@selector(buttonPressedWithIndex:) forControlEvents:UIControlEventTouchUpInside];
            [backgroundView addSubview:alertButton];
            yPosition += alertButton.frame.size.height + 10;
            
        }
        
        // increase the tag index for the cancel button
        i++;
        
        // create the cancel button
        ActionButton * cancel = [ActionButton buttonWithText:cancelButtonTitle cancel:YES];
        frame = cancel.frame;
        frame.origin.x = backgroundView.frame.size.width/2 - frame.size.width/2;
        frame.origin.y = yPosition;
        cancel.frame = frame;
        cancel.tag = i;
        [cancel addTarget:self action:@selector(buttonPressedWithIndex:) forControlEvents:UIControlEventTouchUpInside];
        [backgroundView addSubview:cancel];
        
        yPosition += cancel.frame.size.height + 15;
        
        frame = backgroundView.frame;
        frame.size.width = self.frame.size.width;
        frame.size.height = yPosition;
        backgroundView.frame = frame;
        
        // add the background view and animate the view on screen
        [self addSubview:backgroundView];
        [self animateOn];
        
        
    }
    return self;
}

// method that is fired when one of the ActionButtons is pressed
- (void)buttonPressedWithIndex:(id)sender
{
    // get the button that was pressed
    ActionButton *button = (ActionButton *)sender;
    index = (int)button.tag;
    [self animateOff];
}


// shows the action sheet by adding it to the key window
- (void)showAlert
{
    [[[UIApplication sharedApplication]keyWindow]addSubview:self];
}


// animate the view on to the screen
- (void)animateOn
{
    [UIView animateWithDuration:.23 animations:^{
        
        CGRect frame = backgroundView.frame;
        frame.origin.y -= yPosition;
        backgroundView.frame = frame;
        
    }];
}

// remove the view with animation once removed the delegate method is fired off notifying the
// object that implemented the CustomActionSheet
- (void)animateOff
{
    [UIView animateWithDuration:.23 animations:^{
        
        CGRect frame = backgroundView.frame;
        frame.origin.y += yPosition;
        backgroundView.frame = frame;
        
    } completion:^(BOOL complete){
        [_delegate modalAlertPressed:self withButtonIndex:index];
        [self removeFromSuperview];
    }];
}
@end

This is a really basic example and hopefully will be a great starting point if you ever needed a custom designed UIActionSheet. Let me know if you have any questions or comments in the comment section below, I would love to hear from you! Also if you ever want to talk about iOS or any kind of development send me a message on twitter. Thanks for reading!

UIView Animations with Objective C for iOS

When developing an app, animations can make the defierence between a good user experience and a great one. Like most things in programing there are serveral ways to animate ui components, but I will be using the UIView class with block-based animation methods. This works with iOS 4 and greater.

UIView animateWithDuration: and block-based animation methods

There are 3 main methods that I use but there are a couple others:

[UIView animateWithDuration:0.2
                 animations:^{
       // animations go here
}];


[UIView animateWithDuration:0.2
                 animations:^{
                     // animations go here
                 }
                completion:^(BOOL finished) {
                    // block fires when animation has finished
}];


[UIView animateKeyframesWithDuration:0.2
                               delay:0.0
                             options:UIViewKeyframeAnimationOptionCalculationModeLinear
                          animations:^{
                              // animations go here
                          }
                          completion:^(BOOL finished) {
                              // block fires when animaiton has finished
}];

You can take a look at the UIView Class Reference for all the animation methods as well as other techniques for animation.

Lets animate something!

Now that we know what methods to use lets try and animate something. In this example we are animate two views to the left and then back to their original position. There will be 2 buttons to allow you to set the animation options (to view all the available options take a look at the UIView Class Reference), and allow you to see the difference between the two. You can also grab the project on github and follow along.

Setting up the storyboard and .h file

To start we are going to set up the view in the storyboard by creating the IBOutlets and hooking them up in interface builder.

//
//  ViewController.h
//  objectiveCAnimation
//
//  Created by Barrett Breshears on 6/29/14.
//  Copyright (c) 2014 Barrett Breshears. All rights reserved.
//

#import 

@interface ViewController : UIViewController

@property (nonatomic, strong) IBOutlet UIButton *animationOption1;
@property (nonatomic, strong) IBOutlet UIButton *animationOption2;
@property (nonatomic, strong) IBOutlet UIButton *animateBtn;
@property (nonatomic, strong) IBOutlet UIButton *pickerSelectBtn;


@property (nonatomic, strong) IBOutlet UIView *view1;
@property (nonatomic, strong) IBOutlet UIView *view2;

@property (nonatomic, strong) IBOutlet UIPickerView *animationPicker;

@property (nonatomic, assign) UIViewAnimationOptions animation1;
@property (nonatomic, assign) UIViewAnimationOptions animation2;

@property (nonatomic, strong) NSArray *animationNames;

@property (nonatomic, assign) BOOL animationInProgress;

-(IBAction)selectAnimation1:(id)sender;
-(IBAction)selectAnimation2:(id)sender;
-(IBAction)animateViews:(id)sender;

@end

Interface Builder

Animating the views

Animating the views will be super easy. We will be animating the views frame, but you can change this to anything you want (like scale: _view1.transform = CGAffineTransformMakeScale(1.0, 1.0); ), and you can have as many animations as you want in there. Also you will notice that we have a nested animation inside the completion block that animates the view back to the original position.

  // animation 1
    [UIView animateKeyframesWithDuration:0.5
                                   delay:0.0
                                 options:_animation1
                              animations:^{
                                _view1.frame = CGRectMake(_view1.frame.origin.x + 200, _view1.frame.origin.y, _view1.frame.size.width, _view1.frame.size.height);
                              }
                              completion:^(BOOL finished) {
                                  [UIView animateKeyframesWithDuration:0.5
                                                                 delay:0.0
                                                               options:_animation1
                                                            animations:^{
                                                                _view1.frame = CGRectMake(_view1.frame.origin.x - 200, _view1.frame.origin.y, _view1.frame.size.width, _view1.frame.size.height);
                                                            }
                                                            completion:^(BOOL finished) {
                                                                _animationInProgress = NO;
                                                            }];
                              }];
    // animation 2
    [UIView animateKeyframesWithDuration:0.5
                                   delay:0.0
                                 options:_animation2
                              animations:^{
                                  _view2.frame = CGRectMake(_view2.frame.origin.x + 200, _view2.frame.origin.y, _view2.frame.size.width, _view2.frame.size.height);
                              }
                              completion:^(BOOL finished) {
                                  [UIView animateKeyframesWithDuration:0.5
                                                                 delay:0.0
                                                               options:_animation2
                                                            animations:^{
                                                                _view2.frame = CGRectMake(_view2.frame.origin.x - 200, _view2.frame.origin.y, _view2.frame.size.width, _view2.frame.size.height);
                                                            }
                                                            completion:^(BOOL finished) {
                                                                _animationInProgress = NO;
                                                            }];
                              }];

animation
This is a very basic example but it shows how easy adding animation to your views are. If you have any questions please let me know in the comment or sent me messages on twitter @barrettbreshears. Also if this post helped you out I would appreciate if you share with your friends on twitter! Thanks for reading!