Chapter 22
Info Panel

Estimated Time


Compared to what we just went through, this chapter is going to be simple. Since we didn’t want to put a ton of effort into explaining COSMOS in the app, and because we were going to write this long tutorial, we thought it would be best to just link back to where one could find all the info they need.

The info panel is a single-view popup with some text and an external link that points back to C4’s main site. We’re basically going to code the panel by following the design from top to bottom.

This is the goal:

Head over to your InfoPanel.swift file in your project and let’s get started.

Canvas

We set up the canvas by changing its background color:

1
2
3
public override func setup() {
    canvas.backgroundColor = Color(red: 0, green: 0, blue: 0, alpha: 0.33)
}

Create the following method:

1
2
3
4
5
6
func createLogo() {
    //creates an image and positions it before adding it to the screen
    let logo = Image("logo")!
    logo.center = Point(canvas.center.x,canvas.height/6.0)
    canvas.add(logo)
}

Then call that method at the end of setup()

Modify your project’s WorkSpace to look like this:

1
2
3
4
5
class WorkSpace: CanvasController {
    override func setup() {
        canvas.add(InfoPanel().canvas)
    }
}

Check it out:

Pretty simple. A basic black overlay to darken whatever is behind the panel, and a logo floating in the top-center of the screen.

Text

Next, create a method to set up the text label:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func createLabel() {
    let message = "COSMOS is a lovingly\nbuilt app created\nby the C4 team.\n\n\nWe hope you enjoy\ncruising the COSMOS.\n\n\nYou can learn how\nto build this app\n on our site at:"

    let text = UILabel()
    text.font = UIFont(name: "Menlo-Regular", size: 18)
    text.numberOfLines = 40
    text.text = message
    text.textColor = .whiteColor()
    text.textAlignment = .Center
    text.sizeToFit()
    text.center = CGPoint(canvas.center)

    canvas.add(text)
}

This method creates a UILabel because we’re going to have multiple lines of text that are center-aligned. It places the entire label in the center of the screen as well.

Call the method at the end of setup(), and run it:

Next, create a method that builds and styles a text shape that we’ll use as a link to the C4 site. But first, add the following class variable:

1
var link : TextShape?

Then add this method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func createLink() {
    let text = TextShape(text:"http://www.c4ios.com/docs", font: Font(name: "Menlo-Regular", size: 24))
    text.fillColor = white
    text.center = Point(canvas.center.x,canvas.height * 5.0/6.0)

    let a = Point(text.origin.x,text.frame.max.y+8)
    let b = Point(a.x + text.width + 1, a.y)

    let line = Line((a,b))
    line.lineWidth = 2.0
    line.strokeColor = C4Pink

    link = text
    canvas.add(link)
    canvas.add(line)
}

… and call that from setup()

This method creates a text shape and then grabs the bottom corners of the shape’s frame to position a pink link underneath the text. The frame (a Rect) has a nice little property called max which returns the bottom-right corner of the frame. This makes it easy to find out where the bottom (e.g. y) of the frame sits so we can add a line just a few points below that.

I add a +1 to the width of the line because it looks better

Check it out:

Gestures

Now that we’ve got everything laid out properly it’s time to add a bit of behaviour to the panel. There are two things we need to add:

  1. Linking to the C4 site
  2. Closing the panel

We want the text label to act like a button, so we’re going to add a longPress gesture to it, which gives the functionality of being able to press and hold before releasing.

Here’s a basic setup for the gesture:

1
2
3
4
5
6
7
8
9
10
func linkGesture() {
    let press = link?.addLongPressGestureRecognizer { locations, center, state in
        switch state {
        case .Began:
            self.link?.fillColor = C4Pink
        default:
            self.link?.fillColor = white
        }
    }
}

Add that to your class and test it. You’ll notice that after pressing and holding on the text shape it will turn pink. When you release it, it will go back to white. This is basically what we want, except for that delay in turning pink…

To get rid of the delay, add the following after let press = link?.addLongPressGestureRecognizer{ ... }:

1
press?.minimumPressDuration = 0.0

So, we can press the link and it changes. Great. Now we need to make it useful. First, let’s revisit how gesture work. There are 5 states to a gesture:

  1. .Began
  2. .Changed
  3. .Ended
  4. .Cancelled
  5. .Failed

For this gesture in particular we want to track when the starts and ends (or fails or cancels). And then we’ll execute the following logic:

  1. When it starts: turn pink
  2. When it changes: stay pink
  3. When it ends: check if the user released the gesture over top of the link, if so then open the C4 site, otherwise they dragged the touch off of the link and intend not to open the C4 site

To handle this logic, modify the gesture code to look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func linkGesture() {
    let press = link?.addLongPressGestureRecognizer { locations, center, state in
        switch state {
        case .Began, .Changed:
            self.link?.fillColor = C4Pink
        case .Ended:
            if let l = self.link where l.hitTest(center) { 
                UIApplication.sharedApplication().openURL(NSURL(string:"http://http://www.c4ios.com/cosmos/")!)
            }
            fallthrough
        default:
            self.link?.fillColor = white
        }
    }
    press?.minimumPressDuration = 0.0
}

Here are some notes on what’s happening:

  1. The case .Began, .Changed: satisfies points 1 and 2 in the previous list
  2. The if let l = self.link where l.hitTest(center) unwraps the link object and tests to see if the gesture has ended while the touch was still in the bounds of the link.
  3. If the touch ended over top of the link, then the application opens the C4 site by calling .openURL()
  4. The fallthrough allows us to include the final default state in the .Ended case.
  5. The default state changes the link’s color back to white. So, if the gesture ends, fails or gets cancelled by the os then the link turns white again.

Now, call linkGesture() from setup and we can move on to the last step.

Hide Gesture

We want the info panel to disappear when we tap on it anywhere outside of the link. To do this we’ll create an animation and a gesture:

1
2
3
4
5
func hideGesture() {
    canvas.addTapGestureRecognizer { locations, center, state in
        self.hide()
    }
}
1
2
3
4
5
func hide() {
    ViewAnimation(duration: 0.25) { () -> Void in
        self.canvas.opacity = 0.0
    }.animate()
}

Straightforward. Call hideGesture() from setup() and you’re basically done.

Run it, and test the two gestures.

Reveal

We also need a method for revealing the canvas, which we’ll use in the next chapter.

Add the following to your class:

1
2
3
4
5
func show() {
    ViewAnimation(duration: 0.25) { () -> Void in
        self.canvas.opacity = 1.0
    }.animate()
}

Next

This was the last major object we needed to create for COSMOS. The next chapter deals with how to tie everything together into a single unified app.

Here’s a copy of InfoPanel.swift

Well done.