【问题标题】:Rendering text in uppercase without changing the backing string? (iOS7 & Text Kit)在不更改支持字符串的情况下以大写形式呈现文本? (iOS7 和文本工具包)
【发布时间】:2026-01-22 04:25:01
【问题描述】:

我的应用程序仅针对 iOS 7。我有一个UITextView 提供一个NSAttributedString。该字符串代表一个文档,每个段落都设置了许多用于样式的属性。有些段落需要全部大写,但是,用户可以将段落的样式更改为常规大写的样式,因此必须保留字符串的原始大写。

我该怎么做呢?我的第一个想法是使用新的 Text Kit 功能并进行某种字形替换,但我很难理解它是如何工作的。另一个想法是创建一个自定义字体,其中所有字符都是大写的。

再说一次,我不能只更改支持字符串,所以像 uppercaseString 这样的东西不起作用。

【问题讨论】:

    标签: ios uitextview ios7 textkit


    【解决方案1】:

    刚刚研究了一些 TextKit 的东西,我会尝试一下。

    我会创建一个自定义的 NSLayoutManager 子类并覆盖:

    - (void)setGlyphs:(const CGGlyph *)glyphs properties:(const NSGlyphProperty *)props characterIndexes:(const NSUInteger *)charIndexes font:(UIFont *)aFont forGlyphRange:(NSRange)glyphRange
    

    在此方法中,您需要进行自己的字形生成,虽然这听起来很难,如 CoreText 文档“Getting Glyphs for Characters”中所述。基本上,您需要做的就是从self.textStorage 中获取characterIndexes 引用的字符,然后使用CFStringCapitalize() 将其全部设为大写。

    请注意-setGlyphs:… 方法的文档,因为它有一些奇怪之处。

    希望这至少可以为您指明正确的方向。

    【讨论】:

    • 这个答案是正确的,我唯一要补充的是,如果您使用布局管理器的委托方法layoutManager:shouldGenerateGlyphs:properties:characterIndexes:font:forGlyphRange:,则不需要子类化NSLayoutManager
    【解决方案2】:

    这是一篇旧帖子,但由于没有人发布有效的答案,这里是一个。我对CFStringRef 操作和桥接到 ARC 的了解有点有限,如下所示。请随时更正我的代码。

    在您的NSLayoutManagerDelegate 中,实现shouldGenerateGlyphs

    -(NSUInteger)layoutManager:(NSLayoutManager *)layoutManager shouldGenerateGlyphs:(const CGGlyph *)glyphs properties:(const NSGlyphProperty *)props characterIndexes:(const NSUInteger *)charIndexes font:(NSFont *)aFont forGlyphRange:(NSRange)glyphRange {
        // Get correct indices
        NSUInteger location = charIndexes[0];
        NSUInteger length = glyphRange.length;
        
        // Create string reference and convert to uppercase
        CFStringRef str = (__bridge CFStringRef)[self.textStorage.string substringWithRange:(NSRange){ location, length }];
        CFMutableStringRef uppercase = CFStringCreateMutable(NULL, CFStringGetLength(str));
        CFStringAppend(uppercase, str);
        CFStringUppercase(uppercase, NULL);
        
        // Create glyphs for our new uppercase string
        CGGlyph *newGlyphs = GetGlyphsForCharacters((__bridge CTFontRef)(aFont), uppercase);
        [self.layoutManager setGlyphs:newGlyphs properties:props characterIndexes:charIndexes font:aFont forGlyphRange:glyphRange];
        free(newGlyphs);
    
        return glyphRange.length;
    }
    
    CGGlyph* GetGlyphsForCharacters(CTFontRef font, CFStringRef string)
    {
        // Get the string length.
        CFIndex count = CFStringGetLength(string);
     
        // Allocate our buffers for characters and glyphs.
        UniChar *characters = (UniChar *)malloc(sizeof(UniChar) * count);
        CGGlyph *glyphs = (CGGlyph *)malloc(sizeof(CGGlyph) * count);
     
        // Get the characters from the string.
        CFStringGetCharacters(string, CFRangeMake(0, count), characters);
     
        // Get the glyphs for the characters.
        CTFontGetGlyphsForCharacters(font, characters, glyphs, count);
     
        // Free the buffers
        free(characters);
        return glyphs;
    }
    

    我在等宽文本应用程序中成功使用了此代码,但在需要更复杂脚本的用例中,它可能不会那么稳定。

    【讨论】:

      最近更新 更多