Based on Arash R's excellent answer, here is a Swift 4.2 extension that selects the text direction individually for each paragraph in the UITextView.
It determines the language of the last character in each paragraph. This way if you have a numbered list in an RTL language, the paragraph will be RTL.
Call the function from the text change method, and if you populate the UITextView when you first load the ViewController - from ViewDidLoad.
extension UITextView {
func detectRightToLeft() {
if let text = self.text, !text.isEmpty { // Set text, make sure it is not nil
let cleanFile = text.replacingOccurrences(of: "\r", with: "\n")
var newLineIndices:Array<Int> = []
for (index, char) in cleanFile.enumerated() {
if char == "\n" {
newLineIndices.append(index) // Get location of all newline characters
}
}
newLineIndices.insert(-1, at: 0) // Place position 0 at the beginning of the array
newLineIndices.append(cleanFile.count) // Add the location after last character
let tagschemes = NSArray(objects: NSLinguisticTagScheme.language)
let tagger = NSLinguisticTagger(tagSchemes: tagschemes as! [NSLinguisticTagScheme], options: 0)
tagger.string = cleanFile
for i in 0..<newLineIndices.count-1 {
// Determine direction by the last character of paragraph
var taggerCounter = newLineIndices[i+1]-1
var language = tagger.tag(at: taggerCounter, scheme: NSLinguisticTagScheme.language, tokenRange: nil, sentenceRange: nil)
// Neutral characters should make the tagger look at the character before
while language == nil && taggerCounter >= 1 {
taggerCounter -= 1
language = tagger.tag(at: taggerCounter, scheme: NSLinguisticTagScheme.language, tokenRange: nil, sentenceRange: nil)
}
if String(describing: language).range(of: "he") != nil || String(describing: language).range(of: "ar") != nil || String(describing: language).range(of: "fa") != nil {
self.setBaseWritingDirection(.rightToLeft, for: self.textRange(from: self.position(from: self.beginningOfDocument, offset: newLineIndices[i]+1)!, to: self.position(from: self.beginningOfDocument, offset: newLineIndices[i+1])!)!)
print ("Right to Left Paragraph at character \(newLineIndices[i]+1)")
} else {
self.setBaseWritingDirection(.leftToRight, for: self.textRange(from: self.position(from: self.beginningOfDocument, offset: newLineIndices[i]+1)!, to: self.position(from: self.beginningOfDocument, offset: newLineIndices[i+1])!)!)
print ("Left to Right Paragraph at character \(newLineIndices[i]+1)")
}
}
}
}
}
EDIT: Previous version included an option to choose by the first character of the paragraph. That option caused crashes, so I left it out for now. Instead, the current code includes treatment for neutral characters. Another edit: changed the taggerCounter minimal value to 1, to prevent it from ever becoming negative.