I'm working on a project that uses CoreText; I need to initialize a CTRunDelegateCallbacks
:
var imageCallback = CTRunDelegateCallbacks(version: kCTRunDelegateCurrentVersion, dealloc: { (refCon) -> Void in
print("RunDelegate dealloc")
}, getAscent: { ( refCon) -> CGFloat in
return 0
}, getDescent: { (refCon) -> CGFloat in
return 0
}) { (refCon) -> CGFloat in
return 0
}
The parameter refCon
is UnsafeMutablePointer<Void>
type, which is also called void *
type in C. I want to get the pointer's raw value. How to do it?
I do not recommend converting the pointer to a non-pointer type. I'll show you how to do it at the end of this answer, but first I'll show you how you should really handle refCon
.
Create a struct to hold whatever information you need to pass through to the callbacks. Example:
struct MyRunExtent {
let ascent: CGFloat
let descent: CGFloat
let width: CGFloat
}
Then create an UnsafeMutablePointer
using its alloc
class method, and initialize the allocated storage:
let extentBuffer = UnsafeMutablePointer<MyRunExtent>.alloc(1)
extentBuffer.initialize(MyRunExtent(ascent: 12, descent: 4, width: 10))
In your callbacks, convert the pointer argument to an UnsafePointer<MyRunExtent>
and pull what you need out of its memory
:
var callbacks = CTRunDelegateCallbacks(version: kCTRunDelegateVersion1, dealloc: { pointer in
pointer.dealloc(1)
}, getAscent: { pointer in
return UnsafePointer<MyRunExtent>(pointer).memory.ascent
}, getDescent: { pointer in
return UnsafePointer<MyRunExtent>(pointer).memory.descent
}, getWidth: { pointer in
return UnsafePointer<MyRunExtent>(pointer).memory.width
})
Now you can create your delegate, using callbacks
and extentBuffer
:
let delegate = CTRunDelegateCreate(&callbacks, extentBuffer)!
Here's a test:
let richText = NSMutableAttributedString(string: "hello \u{FFFC} world")
richText.addAttribute(kCTRunDelegateAttributeName as String, value: delegate, range: NSMakeRange(6, 1))
let line = CTLineCreateWithAttributedString(richText)
let runs = (CTLineGetGlyphRuns(line) as [AnyObject]).map { $0 as! CTRun }
runs.forEach {
var ascent = CGFloat(0), descent = CGFloat(0), leading = CGFloat(0)
let width = CTRunGetTypographicBounds($0, CFRangeMake(0, 0), &ascent, &descent, &leading)
print("width:\(width) ascent:\(ascent) descent:\(descent) leading:\(leading)")
}
The output (in a playground):
2015-12-21 12:26:00.505 iOS Playground[17525:8055669] -[__NSCFType encodeWithCoder:]: unrecognized selector sent to instance 0x7f94bcb01dc0
width:28.6875 ascent:9.240234375 descent:2.759765625 leading:0.0
width:10.0 ascent:12.0 descent:4.0 leading:0.0
width:32.009765625 ascent:9.240234375 descent:2.759765625 leading:0.0
The first line of output is because the playground execution process can't encode the delegate to send back to Xcode for display, and turns out to be harmless. Anyway, you can see that the bounds of the middle run were computed using my callbacks and the content of my extentBuffer
.
You can get the pointer's “raw value” this way, if the pointer and an Int
are the same size on the running system:
let rawValue = unsafeBitCast(refCon, Int.self)
If they're different sizes, you'll get a fatal error at runtime.
You could cast it to a CGFloat
this way, if the pointer and a CGFloat
are the same size:
let float = unsafeBitCast(refCon, CGFloat.self)