forked from Cocoanetics/DTCoreText
/
DTAttributedTextContentView.h
338 lines (235 loc) · 12.1 KB
/
DTAttributedTextContentView.h
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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
//
// DTAttributedTextContentView.h
// DTCoreText
//
// Created by Oliver Drobnik on 1/9/11.
// Copyright 2011 Drobnik.com. All rights reserved.
//
#import "DTCoreTextLayoutFrame.h"
@class DTAttributedTextContentView;
@class DTCoreTextLayoutFrame;
@class DTTextBlock;
@class DTCoreTextLayouter;
@class DTTextAttachment;
/**
notification that gets sent as soon as the receiver has done a layout pass
*/
extern NSString * const DTAttributedTextContentViewDidFinishLayoutNotification;
/**
Protocol to provide custom views for elements in an DTAttributedTextContentView. Also the delegate gets notified once the text view has been drawn.
*/
@protocol DTAttributedTextContentViewDelegate <NSObject>
@optional
/**
@name Notifications
*/
/**
Called after a layout frame or a part of it is drawn.
@param attributedTextContentView The content view that drew a layout frame
@param layoutFrame The layout frame that was drawn for
@param context The graphics context that was drawn into
*/
- (void)attributedTextContentView:(DTAttributedTextContentView *)attributedTextContentView didDrawLayoutFrame:(DTCoreTextLayoutFrame *)layoutFrame inContext:(CGContextRef)context;
/**
Called before the text belonging to a text block is drawn.
This gives the developer an opportunity to draw a custom background below a text block.
@param attributedTextContentView The content view that drew a layout frame
@param textBlock The text block
@param frame The frame within the content view's coordinate system that will be drawn into
@param context The graphics context that will be drawn into
@param layoutFrame The layout frame that will be drawn for
@returns `YES` is the standard fill of the text block should be drawn, `NO` if it should not
*/
- (BOOL)attributedTextContentView:(DTAttributedTextContentView *)attributedTextContentView shouldDrawBackgroundForTextBlock:(DTTextBlock *)textBlock frame:(CGRect)frame context:(CGContextRef)context forLayoutFrame:(DTCoreTextLayoutFrame *)layoutFrame;
/**
@name Providing Custom Views for Content
*/
/**
Provide custom view for an attachment, e.g. an imageView for images
@param attributedTextContentView The content view asking for a custom view
@param attachment The <DTTextAttachment> that this view should represent
@param frame The frame that the view should use to fit on top of the space reserved for the attachment
@returns The view that should represent the given attachment
*/
- (UIView *)attributedTextContentView:(DTAttributedTextContentView *)attributedTextContentView viewForAttachment:(DTTextAttachment *)attachment frame:(CGRect)frame;
/**
Provide button to be placed over links, the identifier is used to link multiple parts of the same A tag
@param attributedTextContentView The content view asking for a custom view
@param url The `NSURL` of the hyperlink
@param identifier An identifier that uniquely identifies the hyperlink within the document
@param frame The frame that the view should use to fit on top of the space reserved for the attachment
@returns The view that should represent the given hyperlink
*/
- (UIView *)attributedTextContentView:(DTAttributedTextContentView *)attributedTextContentView viewForLink:(NSURL *)url identifier:(NSString *)identifier frame:(CGRect)frame;
/**
Provide generic views for all attachments.
This is only called if the more specific delegate methods are not implemented.
@param attributedTextContentView The content view asking for a custom view
@param string The attributed sub-string containing this element
@param frame The frame that the view should use to fit on top of the space reserved for the attachment
@returns The view that should represent the given hyperlink or text attachment
@see attributedTextContentView:viewForAttachment:frame: and attributedTextContentView:viewForAttachment:frame:
*/
- (UIView *)attributedTextContentView:(DTAttributedTextContentView *)attributedTextContentView viewForAttributedString:(NSAttributedString *)string frame:(CGRect)frame;
@end
enum {
DTAttributedTextContentViewRelayoutNever = 0,
DTAttributedTextContentViewRelayoutOnWidthChanged = 1 << 0,
DTAttributedTextContentViewRelayoutOnHeightChanged = 1 << 1,
};
typedef NSUInteger DTAttributedTextContentViewRelayoutMask;
/**
Attributed Text Content Views display attributed strings generated by DTHTMLAttributedStringBuilder. They can display images and hyperlinks inline or optionally place custom subviews (which get provided via the <delegate> in the appropriate places. By itself content views do not scroll, for that there is the `UIScrollView` subclass <DTAttributedTextView>.
Generally you have two options to providing content:
- set the attributed string
- set a layout frame
The first you would normally use, the second you would use if you are layouting a larger text and then simply want to display individual parts (e.g. pages from an e-book) in a content view.
DTAttributedTextContentView is designed to be used as the content view inside a DTAttributedTextView and thus sizes its intrinsicContentSize always to be the same as the width of the set frame. Use DTAttributedLabel if you don't require scrolling behavior.
*/
@interface DTAttributedTextContentView : UIView
{
NSAttributedString *_attributedString;
DTCoreTextLayoutFrame *_layoutFrame;
UIEdgeInsets _edgeInsets;
NSMutableDictionary *customViewsForAttachmentsIndex;
BOOL _flexibleHeight;
// for layoutFrame
NSInteger _numberOfLines;
NSLineBreakMode _lineBreakMode;
NSAttributedString *_truncationString;
}
/**
@name Sizing
*/
/**
Calculates the suggested frame size that would fit the entire <attributedString> with a maximum width.
This does a full layout pass that is cached in <DTCoreTextLayouter>. If you specify a frame that fits the result from this method then the resulting layoutFrame is reused.
Since this obeys the <edgeInsets> you have to add these to the final frame size.
@param width The maximum width to layout for
@returns The suggested frame size
*/
- (CGSize)suggestedFrameSizeToFitEntireStringConstraintedToWidth:(CGFloat)width; // obeys the edge insets
/**
The size of contents of the receiver. This is possibly used by auto-layout, but also for example if you want to get the size of the receiver necessary for a scroll view
This method is defined as of iOS 6, but to support earlier OS versions
*/
- (CGSize)intrinsicContentSize;
/**
Whether the receiver calculates layout limited to the view bounds.
If set to `YES` then the layout process calculates the layoutFrame with open ended height. If set to ´NO` then the current bounds of the receiver determine the height.
*/
@property (nonatomic, assign) BOOL layoutFrameHeightIsConstrainedByBounds;
/**
@name Layouting
*/
/**
Discards the current <layoutFrame> and creates a new one based on the <attributedString>.
*/
- (void)relayoutText;
/**
The layouter to use for the receiver. Created by default.
By default this is generated automatically for the current <attributedString>. You can also supply your own if you require special layouting behavior.
*/
@property (atomic, strong) DTCoreTextLayouter *layouter;
/**
The layout frame to use for the receiver. Created by default.
A layout frame is basically one rectangle, inset by the <edgeInsets>. By default this is automatically generated for the current <attributedString>. You can also create a <DTCoreTextLayoutFrame> seperately and set this property to display the layout frame. This is usedful for example if you layout entire e-book and then set the <layoutFrame> for displaying individual pages.
*/
@property (atomic, strong) DTCoreTextLayoutFrame *layoutFrame;
/**
@name Working with Custom Subviews
*/
/**
Removes all custom subviews (excluding views representing links) from the receiver.
*/
- (void)removeAllCustomViews;
/**
Removes all custom subviews representing links from the receiver
*/
- (void)removeAllCustomViewsForLinks;
/**
Removes invisible custom subviews and lays out subviews visible in the given rectangle
@param rect The bounds of the visible area to layout custom subviews in.
*/
- (void)layoutSubviewsInRect:(CGRect)rect;
/**
@name Providing Content
*/
/**
The attributed string to display in the receiver
*/
@property (nonatomic, copy) NSAttributedString *attributedString;
/**
The delegate that is in charge of supplying custom behavior for the receiver. It must conform to <DTAttributedTextContentViewDelegate> and provide custom subviews, link buttons, etc.
*/
@property (nonatomic, assign) IBOutlet id <DTAttributedTextContentViewDelegate> delegate; // subtle simulator bug - use assign not __unsafe_unretained
/**
@name Customizing Content Display
*/
/**
The insets to apply around the text content
*/
@property (nonatomic) UIEdgeInsets edgeInsets;
/**
Specifies if the receiver should add extra leading the first line of its content
*/
@property (nonatomic) BOOL shouldAddFirstLineLeading;
/**
Specifies if the receiver should draw image text attachments.
Set to `NO` if you use the delegate methods to provide custom subviews to display images.
*/
@property (nonatomic) BOOL shouldDrawImages;
/**
Specified if the receiver should draw hyperlinks.
If set to `NO` then your custom subview/button for hyperlinks is responsible for displaying hyperlinks. You can use <DTLinkButton> to have links show a differently for normal and highlighted style
*/
@property (nonatomic) BOOL shouldDrawLinks;
/**
Specifies if the receiver should layout custom subviews in layoutSubviews.
If set to `YES` then all custom subviews will always be layouted. Set to `NO` to only layout visible subviews, e.g. in a scroll view. Defaults to `YES` if used stand-alone, `NO` inside a <DTAttributedTextView>.
*/
@property (nonatomic) BOOL shouldLayoutCustomSubviews;
/**
The amount by which all contents of the receiver will offset of display and subview layouting
*/
@property (nonatomic) CGPoint layoutOffset;
/**
The offset to apply for drawing the background.
If you set a pattern color as background color you can have the pattern phase be offset by this value.
*/
@property (nonatomic) CGSize backgroundOffset;
/**
An integer bit mask that determines how the receiver relayouts its contents when its bounds change.
When the view’s bounds change, that view automatically re-layouts its text according to the relayout mask. You specify the value of this mask by combining the constants described in DTAttributedTextContentViewRelayoutMask using the C bitwise OR operator. Combining these constants lets you specify which dimensions will cause a re-layout if modified. The default value of this property is DTAttributedTextContentViewRelayoutOnWidthChanged, which indicates that the text will be re-layouted if the width changes, but not if the height changes.
*/
@property (nonatomic) DTAttributedTextContentViewRelayoutMask relayoutMask;
@end
/**
You can globally customize the layer class to be used for new instances of <DTAttributedTextContentView>. By itself it makes most sense to go with the default `CALayer`. For larger bodies of text, i.e. if there is scrolling then you should use a `CATiledLayer` subclass instead.
*/
@interface DTAttributedTextContentView (Tiling)
/**
Sets the layer class globally to use in new instances of content views. Defaults to `CALayer`.
While being fine for most use cases you should use a `CATiledLayer` subclass for anything larger than a screen full, e.g. in scroll views.
@param layerClass The class to use, should be a `CALayer` subclass
*/
+ (void)setLayerClass:(Class)layerClass;
/**
The current layer class that is used for new instances
@returns The `CALayer` subclass that new instances are using
*/
+ (Class)layerClass;
@end
/**
Methods for drawing the content view
*/
@interface DTAttributedTextContentView (Drawing)
/**
Creates an image from a part of the receiver's content view
@param bounds The bounds of the content to draw
@param options The drawing options to apply when drawing
@see [DTCoreTextLayoutFrame drawInContext:options:] for a list of available drawing options
@returns A `UIImage` with the specified content
*/
- (UIImage *)contentImageWithBounds:(CGRect)bounds options:(DTCoreTextLayoutFrameDrawingOptions)options;
@end