From 83b9d5c682b21d88133f24da0f94dd56bd79e687 Mon Sep 17 00:00:00 2001
From: 单军华
Date: Thu, 19 Jul 2018 13:38:55 +0800
Subject: [PATCH] change
---
screendisplay/Pods/KILabel/KILabel/Source/KILabel.m | 767 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 767 insertions(+), 0 deletions(-)
diff --git a/screendisplay/Pods/KILabel/KILabel/Source/KILabel.m b/screendisplay/Pods/KILabel/KILabel/Source/KILabel.m
new file mode 100755
index 0000000..37dbbff
--- /dev/null
+++ b/screendisplay/Pods/KILabel/KILabel/Source/KILabel.m
@@ -0,0 +1,767 @@
+/***********************************************************************************
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013 Matthew Styles
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ ***********************************************************************************/
+
+#import "KILabel.h"
+
+NSString * const KILabelLinkTypeKey = @"linkType";
+NSString * const KILabelRangeKey = @"range";
+NSString * const KILabelLinkKey = @"link";
+
+#pragma mark - Private Interface
+
+@interface KILabel()
+
+// Used to control layout of glyphs and rendering
+@property (nonatomic, retain) NSLayoutManager *layoutManager;
+
+// Specifies the space in which to render text
+@property (nonatomic, retain) NSTextContainer *textContainer;
+
+// Backing storage for text that is rendered by the layout manager
+@property (nonatomic, retain) NSTextStorage *textStorage;
+
+// Dictionary of detected links and their ranges in the text
+@property (nonatomic, copy) NSArray *linkRanges;
+
+// State used to trag if the user has dragged during a touch
+@property (nonatomic, assign) BOOL isTouchMoved;
+
+// During a touch, range of text that is displayed as selected
+@property (nonatomic, assign) NSRange selectedRange;
+
+@end
+
+#pragma mark - Implementation
+
+@implementation KILabel
+{
+ NSMutableDictionary *_linkTypeAttributes;
+}
+
+#pragma mark - Construction
+
+- (id)initWithFrame:(CGRect)frame
+{
+ self = [super initWithFrame:frame];
+ if (self)
+ {
+ [self setupTextSystem];
+ }
+
+ return self;
+}
+
+- (id)initWithCoder:(NSCoder *)aDecoder
+{
+ self = [super initWithCoder:aDecoder];
+ if (self)
+ {
+ [self setupTextSystem];
+ }
+
+ return self;
+}
+
+// Common initialisation. Must be done once during construction.
+- (void)setupTextSystem
+{
+ // Create a text container and set it up to match our label properties
+ _textContainer = [[NSTextContainer alloc] init];
+ _textContainer.lineFragmentPadding = 0;
+ _textContainer.maximumNumberOfLines = self.numberOfLines;
+ _textContainer.lineBreakMode = self.lineBreakMode;
+ _textContainer.size = self.frame.size;
+
+ // Create a layout manager for rendering
+ _layoutManager = [[NSLayoutManager alloc] init];
+ _layoutManager.delegate = self;
+ [_layoutManager addTextContainer:_textContainer];
+
+ // Attach the layou manager to the container and storage
+ [_textContainer setLayoutManager:_layoutManager];
+
+ // Make sure user interaction is enabled so we can accept touches
+ self.userInteractionEnabled = YES;
+
+ // Don't go via public setter as this will have undesired side effect
+ _automaticLinkDetectionEnabled = YES;
+
+ // All links are detectable by default
+ _linkDetectionTypes = KILinkTypeOptionAll;
+
+ // Link Type Attributes. Default is empty (no attributes).
+ _linkTypeAttributes = [NSMutableDictionary dictionary];
+
+ // Don't underline URL links by default.
+ _systemURLStyle = NO;
+
+ // By default we hilight the selected link during a touch to give feedback that we are
+ // responding to touch.
+ _selectedLinkBackgroundColor = [UIColor colorWithWhite:0.95 alpha:1.0];
+
+ // Establish the text store with our current text
+ [self updateTextStoreWithText];
+}
+
+#pragma mark - Text and Style management
+
+- (void)setAutomaticLinkDetectionEnabled:(BOOL)decorating
+{
+ _automaticLinkDetectionEnabled = decorating;
+
+ // Make sure the text is updated properly
+ [self updateTextStoreWithText];
+}
+
+- (void)setLinkDetectionTypes:(KILinkTypeOption)linkDetectionTypes
+{
+ _linkDetectionTypes = linkDetectionTypes;
+
+ // Make sure the text is updated properly
+ [self updateTextStoreWithText];
+}
+
+- (NSDictionary *)linkAtPoint:(CGPoint)location
+{
+ // Do nothing if we have no text
+ if (_textStorage.string.length == 0)
+ {
+ return nil;
+ }
+
+ // Work out the offset of the text in the view
+ CGPoint textOffset = [self calcGlyphsPositionInView];
+
+ // Get the touch location and use text offset to convert to text cotainer coords
+ location.x -= textOffset.x;
+ location.y -= textOffset.y;
+
+ NSUInteger touchedChar = [_layoutManager glyphIndexForPoint:location inTextContainer:_textContainer];
+
+ // If the touch is in white space after the last glyph on the line we don't
+ // count it as a hit on the text
+ NSRange lineRange;
+ CGRect lineRect = [_layoutManager lineFragmentUsedRectForGlyphAtIndex:touchedChar effectiveRange:&lineRange];
+ if (CGRectContainsPoint(lineRect, location) == NO)
+ return nil;
+
+ // Find the word that was touched and call the detection block
+ for (NSDictionary *dictionary in self.linkRanges)
+ {
+ NSRange range = [[dictionary objectForKey:KILabelRangeKey] rangeValue];
+
+ if ((touchedChar >= range.location) && touchedChar < (range.location + range.length))
+ {
+ return dictionary;
+ }
+ }
+
+ return nil;
+}
+
+// Applies background color to selected range. Used to hilight touched links
+- (void)setSelectedRange:(NSRange)range
+{
+ // Remove the current selection if the selection is changing
+ if (self.selectedRange.length && !NSEqualRanges(self.selectedRange, range))
+ {
+ [_textStorage removeAttribute:NSBackgroundColorAttributeName range:self.selectedRange];
+ }
+
+ // Apply the new selection to the text
+ if (range.length && _selectedLinkBackgroundColor != nil)
+ {
+ [_textStorage addAttribute:NSBackgroundColorAttributeName value:_selectedLinkBackgroundColor range:range];
+ }
+
+ // Save the new range
+ _selectedRange = range;
+
+ [self setNeedsDisplay];
+}
+
+- (void)setNumberOfLines:(NSInteger)numberOfLines
+{
+ [super setNumberOfLines:numberOfLines];
+
+ _textContainer.maximumNumberOfLines = numberOfLines;
+}
+
+- (void)setText:(NSString *)text
+{
+ // Pass the text to the super class first
+ [super setText:text];
+
+ // Update our text store with an attributed string based on the original
+ // label text properties.
+ if (!text)
+ {
+ text = @"";
+ }
+
+ NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:text attributes:[self attributesFromProperties]];
+ [self updateTextStoreWithAttributedString:attributedText];
+}
+
+- (void)setAttributedText:(NSAttributedString *)attributedText
+{
+ // Pass the text to the super class first
+ [super setAttributedText:attributedText];
+
+ [self updateTextStoreWithAttributedString:attributedText];
+}
+
+- (void)setSystemURLStyle:(BOOL)systemURLStyle
+{
+ _systemURLStyle = systemURLStyle;
+
+ // Force refresh
+ self.text = self.text;
+}
+
+- (NSDictionary*)attributesForLinkType:(KILinkType)linkType
+{
+ NSDictionary *attributes = _linkTypeAttributes[@(linkType)];
+
+ if (!attributes)
+ {
+ attributes = @{NSForegroundColorAttributeName : self.tintColor};
+ }
+
+ return attributes;
+}
+
+- (void)setAttributes:(NSDictionary*)attributes forLinkType:(KILinkType)linkType
+{
+ if (attributes)
+ {
+ _linkTypeAttributes[@(linkType)] = attributes;
+ }
+ else
+ {
+ [_linkTypeAttributes removeObjectForKey:@(linkType)];
+ }
+
+ // Force refresh text
+ self.text = self.text;
+}
+
+#pragma mark - Text Storage Management
+
+- (void)updateTextStoreWithText
+{
+ // Now update our storage from either the attributedString or the plain text
+ if (self.attributedText)
+ {
+ [self updateTextStoreWithAttributedString:self.attributedText];
+ }
+ else if (self.text)
+ {
+ [self updateTextStoreWithAttributedString:[[NSAttributedString alloc] initWithString:self.text attributes:[self attributesFromProperties]]];
+ }
+ else
+ {
+ [self updateTextStoreWithAttributedString:[[NSAttributedString alloc] initWithString:@"" attributes:[self attributesFromProperties]]];
+ }
+
+ [self setNeedsDisplay];
+}
+
+- (void)updateTextStoreWithAttributedString:(NSAttributedString *)attributedString
+{
+ if (attributedString.length != 0)
+ {
+ attributedString = [KILabel sanitizeAttributedString:attributedString];
+ }
+
+ if (self.isAutomaticLinkDetectionEnabled && (attributedString.length != 0))
+ {
+ self.linkRanges = [self getRangesForLinks:attributedString];
+ attributedString = [self addLinkAttributesToAttributedString:attributedString linkRanges:self.linkRanges];
+ }
+ else
+ {
+ self.linkRanges = nil;
+ }
+
+ if (_textStorage)
+ {
+ // Set the string on the storage
+ [_textStorage setAttributedString:attributedString];
+ }
+ else
+ {
+ // Create a new text storage and attach it correctly to the layout manager
+ _textStorage = [[NSTextStorage alloc] initWithAttributedString:attributedString];
+ [_textStorage addLayoutManager:_layoutManager];
+ [_layoutManager setTextStorage:_textStorage];
+ }
+}
+
+// Returns attributed string attributes based on the text properties set on the label.
+// These are styles that are only applied when NOT using the attributedText directly.
+- (NSDictionary *)attributesFromProperties
+{
+ // Setup shadow attributes
+ NSShadow *shadow = shadow = [[NSShadow alloc] init];
+ if (self.shadowColor)
+ {
+ shadow.shadowColor = self.shadowColor;
+ shadow.shadowOffset = self.shadowOffset;
+ }
+ else
+ {
+ shadow.shadowOffset = CGSizeMake(0, -1);
+ shadow.shadowColor = nil;
+ }
+
+ // Setup color attributes
+ UIColor *color = self.textColor;
+ if (!self.isEnabled)
+ {
+ color = [UIColor lightGrayColor];
+ }
+ else if (self.isHighlighted)
+ {
+ color = self.highlightedTextColor;
+ }
+
+ // Setup paragraph attributes
+ NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init];
+ paragraph.alignment = self.textAlignment;
+
+ // Create the dictionary
+ NSDictionary *attributes = @{NSFontAttributeName : self.font,
+ NSForegroundColorAttributeName : color,
+ NSShadowAttributeName : shadow,
+ NSParagraphStyleAttributeName : paragraph,
+ };
+ return attributes;
+}
+
+/**
+ * Returns array of ranges for all special words, user handles, hashtags and urls in the specfied
+ * text.
+ *
+ * @param text Text to parse for links
+ *
+ * @return Array of dictionaries describing the links.
+ */
+- (NSArray *)getRangesForLinks:(NSAttributedString *)text
+{
+ NSMutableArray *rangesForLinks = [[NSMutableArray alloc] init];
+
+ if (self.linkDetectionTypes & KILinkTypeOptionUserHandle)
+ {
+ [rangesForLinks addObjectsFromArray:[self getRangesForUserHandles:text.string]];
+ }
+
+ if (self.linkDetectionTypes & KILinkTypeOptionHashtag)
+ {
+ [rangesForLinks addObjectsFromArray:[self getRangesForHashtags:text.string]];
+ }
+
+ if (self.linkDetectionTypes & KILinkTypeOptionURL)
+ {
+ [rangesForLinks addObjectsFromArray:[self getRangesForURLs:self.attributedText]];
+ }
+
+ return rangesForLinks;
+}
+
+- (NSArray *)getRangesForUserHandles:(NSString *)text
+{
+ NSMutableArray *rangesForUserHandles = [[NSMutableArray alloc] init];
+
+ // Setup a regular expression for user handles and hashtags
+ static NSRegularExpression *regex = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ NSError *error = nil;
+ regex = [[NSRegularExpression alloc] initWithPattern:@"(?<!\\w)@([\\w\\_]+)?" options:0 error:&error];
+ });
+
+ // Run the expression and get matches
+ NSArray *matches = [regex matchesInString:text options:0 range:NSMakeRange(0, text.length)];
+
+ // Add all our ranges to the result
+ for (NSTextCheckingResult *match in matches)
+ {
+ NSRange matchRange = [match range];
+ NSString *matchString = [text substringWithRange:matchRange];
+
+ if (![self ignoreMatch:matchString])
+ {
+ [rangesForUserHandles addObject:@{KILabelLinkTypeKey : @(KILinkTypeUserHandle),
+ KILabelRangeKey : [NSValue valueWithRange:matchRange],
+ KILabelLinkKey : matchString
+ }];
+ }
+ }
+
+ return rangesForUserHandles;
+}
+
+- (NSArray *)getRangesForHashtags:(NSString *)text
+{
+ NSMutableArray *rangesForHashtags = [[NSMutableArray alloc] init];
+
+ // Setup a regular expression for user handles and hashtags
+ static NSRegularExpression *regex = nil;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ NSError *error = nil;
+ regex = [[NSRegularExpression alloc] initWithPattern:@"(?<!\\w)#([\\w\\_]+)?" options:0 error:&error];
+ });
+
+ // Run the expression and get matches
+ NSArray *matches = [regex matchesInString:text options:0 range:NSMakeRange(0, text.length)];
+
+ // Add all our ranges to the result
+ for (NSTextCheckingResult *match in matches)
+ {
+ NSRange matchRange = [match range];
+ NSString *matchString = [text substringWithRange:matchRange];
+
+ if (![self ignoreMatch:matchString])
+ {
+ [rangesForHashtags addObject:@{KILabelLinkTypeKey : @(KILinkTypeHashtag),
+ KILabelRangeKey : [NSValue valueWithRange:matchRange],
+ KILabelLinkKey : matchString,
+ }];
+ }
+ }
+
+ return rangesForHashtags;
+}
+
+
+- (NSArray *)getRangesForURLs:(NSAttributedString *)text
+{
+ NSMutableArray *rangesForURLs = [[NSMutableArray alloc] init];;
+
+ // Use a data detector to find urls in the text
+ NSError *error = nil;
+ NSDataDetector *detector = [[NSDataDetector alloc] initWithTypes:NSTextCheckingTypeLink error:&error];
+
+ NSString *plainText = text.string;
+
+ NSArray *matches = [detector matchesInString:plainText
+ options:0
+ range:NSMakeRange(0, text.length)];
+
+ // Add a range entry for every url we found
+ for (NSTextCheckingResult *match in matches)
+ {
+ NSRange matchRange = [match range];
+
+ // If there's a link embedded in the attributes, use that instead of the raw text
+ NSString *realURL = [text attribute:NSLinkAttributeName atIndex:matchRange.location effectiveRange:nil];
+ if (realURL == nil)
+ realURL = [plainText substringWithRange:matchRange];
+
+ if (![self ignoreMatch:realURL])
+ {
+ if ([match resultType] == NSTextCheckingTypeLink)
+ {
+ [rangesForURLs addObject:@{KILabelLinkTypeKey : @(KILinkTypeURL),
+ KILabelRangeKey : [NSValue valueWithRange:matchRange],
+ KILabelLinkKey : realURL,
+ }];
+ }
+ }
+ }
+
+ return rangesForURLs;
+}
+
+- (BOOL)ignoreMatch:(NSString*)string
+{
+ return [_ignoredKeywords containsObject:[string lowercaseString]];
+}
+
+- (NSAttributedString *)addLinkAttributesToAttributedString:(NSAttributedString *)string linkRanges:(NSArray *)linkRanges
+{
+ NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithAttributedString:string];
+
+ for (NSDictionary *dictionary in linkRanges)
+ {
+ NSRange range = [[dictionary objectForKey:KILabelRangeKey] rangeValue];
+ KILinkType linkType = [dictionary[KILabelLinkTypeKey] unsignedIntegerValue];
+
+ NSDictionary *attributes = [self attributesForLinkType:linkType];
+
+ // Use our tint color to hilight the link
+ [attributedString addAttributes:attributes range:range];
+
+ // Add an URL attribute if this is a URL
+ if (_systemURLStyle && ((KILinkType)[dictionary[KILabelLinkTypeKey] unsignedIntegerValue] == KILinkTypeURL))
+ {
+ // Add a link attribute using the stored link
+ [attributedString addAttribute:NSLinkAttributeName value:dictionary[KILabelLinkKey] range:range];
+ }
+ }
+
+ return attributedString;
+}
+
+#pragma mark - Layout and Rendering
+
+- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines
+{
+ // Use our text container to calculate the bounds required. First save our
+ // current text container setup
+ CGSize savedTextContainerSize = _textContainer.size;
+ NSInteger savedTextContainerNumberOfLines = _textContainer.maximumNumberOfLines;
+
+ // Apply the new potential bounds and number of lines
+ _textContainer.size = bounds.size;
+ _textContainer.maximumNumberOfLines = numberOfLines;
+
+ // Measure the text with the new state
+ CGRect textBounds = [_layoutManager usedRectForTextContainer:_textContainer];
+
+ // Position the bounds and round up the size for good measure
+ textBounds.origin = bounds.origin;
+ textBounds.size.width = ceil(textBounds.size.width);
+ textBounds.size.height = ceil(textBounds.size.height);
+
+ if (textBounds.size.height < bounds.size.height)
+ {
+ // Take verical alignment into account
+ CGFloat offsetY = (bounds.size.height - textBounds.size.height) / 2.0;
+ textBounds.origin.y += offsetY;
+ }
+
+ // Restore the old container state before we exit under any circumstances
+ _textContainer.size = savedTextContainerSize;
+ _textContainer.maximumNumberOfLines = savedTextContainerNumberOfLines;
+
+ return textBounds;
+}
+
+- (void)drawTextInRect:(CGRect)rect
+{
+ // Don't call super implementation. Might want to uncomment this out when
+ // debugging layout and rendering problems.
+ // [super drawTextInRect:rect];
+
+ // Calculate the offset of the text in the view
+ NSRange glyphRange = [_layoutManager glyphRangeForTextContainer:_textContainer];
+ CGPoint glyphsPosition = [self calcGlyphsPositionInView];
+
+ // Drawing code
+ [_layoutManager drawBackgroundForGlyphRange:glyphRange atPoint:glyphsPosition];
+ [_layoutManager drawGlyphsForGlyphRange:glyphRange atPoint:glyphsPosition];
+}
+
+// Returns the XY offset of the range of glyphs from the view's origin
+- (CGPoint)calcGlyphsPositionInView
+{
+ CGPoint textOffset = CGPointZero;
+
+ CGRect textBounds = [_layoutManager usedRectForTextContainer:_textContainer];
+ textBounds.size.width = ceil(textBounds.size.width);
+ textBounds.size.height = ceil(textBounds.size.height);
+
+ if (textBounds.size.height < self.bounds.size.height)
+ {
+ CGFloat paddingHeight = (self.bounds.size.height - textBounds.size.height) / 2.0;
+ textOffset.y = paddingHeight;
+ }
+
+ return textOffset;
+}
+
+- (void)setFrame:(CGRect)frame
+{
+ [super setFrame:frame];
+
+ _textContainer.size = self.bounds.size;
+}
+
+- (void)setBounds:(CGRect)bounds
+{
+ [super setBounds:bounds];
+
+ _textContainer.size = self.bounds.size;
+}
+
+- (void)layoutSubviews
+{
+ [super layoutSubviews];
+
+ // Update our container size when the view frame changes
+ _textContainer.size = self.bounds.size;
+}
+
+- (void)setIgnoredKeywords:(NSSet *)ignoredKeywords
+{
+ NSMutableSet *set = [NSMutableSet setWithCapacity:ignoredKeywords.count];
+
+ [ignoredKeywords enumerateObjectsUsingBlock:^(id obj, BOOL *stop) {
+ [set addObject:[obj lowercaseString]];
+ }];
+
+ _ignoredKeywords = [set copy];
+}
+
+#pragma mark - Interactions
+
+- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
+{
+ _isTouchMoved = NO;
+
+ // Get the info for the touched link if there is one
+ NSDictionary *touchedLink;
+ CGPoint touchLocation = [[touches anyObject] locationInView:self];
+ touchedLink = [self linkAtPoint:touchLocation];
+
+ if (touchedLink)
+ {
+ self.selectedRange = [[touchedLink objectForKey:KILabelRangeKey] rangeValue];
+ }
+ else
+ {
+ [super touchesBegan:touches withEvent:event];
+ }
+}
+
+- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
+{
+ [super touchesMoved:touches withEvent:event];
+
+ _isTouchMoved = YES;
+}
+
+- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
+{
+ [super touchesEnded:touches withEvent:event];
+
+ // If the user dragged their finger we ignore the touch
+ if (_isTouchMoved)
+ {
+ self.selectedRange = NSMakeRange(0, 0);
+
+ return;
+ }
+
+ // Get the info for the touched link if there is one
+ NSDictionary *touchedLink;
+ CGPoint touchLocation = [[touches anyObject] locationInView:self];
+ touchedLink = [self linkAtPoint:touchLocation];
+
+ if (touchedLink)
+ {
+ NSRange range = [[touchedLink objectForKey:KILabelRangeKey] rangeValue];
+ NSString *touchedSubstring = [touchedLink objectForKey:KILabelLinkKey];
+ KILinkType linkType = (KILinkType)[[touchedLink objectForKey:KILabelLinkTypeKey] intValue];
+
+ [self receivedActionForLinkType:linkType string:touchedSubstring range:range];
+ }
+ else
+ {
+ [super touchesBegan:touches withEvent:event];
+ }
+
+ self.selectedRange = NSMakeRange(0, 0);
+}
+
+- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
+{
+ [super touchesCancelled:touches withEvent:event];
+
+ // Make sure we don't leave a selection when the touch is cancelled
+ self.selectedRange = NSMakeRange(0, 0);
+}
+
+- (void)receivedActionForLinkType:(KILinkType)linkType string:(NSString*)string range:(NSRange)range
+{
+ switch (linkType)
+ {
+ case KILinkTypeUserHandle:
+ if (_userHandleLinkTapHandler)
+ {
+ _userHandleLinkTapHandler(self, string, range);
+ }
+ break;
+
+ case KILinkTypeHashtag:
+ if (_hashtagLinkTapHandler)
+ {
+ _hashtagLinkTapHandler(self, string, range);
+ }
+ break;
+
+ case KILinkTypeURL:
+ if (_urlLinkTapHandler)
+ {
+ _urlLinkTapHandler(self, string, range);
+ }
+ break;
+ }
+}
+
+#pragma mark - Layout manager delegate
+
+-(BOOL)layoutManager:(NSLayoutManager *)layoutManager shouldBreakLineByWordBeforeCharacterAtIndex:(NSUInteger)charIndex
+{
+ // Don't allow line breaks inside URLs
+ NSRange range;
+ NSURL *linkURL = [layoutManager.textStorage attribute:NSLinkAttributeName atIndex:charIndex effectiveRange:&range];
+
+ return !(linkURL && (charIndex > range.location) && (charIndex <= NSMaxRange(range)));
+}
+
++ (NSAttributedString *)sanitizeAttributedString:(NSAttributedString *)attributedString
+{
+ // Setup paragraph alignement properly. IB applies the line break style
+ // to the attributed string. The problem is that the text container then
+ // breaks at the first line of text. If we set the line break to wrapping
+ // then the text container defines the break mode and it works.
+ // NOTE: This is either an Apple bug or something I've misunderstood.
+
+ // Get the current paragraph style. IB only allows a single paragraph so
+ // getting the style of the first char is fine.
+ NSRange range;
+ NSParagraphStyle *paragraphStyle = [attributedString attribute:NSParagraphStyleAttributeName atIndex:0 effectiveRange:&range];
+
+ if (paragraphStyle == nil)
+ {
+ return attributedString;
+ }
+
+ // Remove the line breaks
+ NSMutableParagraphStyle *mutableParagraphStyle = [paragraphStyle mutableCopy];
+ mutableParagraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
+
+ // Apply new style
+ NSMutableAttributedString *restyled = [[NSMutableAttributedString alloc] initWithAttributedString:attributedString];
+ [restyled addAttribute:NSParagraphStyleAttributeName value:mutableParagraphStyle range:NSMakeRange(0, restyled.length)];
+
+ return restyled;
+}
+
+@end
--
Gitblit v1.8.0