-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCTCardView.m
More file actions
202 lines (168 loc) · 7.98 KB
/
CTCardView.m
File metadata and controls
202 lines (168 loc) · 7.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
//
// CTCardView.m
// CoreText
//
// Created by Matthys Strydom on 2011/01/24.
// Copyright 2011 Polymporph Systems. All rights reserved.
//
#import "CTCardView.h"
#import <CoreText/CoreText.h>
#import "CardDefinition.h"
#import "Rules.h"
@implementation CTCardView
@synthesize Message;
@synthesize CardDefinition;
-(void)fillRuleList {
// The first rule does nothing to any parameters and sees if the text fits.
id defaultRule = [[DefaultSettingRule alloc] init];
// The second rule is to reduce the font until the text fits.
id fontRule = [[ReduceFontSizeRule alloc] init:self.CardDefinition.MinFontSize:1];
RuleList = [[NSArray alloc] initWithObjects:defaultRule, fontRule, nil];
}
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
// Initialization code
self.contentMode = UIViewContentModeRedraw;
}
return self;
}
- (id)initWithCoder:(NSCoder*)coder {
if ((self = [super initWithCoder:coder])) {
// Initialization code
self.contentMode = UIViewContentModeRedraw;
}
return self;
}
// Creates a framesetter, using the given font size.
-(CTFramesetterRef)createFrameSetter:(int)fontSize{
// create paragraph style and assign text alignment to it
CTTextAlignment alignment = self.CardDefinition.TextAllignment;
CTParagraphStyleSetting settings[] = { {kCTParagraphStyleSpecifierAlignment, sizeof(alignment), &alignment} };
CTParagraphStyleRef paragraphStyle = CTParagraphStyleCreate(settings, sizeof(settings) / sizeof(settings[0]));
CTFontRef realFont = CTFontCreateWithFontDescriptor(self.CardDefinition.Font, fontSize, NULL);
CFMutableAttributedStringRef attrStr = CFAttributedStringCreateMutable(NULL, [Message length]);
CFAttributedStringReplaceString (attrStr, CFRangeMake(0, 0), (CFStringRef) Message);
// Special Note: Do not create a range and keep it to pass into all the set attribute methods! This breaks the settings. Dont ask me why.
CFAttributedStringSetAttribute(attrStr, CFRangeMake(0, CFAttributedStringGetLength(attrStr)), kCTFontAttributeName, realFont);
CFAttributedStringSetAttribute(attrStr, CFRangeMake(0, CFAttributedStringGetLength(attrStr)), kCTForegroundColorAttributeName, self.CardDefinition.FontColor);
CFAttributedStringSetAttribute(attrStr, CFRangeMake(0, CFAttributedStringGetLength(attrStr)), kCTParagraphStyleAttributeName, paragraphStyle);
return CTFramesetterCreateWithAttributedString(attrStr);
}
// Goes through the list of rules, until it the text finally fits into the given parameters.
// IMPLICIT LOGIC WARNING: The initial width and height that the method is called with
// is taken to be the maximum bounds.
- (struct DrawingParameters)AdaptParametersAccordingToRules:(struct DrawingParameters)par{
float maxHeight = par.TextBoxHeight;
float maxWidth = par.TextBoxWidth;
CGSize constraints = CGSizeMake(maxWidth, maxHeight);
int messageLength = [Message length];
CFRange fullRange = CFRangeMake(0, messageLength);
NSLog(@"Starting to loop through rules: Number of rules: %i", [RuleList count]);
// Go through the rules and see what adjustments can be made.
for (id object in RuleList) {
id <IRule> rule = (<IRule>)object;
CFRange range;
bool canMakeAdjustment = YES;
while (canMakeAdjustment) {
par = [rule adjustParameters:par];
par.Framesetter = [self createFrameSetter:par.FontSize];
CGSize coreTextSize = CTFramesetterSuggestFrameSizeWithConstraints(par.Framesetter, fullRange, nil, constraints, &range);
if (range.length >= messageLength)
{
NSLog(@"Rule succeeded!");
par.TextBoxHeight = coreTextSize.height;
par.TextBoxWidth = coreTextSize.width;
return par;
}
canMakeAdjustment = [rule canMakeFurtherAdjustment:par];
NSLog(@"Rule did not succeed. Making further adjustments? %i", canMakeAdjustment);
}
}
[NSException raise:@"Got to end of rule set. Temp setback" format:@"blah"];
return par;
/*
// Rule one: Try the framesetter as is, and see if it fits.
CFRange range;
CGSize coreTextSize = CTFramesetterSuggestFrameSizeWithConstraints(par.Framesetter, CFRangeMake(0, messageLength), nil, constraints, &range);
if (range.length >= messageLength) {
par.TextBoxHeight = coreTextSize.height;
par.TextBoxWidth = coreTextSize.width;
}
else {
[NSException raise:@"Got to end of rule set. Temp setback" format:@"blah"];
}
return par;
*/
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Init the rule list if it has not been done
if (RuleList == nil) {
[self fillRuleList];
}
[[UIColor whiteColor] set];
CGContextRef context = (CGContextRef)UIGraphicsGetCurrentContext();
// Fill in the initial parameters of the card to be checked for fitting by the rule system.
struct DrawingParameters par;
par.TextBoxHeight = self.CardDefinition.MaxCardHeight-self.CardDefinition.TopMargin - self.CardDefinition.BottomMargin;
par.TextBoxWidth = self.CardDefinition.CardWidth-self.CardDefinition.LeftMargin - self.CardDefinition.RightMargin;
par.FontSize = self.CardDefinition.MaxFontSize;
par.Framesetter = [self createFrameSetter:self.CardDefinition.MaxFontSize];
// We then pass the drawing parameters to the rules system, getting the final result back.
par = [self AdaptParametersAccordingToRules:par];
float cardHeight = par.TextBoxHeight;
if (self.CardDefinition.MinCardHeight - self.CardDefinition.TopMargin - self.CardDefinition.BottomMargin > cardHeight) {
cardHeight = self.CardDefinition.MinCardHeight - self.CardDefinition.TopMargin - self.CardDefinition.BottomMargin;
}
// Work out the dimentions of all the elements involved.
float totalHeight= rect.size.height;
float totalWidth = rect.size.width;
CGRect pageSize = CGRectMake(0, 0, totalWidth, totalHeight);
float cardDeltaX = (totalWidth - par.TextBoxWidth - self.CardDefinition.LeftMargin - self.CardDefinition.RightMargin) / 2;
float cardDeltaY = (totalHeight - cardHeight - self.CardDefinition.TopMargin - self.CardDefinition.BottomMargin) / 2;
CGRect cardSize = CGRectInset(pageSize, cardDeltaX, cardDeltaY);
float minCardDeltaX = (totalWidth - par.TextBoxWidth - self.CardDefinition.LeftMargin - self.CardDefinition.RightMargin) / 2;
float minCardDeltaY = (totalHeight - par.TextBoxHeight - self.CardDefinition.TopMargin - self.CardDefinition.BottomMargin) / 2;
CGRect minCardSize = CGRectInset(pageSize, minCardDeltaX, minCardDeltaY);
CGRect textRect = CGRectMake(minCardSize.origin.x+self.CardDefinition.LeftMargin,
minCardSize.origin.y+self.CardDefinition.BottomMargin,
par.TextBoxWidth, par.TextBoxHeight);
/*
float textDeltaX = (totalWidth - par.TextBoxWidth) / 2;
float textDeltaY = (totalHeight - par.TextBoxHeight) / 2;
CGRect textRect = CGRectInset(pageSize, textDeltaX, textDeltaY);
*/
// Translate the coordinates to work for text drawing. Google this :)
CGContextTranslateCTM(context, 0, rect.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
//Draw a rectangle
CGContextSetFillColorWithColor(context, self.CardDefinition.CardColor);
//Define a rectangle
CGContextAddRect(context, cardSize);
//Draw it
CGContextFillPath(context);
// Comment this back in to see where the min card rectangle ends up. In other words check if margins are correct.
CGContextSetFillColorWithColor(context, [UIColor greenColor].CGColor);
CGContextAddRect(context, minCardSize);
CGContextFillPath(context);
// Comment this back in to see where the text rectangle ends up.
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextAddRect(context, textRect);
CGContextFillPath(context);
// Draw the text.
[UIBezierPath bezierPathWithRect:[self bounds]];
CGMutablePathRef path = CGPathCreateMutable();
CGPathAddRect(path, NULL, textRect);
CTFrameRef frame = CTFramesetterCreateFrame(par.Framesetter, CFRangeMake(0, 0), path, NULL);
CTFrameDraw(frame, context);
//CFRelease(frame);
//CFRelease(finalPath);
}
- (void)dealloc {
[CardDefinition release];
[Message release];
[RuleList release];
[super dealloc];
}
@end