Slide 1

Slide 1 text

Advanced Graphics with Core Animation ςΟϜ•ΦϦόʔ

Slide 2

Slide 2 text

• Core AnimationͷΞχϝʔγϣϯͷ࣮૷ํ๏ • Core AnimationΛগ͠঺հ͢Δ • CALayer ͷεϒΫϥεͷར༻ํ๏ Overview

Slide 3

Slide 3 text

@TimOliverAU • 2015೥3݄Realmʹೖͬͨ • ΦʔετϥϦΞͷύʔεͷग़਎ • iOS։ൃΛ΍ͬͯΔͷ͸໿6೥ؒ • ΧϥΦέ͕େ޷͖ʂ

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

My Relation with Japan • ؼࠃ͔ͯ͠Β೔ຊޠͷษڧΛ࢝Ί ͨ • 1996೥ʹઍ༿ݝͷখֶߍೖֶ • 2007೥ɺ৽ׁݝͷεΩʔ৔Ͱ ϫʔϗϦ • 2013೥ɺpixivͰiOS։ൃ

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

iComics • ͱΜͰ΋ͳ͍ը૾σʔλ • DRMϑϦʔ•ίϛοΫ•Ϧʔμʔ • Core AnimationΛ݁ߏར༻ ͍ͯ͠Δ • (͓·͚ʹɺRealm΋ʂ ^_^)

Slide 8

Slide 8 text

What’s Core Animation?

Slide 9

Slide 9 text

© Apple Inc. • UIKit͔ΒԼͷϨϕϧ What’s Core Animation? • ΦϖϨʔγϣϯΛGPUʹ సૹ͍ͤͯ͞Δ • UIViewͱਂͭ͘ͳ͕ͬͯΔ • iOSͷάϥϑΟΫ΋Ξχ ϝʔγϣϯ΋؅ཧ͢Δ

Slide 10

Slide 10 text

• ϨΠϠ•ΦϒδΣΫτͷ֊૚ • ϏοτϚοϓͷΑ͏ͳίϯ ςϯπ͕ηοτͰ͖Δ • ͜ͷΑ͏ʹήʔϜͷ։ൃͱ ಉ͡ͷΑ͏ʹͳΔʂ

Slide 11

Slide 11 text

No content

Slide 12

Slide 12 text

Why is it good to know? • iOSͷάϥϑΟοΫε•γεςϜΛ΋ͬͱཧղͰ͖Δ • 60FPSΛ΋ͬͱ؆୯ʹ໨ࢦ͢ࣄ͕Ͱ͖Δʂ • UIViewͷAPIΑΓɺ΋ͬͱ͖Ε͍ɺ΋ͬͱෳࡶ ͳΞχʔγϣϯ͕ՄೳੑʹͳΔ • ͱ͍͏Θ͚ͰɺΞϓϦ͕ଞʹ΋໨ཱͭ!

Slide 13

Slide 13 text

What about Core Graphics?

Slide 14

Slide 14 text

override func drawRect(_ rect: CGRect) { //// General Declarations let context = UIGraphicsGetCurrentContext() //// Color Declarations let fillColor = UIColor(red: 0.808, green: 0.000, blue: 0.000, alpha: 1.000) //// Oval Drawing let ovalPath = UIBezierPath(ovalInRect: CGRectMake(9, 8, 494, 494)) fillColor.setFill() ovalPath.fill() //// Bezier Drawing let bezierPath = UIBezierPath() bezierPath.moveToPoint(CGPointMake(334.34, 360.7)) bezierPath.addCurveToPoint(CGPointMake(196.21, 362.32), controlPoint1: CGPointMake(297.59, 381.88), controlPoint2: CGPointMake(247.05, 384.06)) bezierPath.addCurveToPoint(CGPointMake(99, 279.29), controlPoint1: CGPointMake(155.05, 344.84), controlPoint2: CGPointMake(120.9, 314.25)) bezierPath.addCurveToPoint(CGPointMake(134.91, 301.14), controlPoint1: CGPointMake(109.51, 288.03), controlPoint2: CGPointMake(121.77, 295.02)) bezierPath.addCurveToPoint(CGPointMake(276.84, 301.2), controlPoint1: CGPointMake(187.41, 325.7), controlPoint2: CGPointMake(239.9, 324.01)) bezierPath.addCurveToPoint(CGPointMake(276.79, 301.14), controlPoint1: CGPointMake(276.82, 301.18), controlPoint2: CGPointMake(276.8, 301.16)) bezierPath.addCurveToPoint(CGPointMake(146.29, 165.67), controlPoint1: CGPointMake(224.24, 260.93), controlPoint2: CGPointMake(179.57, 208.49)) bezierPath.addCurveToPoint(CGPointMake(128.78, 142.08), controlPoint1: CGPointMake(139.29, 158.68), controlPoint2: CGPointMake(134.03, 149.94)) bezierPath.addCurveToPoint(CGPointMake(255.76, 238.22), controlPoint1: CGPointMake(169.06, 178.78), controlPoint2: CGPointMake(233, 225.1)) bezierPath.addCurveToPoint(CGPointMake(166.43, 126.34), controlPoint1: CGPointMake(207.6, 187.52), controlPoint2: CGPointMake(164.68, 124.6)) bezierPath.addCurveToPoint(CGPointMake(313.57, 246.95), controlPoint1: CGPointMake(242.63, 203.25), controlPoint2: CGPointMake(313.57, 246.95)) bezierPath.addCurveToPoint(CGPointMake(319.19, 250.36), controlPoint1: CGPointMake(315.92, 248.27), controlPoint2: CGPointMake(317.73, 249.37)) bezierPath.addCurveToPoint(CGPointMake(323.2, 238.22), controlPoint1: CGPointMake(320.72, 246.46), controlPoint2: CGPointMake(322.07, 242.41)) bezierPath.addCurveToPoint(CGPointMake(290.8, 101), controlPoint1: CGPointMake(335.47, 193.64), controlPoint2: CGPointMake(321.46, 142.95)) bezierPath.addCurveToPoint(CGPointMake(386.26, 291.53), controlPoint1: CGPointMake(361.74, 143.82), controlPoint2: CGPointMake(403.78, 224.23)) bezierPath.addCurveToPoint(CGPointMake(384.77, 296.89), controlPoint1: CGPointMake(385.8, 293.34), controlPoint2: CGPointMake(385.3, 295.13)) bezierPath.addCurveToPoint(CGPointMake(385.38, 297.65), controlPoint1: CGPointMake(384.97, 297.14), controlPoint2: CGPointMake(385.18, 297.39)) bezierPath.addCurveToPoint(CGPointMake(406.4, 378.93), controlPoint1: CGPointMake(420.41, 341.35), controlPoint2: CGPointMake(410.78, 387.67)) bezierPath.addCurveToPoint(CGPointMake(334.34, 360.7), controlPoint1: CGPointMake(387.4, 341.82), controlPoint2: CGPointMake(352.22, 353.17)) bezierPath.closePath() bezierPath.miterLimit = 4; UIColor.whiteColor().setFill() bezierPath.fill() //// Text Drawing let textRect = CGRectMake(35, 144, 50, 140) let textTextContent = NSString(string: "{") let textStyle = NSParagraphStyle.defaultParagraphStyle().mutableCopy() as! NSMutableParagraphStyle textStyle.alignment = .Left let textFontAttributes = [NSFontAttributeName: UIFont.systemFontOfSize(107), NSForegroundColorAttributeName: UIColor.whiteColor(), NSParagraphStyleAttributeName: textStyle] let textTextHeight: CGFloat = textTextContent.boundingRectWithSize(CGSizeMake(textRect.width, CGFloat.infinity), options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: textFontAttributes, context: nil).size.height CGContextSaveGState(context) CGContextClipToRect(context, textRect); textTextContent.drawInRect(CGRectMake(textRect.minX, textRect.minY + (textRect.height - textTextHeight) / 2, textRect.width, textTextHeight), withAttributes: textFontAttributes) CGContextRestoreGState(context) //// Text 2 Drawing let text2Rect = CGRectMake(440, 143, 50, 140) let text2TextContent = NSString(string: "}") let text2Style = NSParagraphStyle.defaultParagraphStyle().mutableCopy() as! NSMutableParagraphStyle text2Style.alignment = .Left let text2FontAttributes = [NSFontAttributeName: UIFont.systemFontOfSize(107), NSForegroundColorAttributeName: UIColor.whiteColor(), NSParagraphStyleAttributeName: text2Style] let text2TextHeight: CGFloat = text2TextContent.boundingRectWithSize(CGSizeMake(text2Rect.width, CGFloat.infinity), options: NSStringDrawingOptions.UsesLineFragmentOrigin, attributes: text2FontAttributes, context: nil).size.height CGContextSaveGState(context) CGContextClipToRect(context, text2Rect); text2TextContent.drawInRect(CGRectMake(text2Rect.minX, text2Rect.minY + (text2Rect.height - text2TextHeight) / 2, text2Rect.width, text2TextHeight), withAttributes: text2FontAttributes) CGContextRestoreGState(context) }

Slide 15

Slide 15 text

• ׬શʹCPUͰ࣮ߦ •Core Animationͱ߹Θͤͨ Β͔ͳΓʹ͍͍ʂ •લͷੈ୅ͷσόΠεͰඇ ৗʹ஗͍Մೳੑ •ը૾࡞੒͢ΔAPI Core Graphics

Slide 16

Slide 16 text

PaintCode on the Mac App Store

Slide 17

Slide 17 text

So. Core Animation? import QuartzCore let newLayer = CALayer() newLayer.frame = CGRectMake(0, 0, 100, 100) newLayer.backgroundColor = UIColor.redColor().CGColor • ϨΠϠ•ΦϒδΣΫτͷ֊૚Ͱ࣮૷ • جຊతͳΫϥε͸ CALayer

Slide 18

Slide 18 text

So. Core Animation? import QuartzCore let newLayer = CALayer() newLayer.frame = CGRectMake(0, 0, 100, 100) newLayer.backgroundColor = UIColor.redColor().CGColor newLayer.cornerRadius = 10 • ϨΠϠ•ΦϒδΣΫτͷ֊૚Ͱ࣮૷ • جຊతͳΫϥε͸ CALayer

Slide 19

Slide 19 text

CALayer Where is it in UIKit? UIView UIView

Slide 20

Slide 20 text

CALayer Where is it in UIKit? public class UIView { public var layer: CALayer { get } } UIView

Slide 21

Slide 21 text

Deeply integrated with UIView public class UIView { public var frame: CGRect { get { return self.layer.frame } set { self.layer.frame = newValue } } } let newLayer = CALayer() view.layer.addSublayer(newLayer) • CALayerϓϩύςΟʔ͕UIView Ͱࣔ͞ΕͯΔ • ‘frame’ ͸ CALayerͷ‘position’ ͱ‘bounds’ ϓϩύςΟʔͰ ܭ͍ͬͯΔ.

Slide 22

Slide 22 text

Why is it not a superclass? • UIViewͷlayerͷΫϥε͕มΘΔࣄ΋͋Δɻ • ී௨ͷαϒΫϥεͷ࣮૷ͰෆՄೳ public class MyGradientClass : UIView { override class func layerClass() -> AnyClass { return CAGradientLayer.self } }

Slide 23

Slide 23 text

Mapping contents to CALayer let trySwiftLogo = self.trySwiftLogo() as UIImage let trySwiftLayer = CALayer() trySwiftLayer.contents = trySwiftLogo.CGImage (Ξχϝʔγϣϯ΋Մʂ)

Slide 24

Slide 24 text

Managing the scale of CALayer contents trySwiftLayer.contentsGravity kCAGravityResize kCAGravityResizeAspectFill kCAGravityResizeAspect kCAGravityCenter

Slide 25

Slide 25 text

Tweetbot

Slide 26

Slide 26 text

UIImage UIImageView CALayer kCAGravityLeft CALayer kCAGravityRight

Slide 27

Slide 27 text

Bitmap sampling in CALayer trySwiftLayer.minificationFilter trySwiftLayer.magnificationFilter kCAFilterTrilinear Best Quality (Slowest) kCAFilterLinear Default kCAFilterNearest Lowest Quality (Fast)

Slide 28

Slide 28 text

Bitmap sampling in CALayer

Slide 29

Slide 29 text

Masking CALayer Objects let myLayer = CALayer() myLayer.contents = self.makeRedCircleImage().CGImage let myMask = CALayer() myMask.contents = self.makeMaskImage().CGImage myLayer.mask = myMask + = myLayer myMask

Slide 30

Slide 30 text

Device Layer Pages Layer Arrow Layer Mask Layer

Slide 31

Slide 31 text

Adding Shadows to CALayer let myLayer = view.layer myLayer.shadowColor = UIColor.blackColor().CGColor myLayer.shadowOpacity = 0.75 myLayer.shadowOffset = CGSizeMake(5, 10) myLayer.shadowRadius = 10 // IMPORTANT FOR PERFORMANCE let myShadowPath = UIBezierPath(roundedRect: view.bounds, cornerRadius: 10) myLayer.shadowPath = myShadowPath.CGPath

Slide 32

Slide 32 text

Transforming a CALayer let myLayer = CALayer() myLayer.contents = self.makeTrySwiftLogoImage().CGImage var transform = CATransform3DIdentity transform.m34 = 1.0 / -500 transform = CATransform3DRotate(transform, 45.0f * M_PI / 180.0, 0, 1, 0) myLayer.transform = transform

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

Flipboard

Slide 35

Slide 35 text

Blend Modes with CALayer let myBlendLayer = CALayer() myBlendLayer.setValue(false, forKey: “allowsGroupBlending”) // PRIVATE myBlendLayer.compositingFilter = “screenBlendMode" myBlendLayer.allowsGroupOpacity = false myLayer.addSublayer(myBlendLayer) • ஫ҙɿPrivate APIΛར༻͍ͯ͠Δʂ + =

Slide 36

Slide 36 text

CALayer - “destIn” Blend Mode CALayer - “screenBlendMode” Blend Mode CALayer - “linearLightBlendMode” Blend Mode CAGradientLayer - “colorDodgeBlendMode” Blend Mode CALayer - “multiplyBlendMode” Blend Mode CAShapeLayer - Normal Blend Mode

Slide 37

Slide 37 text

Reveal - http://revealapp.com

Slide 38

Slide 38 text

Animating with Core Animation

Slide 39

Slide 39 text

Compared to UIKit let trySwiftView = UIImageView(image:) let trySwiftView.center = CGPointZero UIView.animateWithDuration(2, delay: 0, options: .CurveEaseInOut, animations: { trySwiftView.center = CGPointMake(0, 500) }, completion: nil)

Slide 40

Slide 40 text

CABasicAnimation let trySwiftLayer = //… let myAnimation = CABasicAnimation(keyPath: “position.x”) myAnimation.duration = 2 myAnimation.fromValue = trySwiftLayer.position.x myAnimation.toValue = trySwiftLayer.position.x + 500 myAnimation.timingFunction = kCAMediaTimingFunctionEaseInEaseOut myAnimation.repeatCount = .infinity trySwiftLayer.addAnimation(myAnimation, forKey: “myAnimationKeyName”)

Slide 41

Slide 41 text

CABasicAnimation let trySwiftLayer = //… let myAnimation = CABasicAnimation(keyPath: “position.x”) myAnimation.duration = 2 myAnimation.fromValue = trySwiftLayer.position.x myAnimation.toValue = trySwiftLayer.position.x + 500 myAnimation.timingFunction = kCAMediaTimingFunctionEaseInEaseOut myAnimation.repeatCount = .infinity trySwiftLayer.addAnimation(myAnimation, forKey: “myAnimationKeyName”)

Slide 42

Slide 42 text

Timing Function let timingFunction = CAMediaTimingFunction(controlPoints: .08, .04, .08, .99) let myAnimation = CABasicAnimation() myAnimation.timingFunction = timingFunction http://cubic-bezier.com

Slide 43

Slide 43 text

Animating a CALayer’s Contents let imageView = UIImageView() let onImage = UIImage() let offImage = UIImage() let myAnim = CABasicAnimation(keyPath: “contents”) myAnim.fromValue = offImage.CGImage myAnim.toValue = onImage.CGImage myAnim.duration = 0.15 imageView.layer.addAnimation(myCrossfadeAnimation, forKey: “contents”) imageView.image = onImage

Slide 44

Slide 44 text

CAKeyframeAnimation let rect = CGRectMake(0, 0, 200, 200) let circlePath = UIBezierPath(ovalInRect:rect) let circleAnimation = CAKeyframeAnimation() circleAnimation.keyPath = “position” circleAnimation.path = circlePath.CGPath circleAnimation.duration = 4 // Manually specify keyframe points // circleAnimation.values = //… // circleAnimation.keyTimes = //.. let trySwiftLayer = //… trySwiftLayer.addAnimation(circleAnimation, forKey: “position”)

Slide 45

Slide 45 text

CAAnimationGroup let myPositionAnimation = CABasicAnimation.animation(keyPath: “position”) let myAlphaAnimation = CABasicAnimation.animation(keyPath: “opacity”) let animationGroup = CAAnimationGroup() animationGroup.timingFunction = kCAMediaTimingFunctionEaseInEaseOut animationGroup.duration = 2 animationGroup.animations = [myPositionAnimation, myAlphaAnimation] let trySwiftLayer = CALayer() trySwiftLayer.addAnimation(animationGroup, forKey: “myAnimations”)

Slide 46

Slide 46 text

Animation Completion Handling // Set a delegate object let myAnimation = CABasicAnimation() myAnimation.delegate = self // Animation completion sent to ‘animationDidStop(anim: finished flag:) // ——— //Set a closure to be executed at the end of this transaction CATransaction.begin() CATransaction.setCompletionBlock({ // Logic to be performed, post animation }) CATransaction.commit()

Slide 47

Slide 47 text

CoreAnimator on the Mac App Store

Slide 48

Slide 48 text

Features of Core Animation Subclasses

Slide 49

Slide 49 text

Features of Core Animation Subclasses • UIViewͷαϒΫϥεͰೖΕΔ • GPUͰ࣮ߦɺಛผͳΠϑΣΫτ • ࣌ʑɺCPUͷΦϖϨʔγϣϯ΋͋Δ public class MyGradientClass : UIView { override class func layerClass() -> AnyClass { return CAGradientLayer.self } }

Slide 50

Slide 50 text

CATileLayer • ৭ʑͳαΠζͰը૾Λ࠶Ϩϯ μʔ • PDF΍SVG΍ϕΫτϧσʔλͳ Βඇৗʹ໾ʹཱͭʂ • Core GraphicsͰόοΫάϥ΢ϯ υͰ࣮ߦ

Slide 51

Slide 51 text

CAGradientLayer • GPUͰάϥδΤϯτΛϨϯ μʔ͢Δ •3Dʹม׵͞ΕͨϨΠϠͰ؆୯ʹ ӨͷΤϑΣΫτ

Slide 52

Slide 52 text

CAReplicaterLayer © iNVASIVECODE 2015
 https://vimeo.com/128046096 • GPUͰҰͭͷϨΠϠΛԿ ճ΋ίϐʔͯ͠දࣔ͢Δ • αϜωʔϧ΍ήʔϜͰ ΋໾ʹཱͭ

Slide 53

Slide 53 text

CAShapeLayer UAProgressView © Urban Apps 2014 • CGPath͔Β৭ʑͳܗ͕දࣔ͞ ͤɺΞχϝʔγϣϯͤ͞Δ • ಡΈࠐΉΞΠίϯʹͱͯ΋߹ͬͯΔ •iOS 7ͷσβΠϯελΠϧʹ΋ࣅ߹ͬͯΔ

Slide 54

Slide 54 text

CAEmitterLayer Particle Playground on the Mac App Store • ϨΠϠͷ’frame’ ͔ΒύʔςΟΫ ϧ͕ग़Δɻ • ήʔϜ΍ΞϓϦ ͷ൓ԠͷΞχϝʔ γϣϯʹࣅ߹͏ɻ

Slide 55

Slide 55 text

Other Layer Subclasses • ήʔϜͷͨΊͷCAEAGLLayer / CAMetalLayer • ׬શͳ3Dͷม׵ͷCATransformLayer • େ͖ͳίϯςϯπΛεΫϩʔϧͷCAScrollLayer • UILabelͱಉ͡ͷΑ͏ʹCATextLayer

Slide 56

Slide 56 text

Conclusion • UIͳΒɺUIView௚઀͡Όͳ͘ɺCALayerͩ • ΋ͬͱ౒ྗ͔ͩΒɺ࠷ॳʹUIKitͰ΍ͬͯΈͨ ํ͕͍͍ • ͪΌΜͱ࢖͑͹ɺ60FPSͰ͔ͬ͜͏͍͍Τ ϑΣΫτ͕ՄೳੑʹͳΔ • ҰॹʹݟࣄͳΞϓϦΛ࡞Ζ͏ʂ

Slide 57

Slide 57 text

Thanks for watching!

Slide 58

Slide 58 text

Thanks for watching! [email protected] @TimOliverAU