$30 off During Our Annual Pro Sale. View Details »

Mastering CGAffineTransform

Chiharu Nameki
September 01, 2018

Mastering CGAffineTransform

マスタリング CGAffineTransform
iOSDC Japan 2018 LT

撮影した映像の向きがおかしい?Viewの位置計算で困ってる?
とても良い機会です。5分でCGAffineTransformをマスターしましょう!
CGAffineTransformはちっぽけなstructですが、使いこなせば非常に強力なAPIです。基本知識から組み合わせて掛けるときの考え方のコツなどを具体例と共にまとめました。もう座標計算に悩んで時間を使うことはありません!

Chiharu Nameki

September 01, 2018
Tweet

More Decks by Chiharu Nameki

Other Decks in Technology

Transcript

  1. ߦ໦ ઍय़ʢͳΊ͖ ͪ͸Δʣ
    גࣜձࣾαΠόʔΤʔδΣϯτ
    ϚελϦϯά
    CGAffineTransform
    iOSDC Japan 2018
    Sep. 1
    Twitter, Qiita, GitHub @Ridwy

    View Slide

  2. ͜Μͳܦݧɺ͋Γ·ͤΜ͔ʁ
    ࣸਅ΍ಈըΛճస͠Α͏ͱͨ͠Β

    Ͳ͔͜΁ߦͬͯ͠·ͬͨ
    UIViewΛΞχϝʔγϣϯ͍ͤͨ͞Μ͚ͩͲ

    transform͕Α͘Θ͔Βͳ͍

    View Slide

  3. CGAffineTransform
    2࣍ݩͷΞϑΟϯม׵ @ Core Graphics framework

    View Slide

  4. ΞϑΟϯม׵ͬͯʁ
    ࠲ඪม׵ͷҰछ
    ฏߦҠಈΛ൐͏ઢܗม׵

    View Slide

  5. ਺ֶతʹ͸ɺ͜Μͳߦྻ
    q p
    T
    q = pT
    ఺pΛม׵TͰqʹҠ͢

    View Slide

  6. SwiftͰͷఆٛ
    public struct CGAffineTransform {
    public var a: CGFloat
    public var b: CGFloat
    public var c: CGFloat
    public var d: CGFloat
    public var tx: CGFloat
    public var ty: CGFloat
    }

    View Slide

  7. ࢖͍ํ

    View Slide

  8. ੜ੒
    static var identity: CGAffineTransform
    init(a: CGFloat, b: CGFloat, c: CGFloat, d: CGFloat,
    tx: CGFloat, ty: CGFloat)
    init(translationX tx: CGFloat, y ty: CGFloat)
    init(scaleX sx: CGFloat, y sy: CGFloat)
    init(rotationAngle angle: CGFloat)
    ฏߦҠಈ ֦େɾॖখ ճస

    View Slide

  9. ద༻
    let q = p.applying(transform)
    CGPoint



    CGSize


    αΠζ
    CGRect


    ྖҬ
    pʹtransformΛద༻ͯ͠qʹҠ͢
    (x, y)
    (x, y) width
    height
    width
    height

    View Slide

  10. q = pABC
    let q = p.applying(a).applying(b).applying(c)
    let q = p.applying(a.concatenating(b).concatenating(c))
    ࿈ଓͯ͠ద༻
    concatenating Ͱ1ͭͷม׵ʹ·ͱΊΒΕΔ
    ͜ΕΒ͸਺ֶతʹ͸ɺ͜͏
    ίʔυͱ਺ࣜͰॻ͘ॱ൪͕Ұॹʂ

    View Slide

  11. ͪΐͬͽΓ਺ֶͷิ଍
    ڭՊॻͰΑ͘ݟ͔͚ΔΞϑΟϯม׵
    ߦͱྻΛೖΕସ͑Δʢ=సஔʣͱੵͷॱ൪͕ٯʹͳΔ
    q = T p
    T T
    T

    View Slide

  12. ۩ମྫ
    αʔόαΠυΤϯδχΞ
    “iOS͔ΒૹΒΕͯདྷΔࣸਅͷαΠζ͕େ͖͗͢·͢
    ਅΜத͔͍͠Βͳ͍ͷͰਖ਼ํܗʹΫϩοϓͯ͠
    ྑ͍ײ͡ʹͯ͠ૹͬͯ໯͑·ͤΜ͔ʁ”

    View Slide

  13. let length: CGFloat = 512
    let squareSize = CGSize(width: length, height: length)
    UIGraphicsBeginImageContextWithOptions(squareSize, false, 1)
    let ctx = UIGraphicsGetCurrentContext()!
    let width = image.size.width
    let height = image.size.height
    let scale = min(1.0, length / min(width, height))
    let transform = CGAffineTransform.identity
    .concatenating(.init(translationX: -width/2, y: -height/2))
    .concatenating(.init(scaleX: scale, y: scale))
    .concatenating(.init(translationX: length/2, y: length/2))
    ctx.concatenate(transform)
    image.draw(in: CGRect(origin: .zero, size: image.size))
    let resultImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    ཁٻΛ͢΂ͯຬͨ͢ɺUIImageΛม׵͢Δίʔυʢશจʣ

    View Slide

  14. let length: CGFloat = 512
    let squareSize = CGSize(width: length, height: length)
    UIGraphicsBeginImageContextWithOptions(squareSize, false, 1)
    let ctx = UIGraphicsGetCurrentContext()!
    let width = image.size.width
    let height = image.size.height
    let scale = min(1.0, length / min(width, height))
    let transform = CGAffineTransform.identity
    .concatenating(.init(translationX: -width/2, y: -height/2))
    .concatenating(.init(scaleX: scale, y: scale))
    .concatenating(.init(translationX: length/2, y: length/2))
    ctx.concatenate(transform)
    image.draw(in: CGRect(origin: .zero, size: image.size))
    let resultImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    ॖখͱΫϩοϓͷͨΊʹΞϑΟϯม׵Λར༻

    View Slide

  15. let length: CGFloat = 512
    let squareSize = CGSize(width: length, height: length)
    UIGraphicsBeginImageContextWithOptions(squareSize, false, 1)
    let ctx = UIGraphicsGetCurrentContext()!
    let width = image.size.width
    let height = image.size.height
    let scale = min(1.0, length / min(width, height))
    let transform = CGAffineTransform.identity
    .concatenating(.init(translationX: -width/2, y: -height/2))
    .concatenating(.init(scaleX: scale, y: scale))
    .concatenating(.init(translationX: length/2, y: length/2))
    ctx.concatenate(transform)
    image.draw(in: CGRect(origin: .zero, size: image.size))
    let resultImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    ॖখͱΫϩοϓͷͨΊʹΞϑΟϯม׵Λར༻

    View Slide

  16. let length: CGFloat = 512
    let squareSize = CGSize(width: length, height: length)
    UIGraphicsBeginImageContextWithOptions(squareSize, false, 1)
    let ctx = UIGraphicsGetCurrentContext()!
    let width = image.size.width
    let height = image.size.height
    let scale = min(1.0, length / min(width, height))
    let transform = CGAffineTransform.identity
    .concatenating(.init(translationX: -width/2, y: -height/2))
    .concatenating(.init(scaleX: scale, y: scale))
    .concatenating(.init(translationX: length/2, y: length/2))
    ctx.concatenate(transform)
    image.draw(in: CGRect(origin: .zero, size: image.size))
    let resultImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    ॖখͱΫϩοϓͷͨΊʹΞϑΟϯม׵Λར༻

    View Slide

  17. let length: CGFloat = 512
    let squareSize = CGSize(width: length, height: length)
    UIGraphicsBeginImageContextWithOptions(squareSize, false, 1)
    let ctx = UIGraphicsGetCurrentContext()!
    let width = image.size.width
    let height = image.size.height
    let scale = min(1.0, length / min(width, height))
    let transform = CGAffineTransform.identity
    .concatenating(.init(translationX: -width/2, y: -height/2))
    .concatenating(.init(scaleX: scale, y: scale))
    .concatenating(.init(translationX: length/2, y: length/2))
    ctx.concatenate(transform)
    image.draw(in: CGRect(origin: .zero, size: image.size))
    let resultImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    ॖখͱΫϩοϓͷͨΊʹΞϑΟϯม׵Λར༻

    View Slide

  18. let length: CGFloat = 512
    let squareSize = CGSize(width: length, height: length)
    UIGraphicsBeginImageContextWithOptions(squareSize, false, 1)
    let ctx = UIGraphicsGetCurrentContext()!
    let width = image.size.width
    let height = image.size.height
    let scale = min(1.0, length / min(width, height))
    let transform = CGAffineTransform.identity
    .concatenating(.init(translationX: -width/2, y: -height/2))
    .concatenating(.init(scaleX: scale, y: scale))
    .concatenating(.init(translationX: length/2, y: length/2))
    ctx.concatenate(transform)
    image.draw(in: CGRect(origin: .zero, size: image.size))
    let resultImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    ॖখͱΫϩοϓͷͨΊʹΞϑΟϯม׵Λར༻

    View Slide

  19. let length: CGFloat = 512
    let squareSize = CGSize(width: length, height: length)
    UIGraphicsBeginImageContextWithOptions(squareSize, false, 1)
    let ctx = UIGraphicsGetCurrentContext()!
    let width = image.size.width
    let height = image.size.height
    let scale = min(1.0, length / min(width, height))
    let transform = CGAffineTransform.identity
    .concatenating(.init(translationX: -width/2, y: -height/2))
    .concatenating(.init(scaleX: scale, y: scale))
    .concatenating(.init(translationX: length/2, y: length/2))
    ctx.concatenate(transform)
    image.draw(in: CGRect(origin: .zero, size: image.size))
    let resultImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    ॖখͱΫϩοϓͷͨΊʹΞϑΟϯม׵Λར༻

    View Slide

  20. ͞Βʹɺ࢖͍ํ

    View Slide

  21. ΞϑΟϯม׵Λ͞Βʹม׵͢Δ
    func translatedBy(x tx: CGFloat, y ty: CGFloat) -> CGAffineTransform
    func scaledBy(x sx: CGFloat, y sy: CGFloat) -> CGAffineTransform
    func rotated(by angle: CGFloat) -> CGAffineTransform

    View Slide

  22. ม׵ͷม׵ʁ
    transform = transform
    .translatedBy(x: 10, y: 0)
    .rotated(by: .pi/2)
    .scaledBy(x: 1.4, y: 1.4)
    .translatedBy(x: 5, y: 0)
    ʢಓҊ಺ͷΑ͏ͳΠϝʔδʣ
    ϩʔΧϧͷ࠲ඪܥ͕มΘ͍ͬͯ͘
    x
    y

    View Slide

  23. ม׵ͷม׵ʁ
    transform = transform
    .translatedBy(x: 10, y: 0)
    .rotated(by: .pi/2)
    .scaledBy(x: 1.4, y: 1.4)
    .translatedBy(x: 5, y: 0)
    ʢಓҊ಺ͷΑ͏ͳΠϝʔδʣ
    ϩʔΧϧͷ࠲ඪܥ͕มΘ͍ͬͯ͘
    x
    y

    View Slide

  24. ม׵ͷม׵ʁ
    transform = transform
    .translatedBy(x: 10, y: 0)
    .rotated(by: .pi/2)
    .scaledBy(x: 1.4, y: 1.4)
    .translatedBy(x: 5, y: 0)
    ʢಓҊ಺ͷΑ͏ͳΠϝʔδʣ
    ϩʔΧϧͷ࠲ඪܥ͕มΘ͍ͬͯ͘
    x
    y

    View Slide

  25. ม׵ͷม׵ʁ
    transform = transform
    .translatedBy(x: 10, y: 0)
    .rotated(by: .pi/2)
    .scaledBy(x: 1.4, y: 1.4)
    .translatedBy(x: 5, y: 0)
    ʢಓҊ಺ͷΑ͏ͳΠϝʔδʣ
    ϩʔΧϧͷ࠲ඪܥ͕มΘ͍ͬͯ͘
    x
    y

    View Slide

  26. ม׵ͷม׵ʁ
    transform = transform
    .translatedBy(x: 10, y: 0)
    .rotated(by: .pi/2)
    .scaledBy(x: 1.4, y: 1.4)
    .translatedBy(x: 5, y: 0)
    ʢಓҊ಺ͷΑ͏ͳΠϝʔδʣ
    ϩʔΧϧͷ࠲ඪܥ͕มΘ͍ͬͯ͘
    x
    y

    View Slide

  27. ਺ֶతʹ͸ɺ͜͏
    concatenating()΍CGRectͳͲͷapplying()ͱ͸൓ରଆ͔Βֻ͚Δ
    let transform = CGAffineTransform.identity
    .translatedBy(x: 10, y: 0) // A
    .rotated(by: .pi/2) // B
    .scaledBy(x: 1.4, y: 1.4) // C
    .translatedBy(x: 5, y: 0) // D
    let q = p.applying(transform)
    q = pDCBA

    View Slide

  28. ۩ମྫ
    ϓϥϯφʔ
    “ࣸਅʹελϯϓΛ͚͍ͭͨͰ͢
    ࢦͰҠಈͰ͖ͯɺ֦େͨ͠Γճͨ͠Γ”

    View Slide

  29. @objc func handlePan(_ recognizer: UIPanGestureRecognizer) {
    let t = recognizer.translation(in: stamp.superview)
    stamp.center = stamp.center.applying(.init(translationX: t.x, y: t.y))
    recognizer.setTranslation(.zero, in: stamp.superview)
    }
    @objc func handlePinch(_ recognizer: UIPinchGestureRecognizer) {
    let scale = recognizer.scale
    stamp.transform = stamp.transform.scaledBy(x: scale, y: scale)
    recognizer.scale = 1
    }
    @objc func handleRotation(_ recognizer: UIRotationGestureRecognizer){
    let rotation = recognizer.rotation
    stamp.transform = stamp.transform.rotated(by: rotation)
    recognizer.rotation = 0
    }
    ֦େॖখͱճస → transform
    Ҡಈ → center
    ελϯϓͷUIViewͷϓϩύςΟΛมߋ࣮ͯ͠ݱ

    View Slide

  30. ·ͱΊʢߟ͑ํͷίπʣ
    • CGPoint, CGSize, CGRectͷ applying() ͸

    superviewͷ࠲ඪܥͷதͰద༻͞ΕΔ
    • ΞϑΟϯม׵Λ͞Βʹม׵͢Δϝιου

    translatedBy(x:y:), scaledBy(x:y:), rotated(by:) ͸

    ϩʔΧϧͳ࠲ඪܥΛม׵͢Δ

    View Slide

  31. ͝੩ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠

    View Slide