Tag Archives: swift

Getting all Swifty

It had to happen eventually. It’s not the first time I looked into this area, but this time I have got further than I have ever done before.

I started writing an app. By this I mean that I have cracked open Apple’s Xcode 6 and started typing in their new programming language Swift. This time, I have got much farther than I have ever done before, and I suspect that the reason for this is that Swift is somewhat more palatable to me than Objective C . Swift feels a little more, though I know its is not, like JavaScript – something I have used for a long time in my web design activities. Now, I have a feeling that this could be deliberate. Someone at Apple may have  thought “you know, all those web people really need to be pulled into the fold of writing apps properly”. Certainly, Swift feels more comfortable to use.

So, I decided to have a go. Make something interesting to the onlooker, but something to challenge me. It just so happens that I had just the thing underway in Hype (a rather good HTML/JavaScript animation application). This is a Metric Clock – which divides the day into ten hours, and each hour into 100 minutes, each comprising of 100 seconds. My implementation can be found here: http://www.worldofpaul.com/metric_clock/

So, I thought, why not try to make this run in iOS, as a native app? It would give me a target to aim for, and I should learn a few things along the way. So, time to make what others will see as a typical beginner’s mistakes.

Double vs CGFloat

I have run into a problem or two. This is partly down to the way Swift handles decimal numbers. There are three kinds of numeric value (that I know of):

Int – an integer is a whole number – so 3 is an integer, 3.1 is not, and if you try to set 3.1 as an integer, you’ll get an error message.

Double – this is a number held to 64-bit accuracy – and can hold numbers as decimals, so 3.1 can be held in a double variable.

CGFloat – this is also a decimal number, but held at 32-bit accuracy.

Unless you are explicit in your definitions, Swift will decide if a numeric value is an Int, a Double or a CGFloat.

So, in the context of my clock, I am running into some issues with this. For example, I will eventually be grabbing time from the system clock, and converting it to decimal time, and calculating the position in degrees (note, covering to radians is a whole other area of confusion to be dealt with another time), and rotating the hands accordingly. So, I am setting up some variables to help:

var secondHandTimer = 0.0
var secondTime = 0.864
var minuteTime = secondTime * 100
var hourTime = minuteTime * 100
var dayTime = hourTime * 10
var mySecondHandDegrees = 3.6 * decimalSeconds
var mySecondHandRemainderDegrees = 360-mySecondHandDegrees
let fullRotation = mySecondHandDegrees * ((M_PI)/180)
// Call to the second hand rotation function, with an initial rotation of 0 degrees
self.secondHandAnimate( mySecondHandRemainderDegrees , theDuration: 0.0 )

So far, so good. However, when we get to secondHandAnimate(), things start to get a little odd:

func secondHandAnimate( theRotation: Double , theDuration: Double ) {
    let opt1 : UIViewKeyframeAnimationOptions = .CalculationModeLinear
    let opt2 : UIViewAnimationOptions = .CurveLinear
    let options = opt1 | UIViewKeyframeAnimationOptions(opt2.rawValue)
    let fullRotation = theRotation*((M_PI)/180)
    UIView.animateKeyframesWithDuration( theDuration,
        delay : 0,
        options : options ,
        animations: {
            UIView.addKeyframeWithRelativeStartTime(0, relativeDuration: 1/3 , animations: {
                self.secondHand.transform = CGAffineTransformMakeRotation( 1/3 * fullRotation)
            })
            UIView.addKeyframeWithRelativeStartTime(1/3, relativeDuration: 1/3 , animations: {
                self.secondHand.transform = CGAffineTransformMakeRotation(2/3 * fullRotation)
            })
            UIView.addKeyframeWithRelativeStartTime(2/3, relativeDuration: 1/3 , animations: {
                self.secondHand.transform = CGAffineTransformMakeRotation(3/3 * fullRotation)
            })
        },
        completion: { finished in
            self.secondHandAnimate(360 , theDuration: 86.4)
        })
    }
}

This is where I start to get errors. Here’s a screenshot of what I get:

Screen Shot 2014-10-29 at 12.04.51
Screen Shot 2014-10-29 at 12.18.41

(For a full explanation about why I’m using keyframes, it’s to do with rotation start and end points being identical in Radians, even though the start is 0 degrees and the end is 360 degrees – more here http://mathewsanders.com/animations-in-swift-part-two/)

It seems that, to my untrained eye, the line starting UIView.addKeyframeWithRelativeStartTime does not seem to like the Double kind of number.

So, I make a small change to the function definition of secondHandAnimate():

func secondHandAnimate( theRotation: Double , theDuration: CGFloat )

and try another test build. This time, a completely different error:

Screen Shot 2014-10-29 at 12.22.49So, it seems that if I want to set the time for a duration of an animation, it HAS to be a Double. BUT keyframes don’t seem to like Double numbers when calculating the relativeDuration part. It seems that I have to very explicit in how I deal with numbers, by explicitly stating what I want them to be:

// assume a static starting point for the time
 var mySecondHandDegrees = CGFloat(3.6 * decimalSeconds)var mySecondHandRemainderDegrees = CGFloat(360-mySecondHandDegrees)

and

func secondHandAnimate( theRotation: CGFloat , theDuration: Double )
 let fullRotation = theRotation*(CGFloat(M_PI)/180)

With these in place, things start to work and I get successful builds, but it feels odd that I just can’t use an Int, CGFloat or Double for animation. I expect I am missing something important and fairly basic here.