再DTCoreText 中添加自定义的文章头部,事实上原理很简单,需要改动DTCoreText 的代码即可。
DTAttributedTextView 事实上是一个ScrollView , 中间包含了一个 DTAttributedTextContentView , 实际上再里面添加一个 headerView 并添加到头部,调整 DTAttributedTextContentView 的相对坐标,记住当滚动的时候会重新刷新 DTAttributedTextContentView , 此时也需要根据你的headerView 做一些偏移,问题就可以得到完美的解决了。这里紧紧添加一些思路,代码的话,我针对自己的改动贴一下,因为需要快速做了一下实验,所以没有太注意写法,大家看到不对的就改呗。PS.对比与DTCoreText 源代码,就可知道我改了哪些地方了。
//
// DTAttributedTextView.m
// CoreTextExtensions
//
// Created by Oliver Drobnik on 1/12/11.
// Copyright 2011 Drobnik.com. All rights reserved.
//
#import "DTAttributedTextView.h"
#import "DTCoreText.h"
#import <QuartzCore/QuartzCore.h>
@interface DTAttributedTextView ()
- (void)setup;
@end
@implementation DTAttributedTextView
{
DTAttributedTextContentView *_attributedTextContentView;
UIView *_backgroundView;
UIView *_headerView;
// these are pass-through, i.e. store until the content view is created
__unsafe_unretained id textDelegate;
NSAttributedString *_attributedString;
BOOL _shouldDrawLinks;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
[self setup];
}
return self;
}
- (void)dealloc
{
_attributedTextContentView.delegate = nil;
[_attributedTextContentView removeObserver:self forKeyPath:@"frame"];
}
- (void)layoutSubviews
{
[super layoutSubviews];
[self contentView];
// layout custom subviews for visible area
[_attributedTextContentView layoutSubviewsInRect: CGRectMake(self.bounds.origin.x, self.bounds.origin.y - self.headerView.bounds.size.height, self.bounds.size.width, self.bounds.size.height + self.headerView.bounds.size.height)];
}
- (void)awakeFromNib
{
[self setup];
}
// default
- (void)setup
{
if (!self.backgroundColor)
{
self.backgroundColor = [DTColor whiteColor];
self.opaque = YES;
return;
}
CGFloat alpha = [self.backgroundColor alphaComponent];
if (alpha < 1.0)
{
self.opaque = NO;
}
else
{
self.opaque = YES;
}
self.autoresizesSubviews = NO;
self.clipsToBounds = YES;
}
// override class e.g. for mutable content view
- (Class)classForContentView
{
return [DTAttributedTextContentView class];
}
#pragma mark External Methods
- (void)scrollToAnchorNamed:(NSString *)anchorName animated:(BOOL)animated
{
NSRange range = [self.contentView.attributedString rangeOfAnchorNamed:anchorName];
if (range.length != NSNotFound)
{
// get the line of the first index of the anchor range
DTCoreTextLayoutLine *line = [self.contentView.layoutFrame lineContainingIndex:range.location];
// make sure we don't scroll too far
CGFloat maxScrollPos = self.contentSize.height - self.bounds.size.height + self.contentInset.bottom + self.contentInset.top;
CGFloat scrollPos = MIN(line.frame.origin.y, maxScrollPos);
// scroll
[self setContentOffset:CGPointMake(0, scrollPos) animated:animated];
}
}
#pragma mark Notifications
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (object == _attributedTextContentView && [keyPath isEqualToString:@"frame"])
{
CGRect newFrame = [[change objectForKey:NSKeyValueChangeNewKey] CGRectValue];
self.contentSize =CGSizeMake(newFrame.size.width, newFrame.size.height + self.headerView.frame.size.height) ;
}
}
#pragma mark Properties
- (DTAttributedTextContentView *)contentView
{
if (!_attributedTextContentView)
{
// subclasses can specify a DTAttributedTextContentView subclass instead
Class classToUse = [self classForContentView];
CGRect rectReal;
if (self.headerView) {
rectReal = CGRectMake(self.bounds.origin.x+8.0f, self.bounds.origin.y + self.headerView.frame.size.height , self.bounds.size.width-15.0f,self.bounds.size.height - self.headerView.frame.size.height);
}
else{
rectReal = CGRectMake(self.bounds.origin.x+8.0f, self.bounds.origin.y , self.bounds.size.width-15.0f,self.bounds.size.height);
}
[self addSubview:self.headerView];
CGRect frame = UIEdgeInsetsInsetRect(rectReal, self.contentInset);
_attributedTextContentView = [[classToUse alloc] initWithFrame:frame];
_attributedTextContentView.userInteractionEnabled = YES;
_attributedTextContentView.backgroundColor = self.backgroundColor;
_attributedTextContentView.shouldLayoutCustomSubviews = NO; // we call layout when scrolling
// adjust opaqueness based on background color alpha
CGFloat alpha = [self.backgroundColor alphaComponent];
if (alpha < 1.0)
{
_attributedTextContentView.opaque = NO;
}
else
{
_attributedTextContentView.opaque = YES;
}
// set text delegate if it was set before instantiation of content view
_attributedTextContentView.delegate = textDelegate;
// pass on setting
_attributedTextContentView.shouldDrawLinks = _shouldDrawLinks;
// set text we previously got
_attributedTextContentView.attributedString = _attributedString;
CGSize neededSize = [_attributedTextContentView sizeThatFits:CGSizeZero];
frame.size = neededSize;
_attributedTextContentView.frame = frame;
self.contentSize =CGSizeMake(neededSize.width, neededSize.height + self.headerView.frame.size.height) ;
// we want to know if the frame changes so that we can adjust the scrollview content size
[_attributedTextContentView addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil];
[self addSubview:_attributedTextContentView];
}
return _attributedTextContentView;
}
- (void)setBackgroundColor:(DTColor *)newColor
{
if ([newColor alphaComponent]<1.0)
{
super.backgroundColor = newColor;
_attributedTextContentView.backgroundColor = [DTColor clearColor];
self.opaque = NO;
}
else
{
super.backgroundColor = newColor;
if (_attributedTextContentView.opaque)
{
_attributedTextContentView.backgroundColor = newColor;
}
}
}
- (UIView *)backgroundView
{
if (!_backgroundView)
{
_backgroundView = [[UIView alloc] initWithFrame:self.bounds];
_backgroundView.backgroundColor = [DTColor whiteColor];
// default is no interaction because background should have no interaction
_backgroundView.userInteractionEnabled = NO;
[self insertSubview:_backgroundView belowSubview:self.contentView];
// make content transparent so that we see the background
_attributedTextContentView.backgroundColor = [DTColor clearColor];
_attributedTextContentView.opaque = NO;
}
return _backgroundView;
}
- (void)setBackgroundView:(UIView *)backgroundView
{
if (_backgroundView != backgroundView)
{
[_backgroundView removeFromSuperview];
_backgroundView = backgroundView;
if (_attributedTextContentView)
{
[self insertSubview:_backgroundView belowSubview:_attributedTextContentView];
}
else
{
[self addSubview:_backgroundView];
}
if (_backgroundView)
{
// make content transparent so that we see the background
_attributedTextContentView.backgroundColor = [DTColor clearColor];
_attributedTextContentView.opaque = NO;
}
else
{
_attributedTextContentView.backgroundColor = [DTColor whiteColor];
_attributedTextContentView.opaque = YES;
}
}
}
- (void)setAttributedString:(NSAttributedString *)string
{
_attributedString = string;
// might need layout for visible custom views
[self setNeedsLayout];
if (_attributedTextContentView)
{
// pass it along if contentView already exists
_attributedTextContentView.attributedString = string;
// adjust content size right away
self.contentSize = CGSizeMake(_attributedTextContentView.frame.size.width, _attributedTextContentView.frame.size.height + self.headerView.frame.size.height);
//_attributedTextContentView.frame.size;
}
}
- (NSAttributedString *)attributedString
{
return _attributedString;
}
- (void)setFrame:(CGRect)frame
{
if (!CGRectEqualToRect(self.frame, frame))
{
if (self.frame.size.width != frame.size.width)
{
// height does not matter, that will be determined anyhow
CGRect contentFrame = CGRectMake(0, 0, frame.size.width - self.contentInset.left - self.contentInset.right, 0);
_attributedTextContentView.frame = contentFrame;
}
[super setFrame:frame];
}
}
- (void)setTextDelegate:(id<DTAttributedTextContentViewDelegate>)aTextDelegate
{
// store unsafe pointer to delegate because we might not have a contentView yet
textDelegate = aTextDelegate;
// set it if possible, otherwise it will be set in contentView lazy property
_attributedTextContentView.delegate = aTextDelegate;
}
- (id<DTAttributedTextContentViewDelegate>)textDelegate
{
return _attributedTextContentView.delegate;
}
- (void)setShouldDrawLinks:(BOOL)shouldDrawLinks
{
_shouldDrawLinks = shouldDrawLinks;
_attributedTextContentView.shouldDrawLinks = YES;
}
- (UIView *)headerView
{
return _headerView;
}
- (void)setHeaderView:(UIView *)headerView
{
if (_headerView != headerView)
{
[_headerView removeFromSuperview];
_headerView = headerView;
{
[self addSubview:_headerView];
}
}
}
@synthesize attributedTextContentView = _attributedTextContentView;
@synthesize attributedString = _attributedString;
@synthesize textDelegate = _textDelegate;
//@synthesize headerView = _headerView;
@synthesize shouldDrawLinks = _shouldDrawLinks;
@end