This blogged has moved

This blogged has moved onto a new domain and a new fresh look. The content will still be available here and some of it will move over and be refreshed at the new site.

The first new post: “Making drawRect: accessible” was just posted at the other site. Go check it out.

The math behind transforms

In my previous post I talked about the transforms involved in rotating a view around an external point but I also said that you don’t need to understand matrices to work with transforms. If you didn’t accept that and still wanted to know how matrices make transforms work then this post is for you.

This post does not aim to cover all of linear algebra, just enough to understand transforms.

Our goal

We have objects on our screen that are visually defined by the four corners of the rectangle they lie within. Even objects that are not rectangles themselves can be contained by a rectangle. We know that applying a transform to this object on screen causes it to change its position, size or rotation on screen. The transform is used to calculate the new positions of the four corners and everything inside the rectangle stretches to fill the rectangle just as before the transform. Our goal is to understand how the new positions are being calculated for different kinds of transforms.

Math, a lot of math

Don’t let that section title scare you. You came here to learn, remember?

Math in more than one dimension

In “normal” math, that everyone should be familiar with, we have numbers. Sometimes when we want to make calculations but don’t know all the numbers beforehand we substitute them with letters and are able to make calculations with those letters instead. If we drew a line we could represent these numbers on that line as the distance to the right of “origo”, the point representing zero. The “to the right”-part enables us to represent negative numbers by placing them left of origo.

Addition of two numbers can be illustrated as drawing an arrow with the length of the first number from origo and another arrow with the length of the second number beginning at the end of the first arrow. The sum of the two numbers is the arrow that goes from origo to the end of the second arrow. This can be seen in the image below (with a = 2 and b = 3).

-2 -10 1 2 3 4 5 6 7aba+b
An illustration of two numbers being added.

The same works for negative numbers (for example a = 4 and b = –6) as can be seen below.

-2 -10 1 2 3 4 5 6 7aba+b
An illustration of two numbers being subtracted.

In a similar fashion we can represent multiplication as taking either of the arrows and adding it to the end of itself the same number of times as the length of the other arrow.

It gets a bit silly drawing numbers like this when we all know how to add simple numbers so let’s take things one step further. If we call the line that we drew arrows along an axis and we add another axis perpendicular to the one we already had then we get a plane. Any point on this plane can now be defined by two coordinates, one for each axis. We call the horizontal axis the x-axis and the vertical axis the y-axis. Now we can describe any point on this plane by its x and y value. This plane is just like the screen of our devices.

(x, y)-2 -11 2 3 4 5 6 77654321-1-2
An arrow pointing to a coordinate in a 2D plane.

The arrow in this plane is unlike the arrows along the line above since they require two numbers to describe instead of one. We call these new arrow vectors and the old arrows scalars. If we want to we can draw the same scalars as we did before along the x-axis but when that arrow is represented in this plane it is no longer a scalar. Instead it is a vector with a y-coordinate of 0.

Just as we did with the scalars we can represent the addition of two vectors as placing either of them at the point of the other. The result of the addition is the vector that points to the same point as the second vector now does. You may notice that vector addition works by adding the two x components together into a new x component and the y components together into a new y component. It works in both directions, adding either vector to the other, you can try it on a piece of paper if you want to.

-2 -11 2 3 4 5 6 77654321-1-2→a→b→→→→a+ba+b
An illustration of two vectors being added together.

Adding one vector to itself over and over is just like the multiplication we did with scalars. In a plane like this we call it scalar multiplication since we are multiplying our vector with a scalar.

Multiplying a vector with another vector gets a little bit trickier to wrap our heads around. What does it really mean to multiply a vector with another vector? Considering that vector addition added the two x and y components it shouldn’t surprise us that the two x and y components are multiplied. What likely is a surprise however, is that the result of these multiplications in then added into a scalar. That is right, the multiplication of two vectors is a scalar. This kind of multiplication is most often called the “dot product“ since a dot is used for the multiplication sign.

There are other very powerful operations that we can do with vectors like calculating the cross product. While this is a very important part of linear algebra and 3D computer graphics, it is not necessary for understanding transforms so we will skip it for this article.

We build upon our 2D plane by adding another axis that is perpendicular to both axes and call it the z-axis[1]. We now have a full Cartesian coordinate system that can be used to represent any point in 3D space using its x, y and z coordinates.

Unlike last time we added a dimension to our space we still call the arrow that points to a coordinate in space a vector. Sometimes for differentiation we call these vectors 3D vectors, but a vector can really have any amount of values as long as they are in a single row or single column but never both.

If a vector were to have both rows and columns it wouldn’t be a vector any more. It would be a matrix. Matrices are as far as we will go here. In other scientific areas there are things with one more dimension than a matrix, called a tensor.

There is no good way of visually representing a matrix in a plane or space like we did for a vector so we will skip that for now. Instead let us focus on how they add and multiply.

Addition of two matrices is simply a new matrix with every value being the addition of the corresponding values for that row and column in the original two matrices. The only interesting thing to note is that two matrices can only be added together of they have the same size, i.e. the same number of rows and columns.

Matrix multiplication on the other hand is where things get interesting. In matrix multiplication the values of a row in the first matrix (hereafter called MA) is multiplied with the values of a column in the other matrix (hereafter called MB). Just like with the dot product (vector multiplication) this produces a scalar value which is the value for that specific row and column in the resulting matrix. Since the multiplication is done for every row, column pair of the two matrices the resulting matrix will have the same number of rows as MA and the same number of columns as MB. Important to note is that order matters[2]. MA × MB ≠ MB × MA.

The best way to understand matrix multiplication is to start with the empty result and fill in the value for each row and column one at a time. The resulting value for the first row and first column is the same as the dot product of the first row of MA and the first column of MB. Since we are calculating the dot product of MA’s rows and MB’s columns they need to have the same number of elements, i.e. MA needs to have the same number of columns and MB has rows. (No, that was not a typo. The number of elements in each row is the same as the number of columns and vice versa.)

A1,1 A1,2
A2,1 A2,2
A3,1 A3,2
A4,1 A4,2
×
B1,1 B1,2 B1,3
B2,1 B2,2 B2,3
=
C1,1 C1,2 C1,3
C2,1 C2,2 C2,3
C3,1 C3,2 C3,3
C4,1 C4,2 C4,3
Hover the matrix C…
A 4×2 matrix multiplied with a 2×3 matrix.

If we think of a vector as a matrix with only one column then we can multiply a matrix with that vector and transform it into a new vector. This means that we have a mathematical way of transforming the points on our screen into other points.

The identity matrix

Before actually changing the points of our view, let us figure out how we can multiply a matrix with a vector and have it be transformed into the exact same vector. We could just multiply it with the scalar 1 but we really want to use a matrix for this. For each row-column combination in the matrix we put a value at i, j where i is the row and j is the column. Since our vector has three rows we need to have three columns and since we want the result to have three rows we need to have three rows as well leaving us with a matrix with three rows and three columns, often referred to as a 3×3 matrix.

M1,1 M1,2 M1,3
M2,1 M2,2 M2,3
M3,1 M3,2 M3,3
×
x
y
z
=
xnew
ynew
znew
A matrix multiplied with a vector to get a new vector.

This matrix multiplication can also be described by these three equations.

{
xnew = M1,1 ⋅ x + M1,2 ⋅ y + M1,3 ⋅ z
ynew = M2,1 ⋅ x + M2,2 ⋅ y + M2,3 ⋅ z
znew = M3,1 ⋅ x + M3,2 ⋅ y + M3,3 ⋅ z
The 3 equations for a matrix and vector multiplication.

We quickly see that the values on the diagonal, where i is equal to j, are ones and that all other values are zeroes. This special matrix is called the identity matrix and is often represented as the letter “I”. Now that we know how to construct a matrix that doesn’t modify the multiplied vector we can start looking at matrixes that do.

Transforming matrices

There are three basic kinds of transforms: scaling, translating and rotating. No matter which one we are talking about we can look at the transformed x, y, and z values separately by looking at each row of the matrix.

Scaling

Scaling a rectangle is very easy to describe: the rectangle changes its size by the scale factor without moving or losing its aspect ratio. This means that every corner increases its distance to the center without changing the angle. For this to happen, all components of the vector must increase by the same factor, the scale factor. If we were to make the rectangle twice as big we would want the resulting vector have all it’s x, y and z values twice that of the original vector.

Going back to our three equations from above we see that all the values along the diagonal are now twos instead of ones (the zeroes are still zeroes). If we put different values along the diagonal (for example 2 for the first row and 1 for the other) we will see that the rectangle stretches and loses its aspect ratio, a fun scaling effect and a perfectly valid scaling transform along that axis.

No matter how we change the values along the diagonal the rectangle never moves so let’s look at how to move the rectangle.

Translating

One way of moving the rectangle would be to add a vector to all four points defining the corners which would create new vectors pointing to the new points. This works just fine but we really want to make it work with matrices because matrices can be multiplied with each other which will later prove to be very powerful. Looking at the equations for our 3×3 matrix we eventually realize that something is missing. We only have a means of specifying x, y and z multiplicands but we have no way to use a constant.

So we add a constant for each of the three equations and see how that affects the matrix. Since we now have four elements being added in the equation we must have four columns in the matrix and thus also four rows in our vector.

Don’t run off being scared that we introduced a fourth dimension or something. The fourth value of our vector is quite harmless, it’s 1. Our vector is now (x, y, z, 1). The fourth value isn’t used to represent our point in 3D space, it’s only used for matrix multiplication. We are not quite finished yet. Our resulting vector would lose its fourth row unless we made sure that the matrix also had a fourth row.

Now we have a matrix of ones along the diagonal (we still want the fourth value of the vector to keeps its value after the multiplication) and three constants along the very right edge of the matrix. These three constants reflect the translation along each of the three axis.

1 0 0 Cx
0 1 0 Cy
0 0 1 Cz
0 0 0 1
×
x
y
z
1
=
x + Cx
y + Cy
z + Cz
1
A translation transformation matrix.

Let’s revisit scaling to ensure that it still works for us with our new 4×4 matrix.

Scaling again

We don’t want to move when we are scaling so the rightmost column is set to zeroes (except for the diagonal). We quickly realize that scaling still works with our new 4×4 matrix. This means that it’s time to go on to rotation.

Rotating

Just like we could scale along only one axis we can also rotate around only one axis. In fact, we most often do. If you ask someone to describe a rotation on a screen without telling them which axis they will probably describe a flat rotation like the hands of a clock does. What axis would that be?

A neat trick to figure out rotation along an axis is to take your right hand and curl your fingers without closing your hand and then point the thumb straight up. If you now point your thumb along the axis you are rotating along and turn your wrist your fingers will curve in the direction of the rotation. Alternatively you could place your hand so that your fingers curve like the rotation you had in mind to figure out the axis. You may notice that a clockwise and counter-clockwise rotations is done in the negative or positive direction along that axis.

A 2D rotation

The common flat rotation is done around the z-axis so z-values for the rotated points will remain unchanged but x- and -values may change. I said “may change” since a 360º rotation around any angle takes us to the same point as before.

To figure out how the x and y values change in a rotation around the z-axis we look at the two vectors (1,0,0) and (0,1,0). If we draw a circle in the center of our x,y-plane with the same radius as the distance to our points, we expect the points to move along the edge of this circle. We can easily imagine a counter-clockwise rotation of θ for both of these vectors and draw two new vectors that point to our expected end result. Basic trigonometry (sine and cosine) helps us express how the new points relate to the old points.

(1, 0, 0)(0, 1, 0)θθ(cos θ, sin θ, 0)(-sin θ, cos θ, 0)
The rotation of two vectors

The transformed x-only-vector gives the values for the first column of our rotation matrix and the transformed y-only-vector gives us the values for the second column of our rotation matrix. The third or fourth columns don’t alter the transformed vector so these are the same as for an identity matrix. The resulting rotation matrix is thus:

cos θ -sin θ 0 0
sin θ cos θ 0 0
0 0 1 0
0 0 0 1
×
x
y
z
1
=
cos θ⋅x-sin θ⋅y
sin θ⋅x+cos θ⋅y
z
1
The rotation matrix for a rotation around the z-axis.

To verify that this matrix works for a vector with both x and y components is left as an exercise for the reader. Pick a new vector with both x and y components and use the above matrix to calculate the rotated vector. Finally draw the rotated vector in a 2D plane to that the end result meets our expectations.

3D rotations and perspective

By applying the same techniques to rotations around the x-axis and y-axis we can figure out their rotation transforms (seen below).

Rx(θ) =
1 0 0 0
0 cos θ -sin θ 0
0 sin θ cos θ 0
0 0 0 1

Ry(θ) =
cos θ 0 sin θ 0
0 1 0
-sin θ 0 cos θ 0
0 0 0 1

Rz(θ) =
cos θ -sin θ 0 0
sin θ cos θ 0 0
0 0 1 0
0 0 0 1

The individual rotation matrices for all three axes.

While the point is correctly transformed in 3D space it doesn’t look like a 3D rotation at all. This is because the 3D point is projected to the 2D screen without perspective. If you would go back and scale or translate the z-value you would experience the same problem (though there you would see no difference at all). This is not how we expect 3D objects to look. We expect objects far away to appear smaller and objects up close to appear bigger.

It turns out that computer graphics has one more trick up its sleeve. We always make sure that the fourth value of our vector is 1. If it isn’t then we divide the whole vector with that value so that it becomes 1 (we use scalar division so every value is divided individually). That means that the transformation matrix with 1, 1, 1, 2 on the diagonal will scale x,y,z by 0.5 (since it’s 1/2). To create the illusion of perspective we want to get a larger-than-one fourth value of our vector for distant points and a smaller-than-one value for nearby points. To achieve that we want some constant value in the third row and fourth column of our transformation matrix, since it’s going to be multiplied with our z-value. What constant value? The short answer is: a value that makes the perspective look good.

One way of thinking about it is that the views we are transforming are a few hundred points wide/high so a rotation is going to cause the far-off points to be a few hundred points away from us. Since a change from 1 to 2 halved the size of the view on screen and we are talking about c⋅z where z is a few hundred we probably want c to be 1/a few hundred. To no surprise a typical value for this constant could be –1/500. The minus sign comes from he fact that the z-axis points into the screen instead of out of the screen.

Another way of thinking about this is to image that the screen is a certain distance, d, from where we are looking (abstractly speaking). The distance is in some made up unit which is not at all related to how far our eyes are from the screen in real life. Since the screen is two dimensional everything needs to be projected onto that screen. The point we are looking from and the point we are looking at stays fixed in 3D space but we can decide how far off the screen is. If we move the screen closer to us then we get more perspective and if we move it away from us then we get less perspective.

zobjectprojection“screen”usd
The size of an object on our “screen”.

The constant for our perspective depends on the distance to our screen as –1/d. This is just the same as we had before. It is only another explanation of what that value means. A greater denumerator means a greater distance to the screen which means less perspective. As mentioned above, it turns out that 500 units is a suitable distance to the “screen”.

Combining multiple transforms

One of the truly powerful things with transformation matrices is how they can be applied one after another and how they can be combined (also known as concatenated). You may remember from the previous post that the order of the transformations matter. Rotating and then translating is not the same as translating and then rotating. By now this should sound very familiar to you. Matrix multiplication works the exact way and a transform is just a matrix, remember. It turns out that you can take two transformation matrixes and multiply them and you will get a new transformation matrix that describes the total transformation in a single multiplication.

Lets take an example. We want to translate, then rotate and then translate the four corners of a small view (the same transform we did in the previous post). The resulting matrix of the multiplication is

Tx(-c) × Rz(θ) × Tx(c) =
cos θ -sin θ 0 c⋅cos θ - c
cos θ sin θ 0 c⋅sin θ
0 0 1 0
0 0 0 1
The multiplied transforms to translate-rotate-translate.

Given some arbitrary angle, translation distance and corners we can calculate the new points for our corners after the transformation. By drawing the new corners in our coordinate system we can see that the rectangle ends up where we expect it to. Inputting values into the matrix and drawing the transformed corners are left as an exercise for the reader.

In just the same way we can take any number of transforms and multiply them in the order they should be applied to pre-calculate the one transformation matrix that encapsulates the total transformation of all the others.

Update

Thanks Richard Turton for correcting my English.


After reading all of that I hope that you no longer feel that transforms are little pieces of black magic being applied to your views. If you have any feedback, comments or corrections I would love to hear them. I’m @davidronnqvist in Twitter and @ronnqvist on ADN.


  1. This is in fact one of the things that the cross product of two vectors is used to calculate.  ↩

  2. This is just like for the different transforms in the previous article. Rotation and then Translation is not the same as Translation and then Rotation.  ↩

Translate-rotate-translate?

When working with transforms and wanting to rotate around some other point than center or origo you hear that you need to translate (move), then rotate and then translate back. Let me explain what that means.

Behind the scenes

When you apply a transform to a view the coordinates of that views corners gets multiplied by a transformation matrix to calculate their new positions. When you apply multiple transforms to a view the matrixes for the different transforms are multiplied with the corners of your view in the order that they should be applied[1]. The important thing to take away from this is that the order is important. Translating first and then rotating does not give the same result as rotating first and then translating. The reason for this is that matrixes are not commutative, which means that Mtrans × Mrotate ≠ Mrotate × Mtrans.

Don’t worry! You don’t need to understand matrixes to apply transforms as long as you know that order matters. The animations below illustrate how the order of translating and rotating makes a difference.

◀◀
begin
end
view
  1. translate
  2. rotate
  3. translate
A view being translated, rotated and translated back.

Creating such a transform in code

Both Core Animation and Core Graphics provide nice abstractions for working with transforms. In Core Graphics we have 2D affine transforms called CGAffineTransform and in Core Animation we have 3D transforms called CATransform3D. In the rest of the post I’m going to use the 3D transforms but it all applies to affine transforms as well.

You can create a new rotation transform by calling CATransform3DMakeRotation(…) or even rotate an existing transform using CATransform3DRotate(…). Translating and scaling has similar methods. That’s means that our three step transform is three lines of code (excluding two variables for the angle and the distance):

CGFloat thirtyDegrees = 30.0 * M_PI / 180.0;
CGFloat distanceToRotationPoint = 100.0;
CATransform3D externalRotation = 
    CATransform3DMakeTranslation(-distanceToRotationPoint, 0.0, 0.0);
externalRotation = 
    CATransform3DRotate(externalRotation, -thirtyDegrees, 0.0, 0.0, 1.0);
externalRotation = 
    CATransform3DTranslate(externalRotation, distanceToRotationPoint, 0.0, 0.0);

If we set that as the transform of our view we will see that it has moved to the correct position, just as if it was rotated around a pointer other than its center.

Animating a rotation

Being able to set a rotation transform is cool and all but we want to animate the rotation as well. So we create a basic animation from the identity matrix (meaning no transformation) to our translate-rotate-translate transform and add it to a layer.

CABasicAnimation *rotate = [CABasicAnimation animationWithKeyPath:@"transform"];
rotate.fromValue = [NSValue valueWithCATransform3D:CATransform3DIdentity];
rotate.toValue = [NSValue valueWithCATransform3D:externalRotation];
rotate.duration = 1.0;    

[rotatingLayer addAnimation:rotate forKey:@"myRotationAnimation"];
◀◀
begin
end
view
Animating to the translate-rotate-translate transform.

Something is not right. It may not look very strange for small rotation like this one but if we slow down the animation a little and make a rotation of more than π/2 (90º) it will be obvious what is happening. The view moves from the start position to the end position in a straight line! It doesn’t rotate around that point, it’s just strange.

◀◀
begin
end
view
Animating a bigger rotation to see what is wrong.

The point transforms are applied relative to

Lets take a step back and see how this can be solved. If you start thinking that it worked fine for small angles so let’s just do a key frame for every angle. Please just stop and don’t do that.

Let’s look at alternatives. There is a property on the view layer called the anchor point that can change which point the transform is being applied relative to[2]. If we can change the anchor point to the point we want to rotate around we won’t have to deal with translating back and forth, we just rotate. There are some caveats though. Changing the anchor point changes how the frame is drawn relative the position which causes the view to move on screen.

To counter the frame moving we can change the position of the layer to the point we are rotating around. That way the view/layer will appear in the same frame as before.

CGPoint rotationPoint = // The point we are rotating around
CGPoint anchorPoint = CGPointMake((rotationPoint.x-CGRectGetMinX(view.frame))/CGRectGetWidth(view.frame),
                                  (rotationPoint.y-CGRectGetMinY(view.frame))/CGRectGetHeight(view.bounds));

view.layer.anchorPoint = anchorPoint;
view.layer.position = rotationPoint; 

If we are using Auto Layout it becomes a bit more complicated. The answers to this question on Stack Overflow show some of the many ways that it can be done. The rest of the code for this post will assume you are not using Auto Layout. If you are, I refer you to the those answers.

Now that have changed the point we are transforming relative to, we don’t have to translate back and forth so the animation code becomes very simple.

CABasicAnimation *rotate = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotate.toValue = @(-M_PI_2); // The angle we are rotating to
rotate.duration = 1.0;

[view.layer addAnimation:rotate forKey:@"myRotationAnimation"];

And the view finally rotates around the point we were expecting it to.

◀◀
begin
end
view
Finally rotating around the point we wanted.

  1. In reality the different transformation matrixes are being multiplied in the order they should be applied to get a single transformation matrix that reflects all the transformations in total.  ↩

  2. You can read more about the anchor point in my article for iDeveloper.tv  ↩

Read about the anchorPoint over at iDeveloper TV

While browsing Stack Overflow over the past few weeks there was many occasions when I wanted to say: “Go read this blog post about the anchorPoint, it will explain things to you.” Luckily I can say that now.

Have a look at the iDeveloper TV blog and read my post where I explain most of the mysteries of the anchorPoint property on CALayer.

Mini tip: Animations with relative values

Do you ever find yourself writing code like this to do small animations?

CGRect myFrame = myView.frame;
myFrame.origin.y += 10;
// Animate myView to new frame
// ...

If only there was a way to specify relative animations.

A look a the documentation for CAPropertyAnimation reveals an interesting property called additive.

Determines if the value specified by the animation is added to the current render tree value to produce the new render tree value.

It declares that the to and from values of our animation will be added to the existing value instead of being set as the value. It makes the animation become relative. The pre-calculation of the new frame is no longer necessary. We can just animate the position from (0, 0) to (0, 10) relative to the existing value.

CABasicAnimation *jump = [CABasicAnimation animationWithKeyPath:@"position"];
jump.additive = YES; // fromValue and toValue will be relative instead of absolute values
jump.fromValue = [NSValue valueWithCGPoint:CGPointZero];
jump.toValue = [NSValue valueWithCGPoint:CGPointMake(0.0, -10.0)]; // y increases downwards on iOS
jump.autoreverses = YES; // Animate back to normal afterwards
jump.duration = 0.2; // The duration for one part of the animation (0.2 up and 0.2 down)
[myView.layer addAnimation:jump forKey:@"myJumpAnimation"];

The same can be done with any other kind of property. Want to rotate a view 90 degrees (and not to a 90 degree rotation)?

CABasicAnimation *rotate = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotate.additive = YES; // fromValue and toValue will be relative instead of absolute values
rotate.fromValue = @0;
rotate.toValue = @(M_PI_4);
[myView.layer addAnimation:rotate forKey:@"myRotationAnimation"];

Update

Thanks Ole Begemann for pointing out that there is a simpler way of doing relative animations. By setting the byValue to the relative change you can leave out the fromValue and additive properties. The example used in this post would then look like this:

CABasicAnimation *jump = [CABasicAnimation animationWithKeyPath:@"position"];
jump.byValue = [NSValue valueWithCGPoint:CGPointMake(0.0, -10.0)]; // y increases downwards on iOS
jump.autoreverses = YES; // Animate back to normal afterwards
jump.duration = 0.2; // The duration for one part of the animation (0.2 up and 0.2 down)
[myView.layer addAnimation:jump forKey:@"myJumpAnimation"];

You can read more on how to combine fromValue, byValue and toValue in the documentation for CABasicAnimation.

The approach of setting additive = YES also works for CAKeyframeAnimation.

CocoaHeads@iZettle

The CocoaHeads Stockholm meetings are back on a monthly schedule after the brief summer break. If you live in Stockholm I can highly recommend going next month1. A big thank to iZettle for hosting the event and giving a cool demo of a credit cart payment using an iPad.

I had a great time talking to all the nice people and got some good inspiration to continue working on some “secret” projects that I have in the pipeline.


  1. Say hi if you see me there, I’m a regular attendee. 

Mini tip: Custom iOS controls with target-action

Sometimes you want to, or “have” to, create a custom control for your iOS interface design. You know a little bit about good design, so you know that your control should be loosely coupled to its container view and controller. Since you are creating a custom control, and not just a custom view, communication need to go both ways. It is reasonable for the controller to know about the control so setting data directly is okay but getting data back in a decoupled maner is slightly more difficult. One thing that might come to mind is delegation. Your control could define a delegate protocol that the controller implements. While this works fine there are better solutions.

If we take a look at the controls that are available in Apple’s frameworks we see a common pattern. There is a common superclass called UIControl that defines a set of methods with “target” and “action” in them. Target–Action (also called Outlet, Target and Action) is a design pattern1 where an outside object can register itself as the target with a certain action for an event. Whenever the event happens on the control the action message is sent to that target.

If you are designing a custom control that needs to notify an arbitrary object whenever the controls value changes you may think that you need to write a lot of code to keep track of the target and calling the action method on the target. This is especially true if you take a quick glance at all the methods like sendAction:to:forEvent: in the UIControl class documentation. Luckily, UIControl already does all that for you, just by being a subclass you get all the adding, removing and messaging targets for free. The only thing you need to do is tell yourself (remember you are a UIControl subclass) that the event has occurred and it will send the correct actions messages to the correct targets. For you, it’s only one line of code:

[self sendActionsForControlEvents:UIControlEventValueChanged];

  1. If you are familiar Cocoa and Cocoa Touch and want to learn more about the design patterns that make them such powerful frameworks, I would recommend the book Cocoa Design Patterns by Erik M. Buck and Donald A. Yacktman. 

Mini tip: array intersections and differences

In maths and computer science we sometimes use sets, unordered collections of unique entries. NSMutableSet has good basic support for set-operations like unions, intersections, relative complements (also called difference) with operations like unionSet:, minusSet: and intersectSet:. There is also support for subsets.

The other extremely common data type is an array, an ordered collection of entries. We don;t usually talk about “array intersections” or “array complements” but they can sometimes come to good use. While there is no built in methods for this, NSPredicate makes it really easy.

It is not clearly defined if the union of two arrays should be contain duplicates or not. As we will see, we can easily achieve both.

Examples

Imagine that we have two arrays: (3,4,5,6,7) and (0, 2, 4, 6, 8) and what to know the intersection and the relative complement between these.

Array intersection

The intersection of two collections is the entries that exist in both of them. The intersect of two sets

With arrays this can be achieved by filtering either of the arrays (preferably the smaller for performance reasons) to only include objects that exist in the other array. A predicate that will do this reads as clearly as this:

NSPredicate *intersectPredicate = [NSPredicate predicateWithFormat:@"SELF IN %@", otherArray];
NSArray *intersect = [firstArray filteredArrayUsingPredicate:intersectPredicate];

Relative complement

For the relative complement (the difference) the order matters. It helps to think of it as a subtraction, we remove the common entries from either array. The relative complement of two sets

This is only a slight modification from the example above. This time we want to keep the entries that does not exist in the other array:

NSPredicate *relativeComplementPredicate = [NSPredicate predicateWithFormat:@"NOT SELF IN %@", otherArray];
NSArray *relativeComplement = [firstArray filteredArrayUsingPredicate:relativeComplementPredicate];

Union

The union of two sets is the entires for both the first and the second set. Sets only contain unique values but arrays don’t always do that. The question is if the “union” of two arrays should add the intersect twice or not. The union of two sets

Adding all objects

The method arrayByAddingObjectsFromArray: makes this trivial:

NSArray *union = [firstArray arrayByAddingObjectsFromArray:otherArray];

Only adding the intersect once

The filled circles (known as Venn diagrams) come to great aid here. We can easily see that what we want is the relative complement added to all the entries in the second array.

// Calculating the relative complement was shown above ...
NSArray *union = [relativeComplement arrayByAddingObjectsFromArray:otherArray]; 

Note that this still allows for duplicate entries since either of the two arrays can contain an entry more then once. Also the intersect can contain the same entry more then once.

Time to reconsider dot-notation?

Modern Objective-C

With yesterdays launch of Mountain Lion and Xcode 4.4 (for Lion as well) I’m finally allowed to commit code that uses “Modern Objective-C”. For those who don’t know what I’m talking about: have a look at the Clang documentation for Objective-C Literals.

It’s amazing isn’t it? Things that used to be so tedious becomes so simple1.

Apart from Objective-C Literals, the part that interests me the most is Object Subscripting. It which allows you to access an object inside a collection with a subscript operator (array[index];) which by itself is much better then the previous [array objectAtIndex:index]. Though the use of square brackets have a strange smell to me.

Imagine being the table view data source and reading the appropriate data to display at a certain row. Your code would look something like this:

// Get a cell ...
MyClass *myObject = [myDataArray objectAtIndex:[indexPath row]];
// Configure the cell using the data in myObject ...

The same code using object subscripting would look like this:

// Get a cell ...
MyClass *myObject = myDataArray[[indexPath row]];
// Configure the cell using the data in myObject ...

Using double square brackets is the correct syntax but has a very strange look to it. It would of course look sane if you used dot syntax (myDataArray[indexPath.row]) but I never liked dot syntax to begin with.

The case agains dot syntax (for me)

I don’t really dislike dot-syntax. To be honest, it doesn’t matter to me. What do matters to me is consistency.  Some methods looks and behave like custom setters but are in fact methods (like setContentOffset: animated:).

When you have methods like this and use dot-syntax you end up with code that looks like it was written by two different people who had a fight over code conventions and refused to change how they write their own code. I become both those people and look at my code, hating both the dot-syntax and the non-dot-syntax code for different reasons. Sicking with non-dot-syntax has always made more sense to me as it was looked more consistent.

Time to reconsider?

I feel like Modern Objective-C introduces some things I really love and some things that feel very strange to me. Using only some of the new convenions would probably leave with with even more inconsistent code which would be worse than before.

Am I missing out on the awesomeness of modern additions to Objective-C becaue of this? Maybe it’s time to reconsider my stand on dot-syntax…


  1. I’m looking at you NSNumber… 

CocoaHeads@Toca Boca

I loved yesterday’s CocoaHeads Stockholm meet up at Toca Boca. We had three great presentations, two about asynchronous Objective-C and Reactive Cocoa and one about Core Animation (which I gave). I also believe that we had a new attendee record, which is great.

Fantastic talks with a lot of people after the presentations. I’m definitely hoping for some nice new language features being introduced at WWDC. The asynchronous programming in Windiws 8 looks really good. Hopefully Apple will come out with something similar that fits the rest of Objective-C. That’s my biggest wish for this years event: Objective-C 3.0.


The slides from my presentation and the source code for all the demos can be found on GitHub.