Quantcast
Channel: Kodeco | High quality programming tutorials: iOS, Android, Swift, Kotlin, Unity, and more
Viewing all articles
Browse latest Browse all 4398

PaintCode Tutorial for Developers: Custom Progress Bar

$
0
0

PaintCode-ProgressBar-feature

As you may know, PaintCode is a fantastic little Mac app that turns your vector graphic designs into Core Graphics code you can easily use in your iOS apps.

A while back we wrote a PaintCode tutorial for designers and a PaintCode tutorial for developers that taught you how to create and animate a stop watch control in PaintCode, and integrate it into your own iOS app. You learned a lot; but that only scratches the surface of what PaintCode can do!

In this intermediate PaintCode tutorial, you’ll draw a dynamic custom progress bar in PaintCode. You’ll then add this progress bar to Songy, a music player app, to show the progress of the song playing from your device’s music library.

To properly test Songy, you will need a physical iOS device with at least one song stored in your music library. You can still follow along with the simulator, but you won’t be able to see the progress bar update in real time.

Without further, it’s time to get started on your custom progress bar!

Getting Started

First make sure you have a paid copy of PaintCode. The free trial will not work for this tutorial; it has a number of restrictions such as the inability to copy/paste, the inability to export code, and more.

Then open PaintCode and create a new document by going to File\New on the main menu.

Ensure that your PaintCode screen is set up with the Library on the left and that the Code panel at the bottom is visible and set to iOS > Swift. It’s always interesting to see the generated code as you move through the tutorial – if nothing else, it’s a cool way of learning Core Graphics! :]

PaintCodeInitial

Use the Inspector to rename the canvas to Progress Bar and set the Width to 290 and the Height to 13:

02-CanvasName1

Your canvas is now the correct size for the progress bar you’ll be making in this tutorial.

The basic elements that make up the progress bar are two rectangles with a corner radius. The first rectangle will be a stroke outline of the entire progress bar. The second rectangle will be a solid fill, which will indicate how much of the song track has been played.

In the toolbar at the top, click on the Rect button (the left-most button), then click and drag inside the canvas to create a rectangle. Don’t worry about how things look just yet – you’ll use the Inspector to fix that.

Click on the newly created rectangle to select it, then use the Inspector to change the name of the rectangle to Progress Outline. Set the Radius to 5, then modify the position and size of the rectangle to have X and Y values of 1, Width of 288, and Height of 10. Set the Fill color to No Fill and assign any color you like to Stroke:

ProgressOutline

You’ll change the final color once you’ve finished creating the progress bar.

Check out your canvas and you should have something that resembles the following image:

ProgressOutline

Next up – a second rectangle for the song progress indicator.

Click the Rect button in the toolbar and then click and drag inside of your canvas. In the Inspector, change the name of the rectangle to Progress Active, the Radius to 5, the X and Y values to 1, Width to 288, and Height to 10.

Select a Fill color and click the x next to Stroke to remove it:

ProgressActive

Your progress bar should look like the following:

ProgressBarActive

PaintCode Frames

Frames in PaintCode let you to create flexible elements. For example, you’ve created a progress bar that’s exactly 288 pixels wide. However, you probably want this control to resize for different screen sizes.

When you create a frame, PaintCode encloses the generated drawing code into a method where one of the parameters is the frame’s rectangle. Using PaintCode, you can then adjust how objects, such as your progress bar’s rectangle, resize within the frame.

In the top toolbar, click the Frame button and then click anywhere within the gray progress bar.

Look at the generated code in the Code section at the bottom of the screen; it has a method header with a frame property. The origin of your progress elements will depend upon this property.

Time to enclose your control in a frame.

Click Frame in the Browser pane on the right side. Ensure the Apply only to entirely enclosed shapes option is checked; this means that only shapes positioned entirely within the frame will be affected by your changes. This keeps the grey progress bar from moving out of position while you change the frame’s properties.

When the progress bar is fully enclosed, any changes you make to the frame’s properties will also change the progress bar’s properties.

Change the X and Y values to 0, Width to 290 and Height to 12:

ProgressFrame

If you look at the code at the bottom of the screen, you’ll see the Progress Outline rectangle width still doesn’t depend on the frame value:

//// Progress Outline Drawing
let progressOutlinePath = UIBezierPath(roundedRect: CGRect(x: frame.minX + 1, y: frame.minY + 1, width: 288, height: 10), cornerRadius: 5)
UIColor.blackColor().setStroke()
progressOutlinePath.lineWidth = 1
progressOutlinePath.stroke()

To fix this, you’ll configure the resizing constraints in the Inspector. Select the Progress Outline rectangle:

ResizingConstraints1

The constraints here indicate the relationship between the shape and its frame. In this case, the width constraint is a straight line, meaning the rectangle’s width doesn’t depend on the frame. Click the Width constraint to change it to a wavy line:

ResizingConstraints

Now your rectangle will resize when the frame resizes. The generated code for progressOutlinePath changes to reflect this:

let progressOutlinePath = UIBezierPath(roundedRect: CGRectMake(frame.minX + 1, frame.minY + 1,
         floor((frame.width - 1) * 0.99654 + 0.5), 10), cornerRadius: 5)

When you resize Frame, Progress Outline will resize too:

ResizeFrame

Note: In the above animated GIF demonstrating how Progress Outline resizes with the frame, I’ve hidden the Progress Active rectangle just for demonstration purposes.

Voilà – you have the basic progress bar. Now to dynamically resize Progress Active.

Setting up Variables

Now that you have the basic progress bar ready, it’s time to use one of PaintCode’s coolest features: Variables. In the left pane, you’ll see a section at the very bottom named Variables. Click the plus button and select Number from the popup menu.

Rename the number variable to progress and dismiss the popover:

ProgressVariable

Click on the Progress Active element in the right pane to select it. You’ll now connect the progress variable to the Progress Active element’s width.

Drag from the circle next to the numeric value of the progress variable you just created to the Width property for Progress Active:

Variable

Click and drag up and down over the numeric value of the progress variable; you’ll see the progress bar’s width change to match the value of the variable:

VariableActive

Magical2

Note: Along with numeric variables like this one that dynamically change numeric attributes, you can also create expression variables. Want the width to grow twice as fast as the variable? Create an expression that multiplies the progress number variable by two – and use that expression variable as the input for the bar’s width attribute.

Awesome job! You’ve finished the progress bar in PaintCode and are ready to take it into your iOS project.

Adding the Progress Bar to your Project

Before exporting the progress bar from PaintCode, download the starter project and take a look at what it contains.

The Starter Project

Songy shows a list of music stored in your Music library along with some buttons for you to interact with. Build and run the application to see what the app currently looks like; You should do this on a device with music in the device’s library so you have some music show up in the table view.

11-StarterProject

Play any song; you’ll notice there’s no progress indicator for the current song. This is where your custom progress bar comes into play.

Start by looking at Main.storyboard. This consists of a single scene with a table view to show the list of songs along with their album art. Below the table view, there are three labels to show the current song name, artist name, and album name.

While not visible, right below the last label is a UIView that will contain your custom progress bar. Finally, you have four buttons to change to the previous and next songs, play or pause, and stop the current song.

Switch over to ViewController.swift and briefly walk through the code that’s in place. At the top of the class there are outlets for all of the UI elements that the app needs to talk to.

Below these is an MPMusicPlayerController property that is initialized with the application music player, followed immediately by an array of MPMediaItem objects that return all of the items in your music library.

Below the property declarations, viewDidLoad() calls updatePlayerLabels(_:artist:album:) to clear the three labels of any placeholder text that may have been there when loading the controller from the storyboard. It also schedules an instance of NSTimer to call refresh().

updatePlayerLabels(_:artist:album:) updates the current song name, artist and album name.

refresh() is called repeatedly by the timer. It gets the current playing item (if one exists) and calls updatePlayerLabels(_:artist:album:) to show the information corresponding to the current song that is playing – or nothing if there is no song playing. This is the method you’ll modify to update your progress bar.

The IBAction methods for the Next, Play/Pause, Previous, and Stop buttons do what you’d expect as well as guard against the event that no song is currently playing. This will prevent nasty crashes.

Finally, the table view data source and delegate methods take care of displaying the music library information, and plays the song when one is selected.

Time to switch back to PaintCode so you can export the progress bar into the project.

Adding the PaintCode Code to Your App Using StyleKit

If you’ve read our PaintCode Tutorial for Designers, you’ll know all about the StyleKit class, which is generated by PaintCode and contains all the drawing code. Canvases are automatically added to the StyleKit catalog, so your progress bar is all ready to go.

In PaintCode, click on File\Export. In the popup window, click the StyleKit button and fill out the required information as follows:

PaintCodeExport

Click the Export button. When prompted, locate the Songy project code folder and click Save.

Back in Xcode, add the exported Swift file to your project either by dragging it into the Project Navigator, or by right-clicking the Songy folder in the Project Navigator and selecting Add files to “Songy”…:

13-AddingSwiftFile

You’ve now added the drawing code to your project.

If you look at PaintCodeTutorial.swift you’ll see that it’s a simple NSObject with a drawProgressBar() method. Next you need to create a UIView subclass that calls this method to draw the progress bar.

Right-click the Songy folder in the Project Navigator and select New File…. Choose iOS\Source\Cocoa Touch Class and click Next. Name the class ProgressBar and make it a subclass of UIView. Click Next and save the file into your project directory.

Open ProgressBar.swift and uncomment drawRect(_:). By overriding this method you can do any custom drawing you need.

Call the generated PaintCode code like so in drawRect(_:):

override func drawRect(rect: CGRect) {
  PaintCodeTutorial.drawProgressBar()
}

I always forget to change the UIView class in the storyboard – so I’ll remind you of it now! :] Switch over to Main.storyboard.

Inside the storyboard, locate the UIView below the labels. It’s white, so you might not be able to see it in the scene. Select Progress View from the Document Outline. With the view selected, switch to the Identity Inspector and change the class to ProgressBar.

Changing the class name in the identity inspector

Build and run your application; you’ll see the first version of your custom progress bar:

FirstProgressBar

Yup – it’s that easy to integrate PaintCode elements in your app! Simply export the StyleKit from PaintCode and call the StyleKit drawing method from drawRect(_:) in a UIView subclass.

The bar doesn’t quite stretch to the device width. This is because you’re using the default version of the PaintCode method – you’ll fix that shortly.

One of the best features of PaintCode is its tight integration with Xcode projects. You’ll next make a few changes in PaintCode and re-export the progress bar out to Xcode.

Adding Colors in StyleKits

Your progress bar is still a boring shade of gray. However, you can make it any color you like to fit the style of your app. If you decide to change the color, it’s easy to use StyleKit to adjust it and re-export.

You’ll create the color in the StyleKit Catalog; anything you create in the catalog will be available to you in your Xcode project.

In PaintCode, click on the StyleKit tab. Click Add Color and then Add New Color…. Choose any color you like for your progress bar and change the name of the color to something that better represents the color; I used PinkColor to represent a light pink:

09-StyleKitColor1

Back in the tab for your progress bar, change the Fill color of Progress Active and the Stroke color of Progress Outline to your new color:

StrokeColor

To re-export your changes to Xcode, simply press Cmd+R or choose File\Export Again from the top menu. PaintCode conveniently remembers where you previously saved the generated code and overwrites it.

Build and run your app again; the progress bar has been magically updated:

PinkProgressBar

And you didn’t need to touch a single line of code! How cool. :]

Making a Dynamic Progress Bar

Okay, you were promised a dynamic progress bar: one that reacts to changes within the app. To get this working, you’ll add a computed property to the ProgressBar view to hold the progress of a song and call the PaintCode method using this value.

In ProgressBar.swift add these properties to the class:

private var innerProgress: CGFloat = 0.0
var progress : CGFloat {
  set (newProgress) {
    if newProgress > 1.0 {
      innerProgress = 1.0
    } else if newProgress < 0.0 {
      innerProgress = 0
    } else {
      innerProgress = newProgress
    }
    setNeedsDisplay()
  }
  get {
    return innerProgress * bounds.width
  }
}

progress looks like a lengthy computed property, but it’s simply checking the normalized newProgress value to make sure it’s not doing any drawing outside the bounds of the progress bar. The setter calls setNeedsDisplay() to redraw the progress bar while the getter returns the internal, normalized progress multiplied by the view’s bound’s width.

Change drawRect(_:) to the following:

override func drawRect(rect: CGRect) {
  PaintCodeTutorial.drawProgressBar(frame: bounds,
      progress: progress)
}

The first parameter is the bounds of the view subclass so that the progress bar will stretch appropriately. The second parameter is the song progress for the variable you set up in PaintCode.

And that’s it for the progress bar! You don’t need to do any more work in this file. However, you need to hook the progress bar up to report the song’s progress.

Connecting the Progress Bar to Music

Open ViewController.swift and locate the IBOutlet progressView. Change it from a UIView to be of type ProgressBar:

@IBOutlet var progressView: ProgressBar!

Scroll down to refresh() and add the following right before you call updatePlayerLabels(_:artist:album:) in the if section of the conditional:

if (mediaPlayer.currentPlaybackTime /
                currentSong.playbackDuration) .isNormal {
  progressView.progress = CGFloat(mediaPlayer.currentPlaybackTime /
                currentSong.playbackDuration)
} else {
  progressView.progress = 0.0
}

This code compares the current playback time versus the total duration of the song, checks that there isn’t a division by zero and updates the progress property on the progress view. This should give you a normalized value between 0 and 1.0. When you set progress, the setter in ProgressBar will update the progress bar accordingly.

In the else section of the same conditional, add the following line before calling updatePlayerLabels(_:artist:album:):

progressView.progress = 0.0

This sets progress to 0, since no song is playing.

This is what the final refresh() looks like:

func refresh() {
  if let currentSong = mediaPlayer.nowPlayingItem {
    let title = currentSong.title ?? ""
    let artist = currentSong.artist ?? ""
    let album = currentSong.albumTitle ?? ""
 
    if (mediaPlayer.currentPlaybackTime /
               currentSong.playbackDuration) .isNormal {
      progressView.progress = CGFloat(mediaPlayer.currentPlaybackTime /
               currentSong.playbackDuration)
    } else {
      progressView.progress = 0.0
    }
    updatePlayerLabels(title, artist: artist, album: album)
  } else {
    progressView.progress = 0.0
    updatePlayerLabels("", artist: "", album: "")
  }
}

That’s it; build and run your app and check out your progress bar! Remember that you’ll need a physical device to play songs and see the progress bar update:

DynamicBar1

Changing PaintCode Generated Code

At the very beginning of the track, the progress bar fill bleeds outside the progress bar outline. You can fix this by clipping the fill to stay within the outline.

Open PaintCodeTutorial.swift and find the following line in drawProgressBar(frame:number:):

progressOutlinePath.stroke()

Add the following line immediately after the line above:

progressOutlinePath.addClip()

This adds a clipping path so progressActivePath won’t bleed outside the outline any more. It’s a small visual fix, but goes a long way towards making things look polished.

Note: Instead of manually adding the clipping path. I wanted to show this so you know you can change things, play with the code and experiment with Core Graphics. However, remember that if you now go back to PaintCode and re-export the document, your code will be overwritten by the generated code.

Build and run your project one final time and check out your pixel-perfect custom dynamic progress bar:

14-ProgressBarLive

Where to Go From Here?

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

In this tutorial, you created a robust element within PaintCode, set up StyleKits and Variables and exported all of that into an iOS project for your use.

With this knowledge, you could try creating other iOS elements such as buttons, spinners, segmented controls, switches, or even custom components you’ve always wanted to create but thought were too difficult to acheive in Core Graphics.

Questions? Feedback? Comments? Join the forum discussion below and share them with us!

The post PaintCode Tutorial for Developers: Custom Progress Bar appeared first on Ray Wenderlich.


Viewing all articles
Browse latest Browse all 4398

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>