How to get text / String from nth line of UILabel?

前端 未结 10 1649
孤街浪徒
孤街浪徒 2020-11-27 05:27

Is there an easy way to get (or simply display) the text from a given line in a UILabel?

My UILabel is correctly displaying my text and laying it out beautifully bu

10条回答
  •  庸人自扰
    2020-11-27 05:29

    Sorry, my reputation is too low to place a comment. This is a comment to https://stackoverflow.com/a/53783203/2439941 from Philipp Jahoda.

    Your code snippet worked flawless, until we enabled Dynamic Type on the UILabel. When we set the text size to the largest value in the iOS Settings app, it started to miss characters in the last line of the returned array. Or even missing the last line completely with a significant amount of text.

    We managed to resolve this by using a different way to get frame:

    let frameSetter = CTFramesetterCreateWithAttributedString(attStr as CFAttributedString)
    let path = UIBezierPath(rect: CGRect(x: 0, y: 0, width: self.frame.width, height: .greatestFiniteMagnitude))
    let frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, attStr.length), path.cgPath, nil)
    guard let lines = CTFrameGetLines(frame) as? [Any] else { return nil }
    

    Now it works correctly for any Dynamic Type size.

    The complete function is then:

    extension UILabel {
    
        /// creates an array containing one entry for each line of text the label has
        var lines: [String]? {
    
            guard let text = text, let font = font else { return nil }
    
            let attStr = NSMutableAttributedString(string: text)
            attStr.addAttribute(NSAttributedString.Key.font, value: font, range: NSRange(location: 0, length: attStr.length))
    
            let frameSetter = CTFramesetterCreateWithAttributedString(attStr as CFAttributedString)
            let path = UIBezierPath(rect: CGRect(x: 0, y: 0, width: self.frame.width, height: .greatestFiniteMagnitude))
            let frame = CTFramesetterCreateFrame(frameSetter, CFRangeMake(0, attStr.length), path.cgPath, nil)
            guard let lines = CTFrameGetLines(frame) as? [Any] else { return nil }
    
            var linesArray: [String] = []
    
            for line in lines {
                let lineRef = line as! CTLine
                let lineRange = CTLineGetStringRange(lineRef)
                let range = NSRange(location: lineRange.location, length: lineRange.length)
                let lineString = (text as NSString).substring(with: range)
                linesArray.append(lineString)
            }
            return linesArray
        }
    }
    

提交回复
热议问题