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

try! Swift - Advanced Graphics with Core Animation

try! Swift - Advanced Graphics with Core Animation

Tim Oliver

March 02, 2016
Tweet

More Decks by Tim Oliver

Other Decks in Technology

Transcript

  1. My Relation with Japan • ؼࠃ͔ͯ͠Β೔ຊޠͷษڧΛ࢝Ί ͨ • 1996೥ʹઍ༿ݝͷখֶߍೖֶ •

    2007೥ɺ৽ׁݝͷεΩʔ৔Ͱ ϫʔϗϦ • 2013೥ɺpixivͰiOS։ൃ
  2. © Apple Inc. • UIKit͔ΒԼͷϨϕϧ What’s Core Animation? • ΦϖϨʔγϣϯΛGPUʹ

    సૹ͍ͤͯ͞Δ • UIViewͱਂͭ͘ͳ͕ͬͯΔ • iOSͷάϥϑΟΫ΋Ξχ ϝʔγϣϯ΋؅ཧ͢Δ
  3. Why is it good to know? • iOSͷάϥϑΟοΫε•γεςϜΛ΋ͬͱཧղͰ͖Δ • 60FPSΛ΋ͬͱ؆୯ʹ໨ࢦ͢ࣄ͕Ͱ͖Δʂ

    • UIViewͷAPIΑΓɺ΋ͬͱ͖Ε͍ɺ΋ͬͱෳࡶ ͳΞχʔγϣϯ͕ՄೳੑʹͳΔ • ͱ͍͏Θ͚ͰɺΞϓϦ͕ଞʹ΋໨ཱͭ!
  4. 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) }
  5. So. Core Animation? import QuartzCore let newLayer = CALayer() newLayer.frame

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

    = CGRectMake(0, 0, 100, 100) newLayer.backgroundColor = UIColor.redColor().CGColor newLayer.cornerRadius = 10 • ϨΠϠ•ΦϒδΣΫτͷ֊૚Ͱ࣮૷ • جຊతͳΫϥε͸ CALayer
  7. CALayer Where is it in UIKit? public class UIView {

    public var layer: CALayer { get } } UIView
  8. 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’ ϓϩύςΟʔͰ ܭ͍ͬͯΔ.
  9. Why is it not a superclass? • UIViewͷlayerͷΫϥε͕มΘΔࣄ΋͋Δɻ • ී௨ͷαϒΫϥεͷ࣮૷ͰෆՄೳ

    public class MyGradientClass : UIView { override class func layerClass() -> AnyClass { return CAGradientLayer.self } }
  10. Mapping contents to CALayer let trySwiftLogo = self.trySwiftLogo() as UIImage

    let trySwiftLayer = CALayer() trySwiftLayer.contents = trySwiftLogo.CGImage (Ξχϝʔγϣϯ΋Մʂ)
  11. Masking CALayer Objects let myLayer = CALayer() myLayer.contents = self.makeRedCircleImage().CGImage

    let myMask = CALayer() myMask.contents = self.makeMaskImage().CGImage myLayer.mask = myMask + = myLayer myMask
  12. 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
  13. 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
  14. Blend Modes with CALayer let myBlendLayer = CALayer() myBlendLayer.setValue(false, forKey:

    “allowsGroupBlending”) // PRIVATE myBlendLayer.compositingFilter = “screenBlendMode" myBlendLayer.allowsGroupOpacity = false myLayer.addSublayer(myBlendLayer) • ஫ҙɿPrivate APIΛར༻͍ͯ͠Δʂ + =
  15. CALayer - “destIn” Blend Mode CALayer - “screenBlendMode” Blend Mode

    CALayer - “linearLightBlendMode” Blend Mode CAGradientLayer - “colorDodgeBlendMode” Blend Mode CALayer - “multiplyBlendMode” Blend Mode CAShapeLayer - Normal Blend Mode
  16. 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)
  17. 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”)
  18. 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”)
  19. Timing Function let timingFunction = CAMediaTimingFunction(controlPoints: .08, .04, .08, .99)

    let myAnimation = CABasicAnimation() myAnimation.timingFunction = timingFunction http://cubic-bezier.com
  20. 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
  21. 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”)
  22. 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”)
  23. 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()
  24. Features of Core Animation Subclasses • UIViewͷαϒΫϥεͰೖΕΔ • GPUͰ࣮ߦɺಛผͳΠϑΣΫτ •

    ࣌ʑɺCPUͷΦϖϨʔγϣϯ΋͋Δ public class MyGradientClass : UIView { override class func layerClass() -> AnyClass { return CAGradientLayer.self } }
  25. CAShapeLayer UAProgressView © Urban Apps 2014 • CGPath͔Β৭ʑͳܗ͕දࣔ͞ ͤɺΞχϝʔγϣϯͤ͞Δ •

    ಡΈࠐΉΞΠίϯʹͱͯ΋߹ͬͯΔ •iOS 7ͷσβΠϯελΠϧʹ΋ࣅ߹ͬͯΔ
  26. CAEmitterLayer Particle Playground on the Mac App Store • ϨΠϠͷ’frame’

    ͔ΒύʔςΟΫ ϧ͕ग़Δɻ • ήʔϜ΍ΞϓϦ ͷ൓ԠͷΞχϝʔ γϣϯʹࣅ߹͏ɻ
  27. Other Layer Subclasses • ήʔϜͷͨΊͷCAEAGLLayer / CAMetalLayer • ׬શͳ3Dͷม׵ͷCATransformLayer •

    େ͖ͳίϯςϯπΛεΫϩʔϧͷCAScrollLayer • UILabelͱಉ͡ͷΑ͏ʹCATextLayer