How to get the range of the first line in a string?

断了今生、忘了曾经 提交于 2019-12-24 05:52:13

问题


I would like to change the formatting of the first line of text in an NSTextView (give it a different font size and weight to make it look like a headline). Therefore, I need the range of the first line. One way to go is this:

guard let firstLineString = textView.string.components(separatedBy: .newlines).first else {
    return
}
let range = NSRange(location: 0, length: firstLineString.count)

However, I might be working with quite long texts so it appears to be inefficient to first split the entire string into line components when all I need is the first line component. Thus, it seems to make sense to use the firstIndex(where:) method:

let firstNewLineIndex = textView.string.firstIndex { character -> Bool in
    return CharacterSet.newlines.contains(character)
}
// Then: Create an NSRange from 0 up to firstNewLineIndex. 

This doesn't work and I get an error:

Cannot convert value of type '(Unicode.Scalar) -> Bool' to expected argument type 'Character'

because the contains method accepts not a Character but a Unicode.Scalar as a parameter (which doesn't really make sense to me because then it should be called a UnicodeScalarSet and not a CharacterSet, but nevermind...).

My question is:

How can I implement this in an efficient way, without first slicing the whole string?

(It doesn't necessarily have to use the firstIndex(where:) method, but appears to be the way to go.)


回答1:


A String.Index range for the first line in string can be obtained with

let range = string.lineRange(for: ..<string.startIndex)

If you need that as an NSRange then

let nsRange = NSRange(range, in: string)

does the trick.




回答2:


You can use rangeOfCharacter, which returns the Range<String.Index> of the first character from a set in your string:

extension StringProtocol where Index == String.Index {
    var partialRangeOfFirstLine: PartialRangeUpTo<String.Index> {
        return ..<(rangeOfCharacter(from: .newlines)?.lowerBound ?? endIndex)
    }
    var rangeOfFirstLine: Range<Index> {
        return startIndex..<partialRangeOfFirstLine.upperBound
    }
    var firstLine: SubSequence {
        return self[partialRangeOfFirstLine]
    }
}

You can use it like so:

var str = """
    some string 
    with new lines
"""
var attributedString = NSMutableAttributedString(string: str)
let firstLine = NSAttributedString(string: String(str.firstLine))
// change firstLine as you wish

let range = NSRange(str.rangeOfFirstLine, in: str)
attributedString.replaceCharacters(in: range, with: firstLine)


来源:https://stackoverflow.com/questions/54831193/how-to-get-the-range-of-the-first-line-in-a-string

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!