iOS7 TextKit:项目符号对齐

Posted

技术标签:

【中文标题】iOS7 TextKit:项目符号对齐【英文标题】:iOS7 TextKit: bullet point alignment 【发布时间】:2013-10-25 08:30:59 【问题描述】:

我正在编写一个仅适用于 ios 7 的应用程序,并且我正在尝试在不可编辑的 UITextView 中对项目符号点进行适当的格式设置。

只需插入一个项目符号字符就很容易了,但当然不会出现左缩进。在 iOS 7 上,在项目符号点后设置左缩进的最简单方法是什么?

提前致谢,

弗兰克

【问题讨论】:

在项目符号前后添加标签。 我的意思是整个多行段落的缩进,而不仅仅是第一行。 【参考方案1】:

所以我环顾四周,这里是从邓肯的回答中提取的最小代码,以使其工作:

NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:yourLabel.text];

NSMutableParagraphStyle *paragrahStyle = [[NSMutableParagraphStyle alloc] init];
[paragrahStyle setParagraphSpacing:4];
[paragrahStyle setParagraphSpacingBefore:3];
[paragrahStyle setFirstLineHeadIndent:0.0f];  // First line is the one with bullet point
[paragrahStyle setHeadIndent:10.5f];    // Set the indent for given bullet character and size font

[attributedString addAttribute:NSParagraphStyleAttributeName value:paragrahStyle
                         range:NSMakeRange(0, [self.descriptionLabel.text length])];

yourLabel.attributedText = attributedString;

这是我的应用程序中的结果:

【讨论】:

【参考方案2】:

下面是我用来设置项目符号段落的代码。这直接来自工作应用程序,用于将样式应用于整个段落,以响应用户单击格式按钮。我已尝试放入所有依赖方法,但可能遗漏了一些。

请注意,我以厘米为单位设置了大多数缩进,因此在列表末尾使用了转换函数。

我还在检查是否存在制表符(iOS 上没有制表键!)并自动插入破折号和制表符。

如果您只需要段落样式,请查看下面最后几个设置 firstLineIndent 等的方法。

请注意,这些调用都包含在 [textStorage beginEditing/endEditing] 中。尽管下面的 (IBAction) 方法没有被 UI 对象直接调用。

        - (IBAction) styleBullet1:(id)sender
        
            NSRange charRange = [self rangeForUserParagraphAttributeChange];
            NSTextStorage *myTextStorage = [self textStorage];

            // Check for "-\t" at beginning of string and add if not found
            NSAttributedString *attrString = [myTextStorage attributedSubstringFromRange:charRange];
            NSString *string = [attrString string];

            if ([string rangeOfString:@"\t"].location == NSNotFound) 
                NSLog(@"string does not contain tab so insert one");
                NSAttributedString * aStr = [[NSAttributedString alloc] initWithString:@"-\t"];
                // Insert a bullet and tab
                [[self textStorage] insertAttributedString:aStr atIndex:charRange.location];

             else 
                NSLog(@"string contains tab");
            

            if ([self isEditable] && charRange.location != NSNotFound)
            
                [myTextStorage setAttributes:[self bullet1Style] range:charRange];
            
        

        - (NSDictionary*)bullet1Style
        
            return [self createStyle:[self getBullet1ParagraphStyle] font:[self normalFont] fontColor:[UIColor blackColor] underlineStyle:NSUnderlineStyleNone];

        

        - (NSDictionary*)createStyle:(NSParagraphStyle*)paraStyle font:(UIFont*)font fontColor:(UIColor*)color underlineStyle:(int)underlineStyle
        
            NSMutableDictionary *style = [[NSMutableDictionary alloc] init];
            [style setValue:paraStyle forKey:NSParagraphStyleAttributeName];
            [style setValue:font forKey:NSFontAttributeName];
            [style setValue:color forKey:NSForegroundColorAttributeName];
            [style setValue:[NSNumber numberWithInt: underlineStyle] forKey:NSUnderlineStyleAttributeName];

            FLOG(@" font is %@", font);

            return style;
        

        - (NSParagraphStyle*)getBullet1ParagraphStyle
        
            NSMutableParagraphStyle *para;
            para = [self getDefaultParagraphStyle];
            NSMutableArray *tabs = [[NSMutableArray alloc] init];
            [tabs addObject:[[NSTextTab alloc] initWithTextAlignment:NSTextAlignmentLeft location:[self ptsFromCMF:1.0] options:nil]];
            //[tabs addObject:[[NSTextTab alloc] initWithType:NSLeftTabStopType location:[self ptsFromCMF:1.0]]];
            [para setTabStops:tabs];
            [para setDefaultTabInterval:[self ptsFromCMF:2.0]];
            [para setFirstLineHeadIndent:[self ptsFromCMF:0.0]];
            //[para setHeaderLevel:0];
            [para setHeadIndent:[self ptsFromCMF:1.0]];
            [para setParagraphSpacing:3];
            [para setParagraphSpacingBefore:3];
            return para;
        
    - (NSMutableParagraphStyle*)getDefaultParagraphStyle
    
        NSMutableParagraphStyle *para;
        para = [[NSParagraphStyle defaultParagraphStyle]mutableCopy];
        [para setTabStops:nil];
        [para setAlignment:NSTextAlignmentLeft];
        [para setBaseWritingDirection:NSWritingDirectionLeftToRight];
        [para setDefaultTabInterval:[self ptsFromCMF:3.0]];
        [para setFirstLineHeadIndent:0];
        //[para setHeaderLevel:0];
        [para setHeadIndent:0.0];
        [para setHyphenationFactor:0.0];
        [para setLineBreakMode:NSLineBreakByWordWrapping];
        [para setLineHeightMultiple:1.0];
        [para setLineSpacing:0.0];
        [para setMaximumLineHeight:0];
        [para setMinimumLineHeight:0];
        [para setParagraphSpacing:6];
        [para setParagraphSpacingBefore:3];
        //[para setTabStops:<#(NSArray *)#>];
        [para setTailIndent:0.0];
        return para;
    
-(NSNumber*)ptsFromCMN:(float)cm

    return [NSNumber numberWithFloat:[self ptsFromCMF:cm]];

-(float)ptsFromCMF:(float)cm

    return cm * 28.3464567;

【讨论】:

感谢代码示例。我敢肯定,提取最少的代码会很有趣,但这比试图在 TextKit api doc 中找到自己的方式要好得多 :-) 试试参数 setFirstLineHeadIndent 和 setHeadIndent 和 setTabStops,你可能需要这三个参数一起创建一个正确缩进的项目符号段落。但是请记住,您正在设置段落样式,因此如果您不设置所有内容,您可能会得到一些奇怪的结果。 @DuncanGroenewald 谢谢! FWIW,我使用了一个非常缩小的版本来做一个编号列表。基本上,我设置了一个值为“X”的NSTextTab(在我的情况下为 20.0f)并将我的 headIndent 设置为相同的值。然后,我的字符串是@"1.\t这里的长文本\n2.\t更多长文本"。它使所有内容都完美对齐,即使换行也是如此。谢谢! 仅供参考,如果您正在寻找提取的最小代码,请向下滚动到我的答案:***.com/a/26715297/1459762【参考方案3】:

这是我找到的最简单的解决方案:

let bulletList = UILabel()
let bulletListArray = ["line 1 - enter a bunch of lorem ipsum here so it wraps to the next line", "line 2", "line 3"]
let joiner = "\n"

var paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.headIndent = 10
paragraphStyle.firstLineHeadIndent = 0

let attributes = [NSParagraphStyleAttributeName: paragraphStyle]
let bulletListString = joiner.join(bulletListArray.map  "• \($0)" )

bulletList.attributedText = NSAttributedString(string: bulletListString, attributes: attributes)

理论是数组中的每个字符串都像“段落”一样,段落样式在第一行缩进为 0,使用 map 方法添加了一个项目符号。然后对于每行后缩进 10 px (调整字体度量的间距)

【讨论】:

【参考方案4】:

其他答案依赖于使用恒定值设置缩进大小。这意味着如果您要更改字体,则必须手动更新它,如果您使用的是动态类型,则无法正常工作。幸运的是,测量文本很容易。

假设你有一些文本和一些属性:

NSString *text = @"• Some bulleted paragraph";
UIFont *font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
NSDictionary *attributes = @NSFontAttributeName: font;

以下是如何测量项目符号并相应地创建段落样式:

NSString *bulletPrefix = @"• ";
CGSize size = [bulletPrefix sizeWithAttributes:attributes];
NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
paragraphStyle.headIndent = size.width;

我们将它插入到我们的属性中并创建属性字符串:

NSMutableDictionary *indentedAttributes = [attributes mutableCopy];
indentedAttributes[NSParagraphStyleAttributeName] = [paragraphStyle copy];
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:text attributes:indentedAttributes];

【讨论】:

【参考方案5】:

斯威夫特 4

我为NSAttributedString 做了一个扩展,添加了一个方便的初始化器,可以正确缩进不同类型的列表。

extension NSAttributedString 

    convenience init(listString string: String, withFont font: UIFont) 
        self.init(attributedListString: NSAttributedString(string: string), withFont: font)
    

    convenience init(attributedListString attributedString: NSAttributedString, withFont font: UIFont) 
        guard let regex = try? NSRegularExpression(pattern: "^(\\d+\\.|[•\\-\\*])(\\s+).+$",
                                                   options: [.anchorsMatchLines]) else  fatalError() 
        let matches = regex.matches(in: attributedString.string, options: [],
                                    range: NSRange(location: 0, length: attributedString.string.utf16.count))
        let nsString = attributedString.string as NSString
        let mutableAttributedString = NSMutableAttributedString(attributedString: attributedString)

        for match in matches 
            let size = NSAttributedString(
                string: nsString.substring(with: match.range(at: 1)) + nsString.substring(with: match.range(at: 2)),
                attributes: [.font: font]).size()
            let indentation = ceil(size.width)
            let range = match.range(at: 0)

            let paragraphStyle = NSMutableParagraphStyle()

            if let style = attributedString.attribute(.paragraphStyle, at: 0, longestEffectiveRange: nil, in: range)
                as? NSParagraphStyle 
                paragraphStyle.setParagraphStyle(style)
            

            paragraphStyle.tabStops = [NSTextTab(textAlignment: .left, location: indentation, options: [:])]
            paragraphStyle.defaultTabInterval = indentation
            paragraphStyle.firstLineHeadIndent = 0
            paragraphStyle.headIndent = indentation

            mutableAttributedString.addAttribute(.font, value: font, range: range)
            mutableAttributedString.addAttribute(.paragraphStyle, value: paragraphStyle, range: range)
        

        self.init(attributedString: mutableAttributedString)
    

示例用法:

每个项目符号后的空格数等无关紧要。代码将根据您决定在项目符号后使用的制表符或空格数动态计算适当的缩进宽度。

如果属性字符串已经有段落样式,便利初始化器将保留该段落样式的选项并应用它自己的一些选项。

支持的符号:•、-、*、后跟句点的数字(例如 8.)

【讨论】:

【参考方案6】:

你们都可以使用属性检查器来做这个简单的事情,选择缩进字段并做任何你想做的改变:)

【讨论】:

【参考方案7】:

基于thisispete 的解决方案,更新到 Swift 4.2。

斯威夫特 4.2

let array = ["1st", "2nd", "3rd"]
let textView = UITextView()
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.firstLineHeadIndent = 0
paragraphStyle.headIndent = 12
let bulletListText = array.map  "• \($0)" .joined(separator: "\n")
let attributes = [
    NSAttributedString.Key.paragraphStyle: paragraphStyle,
    NSAttributedString.Key.font: UIFont.systemFont(ofSize: 17.0)
]
textView.attributedText = NSAttributedString(string: bulletListText, attributes: attributes)

【讨论】:

【参考方案8】:

我基于 Lukas 实现做了一个快速的解决方案(目前是 Swift 2.3)。我对没有项目符号的行有一点问题,所以我做了扩展,所以你可以选择传递一个范围来应用段落样式。

extension String

    func getAllignedBulletPointsMutableString(bulletPointsRange: NSRange = NSMakeRange(0, 0)) -> NSMutableAttributedString

        let attributedString: NSMutableAttributedString = NSMutableAttributedString(string: self)
        let paragraphStyle = NSMutableParagraphStyle()
        paragraphStyle.paragraphSpacing = 0
        paragraphStyle.paragraphSpacingBefore = 0
        paragraphStyle.firstLineHeadIndent = 0
        paragraphStyle.headIndent = 7.5

        attributedString.addAttributes([NSParagraphStyleAttributeName: paragraphStyle], range: bulletPointsRange)
        return attributedString
    


【讨论】:

以上是关于iOS7 TextKit:项目符号对齐的主要内容,如果未能解决你的问题,请参考以下文章

TextKit 和旁白

插入符号在 UITextView 和 Text Kit 中的长按空白处消失

如何使用 iOS7 Text Kit 在附件周围环绕文本?

关于Text Kit 一些事

iOS 7 Text Kit:NSLayoutManager 何时填充了它的最后一个 NSTextContainer?

iOS富文本