3d printed iPhone stencil for protyping

I got my Printrbot simple about 6 months ago, and love it. Ever since I have got it I have been trying to figure out something cool to make. Last week I saw an ad for an iPhone stencil made out of metal, and thought is was pretty sweet http://www.uistencils.com/products/iphone-stencil-kit. I thought to myself hey I have a 3d printer, I can make things like this! I download things from thingiverse all the time how hard could it be! Well it wasn’t easy and I’m sure there are better ways of doing this. If you enjoy this awesomeness please follow me on twitter and leave a comment bellow! I’m always looking for friends online, and love to talk about mobile dev  and other cool stuff, so follow me already!

Download the .STL file:
(I suffered so you don’t have to)
iphone.stl

Create an iPhone stencil

First thing I did was download a picture of an iPhone 5s and open it in photoshop.
Screen Shot 2014-08-14 at 4.21.57 PMNext remove everything we don’t need in the image and save it as a png.
Screen Shot 2014-08-14 at 4.19.25 PM

Next I used a program called sketch up where can be downloaded here: http://www.sketchup.com/ . Open your png in sketch up and trace the entire image using the line and arch tools. This is the really lame part and takes forever. Once the image is outlined use the push/pull tool to give the selection thickness. Once you have something you like export it as an stl file and you are ready to print!

Screen Shot 2014-08-14 at 4.34.15 PMFinally open you .stl file in your perfered 3d printing program. I use Repetier with my Printerbot simple. Send the print job to your printer and that it! Unless you are terrible at configuring your printer and it takes like 20 print attempts to figure out why your printer isn’t doing what you want and you really start to wonder why you even bought the dang machine in the first place.

Screen Shot 2014-08-14 at 4.34.54 PM

So I couldn’t get my printer to work like I wanted to and modified the object to be only a couple of layers. It turned out pretty good. My PrintrBot simple only has a 100 mm x 100 mm x 100 mmx print area so the stencil isn’t actually to size but I am always writing my app ideas down in a note book I have and I actually like the smaller size to save space.

 

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!