Chiharu Nameki
September 01, 2018
740

# Mastering CGAffineTransform

マスタリング CGAffineTransform
iOSDC Japan 2018 LT

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

## Chiharu Nameki

September 01, 2018

## Transcript

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

1 Twitter, Qiita, GitHub @Ridwy

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 }

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ͭͷม׵ʹ·ͱΊΒΕΔ ͜ΕΒ͸਺ֶతʹ͸ɺ͜͏ ίʔυͱ਺ࣜͰॻ͘ॱ൪͕Ұॹʂ

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() ॖখͱΫϩοϓͷͨΊʹΞϑΟϯม׵Λར༻

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

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:) ͸  ϩʔΧϧͳ࠲ඪܥΛม׵͢Δ