I wish to draw a customizable line inside a UITextView
consisting of some text (using NSAttributedString
)
Here's what I tried
NSString *unicodeStr = [NSString stringWithFormat:@"%C%C%C", 0x00A0, 0x0009, 0x00A0]; //nbsp, tab, nbsp
NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:unicodeStr];
NSRange strRange = NSMakeRange(0, str.length);
NSMutableParagraphStyle *const tabStyle = [[NSMutableParagraphStyle alloc] init];
tabStyle.headIndent = 16; //padding on left and right edges
tabStyle.firstLineHeadIndent = 16;
tabStyle.tailIndent = -16;
NSTextTab *listTab = [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentCenter location:40 options:@{}]; //this is how long I want the line to be
tabStyle.tabStops = @[listTab];
[str addAttribute:NSParagraphStyleAttributeName value:tabStyle range:strRange];
[str addAttribute:NSStrikethroughStyleAttributeName value:[NSNumber numberWithInt:2] range:strRange];
But no matter what value I provide for the tab stop location (40 in this case) and tailIndent (-16 here), the line only respects the headIndent and spans the entire UITextView width (minus the headIndent of course).
EDIT - I am pretty sure the issue is because I am not using the correct unicode chars (although they seem to be the logical choice). In case this gives someone a hint, if I add a space after the 2nd nbsp i.e. towards the end, the tab is limited to a single tab length
Is it your expected result?
Can you try this:
NSTextTab *listTab = [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentCenter location:self.textView.frame.size.width - tabStyle.firstLineHeadIndent + tabStyle.tailIndent options:@{}];
And this is the full code:
- (void) viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
NSString *unicodeStr = @"\n\u00a0\t\t\n";
NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:unicodeStr];
NSRange strRange = NSMakeRange(0, str.length);
NSMutableParagraphStyle *const tabStyle = [[NSMutableParagraphStyle alloc] init];
tabStyle.headIndent = 16; //padding on left and right edges
tabStyle.firstLineHeadIndent = 16;
tabStyle.tailIndent = -70;
NSTextTab *listTab = [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentCenter location:self.textView.frame.size.width - tabStyle.headIndent + tabStyle.tailIndent options:@{}]; //this is how long I want the line to be
tabStyle.tabStops = @[listTab];
[str addAttribute:NSParagraphStyleAttributeName value:tabStyle range:strRange];
[str addAttribute:NSStrikethroughStyleAttributeName value:[NSNumber numberWithInt:2] range:strRange];
NSAttributedString *htmlStr = [[NSAttributedString alloc] initWithData:[@"<h1>Lorem ipsum dolor sit er elit lamet</h1>" dataUsingEncoding:NSUnicodeStringEncoding] options:@{ NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType } documentAttributes:nil error:nil];
[str insertAttributedString:htmlStr atIndex:0];
[str appendAttributedString:htmlStr];
self.textView.attributedText = str;
}
Here's what I did to solve the same issue. It uses a subclass of NSTextTab
:
import UIKit
class RightAnchoredTextTab : NSTextTab {
weak var textContainer : NSTextContainer!
required init(textAlignment alignment: NSTextAlignment, location loc: CGFloat, options: [String : AnyObject], textContainer aTextContainer : NSTextContainer) {
super.init(textAlignment: alignment, location: loc, options: options)
textContainer = aTextContainer
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override var location: CGFloat {
get {
return textContainer.size.width-textContainer.lineFragmentPadding*2-super.location
}
}
}
And similar bits similar to other solutions:
func horizontalLine(indent : CGFloat = 30, width : CGFloat = 1) -> NSAttributedString
{
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.tabStops = []
paragraphStyle.addTabStop(NSTextTab(textAlignment: .Left, location: indent, options: [:]))
paragraphStyle.addTabStop(RightAnchoredTextTab(textAlignment: .Right, location: indent, options: [:], textContainer : textView.textContainer))
paragraphStyle.alignment = .Left
let attributes = [NSParagraphStyleAttributeName : paragraphStyle, NSStrikethroughStyleAttributeName : width]
textView.backgroundColor = UIColor.yellowColor()
let ZeroWidthNonBreakingSpace = "\u{FEFF}"
return NSAttributedString(string: "\t\(ZeroWidthNonBreakingSpace)\t\(ZeroWidthNonBreakingSpace)\n", attributes: attributes)
}
A few notes:
NSTextContainer
has a lineFragmentPadding
. If you've every wondered why you have to indent a right alignment tab by 10 this is it: the default value is 5.\n
meant there was no strikethrough. No idea why.UITextView
calls NSTextTab.location
again when the bounds are changed.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With