2015年5月6日水曜日

Core Animation Lesson.(カスタム描画/Lesson1-7)

Core Animation Lesson.(Lesson1-7)
カスタム描画.(Lesson1-7)

CALayerのcontentsプロパティを設定するとこだけが背景画像を描画する方法では有りません。UIViewのdrawRectメソッドを実装する事でCoreGraphicsやUIKitを利用し、ダイレクトに背景画像を描画することができます。drawRectメソッドにはデフォルト実装が有りませんが、Viewが最初に表示された時に一度呼び出され、以降は更新が必要になった場合に自動的に呼び出されます。明示的に再描画が必要な時はsetNeedsDisplayを呼び出す事で再描画が行われますので、再描画したいからといってコード内からdrawRectメソッドを直接呼び出してはいけません。

drawRectメソッドの実装

UIViewクラスを継承した新しいクラスを作成し、Storyboard上でViewを配置しCustomクラスとして新しく作成したクラスを指定します。

図. カスタムクラスの設定
drawRectメソッドでは、青色に塗りつぶされた楕円を描画するコードになっています。(以下コード)
class Lesson1_7View: UIView {

    // custom draw
    override func drawRect(rect: CGRect) {
        // get context
        let context: CGContext! = UIGraphicsGetCurrentContext()
        
        // draw with context
        // set fill color
        CGContextSetFillColorWithColor(context, UIColor.blueColor().CGColor)
        // draw ellpise in rect
        CGContextFillEllipseInRect(context, rect)

    }
}
図. カスタム描画
独自に追加したレイヤへのカスタム描画

CALayerにはdelegateプロパティが存在します。それがCALayerDelegateで、displayLayerとdrawLayer メソッドが定義されています。iOSの場合はUIViewを新たに作成するとCALayerも必ず生成されます。この予め生成されているCALayerの場合はUIView側でCALayerDelegateが実装されているため、改めて個別に実装する必要はありません。素直にdrawRectメソッドを実装すれば良いでしょう。

逆に新たに作成したCALayerでカスタム描画したい場合にCALayerDelegateを実装する必要があります。因みに、CALayerDelegateはCALayerクラスにNSObjectのエクステンション(カテゴリ)で定義されており、リファレンスにはInformal Protocolと表現されています。ですので、明示的にCALayerDelegateを採用するコード(クラス定義)を書く必要は無いようです。

extension NSObject {
    
    /* If defined, called by the default implementation of the -display
     * method, in which case it should implement the entire display
     * process (typically by setting the `contents' property). */
    
    func displayLayer(layer: CALayer!)
    
    /* If defined, called by the default implementation of -drawInContext: */
    
    func drawLayer(layer: CALayer!, inContext ctx: CGContext!)
    
    /* Called by the default -layoutSublayers implementation before the layout
     * manager is checked. Note that if the delegate method is invoked, the
     * layout manager will be ignored. */
    
    func layoutSublayersOfLayer(layer: CALayer!)
    
    /* If defined, called by the default implementation of the
     * -actionForKey: method. Should return an object implementating the
     * CAAction protocol. May return 'nil' if the delegate doesn't specify
     * a behavior for the current event. Returning the null object (i.e.
     * '[NSNull null]') explicitly forces no further search. (I.e. the
     * +defaultActionForKey: method will not be called.) */
    
    func actionForLayer(layer: CALayer!, forKey event: String!) -> CAAction!
}
コード. CALayerクラス内にあるエクステンション定義(Swift)

CALayerはdelegateが設定されているかを調べます。そして、設定されている場合はdisplayLayerメソッドが実装されているか確認します。実装されている場合は呼び出しを行い、もしdisplayLayer実装されていなかった場合はさらにdrawLayerメソッドが実装されているか確認し、実装されている場合はdrawLayerメソッドを呼び出します。
要するに、displayLayer → drawLayer の順で呼び出されるわけですが、displayLayerが実装されている場合はdrawLayerが実装されていても、呼び出されませんので注意が必要です。

新たにCALayerを作成して、そのレイヤをカスタム描画してみます。新たに作成したレイヤには赤色の円を描いています。

図. 独自に追加したレイヤのカスタム描画
class Lesson1_7ViewController: UIViewController {
    @IBOutlet weak var myView: Lesson1_7View!
    
    private var newLayer: CALayer!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        
        // create new layer
        newLayer = CALayer()
        newLayer.frame = CGRectMake(0, 0, 100, 100)
        newLayer.contentsScale = UIScreen.mainScreen().scale
        // set delegate
        newLayer.delegate = self
        
        // add new layer to uiview hosted layer
        myView.layer.addSublayer(newLayer)
        
        // display new layer
        newLayer.display()
    }
    
    override func viewDidDisappear(animated: Bool) {
        // clean up
        newLayer.delegate = nil
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

/***
    override func displayLayer(layer: CALayer!) {
        //
    }
***/
    
    override func drawLayer(layer: CALayer!, inContext ctx: CGContext!) {
        NSLog("perform drawLayer.")
        //
        CGContextSetFillColorWithColor(ctx, UIColor.redColor().CGColor)
        CGContextFillEllipseInRect(ctx, layer.frame)
    }
}

以下コードが新しいレイヤを作成し、delegateを設定しています。

// create new layer
newLayer = CALayer()
newLayer.frame = CGRectMake(0, 0, 100, 100)
newLayer.contentsScale = UIScreen.mainScreen().scale

// set delegate
newLayer.delegate = self

そして、以下がカスタムコード描画部分です。CGContextオブジェクトが引数にあるので、drawLayerメソッドの方を実装しています。drawLayerを呼び出したいため、displayLayerメソッドはコメントアウトにしています。

override func drawLayer(layer: CALayer!, inContext ctx: CGContext!) {
    NSLog("perform drawLayer.")
    //
    CGContextSetFillColorWithColor(ctx, UIColor.redColor().CGColor)
    CGContextFillEllipseInRect(ctx, layer.frame)
}



今回使用したサンプルプログラムはGithub上のCoreAnimationLessonに置いています。


AD.

0 件のコメント :

コメントを投稿