Search code examples
ioscgaffinetransform

CGAffineTransform: How to calculate multiply CGAffineTransform?


I need transform view from origin(250, 250) to origin(352, 315), and width/height change from (100.0, 100.0) to (68, 68). I know I can combine several CGAffineTransform function together, such as scale, rotate, translate. But i don't know how to count the order of those transformations, and the exact parameter of them. I have try several time, but can't move the view to correct position.

Anyone can help?


Solution

  • A little understanding about what is happening behind the scenes is always nice in these matrix transformations.

    Apple docs has a great documentation about transforms, so let's use it.


    A translation matrix looks like :

    |  1   0   0  |
    |  0   1   0  |
    |  tx  ty  1  |
    

    where (tx, ty) is your translation vector.


    A scaling matrix looks like :

    |  sx  0   0  |
    |  0   sy  0  |
    |  0   0   1  |
    

    where sxand sy are the scale factor in the X and Y axis.


    You want to concatenate these matrix using CGAffineTransformConcat, but as according to its doc :

    Note that matrix operations are not commutative—the order in which you concatenate matrices is important. That is, the result of multiplying matrix t1 by matrix t2 does not necessarily equal the result of multiplying matrix t2 by matrix t1.

    You have to translate your view before scaling it, otherwise your translation vector will be scaled according to sx and sy coefficients.

    Let's show it easily :

    let scaleMatrix = CGAffineTransformMakeScale(0.68, 0.68)
    let translateMatrix = CGAffineTransformMakeTranslation(102, 65)
    
    let translateThenScaleMatrix = CGAffineTransformConcat(scaleMatrix, translateMatrix)
    NSLog("translateThenScaleMatrix : \(translateThenScaleMatrix)")
    // outputs : CGAffineTransform(a: 0.68, b: 0.0, c: 0.0, d: 0.68, tx: 102.0, ty: 65.0)
    // the translation is the same
    
    let scaleThenTranslateMatrix = CGAffineTransformConcat(translateMatrix, scaleMatrix)
    NSLog("scaleThenTranslateMatrix : \(scaleThenTranslateMatrix)")
    // outputs : CGAffineTransform(a: 0.68, b: 0.0, c: 0.0, d: 0.68, tx: 69.36, ty: 44.2)
    // the translation has been scaled too
    

    And let's prove it mathematically. Please note that when you perform an operation A then an operation B, the related matrix is computed by doing matB*matA, the first operation is on the right. Since multiplication is not commutative for matrix, it's important.

    // Translate then  scaling :
    |  sx  0   0  |   |  1   0   0  |   |    sx   0    0 |
    |  0   sy  0  | . |  0   1   0  | = |    0    sy   0 |
    |  0   0   1  |   |  tx  ty  1  |   |    tx   ty   1 |
    // The resulting matrix has the same value for translation
    
    // Scaling then translation :
    |  1   0   0  |   |  sx  0   0  |   |    sx     0      0 |
    |  0   1   0  | . |  0   sy  0  | = |    0      sy     0 |
    |  tx  ty  1  |   |  0   0   1  |   |  sx.tx   sy.ty   1 |
    // The translation values are affected by scaling coefficient