How To Make A Simple Drawing App with UIKit

This is a blog post by iOS Tutorial Team member Abdul Azeem, software architect and co-founder at Datainvent Systems, a software development and IT services company. At some stage in all of our lives, we enjoyed drawing pictures, cartoons, and other stuff. For me it was using a pen and paper when I was growing […] By .

Leave a rating/review
Save for later
Share
You are currently viewing page 4 of 5 of this article. Click here to view the first page.

Finishing Touches — A Custom Color Selector

Currently, you have 10 color buttons on your Drawing Canvas screen. However, with the custom RGB color selector, the discriminating artists using your app will have the ability to control to pick any available color from the RGB range.

Note: The RGB color model is an additive color model in which red, green, and blue light are added together in various ways to reproduce a broad array of colors. In iOS, the degree of intensity of red, green or blue is expressed with a floating point value between 0.0 and 1.0.

Various degrees of resolution are used in various computers – many years ago 24-bit color (“True color”), so called because each of R, G and B could have values from 0 to 255 (8 bits per channel, 24 bits total) was very high end but now it is common.

In this app, you will implement a true color 24-bit picker with 3 sliders that can have value from 0-255 for each of R, G and B channels.

Start with the RGB Color Selector control. Open MainStoryboard.storyboard and drag three UISliders and a few labels onto the Settings screen as you can see below:

Settings Layout 2

Set the range of Sliders from 0-255. Drag IBOutlets to SettingsViewController.h for each of the sliders, naming them redControl, greenControl and blueControl. Drag to connect the Value Changed action for all three to sliderChanged just as you did for the brushControl and opacityControl sliders.

Also connect each of the right-side labels to outlets so you can show the current value of each, naming them redLabel, greenLabel and blueLabel.

Since you’ve provided a preview of the brush size and opacity, you might as well provide a preview of the new brush color! :] The brush preview will be shown in the selected RGB color – as well, the opacity preview will be shown in the RGB color. No need for an extra image; you’ll re-use what you already have!

Open SettingsViewController.h and add the following three properties to save the current RGB values.

@property CGFloat red;
@property CGFloat green;
@property CGFloat blue;

Also synthesize these in SettingsViewController.m if you need to:

@synthesize red;
@synthesize green;
@synthesize blue;

Now open SettingsViewController.m and make the following changes to the SliderChanged function:

- (IBAction)sliderChanged:(id)sender {
    
    UISlider * changedSlider = (UISlider*)sender;
    
    if(changedSlider == self.brushControl) {
        
        self.brush = self.brushControl.value;
        self.brushValueLabel.text = [NSString stringWithFormat:@"%.1f", self.brush];
        
    } else if(changedSlider == self.opacityControl) {
        
        self.opacity = self.opacityControl.value;
        self.opacityValueLabel.text = [NSString stringWithFormat:@"%.1f", self.opacity];
        
    } else if(changedSlider == self.redControl) {
        
        self.red = self.redControl.value/255.0;
        self.redLabel.text = [NSString stringWithFormat:@"Red: %d", (int)self.redControl.value];
        
    } else if(changedSlider == self.greenControl){
        
        self.green = self.greenControl.value/255.0;
        self.greenLabel.text = [NSString stringWithFormat:@"Green: %d", (int)self.greenControl.value];
    } else if (changedSlider == self.blueControl){
        
        self.blue = self.blueControl.value/255.0;
        self.blueLabel.text = [NSString stringWithFormat:@"Blue: %d", (int)self.blueControl.value];
        
    }
    
    UIGraphicsBeginImageContext(self.brushPreview.frame.size);
    CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
    CGContextSetLineWidth(UIGraphicsGetCurrentContext(),self.brush);
    CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), self.red, self.green, self.blue, 1.0);
    CGContextMoveToPoint(UIGraphicsGetCurrentContext(), 45, 45);
    CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), 45, 45);
    CGContextStrokePath(UIGraphicsGetCurrentContext());
    self.brushPreview.image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    UIGraphicsBeginImageContext(self.opacityPreview.frame.size);
    CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
    CGContextSetLineWidth(UIGraphicsGetCurrentContext(),self.brush);
    CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), self.red, self.green, self.blue, self.opacity);
    CGContextMoveToPoint(UIGraphicsGetCurrentContext(),45, 45);
    CGContextAddLineToPoint(UIGraphicsGetCurrentContext(),45, 45);
    CGContextStrokePath(UIGraphicsGetCurrentContext());
    self.opacityPreview.image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
}

In above code, notice how the variable values are updated as the sliders are being updated.

To ensure the correct values are displayed for color when settings opens, change prepareForSegue in ViewController.m to:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    
    SettingsViewController * settingsVC = (SettingsViewController *)segue.destinationViewController;
    settingsVC.delegate = self;
    settingsVC.brush = brush;
    settingsVC.opacity = opacity;
    settingsVC.red = red;
    settingsVC.green = green;
    settingsVC.blue = blue;
    
}

Now that SettingsViewController has the values to display, update viewWillAppear in SettingsViewController.m to:

- (void)viewWillAppear:(BOOL)animated {
    
    // ensure the values displayed are the current values
            
    int redIntValue = self.red * 255.0;
    self.redControl.value = redIntValue;
    [self sliderChanged:self.redControl];
    
    int greenIntValue = self.green * 255.0;
    self.greenControl.value = greenIntValue;
    [self sliderChanged:self.greenControl];
    
    int blueIntValue = self.blue * 255.0;
    self.blueControl.value = blueIntValue;
    [self sliderChanged:self.blueControl];
        
    self.brushControl.value = self.brush;
    [self sliderChanged:self.brushControl];
    
    self.opacityControl.value = self.opacity;
    [self sliderChanged:self.opacityControl];
    
}

Finally open ViewController.m and make the following changes to the closeSettings method:

#pragma mark - SettingsViewControllerDelegate methods

- (void)closeSettings:(id)sender {
    
    brush = ((SettingsViewController*)sender).brush;
    opacity = ((SettingsViewController*)sender).opacity;
    red = ((SettingsViewController*)sender).red;
    green = ((SettingsViewController*)sender).green;
    blue = ((SettingsViewController*)sender).blue;
    [self dismissViewControllerAnimated:YES completion:nil];
}

In the above code, as the SettingsViewController closes, the updated RGB values are being fetched as well.

All right — time for another compile and run stage! Put the Color Picker through its paces. The selected RGB color, which is displayed in RGBPreview, is now the default brush stroke color on the drawing canvas!

Settings screen improved

But what good is all of these wonderful works of art if you can’t share them with the world? Since you can’t stick the pictures up on your refrigerator, you’ll tweet them in the next step of this tutorial! :]

Finishing Touches – Share and Enjoy!

Since iOS5 Twitter is natively incorporated in iOS, you will use iOS5 Twitter APIs to send the tweet along with your beautiful masterpiece.

Luckily this is super-easy! First, set ViewController as a UIActionSheetDelegate in ViewController.h:

@interface ViewController : UIViewController <SettingsViewControllerDelegate, UIActionSheetDelegate> {

Then open ViewController.m and make the following changes to the save function.

- (IBAction)save:(id)sender {

    UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:@""
                                                             delegate:self
                                                    cancelButtonTitle:nil
                                               destructiveButtonTitle:nil
                                                    otherButtonTitles:@"Save to Camera Roll", @"Tweet it!", @"Cancel", nil];
    [actionSheet showInView:self.view];
}

In above code, you are displaying an UIActionSheet with just two options, Save & Tweet.

Now you need a responder function for the UIActionSheet. Add the following code to the ViewController.m file.

- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
	if (buttonIndex == 1)
	{
       //Tweet Image
        }
	if (buttonIndex == 0)
	{
        //Save Image
        }
}

This is the delegate method for UIActionSheet. Let’s implement both cases one at a time.

Finishing Touches – Saving for posterity

Before you can tweet your creations to the world, you first need to add some Save logic.

Make the following modification to ViewController.m:

- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
    if (buttonIndex == 1)
    {
        //Tweet Image
    }
    if (buttonIndex == 0)
    {
        UIGraphicsBeginImageContextWithOptions(mainImage.bounds.size, NO,0.0);
        [mainImage.image drawInRect:CGRectMake(0, 0, mainImage.frame.size.width, mainImage.frame.size.height)];
        UIImage *SaveImage = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        UIImageWriteToSavedPhotosAlbum(SaveImage, self,@selector(image:didFinishSavingWithError:contextInfo:), nil);
    }
}

- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
    // Was there an error?
    if (error != NULL)
    {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"Image could not be saved.Please try again"  delegate:nil cancelButtonTitle:nil otherButtonTitles:@"Close", nil];
        [alert show];
    } else {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Success" message:@"Image was successfully saved in photoalbum"  delegate:nil cancelButtonTitle:nil otherButtonTitles:@"Close", nil];
        [alert show];
    }
}

In above code, you have first generated the drawing, then saved it to Camera Roll.

To generate the drawing, you simply obtain a snapshot of the main drawing canvas which is mainImage.image.

You first create a new Graphics Context, then draw the mainImage.image in that context. After that, you get a new Image from the graphics context and save it to Camera Roll.

Give it a try — build and run the app! Draw something on the screen and try to save it. If the image is successfully saved, you will see a popup displayed on screen as in the image below: