自动布局+RTL+UILabel文本对齐

28

我终于开始尝试使用自动布局了,但似乎无法弄清楚如何使从右到左(RTL)的支持按照我期望/所需的方式工作...

我在Interface Builder中设计了视图,如下所示:

IB

使用英语时,生成的应用程序运行正常:

English

但是,当切换到RTL语言(此处为阿拉伯语)时,整个视图会翻转(很好),但是UILabel的文本仍然是左对齐的。 我希望它是右对齐的,以使其与UIImageView相邻。

Arabic

显然我遗漏了某些东西和/或这不包含在自动布局中。

在使用RTL语言时,我是否应手动设置textAlignment


我不认为AutoLayout会改变它布局的元素的内部状态。在这种情况下,最好的方法就是手动操作。 - Stavash
这个问题的解决方案是什么? - Nick Ginanto
@NickGinanto 这取决于您的具体要求,如果您希望标签的宽度基于其固有内容大小,则DarthMike的答案是最好的选择(我实际上曾经想过接受这个答案)。但是,如果您需要标签的宽度保持不变,则需要按照Ken的答案我的答案所提到的采用NSTextAlignmentNatural的方法。我没有在iOS 7+上尝试过它(我使用固有ContentSize)。 - Steve Wilford
谢谢,似乎在IB中设置自然对齐的唯一方法是使用属性字符串。 - Nick Ginanto
同样类型的需求是我的。我该如何在自动布局中实现这个? - Jitendra
显示剩余7条评论
10个回答

34
你需要使用NSTextAlignmentNatural。这会根据所加载的应用程序语言(而不是脚本)来推断文本对齐方式。

如果你正在使用 iOS 9 或更高版本(使用 Xcode 7),可以在故事板中设置此选项(选择---对齐选项)。 如果需要面向早期版本,则需要创建标签的outlet并在awakeFromNib中设置对齐方式。

- (void)awakeFromNib {
    [[self label] setTextAlignment:NSTextAlignmentNatural];
}

1
这听起来很有前途。我明天会试一试。 - Steve Wilford
我接受这个答案,因为它指引了我正确的方向,但实际上并不是那么简单。请查看我的回答以获取详细信息。 - Steve Wilford
1
Xcode 7 允许在标签上设置自然对齐。 - progrmr
稍作改进:检查居中对齐的文本...if (self.textAlignment != NSTextAlignmentCenter) { [self setTextAlignment:NSTextAlignmentNatural]; } - aqavi_paracha
更正:setTextAlignment 应该在 awakeFromNib 之后调用(例如在 viewDidLoad 中),否则,Storyboard 级别定义的 UILabel 属性将被应用(因此会覆盖您定义的任何内容...) - Ariel Malka
当您想要将文本居中对齐时,它也会将其更改为右对齐。 - M Afham

13

对我来说,那些解决方案并没有帮助我,最终我采取了一种相当丑陋的方法,但这是唯一有效的方法。我将其添加为NSString类别:

NSString+Extras.h:

#import <Foundation/Foundation.h>

@interface NSString (Extras)
- (NSTextAlignment)naturalTextAligment;
@end

NSString+Extras.m:

#import "NSString+Extras.h"

@implementation NSString (Extras)

- (NSTextAlignment)naturalTextAligment {
    NSArray *tagschemes = [NSArray arrayWithObjects:NSLinguisticTagSchemeLanguage, nil];
    NSLinguisticTagger *tagger = [[NSLinguisticTagger alloc] initWithTagSchemes:tagschemes options:0];
    [tagger setString:self];
    NSString *language = [tagger tagAtIndex:0 scheme:NSLinguisticTagSchemeLanguage tokenRange:NULL sentenceRange:NULL];
    if ([language rangeOfString:@"he"].location != NSNotFound || [language rangeOfString:@"ar"].location != NSNotFound) {
        return NSTextAlignmentRight;
    } else {
        return NSTextAlignmentLeft;
    }
}
@end

为了检测语言,我使用了这个SO答案


只有在您根据系统范围的用户设置让操作系统加载正确的本地化应用程序并且在您的应用程序中具有ar.lproj或he.lproj时,“自然”对齐才有效。如果您有一个应用内语言选择器,则自然对齐将无法工作,需要使用您的代码。 - lensovet
好的,@lensovet,“自然”表示自然的意味。而且自然的意味是希伯来语/阿拉伯语应该是从右到左的。系统尝试使用setTextAlignment:NSTextAlignmentNatural 来执行此操作,但根据我的经验它并不像应该那样工作。虽然我的解决方案不是很“干净”,但确实有效;它以自然的方式对齐文本,无论系统语言如何。您想要的是类似于“系统语言对齐”的东西,这不是“自然对齐”。 - Aviel Gross
欢迎您提交错误报告请求。我只是在说目前的工作方式。它基于应用程序的用户界面语言,而不是文本内容。 - lensovet

5

@Aviel回答关于 Swift UILabel扩展的问题

//MARK: UILabel extension
extension UILabel {
    func decideTextDirection () {
        let tagScheme = [NSLinguisticTagSchemeLanguage]
        let tagger    = NSLinguisticTagger(tagSchemes: tagScheme, options: 0)
        tagger.string = self.text
        let lang      = tagger.tagAtIndex(0, scheme: NSLinguisticTagSchemeLanguage,
            tokenRange: nil, sentenceRange: nil)

        if lang?.rangeOfString("he") != nil ||  lang?.rangeOfString("ar") != nil {
            self.textAlignment = NSTextAlignment.Right
        } else {
            self.textAlignment = NSTextAlignment.Left
        }
    }
}

如何使用它?

label.text = "كتابة باللغة العربية" // Assign text
label.decideTextDirection()          // Decide direction

最好基于(NS)Locale的characterDirection值来确定textAlignment的值。 - Benny Davidovitz

5

回复 Ken 的答案

UILabel 上将 textAlignment 设置为 NSTextAlignmentNatural 是不可能的,这会导致异常抛出:

Terminating app due to uncaught exception 'NSInvalidArgumentException',
reason: 'textAlignment does not accept NSTextAlignmentNatural'

当使用属性文本并在Interface Builder中设置时,它确实可行,如下图所示:

attributed text natural alignment

然而,似乎在本地化storyboard时不会识别属性文本。

为了解决这个问题,我将UILabel在Interface Builder中配置为普通文本,并创建了一个带有标签文本的NSAttributedString,设置了属性字符串的对齐方式,并将其分配给标签的attributedText属性:

-(void)viewDidLoad
{
    [super viewDidLoad];

    NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];
    paragraphStyle.alignment = NSTextAlignmentNatural;

    NSMutableAttributedString *string = [[NSMutableAttributedString alloc] initWithString:self.lbl.text];
    [string addAttribute:NSParagraphStyleAttributeName
                   value:paragraphStyle
                   range:NSMakeRange(0, string.length)];

    self.lbl.attributedText = string;
}

在这种简单情况下,这很好用,但我可以看到当您需要更复杂的属性字符串样式时,它可能会失败。 但是显然,在那种情况下,您可能只会在创建 NSAttributedString 时使用 NSLocalizedString 或等效项。


这听起来像是一个 bug。你已经提交了吗?我肯定会复制它! - danyowdee
1
我还没有提交错误报告,因为在6.1版本的发布说明中已经提到了,就在底部:https://developer.apple.com/library/ios/releasenotes/General/RN-iOSSDK-6_1/#//apple_ref/doc/uid/TP40012869-CH1-SW21 - Steve Wilford
不错的发现!但由于这是一个严重的限制,我会倾向于称其为错误,尽管更适合的是增强请求;-) - danyowdee
4
在iOS 7中不再崩溃,但似乎不能正确地将RTL文本右对齐:/ - Steve Wilford
1
这段代码加上在storyboard中定义我的UILabel为富文本在iOS7上似乎不能正常工作,使用希伯来文本...有什么想法吗?(我确保在运行这些代码时,在我的viewWillAppear中文本是可用和正确的,只包含希伯来字符) - Aviel Gross
显示剩余6条评论

4

我认为在这种情况下,您不需要使用文本对齐来处理标签。

您只需让标签的宽度由intrinsicContentSize决定,并删除标签上的任何宽度约束。这样,标签文本将与视图对齐。

对于x轴,您只需要在标签和图像视图之间设置以下约束:

[imageview]-[label]

这仅是水平间距约束,没有leading或trailing到superview。


好的,您需要添加一个≥标准尾部约束条件,以确保文本不会从屏幕边缘溢出,这可能会发生在更大的字体/动态类型/其他字符串中。 但是,对于针对iOS 8或更早版本的应用程序,这是一种合法的解决方法。 - lensovet

3

@Aviel回答了一个关于Swift 4的UILabel扩展的问题。

extension UILabel {
    func decideTextDirection () {
        let tagScheme = [NSLinguisticTagScheme.language]
        let tagger    = NSLinguisticTagger(tagSchemes: tagScheme, options: 0)
        tagger.string = self.text
        let lang      = tagger.tag(at: 0, scheme: NSLinguisticTagScheme.language,
                                          tokenRange: nil, sentenceRange: nil)

        if lang?.rawValue.range(of: "he") != nil ||  lang?.rawValue.range(of: "ar") != nil {
            self.textAlignment = NSTextAlignment.right
        } else {
            self.textAlignment = NSTextAlignment.left
        }
    }
}

使用方法

label.text = "كتابة باللغة العربية" // Assign text
label.decideTextDirection()          // Decide direction

我会使用:tagger.tag(at: 0, unit: .document, scheme: .language, tokenRange: nil)。 - Nadi Hassan

1
这个函数帮了我一个大忙。
-(void)fnForWritingDirection:(UILabel*)label textFor:(NSString *)stringForText{

    NSMutableAttributedString* attrStr = [[NSMutableAttributedString alloc] initWithString: [stringForText stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];

    NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init];

    [paragraphStyle setBaseWritingDirection:NSWritingDirectionRightToLeft];

    [attrStr addAttribute:NSParagraphStyleAttributeName value:paragraphStyle range:NSMakeRange(0, [attrStr length])];

    label.attributedText=attrStr;

}

1
除非绝对必要,否则不应强制文本的基础书写方向。在这种情况下,它不是必须的。 - lensovet

1

这个方法对我有用:

extension UILabel {
    func decideTextDirection () {
        let tagScheme = [NSLinguisticTagScheme.language]
        let tagger = NSLinguisticTagger(tagSchemes: tagScheme, options: 0)
        tagger.string = self.text
        let lang = tagger.tag(at: 0, scheme: NSLinguisticTagScheme.language, tokenRange: nil, sentenceRange: nil)
        let langDir = NSLocale.characterDirection(forLanguage: lang?.rawValue ?? "en")
        if langDir == .rightToLeft { self.textAlignment = NSTextAlignment.right }
        else { self.textAlignment = NSTextAlignment.left }
    }
}

使用方法:

nameLabel.text = "مهدی"
nameLabel.decideTextDirection()

0

您链接的存储库的README中没有任何说明如何使RTL和LRT支持更容易。请解释为什么链接此存储库以及它与本主题中的原始问题有何关系。 - Adil Hussain

0
这是我的版本。它更简单,同时还处理源文档中的多种语言。
主要是使用 dominantLanguage:
let lang = tagger.dominantLanguage

代码片段:

 extension UILabel {
    func determineTextDirection () {
        guard self.text != nil else {return}

        let tagger = NSLinguisticTagger(tagSchemes: [.language], options: 0)
        tagger.string = self.text

        let lang = tagger.dominantLanguage

        let rtl = lang == "he" || lang == "ar"

        self.textAlignment = rtl ? .right : .left
    }
}

使用方法:

titleLabel.text = "UILabel היפוך שפה עבור"
titleLabel.determineTextDirection()

最后提醒:如果应用程序已本地化并且您可以依赖手机语言,则您需要的解决方案是:“RTL 的自然文本对齐”,即:
titleLabel.textAlignment = .natural

enter image description here

使用NSLinguisticTagger当您的应用程序显示具有不同语言的多行时。或者当您允许在任何语言中进行自由搜索时,而不考虑本地化。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接