如何向NSAttributedString添加制表位并在UITextView中显示

时间:2013-03-19 21:37:18

标签: iphone core-text nsattributedstring

我正在创建一个iOS应用,我想显示一个属性字符串 UITextView中指定的特定制表位。我也想画它们 使用drawRect中的Core Text直接进入UIViews。我想(如果可能的话) 对于在两种情况下使用的相同属性字符串。

所以,在我的应用程序中,我创建了一个CFAttributedStringRef或NSAttributedString和 将CTParagraphStyle属性应用于它。然后,我试图显示 将字符串归因于UITextView,我得到了如下所示的崩溃:

-[__NSCFType headIndent]: unrecognized selector sent to instance 0x7545080

po 0x7545080
$0 = 122966144 CTParagraphStyle:
base writing direction = -1, alignment = 4, line break mode = 0, 
   default tab interval = 0
first line head indent = 0, head indent = 0, tail indent = 0
line height multiple = 0, maximum line height = 0, minimum line height = 0
line spacing adjustment = 0, paragraph spacing = 0, 
   paragraph spacing before = 0
tabs:
(
   "CTTextTab: location = 20, alignment = 0, options = (none)\n",
   "CTTextTab: location = 40, alignment = 0, options = (none)\n",
   "CTTextTab: location = 60, alignment = 0, options = (none)\n",
   "CTTextTab: location = 80, alignment = 0, options = (none)\n",
   "CTTextTab: location = 100, alignment = 0, options = (none)\n",
   "CTTextTab: location = 120, alignment = 0, options = (none)\n"
)

我明白发生了什么事,但我想知道我是否有其他办法 做我想做的事。 iOS上的NSMutableParagraphStyle没有选项卡 停止支持,但我注意到OS X上的NSMutableParagraphStyle确实有这个 能力。这让我认为iOS NSMutableParagraphStyle可能会 有一天支持这个。

与此同时,有没有办法将标签停止添加到CFAttributedStringRef或 NSAttributedString仍然有一个UITextView显示它吗?

有问题的来源是:

- (void)viewWillAppear:(BOOL)animated
{
   CFDictionaryRef attrs = (__bridge CFDictionaryRef) @{};
   CFAttributedStringRef a = CFAttributedStringCreate(
       kCFAllocatorDefault, CFSTR("a\tb\tc\td"), attrs);

   CFMutableAttributedStringRef attrStr;
   attrStr = CFAttributedStringCreateMutableCopy(
       kCFAllocatorDefault, CFAttributedStringGetLength(a), a);

   CFArrayRef tabStops = (__bridge CFArrayRef) @[
       (__bridge id) CTTextTabCreate(0, 20, NULL),
       (__bridge id) CTTextTabCreate(0, 40, NULL),
       (__bridge id) CTTextTabCreate(0, 60, NULL),
       (__bridge id) CTTextTabCreate(0, 80, NULL),
       (__bridge id) CTTextTabCreate(0, 100, NULL),
       (__bridge id) CTTextTabCreate(0, 120, NULL)];

   const CTParagraphStyleSetting paraSettings[] = {
       {kCTParagraphStyleSpecifierTabStops, sizeof(CFArrayRef), &tabStops},
   };

   CTParagraphStyleRef paraStyle = CTParagraphStyleCreate(
       paraSettings, 
       sizeof(paraSettings) / sizeof(CTParagraphStyleSetting));

   CFRange range = CFRangeMake(0, CFAttributedStringGetLength(attrStr));
   CFAttributedStringSetAttribute(
       attrStr, range, kCTParagraphStyleAttributeName, paraStyle);

   CFRelease(paraStyle);
   CFIndex i, count = CFArrayGetCount(tabStops);
   for (i = 0; i < count; i++) {
       CFRelease(CFArrayGetValueAtIndex(tabStops, i));
   }

   [[self textView] setAttributedText:
       (__bridge NSAttributedString *)(attrStr)];
}

4 个答案:

答案 0 :(得分:9)

在iOS 7中你可以这样做:

UIFont *font = [UIFont systemFontOfSize:18.0];
NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
NSInteger cnt;
CGFloat tabInterval = 72.0;
paragraphStyle.defaultTabInterval = tabInterval;
NSMutableArray *tabs = [NSMutableArray array];
for (cnt = 1; cnt < 13; cnt++) {    // Add 12 tab stops, at desired intervals...
    [tabs addObject:[[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentLeft location:tabInterval * cnt options:nil]];
}
paragraphStyle.tabStops = tabs;
NSDictionary *attributes = @{ NSFontAttributeName: font, NSParagraphStyleAttributeName: paragraphStyle};

答案 1 :(得分:4)

这是适用于我的Swift版本:

    let tablInterval: CGFloat = 85.0
    let paragraphStyle = NSMutableParagraphStyle()
    let terms = NSTextTab.columnTerminatorsForLocale(NSLocale.currentLocale())
    let tabStop0 = NSTextTab(textAlignment: .Right, location: 0, options: [NSTabColumnTerminatorsAttributeName:terms])
    let tabStop1 = NSTextTab(textAlignment: .Right, location: tablInterval, options: [NSTabColumnTerminatorsAttributeName:terms])
    let tabStop2 = NSTextTab(textAlignment: .Right, location: tablInterval*2, options: [NSTabColumnTerminatorsAttributeName:terms])
    let tabStop3 = NSTextTab(textAlignment: .Right, location: tablInterval*3, options: [NSTabColumnTerminatorsAttributeName:terms])
    paragraphStyle.addTabStop(tabStop0)
    paragraphStyle.addTabStop(tabStop1)
    paragraphStyle.addTabStop(tabStop2)
    paragraphStyle.addTabStop(tabStop3)

    let attributedString = NSMutableAttributedString(string:text)
    attributedString.addAttribute(NSParagraphStyleAttributeName, value:paragraphStyle, range:rangeAll)

答案 2 :(得分:0)

改进/更正了Swift的答案:

let tablInterval: CGFloat = 20.0
var tabsArray = [NSTextTab]()
for i in 1...6 {
    tabsArray.append(NSTextTab(textAlignment: .Left, location: i*tabInterval, options: [:]))
}
let paraStyle = NSMutableParagraphStyle()
paraStyle.tabStops = tabsArray
textView.attributedString = NSAttributedString(string: text, attributes: [NSAttributedString.Key.ParagraphStyle: paraStyle])

为什么需要这个额外的Swift答案?

较早的Swift答案是有缺陷的,因为它在不先删除默认制表位的情况下添加了制表位(12个左对齐的制表位,间距为28 pt)。因此,它向默认的一组制表位添加了一组新的制表位,而不是用新的制表位来代替它们。使用基于该答案的代码使我非常沮丧,试图弄清楚为什么它对我不起作用,直到我回到documentation for NSMutableParagraphStyle.tabStops并了解默认的制表位:

默认值为一个28点间隔的12个左对齐制表符的数组。

此答案可以解决此问题,并且通过删除(不必要的?)“选项”以及使用循环(使其更简洁)(仅适用于所有制表符间距一致且对齐方式相同的情况)类型)。

答案 3 :(得分:-1)

ios 6.1添加了NSParagraphStyle,但似乎CTParagraphStyleRef和NSParagraphStyle不是免费的桥接。

结果如果您在NSAttributeString中有CTParagraphStyleRef,则会导致:

[__ NSCFType headIndent]:无法识别的选择器发送到实例xxxx

和NSParagraphStyle的其他方法,如对齐。