How To Create a Simple Magazine App with Core Text Ray Wenderlich.pdf
(
2679 KB
)
Pobierz
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
6/17/12
How To Create a Simple Magazine App with Core Text | Ray Wenderlich
Ray
Wenderlich
Tutorials for
iPhone / iOS Developers
and
Gamers
New blog post by @EmperiorEric: http://t.co/g0hQIm9M Reader's App Reviews – June 2012 http://t.co/SUyiGVTP
Store
Forums
Art
Tutorials
5 July 2011
How To Create a Simple
Magazine App with Core Text
21
This is a blog post by iOS Tutorial Team member
Marin Todorov
, a software developer with 12+ years of experience, an independant iOS developer and the creator of
Touch Code Magazine
.
Magazines, Core Text, and Brains!
Core Text is a text engine found in iOS 3.2+ and OSX 10.5+ that gives you fine-grained control over text layout and formatting.
It sits in the sweet spot between UIKit and Core Graphics/Quartz:
In
UIKit
you have UILabel and you add a word or a text line on the screen by simple Drag-and-Drop in IB, but you cannot change the color of individual words.
In
Core Graphics/Quartz
you can do pretty much everything the system is capable of, but you need to calculate the position of each glyph in your text and draw it
on the screen.
Core Text
lies right between the two! You have complete control over position, layout, attributes like color and size, but Core Text takes care of everything else for
you – from word wrap to font rendering and more.
Core Text is especially handy if you are creating a magazine or book app – which work great on the iPad!
This tutorial will get you started with Core Text by taking you through the process of creating a very simple Magazine application using Core Text – for Zombies!
You’ll learn how to:
lay formatted text down on the screen;
fine tune the text’s appearance;
add images inside the text content;
and finally create a magazine app, which loads text markup to easily control the formatting of the rendered text.
eat brains! Ok just kidding, that’s only for the readers of this magazine.
To get the most out of this tutorial, you need to know the basics of iOS development first. If you are new to iOS development, you should check out some of the
other
tutorials
on this site first.
Without further ado, let’s make some happy zombies by making them their very own iPad magazine!
Setting up a Core Text project
Start up Xcode, go to File\New\New Project, choose the iOS\Application\View-based Application, and click Next. Name the project CoreTextMagazine, choose iPad as
Device family, click Next, choose a folder to save your project in, and click Create.
Next thing you have to do is add the Core Text framework to the project:
1. Click on the project file in the Project navigator (the strip on the left hand side)
2. Next select your only target for this project “CoreTextMagazine” in the Targets list
3. Click on the “Build phases” tab
4. Expand the “Link Binary With Libraries” strip and click on the “+” button
5. Choose “CoreText.framework” from the list and click “Add”
www.raywenderlich.com/4147/how‑to‑create‑a‑simple‑magazine‑app‑with‑core‑text
1/18
6/17/12
How To Create a Simple Magazine App with Core Text | Ray Wenderlich
That’s all the setup you need – now it’s time to start adding some code!
Adding a Core Text view
To get on track with Core Text as fast as possible you are going to create a custom UIView, which will use Core Text in its drawRect: method.
Go to File\New\New File, choose iOS\Cocoa Touch\Objective-C class, and click Next. Enter UIView for Subclass of, click Next, name the new class CTView, and click
Save.
In CTView.h just above @interface add the following code to include the Core Text framework:
#import <CoreText/CoreText.h>
In the next step you’re going to set this new custom view as the main view in the application.
Select in the Project navigator the XIB file “CoreTextMagazineViewController.xib”, and bring up the Utilities strip in XCode (this appears whe you select the third tab in
the View section of the top toolbar). From the Utilities strip, select the third icon on the top toolbar to select the Identity tab.
Now just click in the white space in the Interface editor to select the window’s view – you should see in the Utilities strip in the field Class the text “UIView” appearing.
Write in that field “CTView” and hit Enter.
Now your application will show your custom Core Text view when started, but we’ll do that in a moment – let’s first add the code to draw some text so we have what to
test.
Open CTView.m and delete all the predefined methods. Enter the following code to draw a “Hello world” inside your view:
-
(
void
)
drawRect
:(
CGRect
)
rect
{
[
super drawRect
:
rect
]
;
CGContextRef context
=
UIGraphicsGetCurrentContext
()
;
CGMutablePathRef path
=
CGPathCreateMutable
()
;
//1
CGPathAddRect
(
path,
NULL
, self.bounds
)
;
NSAttributedString
*
attString
=
[[[
NSAttributedString
alloc
]
initWithString
:
@"Hello core text world!"
]
autorelease
]
;
//2
CTFramesetterRef framesetter
=
CTFramesetterCreateWithAttributedString
((
CFAttributedStringRef
)
attString
)
;
//3
CTFrameRef frame
=
CTFramesetterCreateFrame
(
framesetter,
CFRangeMake
(
0
,
[
attString length
])
, path,
NULL
)
;
CTFrameDraw
(
frame, context
)
;
//4
CFRelease
(
frame
)
;
//5
CFRelease
(
path
)
;
CFRelease
(
framesetter
)
;
}
Let’s discuss this bit by bit, using the comment markers above to designate each section:
1. Here you need to create a path which bounds the area where you will be drawing text. Core Text on the Mac supports different shapes like rectangles and circles,
but for the moment iOS supports only rectangular shape for drawing with Core Text. In this simple example, you’ll use the entire view bounds as the rectangle
where you will be drawing by creating a CGPath reference from self.bounds.
2. In Core Text you won’t be using NSString, but rather NSAttributedString, as shown here. NSAttributedString is a very powerful NSString derivate class, which
allows you apply formatting attributes to text. For the moment we won’t be using formatting – this just creates a string holding plain text.
3. CTFramesetter is the most important class to use when drawing with Core Text. It manages your font references and your text drawing frames. For the moment
what you need to know is that CTFramesetterCreateWithAttributedString creates a CTFramesetter for you, retains it and initializes it with the supplied attributed
string. In this section, after you have the framesetter you create a frame, you give the CTFramesetterCreateFrame a range of the string to render (we choose the entire
string here) and the rectangle where the text will appear when drawn.
4. Here CTFrameDraw draws the supplied frame in the given context.
5. Finally, all the used objects are released.
Note that when working with Core Text classes you use a set of functions like CTFramesetterCreateWithAttributedString and CTFramesetterCreateFrame instead of
directly using Objective-C objects.
You might think to yourself “Why would I ever want to use C again, I thought I was done with that since we have Objective-C?!”
www.raywenderlich.com/4147/how‑to‑create‑a‑simple‑magazine‑app‑with‑core‑text
2/18
6/17/12
How To Create a Simple Magazine App with Core Text | Ray Wenderlich
Well, many of the low level libraries on iOS are written in plain C for speed and simplicity. Don’t worry though, you’ll find the Core Text functions pretty easy to work
with.
Just one important thing to remember though: don’t forget to always use CFRelease on the references you get from functions which have “Create” in their name.
Believe it or not, that’s all you need to draw some simple text using Core Text! Hit Run and see the result.
Well that does not seem right, does it? Like many of the low level APIs, Core Text uses a Y-flipped coordinate system. To make it even worse, the content is also rendered
flipped downwards! Because of this, keep in mind that if you mix UIKit drawing and Core Text drawing, you might get weird results.
Let’s fix the content orientation! Add the following code just after this line “CGContextRef context = UIGraphicsGetCurrentContext();”:
// Flip the coordinate system
CGContextSetTextMatrix
(
context, CGAffineTransformIdentity
)
;
CGContextTranslateCTM
(
context,
0
, self.bounds.size.height
)
;
CGContextScaleCTM
(
context,
1.0
,
-
1.0
)
;
This is very simple code, which just flips the content by applying a transformation to the view’s context. Just copy/paste it each time you do drawing with CT.
Now hit Run again – congrats on your first Core Text app!
The Core Text Object Model
If you are a bit confused about the CTFramesetter and the CTFrame – that’s OK. Here I’ll make a short detour to explain how Core Text renders text content.
Here’s what the Core Text object model looks like:
You create a CTFramesetter reference and you provide it with NSAttributedString. At this point, an instance of CTTypesetter is automatically created for you, a class that
manages your fonts. Next you use the CTFramesetter to create one or more frames in which you will be rendering text.
When you create a frame you tell it the subrange of text that will be rendered inside its rectangle. Core Text then automatically creates a CTLine for each line of text and
(pay attention here) a CTRun for each piece of text with the same formatting.
As an example, Core Text would create a CTRun if you had several words in a row colored red, then another CTRun for the following plain text, then another CTRun for
a bold sentence, etc. Again: very important – you don’t create CTRun instances, Core Text creates them for you based on the attributes of the supplied
NSAttributedString.
Each of these CTRun objects can adopt different attributes, so you have fine control over kerning, ligatures, width, height and more.
Onto the Magazine App!
To create this magazine app, we need the capability to mark some of the text as having different attributes. We could do this by directly using methods on
NSAttributedString such as setAttributes:range, but this is unwieldy to deal with in practice (unless you like to painstakingly write a ton of code!)
So to make thing simpler to work with, we’ll create a simple text markup parser which will allow us to use simple tags to set formatting in the magazine content.
www.raywenderlich.com/4147/how‑to‑create‑a‑simple‑magazine‑app‑with‑core‑text
3/18
6/17/12
How To Create a Simple Magazine App with Core Text | Ray Wenderlich
Go to File\New\New File, choose iOS\Cocoa Touch\Objective-C class, and click Next. Enter NSObject for Subclass of, click Next, name the new class MarkupParser.m,
and click Save.
Inside MarkupParser.h delete all the text and paste this code – it defines few properties and the method to do the parsing:
#import <Foundation/Foundation.h>
#import <CoreText/CoreText.h>
@interface
MarkupParser
:
NSObject
{
NSString
*
font;
UIColor
*
color;
UIColor
*
strokeColor;
float
strokeWidth;
NSMutableArray
*
images;
}
@property
(
retain, nonatomic
)
NSString
*
font;
@property
(
retain, nonatomic
)
UIColor
*
color;
@property
(
retain, nonatomic
)
UIColor
*
strokeColor;
@property
(
assign, readwrite
)
float
strokeWidth;
@property
(
retain, nonatomic
)
NSMutableArray
*
images;
-(
NSAttributedString
*)
attrStringFromMarkup
:(
NSString
*)
html;
@end
Next open MarkupParser.m and replace the contents with the following:
#import "MarkupParser.h"
@implementation
MarkupParser
@synthesize
font, color, strokeColor, strokeWidth;
@synthesize
images;
-(
id
)
init
{
self
=
[
super init
]
;
if
(
self
)
{
self.font
=
@"Arial"
;
self.color
=
[
UIColor blackColor
]
;
self.strokeColor
=
[
UIColor whiteColor
]
;
self.strokeWidth
=
0.0
;
self.images
=
[
NSMutableArray
array
]
;
}
return
self;
}
-(
NSAttributedString
*)
attrStringFromMarkup
:(
NSString
*)
markup
{
}
-(
void
)
dealloc
{
self.font
=
nil
;
self.color
=
nil
;
self.strokeColor
=
nil
;
self.images
=
nil
;
[
super dealloc
]
;
}
@end
As you see you start pretty easy with the parser code – it just contains properties to hold the font, text color, stroke width and stroke color. Later on we’ll be adding images
inside the text, so you need an array where you’re going to keep the list of images in the text.
Writing a parser is usually pretty hard work, so I’m going to show you how to build a very very simple one using regular expressions. This tutorial’s parser will be very
simple and will support only opening tags – i.e. a tag will set the style of the text after the tag, the style will be applied until a new tag is found. The text markup will look
like this:
These are <font color="red">red<font color="black"> and
<font color="blue">blue <font color="black">words.
and will produce output like this:
These are
red
and
blue
words.
For the purpose of the tutorial such markup will be quite sufficient. For your projects you can develop it further if you'd like to.
Let's get parsin'!
Inside the attrStringFromMarkup: method add the following:
NSMutableAttributedString
*
aString
=
[[
NSMutableAttributedString
alloc
]
initWithString
:
@""
]
;
//1
NSRegularExpression
*
regex
=
[[
NSRegularExpression alloc
]
initWithPattern
:
@"(.*?)(<[^>]+>|
\\
Z)"
options
:
NSRegularExpressionCaseInsensitive|NSRegularExpressionDotMatchesLineSeparators
error
:
nil
]
;
//2
www.raywenderlich.com/4147/how‑to‑create‑a‑simple‑magazine‑app‑with‑core‑text
4/18
6/17/12
How To Create a Simple Magazine App with Core Text | Ray Wenderlich
NSArray
*
chunks
=
[
regex matchesInString
:
markup options
:
0
range
:
NSMakeRange
(
0
,
[
markup length
])]
;
[
regex release
]
;
There are two sections to cover here:
1. First, you set the empty result string to which you'll be adding text as it's found.
2. Next, you create a regex to match chunks of text and tags. This regex will basically match a string of text and a following tag. The regular expression basically says
"Look for any number of characters, until you come across an opening bracket. Then match any number of characters until you hit a closing bracket. Or - stop
processing when you hit the end of the string."
Why are we creating this regular expression? We're going to use it to search the string for every place it matches, and then 1) render the text chunk found; then 2) change
the current styles according to what's found in the tag. This will be repeated until the text is over.
Very simple parser indeed, eh?
Now that you have the chunks of text and all the formatting tags (like the font tag you see a bit above) in the "chunks" array, you'll need to loop trough them and build the
attributed string from the text and tags.
Add this to the method body:
for
(
NSTextCheckingResult
*
b
in
chunks
)
{
NSArray
*
parts
=
[[
markup substringWithRange
:
b.range
]
componentsSeparatedByString
:
@"<"
]
;
//1
CTFontRef fontRef
=
CTFontCreateWithName
((
CFStringRef
)
self.font,
24.0f,
NULL
)
;
//apply the current text style //2
NSDictionary
*
attrs
=
[
NSDictionary
dictionaryWithObjectsAndKeys
:
(
id
)
self.color.CGColor, kCTForegroundColorAttributeName,
(
id
)
fontRef, kCTFontAttributeName,
(
id
)
self.strokeColor.CGColor,
(
NSString
*)
kCTStrokeColorAttributeName,
(
id
)[
NSNumber
numberWithFloat
:
self.strokeWidth
]
,
(
NSString
*)
kCTStrokeWidthAttributeName,
nil
]
;
[
aString appendAttributedString
:[[[
NSAttributedString
alloc
]
initWithString
:[
parts objectAtIndex
:
0
]
attributes
:
attrs
]
autorelease
]]
CFRelease
(
fontRef
)
;
//handle new formatting tag //3
if
([
parts count
]
>
1
)
{
NSString
*
tag
=
(
NSString
*)[
parts objectAtIndex
:
1
]
;
if
([
tag hasPrefix
:
@"font"
])
{
//stroke color
NSRegularExpression
*
scolorRegex
=
[[[
NSRegularExpression alloc
]
initWithPattern
:
@"(?<=strokeColor=
\"
)
\\
w+"
options
:
0
error
[
scolorRegex enumerateMatchesInString
:
tag options
:
0
range
:
NSMakeRange
(
0
,
[
tag length
])
usingBlock
:^(
NSTextCheckingResult
*
match, NSMatchingFlags flags,
if
([[
tag substringWithRange
:
match.range
]
isEqualToString
:
@"none"
])
{
self.strokeWidth
=
0.0
;
}
else
{
self.strokeWidth
=
-
3.0
;
SEL
colorSel
=
NSSelectorFromString
([
NSString
stringWithFormat
:
@"%@Color"
,
[
tag substringWithRange
:
match.range
]])
;
self.strokeColor
=
[
UIColor performSelector
:
colorSel
]
;
}
}]
;
//color
NSRegularExpression
*
colorRegex
=
[[[
NSRegularExpression alloc
]
initWithPattern
:
@"(?<=color=
\"
)
\\
w+"
options
:
0
error
:
NULL
]
autorelease
[
colorRegex enumerateMatchesInString
:
tag options
:
0
range
:
NSMakeRange
(
0
,
[
tag length
])
usingBlock
:^(
NSTextCheckingResult
*
match, NSMatchingFlags flags,
SEL
colorSel
=
NSSelectorFromString
([
NSString
stringWithFormat
:
@"%@Color"
,
[
tag substringWithRange
:
match.range
]])
;
self.color
=
[
UIColor performSelector
:
colorSel
]
;
}]
;
//face
NSRegularExpression
*
faceRegex
=
[[[
NSRegularExpression alloc
]
initWithPattern
:
@"(?<=face=
\"
)[^
\"
]+"
options
:
0
error
:
NULL
]
autorelease
[
faceRegex enumerateMatchesInString
:
tag options
:
0
range
:
NSMakeRange
(
0
,
[
tag length
])
usingBlock
:^(
NSTextCheckingResult
*
match, NSMatchingFlags flags,
self.font
=
[
tag substringWithRange
:
match.range
]
;
}]
;
}
//end of font parsing
}
}
return
(
NSAttributedString
*)
aString;
Phew, this is a lot of code! But don't worry, we'll go over it here section by section.
1. You iterate over the chunks matched by the prior regular expression, and in this section you split the chunks by the "<" character (the tag opener). As a result, in
parts[0] you have the text to add to the result and in parts[1] you have the content of the tag that changes the formatting for the text that follows.
2. Next you create a dictionary holding a number of formatting options - this is the way you can pass formatting attributes to a NSAttributedString. Have a look at the
key names - they are Apple defined constants which are pretty self-explanatory (of you can check out Apple's
Core Text String Attributes Reference
for full details).
By calling appendAttributedString: the new text chunk with applied formatting is added to the result string.
3. Finally, you check if there's a tag found after the text; if it starts with "font" a regex is ran for every possible tag attribute. For the "face" attribute the name of the font
is saved in self.font, for "color" I and you did a little trickery: for <font color="red"> the text value "red" is taken by the colorRegex and then a selector "redColor"
is created and performed on the UIColor
class - this (hehe) returns a UIColor instance of a red color. Note this trick works only for the predefined colors of UIColor (and can even cause your code to crash if
you pass in a selector that does not exist!) but this is sufficient for this tutorial. The stroke color attribute works much like the color attribute, but if the value of
strokecolor is "none" just sets the stroke widht to 0.0, so no stroke will be applied to the text.
Note:
If you're unsatiably curious how the regular expressions in this section work, they are basically saying ("Use the look-behind assertion to look for any text that is
preceded by color=". Then match any normal word character (which does not include a quote), which basically keeps matching until the close quote is found. For more
details, check out Apple's
NSRegularExpression class reference
.
Right! Half the work of rendering formatted text is done - now attrStringFromMarkup: can take markup in and spit a NSAttributedString out ready to be fed to Core Text.
So let's pass in a string to render, and try it out!
www.raywenderlich.com/4147/how‑to‑create‑a‑simple‑magazine‑app‑with‑core‑text
5/18
Plik z chomika:
piotrbard
Inne pliki z tego folderu:
Objective-C_for_Absolute_Beginners.pdf
(14232 KB)
Blocks Programming Topics - Apple (2011).pdf
(239 KB)
Wrox Professional iPhone and iPad Database Application Programming.pdf
(71970 KB)
Apress - Pro Core Data for iOS.pdf
(7871 KB)
AppleScript Language Guide.pdf
(2230 KB)
Inne foldery tego chomika:
AppleMagazine
Filmy
iATKOS ML2 10.8.2 (Build 12C54)
iMagazine
Inne
Zgłoś jeśli
naruszono regulamin