Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Mastering CGAffineTransform

4ef794bdea63fc6153845bb26bac38a2?s=47 Chiharu Nameki
September 01, 2018

Mastering CGAffineTransform

マスタリング CGAffineTransform
iOSDC Japan 2018 LT

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

4ef794bdea63fc6153845bb26bac38a2?s=128

Chiharu Nameki

September 01, 2018
Tweet

Transcript

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

    1 Twitter, Qiita, GitHub @Ridwy
  2. ͜Μͳܦݧɺ͋Γ·ͤΜ͔ʁ ࣸਅ΍ಈըΛճస͠Α͏ͱͨ͠Β
 Ͳ͔͜΁ߦͬͯ͠·ͬͨ UIViewΛΞχϝʔγϣϯ͍ͤͨ͞Μ͚ͩͲ
 transform͕Α͘Θ͔Βͳ͍

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

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

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

  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 }
  7. ࢖͍ํ

  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) ฏߦҠಈ ֦େɾॖখ ճస
  9. ద༻ let q = p.applying(transform) CGPoint
 
 ఺ CGSize
 


    αΠζ CGRect
 
 ྖҬ pʹtransformΛద༻ͯ͠qʹҠ͢ (x, y) (x, y) width height width height
  10. q = pABC let q = p.applying(a).applying(b).applying(c) let q =

    p.applying(a.concatenating(b).concatenating(c)) ࿈ଓͯ͠ద༻ concatenating Ͱ1ͭͷม׵ʹ·ͱΊΒΕΔ ͜ΕΒ͸਺ֶతʹ͸ɺ͜͏ ίʔυͱ਺ࣜͰॻ͘ॱ൪͕Ұॹʂ
  11. ͪΐͬͽΓ਺ֶͷิ଍ ڭՊॻͰΑ͘ݟ͔͚ΔΞϑΟϯม׵ ߦͱྻΛೖΕସ͑Δʢ=సஔʣͱੵͷॱ൪͕ٯʹͳΔ q = T p T T T

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

  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Λม׵͢Δίʔυʢશจʣ
  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() ॖখͱΫϩοϓͷͨΊʹΞϑΟϯม׵Λར༻
  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() ॖখͱΫϩοϓͷͨΊʹΞϑΟϯม׵Λར༻
  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() ॖখͱΫϩοϓͷͨΊʹΞϑΟϯม׵Λར༻
  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() ॖখͱΫϩοϓͷͨΊʹΞϑΟϯม׵Λར༻
  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() ॖখͱΫϩοϓͷͨΊʹΞϑΟϯม׵Λར༻
  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() ॖখͱΫϩοϓͷͨΊʹΞϑΟϯม׵Λར༻
  20. ͞Βʹɺ࢖͍ํ

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

  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ͷϓϩύςΟΛมߋ࣮ͯ͠ݱ
  30. ·ͱΊʢߟ͑ํͷίπʣ • CGPoint, CGSize, CGRectͷ applying() ͸
 superviewͷ࠲ඪܥͷதͰద༻͞ΕΔ • ΞϑΟϯม׵Λ͞Βʹม׵͢Δϝιου


    translatedBy(x:y:), scaledBy(x:y:), rotated(by:) ͸
 ϩʔΧϧͳ࠲ඪܥΛม׵͢Δ
  31. ͝੩ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠