マスタリング CGAffineTransform iOSDC Japan 2018 LT
撮影した映像の向きがおかしい?Viewの位置計算で困ってる? とても良い機会です。5分でCGAffineTransformをマスターしましょう! CGAffineTransformはちっぽけなstructですが、使いこなせば非常に強力なAPIです。基本知識から組み合わせて掛けるときの考え方のコツなどを具体例と共にまとめました。もう座標計算に悩んで時間を使うことはありません!
ߦ ઍय़ʢͳΊ͖ ͪΔʣגࣜձࣾαΠόʔΤʔδΣϯτϚελϦϯάCGAffineTransformiOSDC Japan 2018Sep. 1Twitter, Qiita, GitHub @Ridwy
View Slide
͜Μͳܦݧɺ͋Γ·ͤΜ͔ʁࣸਅಈըΛճస͠Α͏ͱͨ͠Β Ͳ͔͜ߦͬͯ͠·ͬͨUIViewΛΞχϝʔγϣϯ͍ͤͨ͞Μ͚ͩͲ transform͕Α͘Θ͔Βͳ͍
CGAffineTransform2࣍ݩͷΞϑΟϯม @ Core Graphics framework
ΞϑΟϯมͬͯʁ࠲ඪมͷҰछฏߦҠಈΛ͏ઢܗม
ֶతʹɺ͜Μͳߦྻq pTq = pTpΛมTͰqʹҠ͢
SwiftͰͷఆٛpublic struct CGAffineTransform {public var a: CGFloatpublic var b: CGFloatpublic var c: CGFloatpublic var d: CGFloatpublic var tx: CGFloatpublic var ty: CGFloat}
͍ํ
ੜstatic var identity: CGAffineTransforminit(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)ฏߦҠಈ ֦େɾॖখ ճస
ద༻let q = p.applying(transform)CGPoint CGSize αΠζCGRect ྖҬpʹtransformΛద༻ͯ͠qʹҠ͢(x, y)(x, y) widthheightwidthheight
q = pABClet q = p.applying(a).applying(b).applying(c)let q = p.applying(a.concatenating(b).concatenating(c))࿈ଓͯ͠ద༻concatenating Ͱ1ͭͷมʹ·ͱΊΒΕΔ͜ΕΒֶతʹɺ͜͏ίʔυͱࣜͰॻ͘ॱ൪͕Ұॹʂ
ͪΐͬͽΓֶͷิڭՊॻͰΑ͘ݟ͔͚ΔΞϑΟϯมߦͱྻΛೖΕସ͑Δʢ=సஔʣͱੵͷॱ൪͕ٯʹͳΔq = T pT TT
۩ମྫαʔόαΠυΤϯδχΞ“iOS͔ΒૹΒΕͯདྷΔࣸਅͷαΠζ͕େ͖͗͢·͢ਅΜத͔͍͠Βͳ͍ͷͰਖ਼ํܗʹΫϩοϓͯ͠ྑ͍ײ͡ʹͯ͠ૹͬͯ͑·ͤΜ͔ʁ”
let length: CGFloat = 512let squareSize = CGSize(width: length, height: length)UIGraphicsBeginImageContextWithOptions(squareSize, false, 1)let ctx = UIGraphicsGetCurrentContext()!let width = image.size.widthlet height = image.size.heightlet 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Λม͢Δίʔυʢશจʣ
let length: CGFloat = 512let squareSize = CGSize(width: length, height: length)UIGraphicsBeginImageContextWithOptions(squareSize, false, 1)let ctx = UIGraphicsGetCurrentContext()!let width = image.size.widthlet height = image.size.heightlet 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()ॖখͱΫϩοϓͷͨΊʹΞϑΟϯมΛར༻
͞Βʹɺ͍ํ
ΞϑΟϯมΛ͞Βʹม͢Δfunc translatedBy(x tx: CGFloat, y ty: CGFloat) -> CGAffineTransformfunc scaledBy(x sx: CGFloat, y sy: CGFloat) -> CGAffineTransformfunc rotated(by angle: CGFloat) -> CGAffineTransform
มͷมʁtransform = transform.translatedBy(x: 10, y: 0).rotated(by: .pi/2).scaledBy(x: 1.4, y: 1.4).translatedBy(x: 5, y: 0)ʢಓҊͷΑ͏ͳΠϝʔδʣϩʔΧϧͷ࠲ඪܥ͕มΘ͍ͬͯ͘xy
ֶతʹɺ͜͏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) // Dlet q = p.applying(transform)q = pDCBA
۩ମྫϓϥϯφʔ“ࣸਅʹελϯϓΛ͚͍ͭͨͰ͢ࢦͰҠಈͰ͖ͯɺ֦େͨ͠Γճͨ͠Γ”
@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.scalestamp.transform = stamp.transform.scaledBy(x: scale, y: scale)recognizer.scale = 1}@objc func handleRotation(_ recognizer: UIRotationGestureRecognizer){let rotation = recognizer.rotationstamp.transform = stamp.transform.rotated(by: rotation)recognizer.rotation = 0}֦େॖখͱճస → transformҠಈ → centerελϯϓͷUIViewͷϓϩύςΟΛมߋ࣮ͯ͠ݱ
·ͱΊʢߟ͑ํͷίπʣ• CGPoint, CGSize, CGRectͷ applying() superviewͷ࠲ඪܥͷதͰద༻͞ΕΔ• ΞϑΟϯมΛ͞Βʹม͢Δϝιου translatedBy(x:y:), scaledBy(x:y:), rotated(by:) ϩʔΧϧͳ࠲ඪܥΛม͢Δ
͝੩ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠