I would like to do a drop cap first character in a UILabel
using the attributedText
NSAttributedString
property only. Like this:
http://www.interpretationbydesign.com/wp-content/uploads/2009/02/drop_caps.gif
I have experimented with adjusting the base line for the range of the first character to a negative value, and it works for aligning the top of the first char with the top of the rest of the first line. But I have not found any way to make the other lines flow to the right of the drop capped character.
Can this be solved using NSAttributedString only
, or do I have to split the string and render it myself using Core Text?
CoreText cannot do drop caps because it consists of lines made up of glyph runs. A drop cap would cover multiple lines which is not supported.
To achieve this effect you would have to draw the cap separately and then draw the rest of the text in a path that goes around it.
Long story short: not possible in UILabel, possible, but a fair bit of work with CoreText.
The steps to do it with CoreText are:
As everyone else mentioned, it's not possible to do this with only
NSAttributedString
. Nikolai has the right approach, usingCTFrameSetters
. However it is possible to tell the framesetter to render text in a specific area (i.e. defined by a CGPath).You'll have to create 2 framesetters, one for the drop cap and the other for the rest of the text.
Then, you grab the frame of the drop cap and build a
CGPathRef
that runs around the space of the frame of the drop cap.Then, you render both framesetters into your view.
I've created a sample project with an object called DropCapView which is a subclass of UIView. This view renders the first character and wraps the remaining text around it.
It looks like this:
There are quite a few steps, so I've added a link to a github project hosting the example. There are comments in the project that will help you along.
DropCap project on GitHub
You'll have to play around with the shape of the
textBox
element (i.e. the CGPathRef) for padding around the edges of the view, and to tighten it up to the drop cap letter as well.Here are the guts of the drawing method:
P.S. this comes with some inspiration from: https://stackoverflow.com/a/9272955/1218605
Not a perfect solution, but you should give DTCoreText a try and render your normal
NSString
as anformatted HTML
. Within HTML it is possible to "Drop cap" a letter.No, this cannot be done with an
NSAttributedString
and standard string drawing only.Since the drop cap is a property of a paragraph the
CTParagraphStyle
would have to contain the information about the drop cap. The only property inCTParagraphStyle
that affects indentation of the start of the paragraph iskCTParagraphStyleSpecifierFirstLineHeadIndent
, but that affects the first line only.There's just no way to tell the
CTFramesetter
how to calculate the beginnings for the second and more rows.The only way is to define your own attribute and write code to draw the string using
CTFramesetter
andCTTypesetter
that acknowledge this custom attribute.If you're using a UITextView you can use
textView.textContainer.exclusionPaths
as Dannie P suggested here.Example in Swift:
Result:
Full example on github