Search code examples
swiftxcodeuilabelarabic

UILabel lineBreakMode set to .ByTruncatingTail causes RTL arabic text to appear incorrectly


I have a UILabel with RTL Arabic text inside. Everything appears correctly as long as I don't change the lineBreakMode. I need it to clip instead of byTruncatingTail in order to avoid showing the ellipses character as I'm trying to show a gradient mask on the left edge.

I've tried changing contentMode, alignment etc but nothing helps. The right side of the label seems to start text from the middle somewhere, instead of from the start (i.e. the right most character in the text).

This is what I see with lineBreakMode = .byClipping

enter image description here

And this is what I see when I remove the lineBreakMode

enter image description here

Here's the code

let arabicLabel = UILabel(frame: .zero)
arabicLabel.semanticContentAttribute = .forceRightToLeft
arabicLabel.numberOfLines = 1
//arabicLabel.lineBreakMode = .byClipping
arabicLabel.text = "عِنْدَمَا1 عِنْدَمَا2 عِنْدَمَا3 عِنْدَمَا4 عِنْدَمَا5 عِنْدَمَا6 قَدِمْتُ عَلَى (صَاحِبِي) عِنْدَمَا7 عِنْدَمَا8 عِنْدَمَا9 عِنْدَمَا10 عِنْدَمَا عِنْدَمَا قَدِمْتُ عَلَى (صَاحِبِي)"
arabicLabel.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(arabicLabel)

Solution

  • After wasting enough time on this, I ended doing it the ugly way, by overriding drawText and adding an arbitrary amount of padding to effectively hide the ellipses character and handling it for both RTL and LTR. I could have based this on the semanticContentAttributes instead, but in my case the label will only ever be right aligned when it contains RTL text. I've also had to override text to automatically detect the dominant language set, and alter the alignment automatically.

      public override func drawText(in rect: CGRect) {
        let extraWidth = fadeLength + 50
        var newRect = rect
    
        if self.textAlignment == .right {
          newRect.origin.x -= extraWidth
          newRect.size.width += extraWidth
        } else {
          newRect.size.width += extraWidth
        }
    
        super.drawText(in: newRect)
      }