mirror of
https://github.com/wu736139669/ManagerDemo.git
synced 2026-08-05 05:55:42 +00:00
add pods
This commit is contained in:
+413
@@ -0,0 +1,413 @@
|
||||
//
|
||||
// DTAttributedTextView.m
|
||||
// DTCoreText
|
||||
//
|
||||
// Created by Oliver Drobnik on 1/12/11.
|
||||
// Copyright 2011 Drobnik.com. All rights reserved.
|
||||
//
|
||||
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
|
||||
#import "DTAttributedTextView.h"
|
||||
#import "DTCoreText.h"
|
||||
#import "DTTiledLayerWithoutFade.h"
|
||||
#import "DTBlockFunctions.h"
|
||||
|
||||
|
||||
@interface DTAttributedTextView ()
|
||||
|
||||
- (void)_setup;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@implementation DTAttributedTextView
|
||||
{
|
||||
UIView *_backgroundView;
|
||||
|
||||
// these are pass-through, i.e. store until the content view is created
|
||||
DT_WEAK_VARIABLE id textDelegate;
|
||||
NSAttributedString *_attributedString;
|
||||
|
||||
BOOL _shouldDrawLinks;
|
||||
BOOL _shouldDrawImages;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame
|
||||
{
|
||||
self = [super initWithFrame:frame];
|
||||
|
||||
if (self)
|
||||
{
|
||||
[self _setup];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
[self attributedTextContentView];
|
||||
|
||||
// layout custom subviews for visible area
|
||||
[_attributedTextContentView layoutSubviewsInRect:self.bounds];
|
||||
}
|
||||
|
||||
- (void)awakeFromNib
|
||||
{
|
||||
[self _setup];
|
||||
}
|
||||
|
||||
// default
|
||||
- (void)_setup
|
||||
{
|
||||
if (self.backgroundColor)
|
||||
{
|
||||
CGFloat alpha = [self.backgroundColor alphaComponent];
|
||||
|
||||
if (alpha < 1.0)
|
||||
{
|
||||
self.opaque = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.opaque = YES;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self.backgroundColor = [DTColor whiteColor];
|
||||
self.opaque = YES;
|
||||
}
|
||||
|
||||
self.autoresizesSubviews = NO;
|
||||
self.clipsToBounds = YES;
|
||||
|
||||
// defaults
|
||||
_shouldDrawLinks = YES;
|
||||
_shouldDrawImages = 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.attributedTextContentView.attributedString rangeOfAnchorNamed:anchorName];
|
||||
|
||||
if (range.location != NSNotFound)
|
||||
{
|
||||
[self scrollRangeToVisible:range animated:animated];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)scrollRangeToVisible:(NSRange)range animated:(BOOL)animated
|
||||
{
|
||||
// get the line of the first index of the anchor range
|
||||
DTCoreTextLayoutLine *line = [self.attributedTextContentView.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];
|
||||
}
|
||||
|
||||
- (void)relayoutText
|
||||
{
|
||||
DTBlockPerformSyncIfOnMainThreadElseAsync(^{
|
||||
|
||||
// need to reset the layouter because otherwise we get the old framesetter or cached layout frames
|
||||
_attributedTextContentView.layouter=nil;
|
||||
|
||||
// here we're layouting the entire string, might be more efficient to only relayout the paragraphs that contain these attachments
|
||||
[_attributedTextContentView relayoutText];
|
||||
|
||||
// layout custom subviews for visible area
|
||||
[self setNeedsLayout];
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark - Working with a Cursor
|
||||
|
||||
- (NSInteger)closestCursorIndexToPoint:(CGPoint)point
|
||||
{
|
||||
// the point is in the coordinate system of the receiver, need to convert into those of the content view first
|
||||
CGPoint pointInContentView = [self.attributedTextContentView convertPoint:point fromView:self];
|
||||
|
||||
return [self.attributedTextContentView closestCursorIndexToPoint:pointInContentView];
|
||||
}
|
||||
|
||||
- (CGRect)cursorRectAtIndex:(NSInteger)index
|
||||
{
|
||||
CGRect rectInContentView = [self.attributedTextContentView cursorRectAtIndex:index];
|
||||
|
||||
// the point is in the coordinate system of the content view, need to convert into those of the receiver first
|
||||
CGRect rect = [self.attributedTextContentView convertRect:rectInContentView toView:self];
|
||||
|
||||
return rect;
|
||||
}
|
||||
|
||||
#pragma mark Notifications
|
||||
- (void)contentViewDidLayout:(NSNotification *)notification
|
||||
{
|
||||
DTBlockPerformSyncIfOnMainThreadElseAsync(^{
|
||||
|
||||
NSDictionary *userInfo = [notification userInfo];
|
||||
CGRect optimalFrame = [[userInfo objectForKey:@"OptimalFrame"] CGRectValue];
|
||||
|
||||
CGRect frame = UIEdgeInsetsInsetRect(self.bounds, self.contentInset);
|
||||
|
||||
// ignore possibly delayed layout notification for a different width
|
||||
if (optimalFrame.size.width == frame.size.width)
|
||||
{
|
||||
_attributedTextContentView.frame = optimalFrame;
|
||||
self.contentSize = [_attributedTextContentView intrinsicContentSize];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#pragma mark Properties
|
||||
- (DTAttributedTextContentView *)attributedTextContentView
|
||||
{
|
||||
if (!_attributedTextContentView)
|
||||
{
|
||||
// subclasses can specify a DTAttributedTextContentView subclass instead
|
||||
Class classToUse = [self classForContentView];
|
||||
|
||||
CGRect frame = UIEdgeInsetsInsetRect(self.bounds, self.contentInset);
|
||||
|
||||
if (frame.size.width<=0 || frame.size.height<=0)
|
||||
{
|
||||
frame = CGRectZero;
|
||||
}
|
||||
|
||||
// make sure we always have a tiled layer
|
||||
Class previousLayerClass = nil;
|
||||
|
||||
// for DTAttributedTextContentView subclasses we force a tiled layer
|
||||
if ([classToUse isSubclassOfClass:[DTAttributedTextContentView class]])
|
||||
{
|
||||
Class layerClass = [DTAttributedTextContentView layerClass];
|
||||
|
||||
if (![layerClass isSubclassOfClass:[CATiledLayer class]])
|
||||
{
|
||||
[DTAttributedTextContentView setLayerClass:[DTTiledLayerWithoutFade class]];
|
||||
previousLayerClass = layerClass;
|
||||
}
|
||||
}
|
||||
|
||||
_attributedTextContentView = [[classToUse alloc] initWithFrame:frame];
|
||||
|
||||
// restore previous layer class if we changed the layer class for the content view
|
||||
if (previousLayerClass)
|
||||
{
|
||||
[DTAttributedTextContentView setLayerClass:previousLayerClass];
|
||||
}
|
||||
|
||||
_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;
|
||||
|
||||
// notification that tells us about the actual size of the content view
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(contentViewDidLayout:) name:DTAttributedTextContentViewDidFinishLayoutNotification object:_attributedTextContentView];
|
||||
|
||||
// temporary frame to specify the width
|
||||
_attributedTextContentView.frame = frame;
|
||||
|
||||
// set text we previously got, this also triggers a relayout
|
||||
_attributedTextContentView.attributedString = _attributedString;
|
||||
|
||||
// this causes a relayout and the resulting notification will allow us to set the final frame
|
||||
|
||||
[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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setContentInset:(UIEdgeInsets)contentInset
|
||||
{
|
||||
if (!UIEdgeInsetsEqualToEdgeInsets(self.contentInset, contentInset))
|
||||
{
|
||||
[super setContentInset:contentInset];
|
||||
|
||||
// height does not matter, that will be determined anyhow
|
||||
CGRect contentFrame = CGRectMake(0, 0, self.frame.size.width - self.contentInset.left - self.contentInset.right, _attributedTextContentView.frame.size.height);
|
||||
|
||||
_attributedTextContentView.frame = contentFrame;
|
||||
}
|
||||
}
|
||||
|
||||
- (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.attributedTextContentView];
|
||||
|
||||
// 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;
|
||||
|
||||
// this causes a relayout and the resulting notification will allow us to set the frame and contentSize
|
||||
}
|
||||
}
|
||||
|
||||
- (NSAttributedString *)attributedString
|
||||
{
|
||||
return _attributedString;
|
||||
}
|
||||
|
||||
- (void)setFrame:(CGRect)frame
|
||||
{
|
||||
CGRect oldFrame = self.frame;
|
||||
|
||||
if (!CGRectEqualToRect(oldFrame, frame))
|
||||
{
|
||||
[super setFrame:frame]; // need to set own frame first because layout completion needs this updated frame
|
||||
|
||||
if (oldFrame.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, _attributedTextContentView.frame.size.height);
|
||||
|
||||
_attributedTextContentView.frame = contentFrame;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (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 = _shouldDrawLinks;
|
||||
}
|
||||
|
||||
- (void)setShouldDrawImages:(BOOL)shouldDrawImages
|
||||
{
|
||||
_shouldDrawImages = shouldDrawImages;
|
||||
_attributedTextContentView.shouldDrawImages = _shouldDrawImages;
|
||||
}
|
||||
|
||||
@synthesize attributedTextContentView = _attributedTextContentView;
|
||||
@synthesize attributedString = _attributedString;
|
||||
@synthesize textDelegate = _textDelegate;
|
||||
|
||||
@synthesize shouldDrawLinks = _shouldDrawLinks;
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user