Last active
June 15, 2022 07:29
-
-
Save stuartjmoore/8461057 to your computer and use it in GitHub Desktop.
Hanging quotes on iOS 7.Basically, we set the UITextView's left inset to the negative width of a double quote, then undo that per line fragment if the line doesn't start with a double quote.And the opposite for an exclusion path lines.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
- (void)loadOrResizeOrSometing { | |
/* | |
* After creating the text view or resizing the font, set the quoteWidth and contentInset. | |
*/ | |
WPTextContainer *textContainer = (WPTextContainer*)paragraphView.textContainer; | |
textContainer.spaceWidth = [self widthForCharacter:@" "]; | |
textContainer.charWidths = @{ | |
@(8220) : @([self widthForCharacter:@"“"]), | |
@(8216) : @([self widthForCharacter:@"‘"]), | |
@('"') : @([self widthForCharacter:@"\""]), | |
@('\'') : @([self widthForCharacter:@"'"]), | |
@(8226) : @([self widthForCharacter:@"•"]), | |
@(9679) : @([self widthForCharacter:@"●"]) | |
}; | |
CGFloat maxCharWidth = [[textContainer.charWidths.allValues valueForKeyPath:@"@max.self"] floatValue]; | |
CGFloat insetWidth = ceilf(maxCharWidth + textContainer.spaceWidth); | |
paragraphView.contentInset = UIEdgeInsetsMake(0, -insetWidth, 0, 0); | |
paragraphView.textContainerInset = UIEdgeInsetsZero; | |
} | |
- (CGFloat)widthForCharacter:(NSString*)charater { | |
CGRect rect = [charater boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) | |
options:0 | |
attributes:@{ NSFontAttributeName: yourTextViewFont } | |
context:nil]; | |
return rect.size.width; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// WPTextContainer.h | |
// WashPost | |
// | |
// Created by Moore, Stuart on 1/16/14. | |
// Copyright (c) 2014 Washington Post. All rights reserved. | |
// | |
#import <UIKit/UIKit.h> | |
@interface WPTextContainer : NSTextContainer | |
@property (nonatomic, weak) UITextView *view; | |
@property (nonatomic, strong) NSDictionary *charWidths; | |
@property (nonatomic, assign) CGFloat spaceWidth; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// WPTextContainer.m | |
// WashPost | |
// | |
// Created by Moore, Stuart on 1/16/14. | |
// Copyright (c) 2014 Washington Post. All rights reserved. | |
// | |
#import "WPTextContainer.h" | |
@implementation WPTextContainer | |
- (instancetype)initWithSize:(CGSize)size { | |
if(self = [super initWithSize:size]) { | |
self.lineFragmentPadding = 0; | |
self.widthTracksTextView = YES; | |
} | |
return self; | |
} | |
- (CGRect)lineFragmentRectForProposedRect:(CGRect)proposedRect | |
atIndex:(NSUInteger)index | |
writingDirection:(NSWritingDirection)direction | |
remainingRect:(CGRect*)remainingRect { | |
CGRect rect = [super lineFragmentRectForProposedRect:proposedRect | |
atIndex:index | |
writingDirection:direction | |
remainingRect:remainingRect]; | |
NSString *string = self.layoutManager.textStorage.string; | |
if(string.length > 0 && index < string.length) { | |
unichar character = [string characterAtIndex:index]; | |
CGFloat charWidth = [self.charWidths[@(character)] floatValue]; | |
if(charWidth) { | |
CGFloat width = charWidth; | |
if((index+1) < string.length && | |
([string characterAtIndex:(index+1)] == ' ' || | |
[string characterAtIndex:(index+1)] == 160 /* */)) | |
width += self.spaceWidth; | |
if(![self isOffsetByExclusionPath:rect.origin.x]) | |
rect.origin.x = -self.view.contentInset.left - width; | |
else | |
rect.origin.x -= width; | |
rect.size.width = self.size.width - rect.origin.x; | |
} else if(![self isOffsetByExclusionPath:rect.origin.x]) { | |
rect.origin.x = -self.view.contentInset.left; | |
rect.size.width = self.size.width - rect.origin.x; | |
} | |
} | |
return CGRectIntegral(rect); | |
} | |
- (BOOL)isOffsetByExclusionPath:(CGFloat)x { | |
return (BOOL)(x > -self.view.contentInset.left); | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment