try! swift - advanced graphics with core animation
TRANSCRIPT
Advanced Graphics with Core Animation
ティム•オリバー
• Core Animationのアニメーションの実装方法
• Core Animationを少し紹介する
• CALayer のスブクラスの利用方法
Overview
@TimOliverAU
• 2015年3月Realmに入った
•オーストラリアのパースの出身
• iOS開発をやってるのは約6年間
•カラオケが大好き!
My Relation with Japan
•帰国してから日本語の勉強を始めた
• 1996年に千葉県の小学校入学
• 2007年、新潟県のスキー場でワーホリ
• 2013年、pixivでiOS開発
iComics
•とんでもない画像データ
• DRMフリー•コミック•リーダー
• Core Animationを結構利用している
• (おまけに、Realmも! ^_^)
What’s Core Animation?
© Apple Inc.
• UIKitから下のレベル
What’s Core Animation?
•オペレーションをGPUに
転送させている
• UIViewと深くつながって
る
• iOSのグラフィクもアニメーションも管理する
•レイヤ•オブジェクトの階層
•ビットマップのようなコンテンツがセットできる
•このようにゲームの開発と同じのようになる!
Why is it good to know?
• iOSのグラフィックス•システムをもっと理解でき
る• 60FPSをもっと簡単に目指す事ができる!
• UIViewのAPIより、もっときれい、もっと複
雑なアニーションが可能性になる
•というわけで、アプリが他にも目立つ!
What about Core Graphics?
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)
}
•完全にCPUで実行
•Core Animationと合わせ
たらかなりにいい!
•前の世代のデバイスで非
常に遅い可能性
•画像作成するAPI
Core Graphics
PaintCode on the Mac App
Store
So. Core Animation?
import QuartzCore
let newLayer = CALayer()
newLayer.frame = CGRectMake(0, 0, 100, 100)
newLayer.backgroundColor = UIColor.redColor().CGColor
•レイヤ•オブジェクトの階層で実装
•基本的なクラスは CALayer
So. Core Animation?
import QuartzCore
let newLayer = CALayer()
newLayer.frame = CGRectMake(0, 0, 100, 100)
newLayer.backgroundColor = UIColor.redColor().CGColor
newLayer.cornerRadius = 10
•レイヤ•オブジェクトの階層で実装
•基本的なクラスは CALayer
CALayer
Where is it in UIKit?
UIViewUIView
CALayer
Where is it in UIKit?
public class UIView {
public var layer: CALayer { get }
}
UIView
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’ プロパティーで計っている.
Why is it not a superclass?
• UIViewのlayerのクラスが変わる事もある。
•普通のサブクラスの実装で不可能
public class MyGradientClass : UIView {
override class func layerClass() -> AnyClass {
return CAGradientLayer.self
}
}
Mapping contents to CALayer
let trySwiftLogo = self.trySwiftLogo() as UIImage
let trySwiftLayer = CALayer()
trySwiftLayer.contents = trySwiftLogo.CGImage
(アニメーションも可!)
Managing the scale of CALayer contentstrySwiftLayer.contentsGravity
kCAGravityResize
kCAGravityResizeAspectFill
kCAGravityResizeAspect
kCAGravityCenter
Tweetbot
UIImage
UIImageVie
w
CALayer
kCAGravityLeft
CALayer
kCAGravityRight
Bitmap sampling in CALayer
trySwiftLayer.minificationFilter trySwiftLayer.magnificationFilter
kCAFilterTrilinearBest Quality
(Slowest)
kCAFilterLinear
Default
kCAFilterNearest
Lowest Quality (Fast)
Bitmap sampling in CALayer
Masking CALayer Objects
let myLayer = CALayer()
myLayer.contents = self.makeRedCircleImage().CGImage
let myMask = CALayer()
myMask.contents = self.makeMaskImage().CGImage
myLayer.mask = myMask
+ =
myLayer myMask
Device Layer
Pages Layer
Arrow Layer
Mask Layer
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
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
Blend Modes with CALayer
let myBlendLayer = CALayer()
myBlendLayer.setValue(false, forKey: “allowsGroupBlending”) // PRIVATE
myBlendLayer.compositingFilter = “screenBlendMode"
myBlendLayer.allowsGroupOpacity = false
myLayer.addSublayer(myBlendLayer)
•注意:Private APIを利用している!
+ =
CALayer - “destIn” Blend ModeCALayer - “screenBlendMode” Blend
ModeCALayer - “linearLightBlendMode” Blend
ModeCAGradientLayer - “colorDodgeBlendMode” Blend
Mode
CALayer - “multiplyBlendMode” Blend Mode
CAShapeLayer - Normal Blend Mode
Reveal - http://revealapp.com
Animating with Core Animation
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)
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”)
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”)
Timing Function
let timingFunction = CAMediaTimingFunction(controlPoints: .08, .04, .08, .99)
let myAnimation = CABasicAnimation()
myAnimation.timingFunction = timingFunction
http://cubic-bezier.com
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
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”)
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”)
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()
CoreAnimator on the Mac App
Store
Features of Core Animation Subclasses
Features of Core Animation Subclasses
• UIViewのサブクラスで入れる
• GPUで実行、特別なイフェクト
•時々、CPUのオペレーションもある
public class MyGradientClass : UIView {
override class func layerClass() -> AnyClass {
return CAGradientLayer.self
}
}
CATileLayer
•色々なサイズで画像を再レン
ダー
• PDFやSVGやベクトルデータ
なら非常に役に立つ!
• Core Graphicsでバックグラウ
ンドで実行
CAGradientLayer
• GPUでグラジエントをレンダ
ーする
•3Dに変換されたレイヤで簡単に
影のエフェクト
CAReplicaterLayer
© iNVASIVECODE 2015
https://vimeo.com/128046096
• GPUで一つのレイヤを
何回もコピーして表示
する
•サムネールやゲームで
も役に立つ
CAShapeLayer
UAProgressView
© Urban Apps 2014
• CGPathから色々な形が表示
させ、アニメーションさせる
•読み込むアイコンにとても合って
る
•iOS 7のデザインスタイルにも似合って
る
CAEmitterLayer
Particle Playground on the Mac App Store
•レイヤの’frame’
からパーティクルが出る。
•ゲームやアプリ
の反応のアニメ
ーションに似合
う。
Other Layer Subclasses
•ゲームのためのCAEAGLLayer /
CAMetalLayer
•完全な3Dの変換のCATransformLayer
•大きなコンテンツをスクロールのCAScrollLayer
• UILabelと同じのようにCATextLayer
Conclusion
• UIなら、UIView直接じゃなく、CALayerだ
•もっと努力だから、最初にUIKitでやってみ
た方がいい
•ちゃんと使えば、60FPSでかっこういいエ
フェクトが可能性になる
•一緒に見事なアプリを作ろう!
Thanks for watching!
Thanks for watching!
[email protected] @TimOliverAU