PaintCode Tutorial: Dynamic Buttons

Felipe Laso Marsetti

This post is also available in: Chinese (Simplified), Japanese, Russian

Easily create dynamic, resizable buttons with PaintCode!

Easily create dynamic, resizable buttons with PaintCode!

PaintCode is a neat app where you can draw user interfaces like in Photoshop – but instead of generating an image, it generates Core Graphics code!

This is really cool for several reasons:

  • Saves development time. If you were writing the Core Graphics code manually, it would take you much longer than using PaintCode. And time is money!
  • Saves binary size. By drawing a view in code, you no longer have to include the images in your binary, reducing your binary size.
  • Make it dynamic. And finally, since you’re creating your view in code you can do some cool things you couldn’t do if you were pre-generating the graphics – like easily changing the color of various aspects of the control at runtime.

Full disclosure: We received some review copies of PaintCode to generate this tutorial. However, any opinions in this article are honest and my own.

To get the most of this tutorial, you need some basic knowledge of iOS development. Also, it is helpful (but not 100% necessary) if you have some basic knowledge of Core Graphics. If you are new to Core Graphics, check out our Core Graphics tutorial series.

Without further ado, it’s time to get started!

Getting Started

PaintCodeIcon.175x175-75

To follow along with this set of PaintCode tutorials, you’re going to need PaintCode. You can download the trial app, or you can get the full app on the Mac App Store.

Note: If you’re reading this post during WWDC 2013, you are in luck: PaintCode is currently having a 25% off WWDC sale!

Even though the app is expensive, in my opinion it is well worth the price for the reasons explained in the introduction. But if you’re not sure, download the trial and go through this tutorial to see for yourself!

Creating Your First Dynamic Button

Before writing a single line of code, you’ll use PaintCode to design your entire button from scratch.

Launch PaintCode, go to File\Save, and name your project DynamicButton. This way you can periodically save your work as you go through this tutorial.

Click the Canvas button on the bottom right of the screen, and set the canvas size to be 480 by 150 pixels as shown in the screenshot below:

PaintCode canvas size in new document

Now change the color of the canvas to dark grey by clicking on the Underlay color value and entering 50 50 50 for the RGB values, as so:

Canvas color in PaintCode

Note: By default you are working on the non-retina version. If you’d like to view the retina version, click the Retina button next to the Canvas button. It’s a toggle button which will switch you between the retina and non-retina versions.

Now click the Round Rect button in the top toolbar and drag out a rectangle in your canvas.

You’ll notice that when you select a shape on your canvas, the panel on the left displays the properties for that shape. So ensure that the rounded rect on your canvas is selected, and set the following properties:

  • X: 4
  • Y: 4
  • Width: 473
  • Height: 41

draw a rectangle in PaintCode

Next you need to change the Fill value to set the button color.

In the left hand pane, click on the value for Fill, choose Add New Gradient… from the pop up dialog, and name the new gradient ButtonGradient. Now, click on the bottom left color stop. The color stops are the circles along the bottom edge of the gradient which indicate the various colors that compose the gradient.

Next, click on the color swatch on the lower right of the dialog, and enter 255 0 0 for the RGB values, as shown in the screenshot below:

Setting the button gradient colors

Click the color swatch again to close the color dialog.

Now, click on the right color stop, click on the color swatch, and modify the RGB values to 112 1 0 as below:

Setting the button gradient colors

PaintCode provides you with the ability you to name each color used in a shape so that you can refer to them later in your code. If the Code pane isn’t open at the bottom of your window, use the View > Code menu option to open it. Take a look at the code created, and you should see the references to the two colors you just created, as highlighted in the following image:

Color variable in PaintCode Code view

It’s a good idea to give your colors descriptive names to keep track of them.

Select the Colors tab located on the bottom section of the left panel, then double-click Fill Color. Change the Name value on the pop up to ButtonColorDark, as shown below:

rename color in PaintCode

Give this color a name as well, following the same steps as above to rename Stroke Color to ButtonColorLight.

Switch back to the shape properties in the center section of the left pane. Click on the Stroke dropdown and choose System Colors > Common Colors > BlackColor, to change the stroke color to black, as illustrated below:

add a stroke color in PaintCode

Increase the Width parameter to 2 under the Stroke sub-section, as so:

stroke width in PaintCode

Now give the button an outer glow by selecting Add New Shadow… from the dropdown for Outer Shadow under the Fill sub-section, like in the screenshot below:

Add new shadow in PaintCode

Change the Name of the shadow to OuterGlow; this gives it a memorable name just as you did with the colors above. Next, click on the dropdown menu next to Color, and click on the white square in the color grid. Click on the color rectangle, which is now white, and lower the opacity to 128.

Change the offsets and blur radius as shown below, and click outside the popup to close it:

Adding a glow in PaintCode

Move to the Colors tab and rename Glow to InnerGlowColor.

Next, add an inner highlight to Inner Shadow by selecting Add New Shadow… from the dropdown. Rename the shadow to Highlight, set the color to white, the alpha to 130, and change the offsets and blur radius as shown below:

The color name may already be set to Shadow Color 2 depending on the workflow you followed, but if not, modify the name of this color to match.

Your button should look like the one below:

Final Button

At this point, the button looks pretty cool, but it’s just a fixed size. To make it resizable, you need to put a frame around it. The idea is that in code you will change the size of this frame, and in the next few steps you will set up PaintCode so that anything inside the frame resizes appropriately.

Adding a Button Frame

Select Frame from the top toolbar and draw a frame around the button shape. Set the frame properties as shown below:

Adding a frame for the button

Multi-select the Frame and the Rounded Rectangle shapes, and group them using the Option-Command-G key combination. Alternately, you can use the Selection > Group menu item to perform the same task. Rename the group to Button.

Note that you can now see the grouped shape and its two sub-shapes in the top section of the left hand pane, as shown below:

Grouping elements

Select the Rounded Rectangle in the left panel. Look at the icon showing the item constraints – it’s the box within a frame, with straight bars and springs next to the shape placement and dimension values.

ResizeWindow

You’ll want your button to be resizable horizontally and vertically, while remaining centered horizontally in the frame. This means the top, left, and right constraints connecting the frame and the box should be straight (think rigid), and the bottom should be a spring (think flexible). This will keep the button centered in the frame.

However, inside the box, the vertical and the horizontal constraints should be springs. This allows the button to stretch to fill the frame.

To modify the constraints of your object, simply click the straight rod or the spring representing the constraint you wish to change to alternate it between a straight rod and a spring.

When you have finished modifying the constraints, they should look like the image below:

Button constraints

All done? Great! Now you can select the Frame surrounding your button, and drag its transform handles around the screen to see the button resize, just as in the screenshot below:

Final button

That completes the creation of your button in PaintCode — now you’re ready to hook it up with some code.

Bringing Your Button Into Your iOS App

You now have a complete button to use in your project — all without writing a stitch of code! But don’t worry, code fiends, it’s time to roll up your sleeves and use this new button in your project.

There’s a starter project all ready for you that you can use to showcase your new button.
Download the PaintCode Starter Project, extract it to a suitable location on your hard drive, open it in Xcode, and take a look at what’s already in place.

Note: The storyboard for the project has Auto Layout turned off. It’s not a requirement for all new projects, but in this case the elements work better with the good old springs and struts.

The project is a tab bar application with three view controllers; one for each PaintCode element you’ll create in each part of this series. In this PaintCode tutorial you will use the ButtonViewController class to host your new dynamic button.

There’s also an empty folder within Classes > Views. In this series, this is where you are going to be creating your custom controls. Rather than creating a control from scratch, you will be subclassing existing controls and replacing the draw code.

In this tutorial, you will be subclassing UIButton. Let’s get started!

Creating Your Custom Button

Inside the Classes folder, right-click on the Views folder. Select New\File… and create a new file with the iOS\Cocoa Touch\Objective-C class template. Name the class ButtonView, and make it a subclass of UIButton.

This class will draw the custom button using the code created by PaintCode.

To get your view all ready for your custom button, open ButtonView.m, delete the initWithFrame: method and uncomment the code for drawRect:. The file should now look like this:

#import "ButtonView.h"
 
@implementation ButtonView
 
- (void)drawRect:(CGRect)rect
{
    // Drawing code
}
 
@end

drawRect: is where the magic happens – this is where you will be putting the code that was generated by PaintCode. If you are not familiar with drawRect: and what it does, refer back to our Core Graphics tutorial series.

Switch back to PaintCode and make sure the Code View is visible. If it’s not visible, use the View > Code menu option to make it visible. Find the option settings along the bottom edge of the main PaintCode view, as shown in the image below:

PaintCode code settings

Set the platform to iOS > Objective-C, the OS version to iOS 5+, origin to Default Origin, and memory management to ARC. If this is your first time using PaintCode — or you haven’t mucked about with these settings before — everything except for memory management should already be set correctly by default.

Copy and paste the code from PaintCode’s code view into drawRect:. The method should now look like the following:

- (void)drawRect:(CGRect)rect
{
    //// General Declarations
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = UIGraphicsGetCurrentContext();
 
    //// Color Declarations
    UIColor* buttonColorDark = [UIColor colorWithRed: 0.439 green: 0.004 blue: 0 alpha: 1];
    UIColor* buttonColorLight = [UIColor colorWithRed: 1 green: 0 blue: 0 alpha: 1];
    UIColor* innerGlowColor = [UIColor colorWithRed: 1 green: 1 blue: 1 alpha: 0.502];
    UIColor* shadowColor2 = [UIColor colorWithRed: 1 green: 1 blue: 1 alpha: 0.51];
 
    //// Gradient Declarations
    NSArray* buttonGradientColors = [NSArray arrayWithObjects:
                                     (id)buttonColorLight.CGColor,
                                     (id)buttonColorDark.CGColor, nil];
    CGFloat buttonGradientLocations[] = {0, 1};
    CGGradientRef buttonGradient = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)buttonGradientColors, buttonGradientLocations);
 
    //// Shadow Declarations
    UIColor* outerGlow = innerGlowColor;
    CGSize outerGlowOffset = CGSizeMake(0.1, -0.1);
    CGFloat outerGlowBlurRadius = 3;
    UIColor* highlight = shadowColor2;
    CGSize highlightOffset = CGSizeMake(0.1, 2.1);
    CGFloat highlightBlurRadius = 2;
 
    //// Frames
    CGRect frame = CGRectMake(0, 0, 480, 49);
 
    //// Button
    {
        //// Rounded Rectangle Drawing
        CGRect roundedRectangleRect = CGRectMake(CGRectGetMinX(frame) + 4, CGRectGetMinY(frame) + 4, CGRectGetWidth(frame) - 7, 41);
        UIBezierPath* roundedRectanglePath = [UIBezierPath bezierPathWithRoundedRect: roundedRectangleRect cornerRadius: 6];
        CGContextSaveGState(context);
        CGContextSetShadowWithColor(context, outerGlowOffset, outerGlowBlurRadius, outerGlow.CGColor);
        CGContextBeginTransparencyLayer(context, NULL);
        [roundedRectanglePath addClip];
        CGContextDrawLinearGradient(context, buttonGradient,
                                    CGPointMake(CGRectGetMidX(roundedRectangleRect), CGRectGetMinY(roundedRectangleRect)),
                                    CGPointMake(CGRectGetMidX(roundedRectangleRect), CGRectGetMaxY(roundedRectangleRect)),
                                    0);
        CGContextEndTransparencyLayer(context);
 
        ////// Rounded Rectangle Inner Shadow
        CGRect roundedRectangleBorderRect = CGRectInset([roundedRectanglePath bounds], -highlightBlurRadius, -highlightBlurRadius);
        roundedRectangleBorderRect = CGRectOffset(roundedRectangleBorderRect, -highlightOffset.width, -highlightOffset.height);
        roundedRectangleBorderRect = CGRectInset(CGRectUnion(roundedRectangleBorderRect, [roundedRectanglePath bounds]), -1, -1);
 
        UIBezierPath* roundedRectangleNegativePath = [UIBezierPath bezierPathWithRect: roundedRectangleBorderRect];
        [roundedRectangleNegativePath appendPath: roundedRectanglePath];
        roundedRectangleNegativePath.usesEvenOddFillRule = YES;
 
        CGContextSaveGState(context);
        {
            CGFloat xOffset = highlightOffset.width + round(roundedRectangleBorderRect.size.width);
            CGFloat yOffset = highlightOffset.height;
            CGContextSetShadowWithColor(context,
                                        CGSizeMake(xOffset + copysign(0.1, xOffset), yOffset + copysign(0.1, yOffset)),
                                        highlightBlurRadius,
                                        highlight.CGColor);
 
            [roundedRectanglePath addClip];
            CGAffineTransform transform = CGAffineTransformMakeTranslation(-round(roundedRectangleBorderRect.size.width), 0);
            [roundedRectangleNegativePath applyTransform: transform];
            [[UIColor grayColor] setFill];
            [roundedRectangleNegativePath fill];
        }
        CGContextRestoreGState(context);
 
        CGContextRestoreGState(context);
 
        [[UIColor blackColor] setStroke];
        roundedRectanglePath.lineWidth = 2;
        [roundedRectanglePath stroke];
    }
 
    //// Cleanup
    CGGradientRelease(buttonGradient);
    CGColorSpaceRelease(colorSpace);    
}

If you read through the code, you will see that it’s using Core Graphics to do the drawing. Although some (or most) of the methods may be new to you, they aren’t really that difficult to understand. Besides, this is what our friend Mr. PaintCode is here for; to avoid having to learn Core Graphics and spend sleepless nights struggling with custom drawing code! :]

Note: Don’t worry about the code, spacing or syntax in drawRect: for now. You’ll come back to this code shortly and make some edits to this method, including updating the syntax to modern Objective-C.

Now it’s time to test the button.

Locate the storyboard in your Project Navigator and go to the ButtonViewController scene. Drag a View object from the Object Library and set its X and Y coordinates to 20 and 35. Next, set its Width and Height to 280 and 41.

Finally, update the view’s Autosizing widget so it only allows the button to resize horizontally, as shown below:

Button view autosizing

Switch to the Identity Inspector and set the class to ButtonView.

Note:You might be wondering why you used a UIView in the storyboard, when ButtonView is a subclass of UIButton. The reason is that UIButton is a subclass of UIView and thus a button is a view.

Later on in this PaintCode tutorial you will make the button act and behave just like it should. After all — it is a UIButton subclass!

Subclass watch out

Time to test things and see your new custom control in action!

Click Run and take a look at your shiny new button for the very first time:

First button test

Umm…the button looks kinda cut off. What’s going on?

Take a look at drawRect: in ButtonView.m:

- (void)drawRect:(CGRect)rect
{
    ...
 
    //// Frames
    CGRect frame = CGRectMake(0, 0, 480, 49);
 
    ...
}

Aha! The button’s frame is being calculated with fixed values for width and height. What you want is to have dynamic width and height values — and respond accordingly to the frame you use to create the button.

To fix this situation this, simply modify the line which sets the frame variable as follows:

- (void)drawRect:(CGRect)rect
{
    ...
 
    //// Frames
    CGRect frame = rect;
 
    ...
}

The above code simply sets the view’s frame as the frame for drawing the button.

Run the project one more time and behold!

First button test fixed

Yay! The button is being drawn just as intended. Rotate your simulator (or your device) and notice how the button resizes horizontally but keeps its height, just as expected.

You’ve made great progress so far — but what if you made the button even more dynamic?

Setting Button Properties Dynamically

Controlling the button’s size dynamically is one thing — but you’re going to take things to the next level and add some sliders to the ButtonViewController scene that control the RGB values for the color of the button.

Switch back to the storyboard and add three Slider objects from the object library and position them right below the button view. In the Size Inspector, update the Autosizing widget for all three sliders as shown:

Slider Autosizing

Optionally, you can move the button down a bit and add a label right above the button that reads “Tap Me” so your users know that it’s a button they can tap and interact with. This is what the scene should look like at this point:

Scene with sliders

In the Attributes Inspector, change the Min Track Tint color of the sliders to the following RGB values:

  • Top slider: R:255 G:0 B:0
  • Middle slider: R:0 G:255 B:0
  • Bottom slider: R:0 G:0 B:255

Your sliders should now resemble the ones in the following screenshot:

Slider colors

Note: Sometimes the Background color of the slider changes when you set the Min Track Tint. This appears to be an issue with Xcode, but you can fix this by simply setting the Background color to Default.

Switch to ButtonViewController.m, and add the following import and class extension with outlets to the top of the file:

#import "ButtonView.h"
 
@interface ButtonViewController ()
 
@property (weak, nonatomic) IBOutlet UISlider *blueSlider;
@property (weak, nonatomic) IBOutlet ButtonView *buttonView;
@property (weak, nonatomic) IBOutlet UISlider *greenSlider;
@property (weak, nonatomic) IBOutlet UISlider *redSlider;
 
@end

Connect the buttonView outlet to the UIView for the button, and the UISlider outlets to the corresponding sliders on the storyboard. If you are unsure how to connect views to outlets, check out our How To Create a Simple iPhone App series.

In order to make the button’s color change as you drag the sliders, the ButtonView class needs to be updated as the slider values change.

Open ButtonView.h and add the following three properties between the @interface and @end lines:

@property (assign, nonatomic) CGFloat blueColor;
@property (assign, nonatomic) CGFloat greenColor;
@property (assign, nonatomic) CGFloat redColor;

These properties simply store the current RGB color of the button.

Now switch to ButtonView.m and take a look at drawRect:. Notice how the section with the Gradient Declarations uses the slightly longer (and older) array notation. To maintain good coding practices, replace the Gradient Declarations line with the following code:

NSArray *buttonGradientColors = @[(id)buttonColorLight.CGColor, (id)buttonColorDark.CGColor];

Next, replace the whole section with the “Color Declarations” comment with the following code:

-(void)drawRect:(CGRect)rect {
    ...
 
    // 1
    UIColor *buttonColorLight = [UIColor colorWithRed:self.redColor green:self.greenColor blue:self.blueColor alpha: 1];
 
    // 2
    if (self.state == UIControlStateHighlighted) {
        buttonColorLight = [UIColor colorWithRed:self.redColor green:self.greenColor blue:self.blueColor alpha:0.5];
    }
 
    // 3
    CGFloat buttonColorLightRGBA[4];
    [buttonColorLight getRed:&buttonColorLightRGBA[0]
                       green:&buttonColorLightRGBA[1]
                        blue:&buttonColorLightRGBA[2]
                       alpha:&buttonColorLightRGBA[3]];
 
    // 4
    UIColor *buttonColorDark = [UIColor colorWithRed:(buttonColorLightRGBA[0] * 0.5)
                                               green:(buttonColorLightRGBA[1] * 0.5)
                                                blue:(buttonColorLightRGBA[2] * 0.5)
                                               alpha:(buttonColorLightRGBA[3] * 0.5 + 0.5)];
    // 5
    UIColor *innerGlowColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.53];
    UIColor *shadowColor2 = [UIColor colorWithRed:1 green:1 blue:1 alpha:0.51];
 
    ...
}

Here’s what the above code does:

  1. Creates a local UIColor variable for the light color of the button (remember, you set that up in the gradient editor) with the color values stored in the class properties.
  2. Lowers the alpha value of the color to 50% if the user is touching the button, which is indicated by the UIControlStateHighlighted state. This gives the user some visual feedback for the button tap.
  3. Loads the RGB color values for the light color into an array of CGFloat values.
  4. Creates the button’s dark color (remember, you set that up in the gradient editor) based on the light color.
  5. Creates the inner glow and shadow colors based on static values.

It’s a good idea to start your application with a default color already set for the button. Since the button is loaded from a storyboard, you’ll need to implement initWithCoder: (in ButtonView.m) to achieve this:

-(id)initWithCoder:(NSCoder *)coder {
    if (self = [super initWithCoder:coder]) {
        self.redColor = 1.0f;
        self.greenColor = 0.0f;
        self.blueColor = 0.0f;
 
        self.contentMode = UIViewContentModeRedraw;
    }
 
    return self;
}

This sets the button’s initial color to red as soon as it is loaded from the storyboard, and the view’s content mode to UIViewContentModeRedraw.

Note: If you’re wondering why initWithCoder: was overridden and not initWithFrame:, it’s because initWithFrame: is used to create a view programmatically, whereas initWithCoder: is used to create UI elements via a NIB or storyboard.

There’s one last thing to do before testing your changes. Switch to ButtonViewController.m and implement viewDidLoad as follows:

-(void)viewDidLoad {
    [super viewDidLoad];
 
    [self.redSlider setValue:self.buttonView.redColor];
    [self.greenSlider setValue:self.buttonView.greenColor];
    [self.blueSlider setValue:self.buttonView.blueColor];
}

The above code sets the value for each slider to the RGB components of the button’s initial color.

Build and run, and modify the slider controls to see how the the button’s color changes, as demonstrated in the screenshot below:

Testing the sliders

Oh, wait — the app isn’t responding! Why is that?

There are outlets hooked up to the sliders, but currently ButtonViewController has no way to detect or respond to changes in the slider values. You’ll need to add some IBAction code to handle these changes.

Responding To Actions

Responding to events from the sliders and the button is not too difficult to implement.

Add the following method to ButtonViewController.m:

-(IBAction)sliderValueChanged:(UISlider *)slider {
    if (slider == self.redSlider) {
        self.buttonView.redColor = self.redSlider.value;
    } else if (slider == self.greenSlider) {
        self.buttonView.greenColor = self.greenSlider.value;
    } else if (slider == self.blueSlider) {
        self.buttonView.blueColor = self.blueSlider.value;
    }
 
    [self.buttonView setNeedsDisplay];
}

The above method receives a pointer to the slider whose value has changed. You compare the pointer against your slider outlets and update the button’s color according to the change made to the slider.

Notice that you call setNeedsDisplay on the button at the very end of the method in order for the draw method to be called again to use the new color.

Now switch to the storyboard, right-click on each slider in turn, and connect the Value Changed event to the ButtonViewController's
sliderValueChanged: method, as shown in the screenshot below:

Connecting the sliders

Build and run your project again, and try once more to move the sliders around to change the button’s color, as below:

Working sliders

Hey — it works! You now have not only a dynamically sizing button, but one whose color can be changed dynamically as well.

Go ahead and tap on your button to see what happens.

Oh, right — it doesn’t appear to do anything yet. The button doesn’t highlight to indicate it was tapped, nor is there any indication that the button is billing your credit card for $99.95 at this very moment. (Just kidding! :])

There are no actions hooked up to this button at this point — that’s your next task!

Finishing Touches

Go back to ButtonView.m and add the following methods:

-(void)setEnabled:(BOOL)enabled {
    [super setEnabled:enabled];
    [self setNeedsDisplay];
}
 
-(void)setHighlighted:(BOOL)value {
    [super setHighlighted:value];
    [self setNeedsDisplay];
}
 
-(void)setSelected:(BOOL)value {
    [super setSelected:value];
    [self setNeedsDisplay];
}

The code above informs the button that it needs to redraw itself whenever it’s enabled and disabled, selected and unselected, or highlighted and unhighlighted. This allows the color changes for the highlighted state to show up.

Next, add the following method to ButtonViewController.m:

-(IBAction)buttonTapped:(UIButton *)button {
    ButtonView *buttonView = (ButtonView *)button;
 
    NSString *messageString = [NSString stringWithFormat:@"Red: %f\nGreen: %f\nBlue: %f\n Alpha: %f",
                               buttonView.redColor,
                               buttonView.greenColor,
                               buttonView.blueColor,
                               buttonView.alpha];
 
    UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Button Colors"
                                                        message:messageString
                                                       delegate:nil
                                              cancelButtonTitle:@"Dismiss"
                                              otherButtonTitles:nil];
    [alertView show];
}

This method simply presents an alert view when the button is tapped, showing the button’s current RGB and alpha values.

On the storyboard, connect this method to the Touch Up Inside event of the button.

Build and run your project one final time, tap the button and check out the results! You should see something similar to the screenshot below:

Final project

Perfect – things are working just as they should!

Where To Go From Here?

You can download the final project with the PaintCode file and Xcode project here.

Congratulations – you are now well on the road to leveraging the full power of PaintCode. It will help you design custom and dynamic UI elements for your apps in a fraction of the time!

There’s still two parts left in the series; part two will walk you through constructing a custom progress indicator and part three will show you how to make some neat dynamic bezier arrows!

For now though, you can take your current project a little further by trying out some of the following sugestions:

  • Play around with PaintCode and learn more about its features and the available tools.
  • Add text, more shadows, or other PaintCode elements or shapes to your button.
  • Override initWithFrame: in ButtonView so you can create dynamic buttons programmatically.
  • Create a custom initializer for ButtonView that lets you set the initial frame and RGB values.

We hope you’ve enjoyed this PaintCode tutorial and we’re looking forward to seeing you in the next parts of the series. Things will get a bit more advanced as you move through the next two parts of this series — but we know you’re up to the task!

Happy PaintCoding – and as always, if you have any questions or comments please join the forum discussion below!

Felipe Laso Marsetti

Felipe Laso is an iOS developer working at Lextech Global Services. He’s also an aspiring game designer/programmer. You can follow him on Twitter as @airjordan12345 or on his blog.

User Comments

9 Comments

  • Hi, first I wanted to thank you for this tutorial, I was just struggling with Paintcode app yesterday and suddenly this tutorial at ray's site!.

    However I'm still having troubles to achieve the button's standar behaviour, for the highlighted, selected, etc states, basically it does nothing when creating a button object from code.

    This is my initialization code (actually empty), and within the drawRect method I only change the following to fit the drawing to the bounds of the button.

    Code: Select all

         //// Changing the drawing rectangle
        //// Subframes
        CGRect group = self.bounds;


    Here's the whole drawRect method: http://pastebin.com/gnKsscrX


    And below the rest of the methods:

    Code: Select all
    - (id)initWithFrame:(CGRect)frame
    {
        self = [super initWithFrame:frame];
        if (self) {
            // Initialization code
        }
        return self;
    }

    -(void)setEnabled:(BOOL)enabled {
        [super setEnabled:enabled];
        [self setNeedsDisplay];
    }

    -(void)setHighlighted:(BOOL)value {
        [super setHighlighted:value];
        [self setNeedsDisplay];
    }

    -(void)setSelected:(BOOL)value {
        [super setSelected:value];
        [self setNeedsDisplay];
    }
    rubs
  • hey rubs,

    In addition to the setHighlighted, setSelected, and setEnabled methods, you need to update drawn rect with the code to change the color if the button is tapped:

    Code: Select all
    // 1
        UIColor *buttonColorLight = [UIColor colorWithRed:self.redColor green:self.greenColor blue:self.blueColor alpha: 1];

        // 2
        if (self.state == UIControlStateHighlighted) {
            buttonColorLight = [UIColor colorWithRed:self.redColor green:self.greenColor blue:self.blueColor alpha:0.5];
        }


    Also, in the tutorial, notice how you initialize the color properties inside initWithCoder:, so if you want to create a button programmatically don't forget to add those initial values inside initWithFrame:

    Hope this helps and thanks for enjoying the tutorial,
    Feli :)
    airjordan12345
  • Yet another great tutorial from you guys!

    Just a little note for those of you who are not ready to pay $99 to get the Core Graphics code that represents the customised graphics in you small scale app project.
    Have a look at my post about SVGm (viewtopic.php?f=4&t=7322) that allows you to convert svg files to code. Draw the graphics in your favourite vector program and convert it to code using the app.
    dfrifeldt
  • GREAT tutorial and app! (purchased after completing the tutorial btw).

    I suspect there is errata with one line of the button code posted on the web page:

    Code: Select all
    //// Button
        {
            //// Rounded Rectangle Drawing
            CGRect roundedRectangleRect = CGRectMake(CGRectGetMinX(frame) + 4, CGRectGetMinY(frame) + 4, CGRectGetWidth(frame) - 7, 41);


    This causes clipping of the button in the view because the Y dimension winds up too large (should be around 34 not 41). The code in the complete project available for download is correct:

    Code: Select all
    //// Button
        {
            //// Rounded Rectangle Drawing
            CGRect roundedRectangleRect = CGRectMake(CGRectGetMinX(frame) + 4, CGRectGetMinY(frame) + 4, CGRectGetWidth(frame) - 7, floor((CGRectGetHeight(frame) - 4) * 0.91111 + 0.5));


    If the code is cut and pasted from the web page (necessary if running PaintCode in the demo mode) the button clips at the bottom.

    Thanks!
    pterotactile
  • There's a bug that causes the button to stretch incorrectly when resized. I fixed this by adding a line to ButtonView's -initWithCoder:

    self.contentMode = UIViewContentModeRedraw;
    intelliot
  • intelliot wrote:There's a bug that causes the button to stretch incorrectly when resized. I fixed this by adding a line to ButtonView's -initWithCoder:

    self.contentMode = UIViewContentModeRedraw;


    The tut has been updated to include this line inside initWithCoder:

    :)

    Many thanks!
    airjordan12345
  • Hello, I'm new to iOS. Can I post this article to my blog?
    traximus
  • The button looks great when you have a white background, but when you have a colored background there is an ugly white frame araound the button. How do I get rid of that?
    imyrvold
  • To answer myself, set the background to Clear Color
    imyrvold

Other Items of Interest

Ray's Monthly Newsletter

Sign up to receive a monthly newsletter with my favorite dev links, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

Hang Out With Us!

Every month, we have a free live Tech Talk - come hang out with us!


Coming up in September: iOS 8 App Extensions!

Sign Up - September

RWDevCon Conference?

We are considering having an official raywenderlich.com conference called RWDevCon in DC in early 2015.

The conference would be focused on high quality Swift/iOS 8 technical content, and connecting as a community.

Would this be something you'd be interested in?

    Loading ... Loading ...

Our Books

Our Team

Tutorial Team

... 49 total!

Update Team

Editorial Team

  • Ryan Nystrom

... 23 total!

Code Team

  • Orta Therox

... 1 total!

Translation Team

  • Victor Grushevskiy

... 33 total!

Subject Matter Experts

  • Richard Casey

... 4 total!