0

I am struggling to determine if some selected text in a UITextView is underlined. I can quite easily check for bold, italics etc with the following code:

let isItalic = textView.font!.fontDescriptor.symbolicTraits.contains(.traitItalic)

However, I can't figure out how to check for underline?

Chris
  • 247
  • 1
  • 4
  • 17
  • What if the first hald is underlined, but not the second one? Do you want "true" or "false"? Is it underlined? – Larme Aug 25 '20 at 16:51

3 Answers3

1

I have just created a sample project and I think you could do something like the following:

class ViewController: UIViewController {

    @IBOutlet weak var textView: UITextView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let attrText1 = NSMutableAttributedString(string: "TestTest", attributes: [.foregroundColor : UIColor.systemTeal, .underlineStyle: NSUnderlineStyle.single.rawValue])
        
        let attrText2 = NSAttributedString(string: " - not underlined", attributes: [.foregroundColor : UIColor.red])
        
        attrText1.append(attrText2)
        
        textView.attributedText = attrText1
    }
    
    func isTextUnderlined(attrText: NSAttributedString?, in range: NSRange) -> Bool {
        guard let attrText = attrText else { return false }
        var isUnderlined = false
        
        attrText.enumerateAttributes(in: range, options: []) { (dict, range, value) in
            if dict.keys.contains(.underlineStyle) {
                isUnderlined = true
            }
        }
        
        return isUnderlined
    }
    
    
    @IBAction func checkButtonDidTap(_ sender: UIButton) {
        print(isTextUnderlined(attrText: textView.attributedText, in: textView.selectedRange))
    }
    
}

Create an extension to get the selectedRange as NSRange:

extension UITextInput {
    var selectedRange: NSRange? {
        guard let range = selectedTextRange else { return nil }
        let location = offset(from: beginningOfDocument, to: range.start)
        let length = offset(from: range.start, to: range.end)
        return NSRange(location: location, length: length)
    }
}
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
finebel
  • 2,227
  • 1
  • 9
  • 20
  • 1
    "selected text in a UITextView" it is not the whole content. Btw Swift is a type inferred language `NSAttributedString.Key` is redundant. – Leo Dabus Aug 25 '20 at 14:05
  • Thank you @finebel - this is working great and returns perfectly. I simply call this: let range = textView.selectedRange if (isTextUnderlined(attrText: textView.attributedText, in: range)) == true { print("Text is underlined") //Update UI Appropriately } else { print("Text is not underlined") //Update UI Appropriately } And voila, I can now update my UI based on the results. I added the range as a variable as I need to do some other work with range based on the return value. – Chris Aug 25 '20 at 18:05
1

I believe underline is not part of the font traits, it must rather be an attribute to the text. You might find the answer to this question useful. I hope it helps you! Enumerate over a Mutable Attributed String (Underline Button)

petrina
  • 36
  • 4
0
 func checkForUnderline(){
    let allWords = self.testView.text.split(separator: " ")
    for word in allWords {
       let result =  self.isLabelFontUnderlined(textView: self.testView, 
subString: word as NSString)
       if(result == true){
            print(word+" is underlined")
        }else{
            print(word+" is not underlined")
        }
    }
}

    func isLabelFontUnderlined (textView: UITextView, subString: 
NSString) -> Bool {
    let nsRange = NSString(string: textView.text).range(of: subString as 
String, options: String.CompareOptions.caseInsensitive)
      if nsRange.location != NSNotFound {
          return self.isLabelFontUnderlined(textView: textView, 
forRange: nsRange)
      }
      return false
}
    
func isLabelFontUnderlined (textView: UITextView, forRange: NSRange) -> 
Bool{
    let attributedText = testView.attributedText!
    var isRangeUnderline = false
    attributedText.enumerateAttributes(in: forRange, 
options:.longestEffectiveRangeNotRequired) { (dict, range, value) in
        if dict.keys.contains(.underlineStyle) {
            if (dict[.underlineStyle] as! Int == 1){
               isRangeUnderline = true
            } else{
                isRangeUnderline = false
            }
        }else{
            isRangeUnderline = false
        }
    }
    return isRangeUnderline
}
soham paul
  • 11
  • 2