iOS,移动开发,国际化,RTL,阿拉伯语
公司项目因为要适配中东市场,也算是做了不少RTL了,但是每次基本上开发结束后都会遇到或多或少的RTL问题...,以下聊些遇到的坑和踩坑记录
- 普通适配
iOS 针对 RTL做过自动兼容的,前提是要使用对约束,不要用 frame,用 frame 苹果不会自动适配的!!!
app 约束建议使用 Leading以及Trailing两个约束条件而不是Left和Right,这样苹果会针对约束自动进行左右转换. - 针对 iOS9以上
首先,建议根据语言判断一下,是否需要RTL
+ (BOOL)isRTLLayout{ //是否要重右到左布局
if([[iOSUtil getLanguage] isEqualToString:@"ar"] || [[iOSUtil getLanguage] isEqualToString:@"he"]){
return YES;
}else{
return NO;
}
}
iOS9 后可以设置强制转换模式
OC: [UIView appearance].semanticContentAttribute = UISemanticContentAttributeForceRightToLeft;
swift: view.semanticContentAttribute = .forceRightToLeft
对应的 xib ,sb 设置为
-
针对于 collectionView 的国际化设置
一般情况下,collectionView 是可以RTL 的,当然,一般情况并不是所有情况,因为真的去测试的时候,会发现可能同样的一套代码, 在 iOS12, iOS11,iOS10, iOS9上,居然可以有分别不同的表现形式... 😀 比如
解决方案1.
修改CollectionViewFlowLayout
extension HorizontalCollectionViewFlowLayout { open override var flipsHorizontallyInOppositeLayoutDirection: Bool { return true }}
flipsHorizontallyInOppositeLayoutDirection是指开启 一个布尔值,指示水平坐标系是否在适当的时间自动翻转。 这个属性是默认关闭的 如果发生无法反转的话,我们需要这样打开
解决方案2. 重写 layout 判断单个 cell 的 x 位置进行反转
class HorizontalCollectionViewFlowLayout: UICollectionViewFlowLayout {
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let layoutAttributes_super : [UICollectionViewLayoutAttributes] = super.layoutAttributesForElements(in: rect) ?? [UICollectionViewLayoutAttributes]()
let layoutAttributes:[UICollectionViewLayoutAttributes] = NSArray(array: layoutAttributes_super, copyItems:true)as! [UICollectionViewLayoutAttributes]
var layoutAttributes_t : [UICollectionViewLayoutAttributes] = [UICollectionViewLayoutAttributes]()
if PhoneUtil.isRTLLayout() {
if let firstAttr = layoutAttributes.first,
let lastAttr = layoutAttributes.last {
if firstAttr.frame.origin.x >= lastAttr.frame.origin.x {
return layoutAttributes // 阿语下从右至左正常排列,直接返回
}
}
let cellCount:Int = self.collectionView?.numberOfItems(inSection: 0) ?? 0
var nowWidth:CGFloat = self.sectionInset.right
for index in (0...cellCount).reversed() {
if let currentAttr:UICollectionViewLayoutAttributes = layoutAttributes[safe:index] {
var nowFarme:CGRect = currentAttr.frame
nowFarme.origin.x = nowWidth
currentAttr.frame = nowFarme;
nowWidth += nowFarme.size.width + self.minimumInteritemSpacing
layoutAttributes_t.append(currentAttr)
}
}
return layoutAttributes
}
return layoutAttributes
}
}
- 关于 初始位置异常
collectionView.scrollToItem(at: IndexPath.init(row: 0, section: 0), at: .left, animated: false)
滚动一下, 如果滚动后出现位置异常,可能需要 关闭一下 bounce
- UILabel 文字异常 Unicode字符串
Unicode字符串由于阅读习惯的差异(阿拉伯语从右往左阅读,其他语言从左往右阅读),所以字符的排序是不一样的,普通语言左边是第一个字符,阿拉伯语右边是第一个字符。
如果是单纯某种文字,不管是阿拉伯语还是英文,系统都是已经帮助我们做好适配了的。然而混排的情况下,系统的适配是有问题的。对于一个string,系统会用第一个字符来决定当前是LTR还是RTL。
那么坑来了,假设有一个这样的字符串@"小明بدأ في متابعتك"(翻译过来为:小明关注了你),在阿拉伯语的情况下,由于阅读顺序是从右往左,我们希望他显示为@"بدأ في متابعتك小明"。然而按照系统的适配方案,是永远无法达到我们期望的。
如果"小明"放前面,第一个字符是中文,系统识别为LTR,从左往右排序,显示为@"小明بدأ في متابعتك"。
如果"小明"放后面,第一个字符是阿拉伯语,系统识别为RTL,从右往左排序,依然显示为@"小明بدأ في متابعتك"。
为了适配这种情况,可以在字符串前面加一些不会显示的字符,强制将字符串变为LTR或者RTL。
在字符串前面添加"\u202B"表示RTL,加"\u202A"LTR。为了统一适配刚刚的情况,我们hook了UILabel的setText:方法
BOOL isRTLString(NSString *string) {
if ([string hasPrefix:@"\u202B"] || [string hasPrefix:@"\u202A"]) {
return YES;
}
return NO;
}
NSString *RTLString(NSString *string) {
if (string.length == 0 || isRTLString(string)) {
return string;
}
if (isRTL()) {
string = [@"\u202B" stringByAppendingString:string];
} else {
string = [@"\u202A" stringByAppendingString:string];
}
return string;
}
@implementation UILabel (HTSRTL)
+ (void)load
{
RTLMethodSwizzling(self, @selector(setText:), @selector(rtl_setText:));
}
- (void)rtl_setText:(NSString *)text
{
[self rtl_setText:RTLString(text)];
}
@end
这种方法虽然能适配RTL,但是由于修改了原来字符串,虽然不会显示出来,但是毕竟多加了字符,会改变原来各个字符的range位置,当我们有特殊逻辑要使用各种range的时候,可能会有问题,对于这种特殊的情况,无法做到统一适配,所以只能具体情况具体处理
文章内容部分引用