Swift 基于内容检测文本方向

Swift 基于内容检测文本方向,swift,Swift,我想知道是否可以根据内容动态设置UITextView的文本方向 默认行为如下:如果用LTR语言开始一行,该行将是LTR,但如果用RTL语言开始下一行,该行的方向将更改为RTL 我想要的是基于第一个设置整个段落的方向 可能吗?我自己也有这个问题。通过一点搜索,我们为UITextView做了这个扩展,它可以检测第一个字母的语言,如果需要的话,可以使用RTL。 您需要在设置文本后调用该函数,因此您可能希望在UITextViewDelegate text change“方法中调用它 当然,这是混乱和不

我想知道是否可以根据内容动态设置
UITextView
的文本方向

默认行为如下:如果用LTR语言开始一行,该行将是LTR,但如果用RTL语言开始下一行,该行的方向将更改为RTL

我想要的是基于第一个设置整个段落的方向


可能吗?

我自己也有这个问题。通过一点搜索,我们为UITextView做了这个扩展,它可以检测第一个字母的语言,如果需要的话,可以使用RTL。 您需要在设置文本后调用该函数,因此您可能希望在UITextViewDelegate text change“方法中调用它

当然,这是混乱和不完美的。但它为我工作。你可以得到的想法

Swift 3:

extension UITextView {
    func detectRightToLeft() {
        if let text = self.text, !text.isEmpty {
            let tagschemes = NSArray(objects: NSLinguisticTagSchemeLanguage)
            let tagger = NSLinguisticTagger(tagSchemes: tagschemes as! [String], options: 0)
            tagger.string = text

            let language = tagger.tag(at: 0, scheme: NSLinguisticTagSchemeLanguage, tokenRange: nil, sentenceRange: nil)
            if language?.range(of: "he") != nil || language?.range(of: "ar") != nil || language?.range(of: "fa") != nil {
                self.text = text.replacingOccurrences(of: "\n", with: "\n")
                self.textAlignment = .right
                self.makeTextWritingDirectionRightToLeft(nil)
            }else{
                self.textAlignment = .left
                self.makeTextWritingDirectionLeftToRight(nil)
            }
        }
    }
}

基于Arash R的优秀答案,这里有一个Swift 4.2扩展,它为UITextView中的每个段落分别选择文本方向

它确定每个段落中最后一个字符的语言。这样,如果您有一个RTL语言的编号列表,则该段落将是RTL

从text change方法调用函数,如果在首次加载ViewController时填充UITextView-从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)")
                }
            }
        }
    }
}
扩展UITextView{ func detectRightToLeft(){ 如果let text=self.text,!text.isEmpty{//Set text,请确保它不是nil 让cleanFile=text.replacingOccurrences(of:“\r”,with:“\n”) var newlineindex:Array=[] 对于cleanFile.enumerated()中的(索引,字符){ 如果char==“\n”{ newlineindex.append(index)//获取所有换行符的位置 } } newlineindex.insert(-1,at:0)//将位置0放置在数组的开头 newlineindex.append(cleanFile.count)//在最后一个字符后添加位置 让tagschemes=NSArray(对象:nsLanguagisticTagScheme.language) 设tagger=NSLinguisticTagger(tagSchemes:tagSchemes as![NSLinguisticTagScheme],选项:0) tagger.string=cleanFile 对于0中的i..=1{ taggerCounter-=1 language=tagger.tag(at:taggerCounter,scheme:nslinguisticstagscheme.language,tokenRange:nil,sentenceRange:nil) } 如果字符串(描述:语言)。范围(of:“he”)!=nil |字符串(描述:语言)。范围(of:“ar”)!=nil |字符串(描述:语言)。范围(of:“fa”)!=nil{ self.setBaseWritingDirection(.right-toleft,for:self.textRange(from:self.beginingofdocument,offset:newlineindex[i]+1)!,to:self.position(from:self.beginingofdocument,offset:newlineindex[i+1])!) 打印(“字符\(换行索引[i]+1]处从右向左的段落”) }否则{ self.setBaseWritingDirection(.leftToRight,for:self.textRange(from:self.beginingofdocument,offset:newlineindex[i]+1)!,to:self.position(from:self.beginingofdocument,offset:newlineindex[i+1])!) 打印(“字符\(换行索引[i]+1]处从左到右的段落”) } } } } }
编辑:以前的版本包含一个选项,可根据段落的第一个字符进行选择。该选项导致崩溃,因此我暂时将其忽略。相反,当前代码包含中性字符的处理。另一个编辑:将taggerCounter最小值更改为1,以防止其变为负值。

Base关于Ron的答案,这里是字符串的一个更一般的扩展

extension String {

    var isRTL: Bool {
        let cleanFile = self.replacingOccurrences(of: "\r", with: "\n")
        var newLineIndices: Array<Int> = []

        for (index, char) in cleanFile.enumerated() {
            if char == "\n" {
                newLineIndices.append(index)
            }
        }

        newLineIndices.insert(-1, at: 0)
        newLineIndices.append(cleanFile.count)

        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 {
            let language = tagger.tag(at: newLineIndices[i + 1] - 1, 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 {
                return true
            } else {
                return false
            }
        }

        return false
    }

    var isLTR: Bool {
        let cleanFile = self.replacingOccurrences(of: "\r", with: "\n")
        var newLineIndices: Array<Int> = []

        for (index, char) in cleanFile.enumerated() {
            if char == "\n" {
                newLineIndices.append(index)
            }
        }

        newLineIndices.insert(-1, at: 0)
        newLineIndices.append(cleanFile.count)

        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 {
            let language = tagger.tag(at: newLineIndices[i + 1] - 1, 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 {
                return false
            } else {
                return true
            }
        }

        return false
    }

}
扩展字符串{
var isRTL:Bool{
让cleanFile=self.replacingOccurrences(of:“\r”,with:“\n”)
var newlineindex:Array=[]
对于cleanFile.enumerated()中的(索引,字符){
如果char==“\n”{
newlineindex.append(索引)
}
}
newlineindex.insert(-1,at:0)
newlineindex.append(cleanFile.count)
让tagschemes=NSArray(对象:nsLanguagisticTagScheme.language)
设tagger=NSLinguisticTagger(tagSchemes:tagSchemes as![NSLinguisticTagScheme],选项:0)
tagger.string=cleanFile

对于0中的i..请看这里:我想它会有帮助。并导致崩溃sometimes@Maysam你能提供一些关于崩溃情况的更多信息吗?此代码在我的应用程序中运行良好。请尝试使用emptytext@Maysam根本不应该发生这种情况。
!text.isEmpty
就是为了这个。
extension String {

    var isRTL: Bool {
        let cleanFile = self.replacingOccurrences(of: "\r", with: "\n")
        var newLineIndices: Array<Int> = []

        for (index, char) in cleanFile.enumerated() {
            if char == "\n" {
                newLineIndices.append(index)
            }
        }

        newLineIndices.insert(-1, at: 0)
        newLineIndices.append(cleanFile.count)

        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 {
            let language = tagger.tag(at: newLineIndices[i + 1] - 1, 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 {
                return true
            } else {
                return false
            }
        }

        return false
    }

    var isLTR: Bool {
        let cleanFile = self.replacingOccurrences(of: "\r", with: "\n")
        var newLineIndices: Array<Int> = []

        for (index, char) in cleanFile.enumerated() {
            if char == "\n" {
                newLineIndices.append(index)
            }
        }

        newLineIndices.insert(-1, at: 0)
        newLineIndices.append(cleanFile.count)

        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 {
            let language = tagger.tag(at: newLineIndices[i + 1] - 1, 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 {
                return false
            } else {
                return true
            }
        }

        return false
    }

}