Skip to content

Instantly share code, notes, and snippets.

@stuartjmoore
Last active June 15, 2022 07:29
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stuartjmoore/8461057 to your computer and use it in GitHub Desktop.
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.
- (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;
}
//
// 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
//
// 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 /* &nbsp; */))
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