简体   繁体   中英

Creating a window graphics context for a CGLayer in iOS outside drawRect: of UIView

UIView creates a window graphics context before a custom drawRect: method is invoked, so inside the method I can get the graphics context. However, it seems that the only way to get a window graphics context in iOS, so I can make a CGLayer object based on a window graphics context only in a custom drawRect: method of a UIView object.

However, I might want a model object to contain a CGLayer object, which is created with the model object itself, and later a lot of views use the layer to draw its content on their own views. One might create a bitmap graphics context to create a CGLayer, but then everything drawn on the screen using the CGLayer object will have characteristics of a bitmap context, as Apple's documents say that drawing with a CGLayer object is restricted to the type of the graphics context used to create the layer.

So my question is, is it really impossible to create a window graphics context outside drawRect: to make a CGLayer object based on it. Or, is there a better way to create and store a CGLayer object for on-screen drawing that may be shared by multiple views? Or is it a completely bogus idea to have such a shared CGLayer object outside a UIView object for a reason I don't realize?

The following examples in Swift show how I have done what the original question asks, in both iOS and OSX.

Note: when you create a CGLayer, the graphics context that you provide is used solely as a reference to initialise the layer. No content or anything are sent to it - it just has to be the right TYPE of context so that the API knows what sort of layer to create.

In this example I create a CGLayer in the view controller and do some drawing on it. Then I render it onto the screen in the drawRect() method of a view.


iOS example

The view controller

import UIKit

class ViewController: UIViewController {
    @IBOutlet var myView: MyView!

    override func viewWillAppear(animated: Bool) {
        // get the current graphics context - any context relevant to the screen 
        // will do for this purpose
        let currentGraphicsContext = UIGraphicsGetCurrentContext()

        // create a GCLayer with low level properties appropriate to the device 
        // screen and a size of 100 x 100
        // this process doesn't send any content to windowGraphicsContext, it just 
        // copies some of the low level fields about the screen
        let newLayer = CGLayerCreateWithContext(currentGraphicsContext, 
                                                CGSize(width: 100, height: 100), 
                                                nil)

        // store a reference to the layer in a property of the view so that the 
        // view can display it
        myView.layerRef = newLayer

        // create a new graphics context for the layer so that we can draw on it
        let myLayerContext = CGLayerGetContext(newLayer)

        // draw some content on the layer
        CGContextSetFillColorWithColor(myLayerContext, UIColor.redColor().CGColor)
        CGContextFillRect(myLayerContext, 
                          CGRect(x: 0, y: 0, width: 100, height: 50))
        CGContextSetFillColorWithColor(myLayerContext, UIColor.blueColor().CGColor)
        CGContextFillRect(myLayerContext, 
                          CGRect(x: 0, y: 50, width: 50, height: 50))
    }
}

The UIView subclass

import UIKit

class MyView: UIView {

    // a reference to the layer created by the view controller
    var layerRef: CGLayer?

    override func drawRect(rect: CGRect) {
        // get a graphics context for the view
        let myViewGraphicsContext = UIGraphicsGetCurrentContext()

        // draw the layer into the view
        CGContextDrawLayerAtPoint(myViewGraphicsContext, 
                                  CGPoint(x: 20 , y: 20), 
                                  self.layerRef)
    }
}

OSX example

The view controller

import Cocoa

class ViewController: NSViewController {
    @IBOutlet var myView: MyView!

    override func viewWillAppear() {
        // get a graphics context for the window that contains the view - any 
        // context relevant to the screen will do
        let windowGraphicsContext = NSGraphicsContext(window: myView.window!)

        // create a GCLayer with low level properties appropriate to the device 
        // screen and a size of 100 x 100
        // this process doesn't send any content to windowGraphicsContext, it just 
        // copies some of the low level fields about the screen
        let newLayer = CGLayerCreateWithContext(windowGraphicsContext.CGContext, 
                     CGSize(width: 100, height: 100), nil)

        // store a reference to the layer in a property of the view, so that the 
        // view can display it
        myView.layerRef = newLayer

        // create a new graphics context for the layer so that we can draw on it
        let myLayerContext = CGLayerGetContext(newLayer)

        // do some drawing on the layer
        CGContextSetFillColorWithColor(myLayerContext, NSColor.redColor().CGColor)
        CGContextFillRect(myLayerContext, 
                          CGRect(x: 0, y: 0, width: 100, height: 50))
        CGContextSetFillColorWithColor(myLayerContext, NSColor.blueColor().CGColor)
        CGContextFillRect(myLayerContext, 
                          CGRect(x: 0, y: 50, width: 50, height: 50))
    }
}

The NSView subclass

import Cocoa

class MyView: NSView {

    // a reference to the layer created by the view controller
    var layerRef: CGLayer?

    override func drawRect(dirtyRect: NSRect) {    
        super.drawRect(dirtyRect)

        // get a graphics context for the view
        let graphicsContext = NSGraphicsContext.currentContext()?.CGContext

        // draw the layer into the view
        CGContextDrawLayerAtPoint(graphicsContext, 
                                  CGPoint(x: 20 , y: 20), 
                                  self.layerRef)
    }
}

Yes, you can create a drawing context for a CALayer from a bitmap.

But the contents of a CGLayer needs a backing bitmap, since it does not save the symbolic (resolution independent) drawing vectors when drawn into, only the resulting pixels. So either you create the backing bitmap, or you use the one handed you by a UIView, or you recreate/redraw the bits from your own command list when required.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM