在 UITextView 中画一条线 - NSAttributedString
Draw a line inside a UITextView - NSAttributedString
我想在 UITextView
中绘制一条可自定义的线,其中包含一些文本(使用 NSAttributedString
)
这是我试过的
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];
但无论我为制表位位置(在本例中为 40)和 tailIndent(此处为 -16)提供什么值,该行仅考虑 headIndent 并跨越整个 UITextView 宽度(当然减去 headIndent) .
EDIT - 我很确定这个问题是因为我没有使用正确的 unicode 字符(尽管它们似乎是合乎逻辑的选择)。万一这给了某人提示,如果我在第二个 nbsp 之后添加一个 space,即接近尾声,则选项卡仅限于单个选项卡长度
这是您的预期结果吗?
你能试试这个吗:
NSTextTab *listTab = [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentCenter location:self.textView.frame.size.width - tabStyle.firstLineHeadIndent + tabStyle.tailIndent options:@{}];
这是完整代码:
- (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;
}
以下是我为解决同一问题所做的工作。它使用 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
}
}
}
与其他解决方案相似的位:
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)
}
一些注意事项:
- 它可能只适用于单个方形文本容器。
NSTextContainer
有一个 lineFragmentPadding
。如果您想知道为什么必须将右对齐制表符缩进 10,就是这样:默认值为 5。
- 在文本前加上
\n
意味着没有删除线。不知道为什么。
- 此解决方案适用于旋转等,因为
UITextView
在边界更改时再次调用 NSTextTab.location
。
对于 swift 5,
let unicodeStr = "\n\u{00a0}\t\t\n"
let str = NSMutableAttributedString(string: unicodeStr)
let strRange = NSRange(location: 0, length: str.length)
let tabStyle = NSMutableParagraphStyle()
tabStyle.headIndent = 0 //padding on left and right edges
tabStyle.firstLineHeadIndent = 0
//tabStyle.tailIndent = -16
let listTab = NSTextTab(textAlignment: .center, location: textView.frame.size.width-10, options: [:]) //this is how long I want the line to be
tabStyle.tabStops = [listTab]
str.addAttribute(.paragraphStyle, value: tabStyle, range: strRange)
str.addAttribute(.strikethroughStyle, value: NSNumber(value: 2), range: strRange)
str.addAttribute(.strikethroughColor, value: UIColor.label, range: strRange)
str.addAttribute(NSAttributedString.Key.font, value: self.currentFont, range: strRange)
str.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.label, range: strRange)
textView.textStorage.append(str)
//To set cursor end of document
let endPosition = self.textView.endOfDocument
self.textView.selectedTextRange = self.textView.textRange(from: endPosition, to: endPosition)
我想在 UITextView
中绘制一条可自定义的线,其中包含一些文本(使用 NSAttributedString
)
这是我试过的
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];
但无论我为制表位位置(在本例中为 40)和 tailIndent(此处为 -16)提供什么值,该行仅考虑 headIndent 并跨越整个 UITextView 宽度(当然减去 headIndent) .
EDIT - 我很确定这个问题是因为我没有使用正确的 unicode 字符(尽管它们似乎是合乎逻辑的选择)。万一这给了某人提示,如果我在第二个 nbsp 之后添加一个 space,即接近尾声,则选项卡仅限于单个选项卡长度
这是您的预期结果吗?
你能试试这个吗:
NSTextTab *listTab = [[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentCenter location:self.textView.frame.size.width - tabStyle.firstLineHeadIndent + tabStyle.tailIndent options:@{}];
这是完整代码:
- (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;
}
以下是我为解决同一问题所做的工作。它使用 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
}
}
}
与其他解决方案相似的位:
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)
}
一些注意事项:
- 它可能只适用于单个方形文本容器。
NSTextContainer
有一个lineFragmentPadding
。如果您想知道为什么必须将右对齐制表符缩进 10,就是这样:默认值为 5。- 在文本前加上
\n
意味着没有删除线。不知道为什么。 - 此解决方案适用于旋转等,因为
UITextView
在边界更改时再次调用NSTextTab.location
。
对于 swift 5,
let unicodeStr = "\n\u{00a0}\t\t\n"
let str = NSMutableAttributedString(string: unicodeStr)
let strRange = NSRange(location: 0, length: str.length)
let tabStyle = NSMutableParagraphStyle()
tabStyle.headIndent = 0 //padding on left and right edges
tabStyle.firstLineHeadIndent = 0
//tabStyle.tailIndent = -16
let listTab = NSTextTab(textAlignment: .center, location: textView.frame.size.width-10, options: [:]) //this is how long I want the line to be
tabStyle.tabStops = [listTab]
str.addAttribute(.paragraphStyle, value: tabStyle, range: strRange)
str.addAttribute(.strikethroughStyle, value: NSNumber(value: 2), range: strRange)
str.addAttribute(.strikethroughColor, value: UIColor.label, range: strRange)
str.addAttribute(NSAttributedString.Key.font, value: self.currentFont, range: strRange)
str.addAttribute(NSAttributedString.Key.foregroundColor, value: UIColor.label, range: strRange)
textView.textStorage.append(str)
//To set cursor end of document
let endPosition = self.textView.endOfDocument
self.textView.selectedTextRange = self.textView.textRange(from: endPosition, to: endPosition)