I am trying to create a UIButton
that allows the selected text to be underlined. This is my current code:
func underline() {
if let textRange = selectedRange {
let attributedString = NSMutableAttributedString(attributedString: textView.attributedText)
textView.textStorage.addAttributes([.underlineStyle : NSUnderlineStyle.single.rawValue], range: textRange)
}
}
It is currently underlining but the problem I am having is that I need to check if the current text is underlined already and if it is remove the underline. I can't seem to work this out with the NSMutableAttributedString
.
I am doing this with an Italic UIButton
like so:
func italic() {
if let textRange = selectedRange {
let attributedString = NSAttributedString(attributedString: textView.attributedText)
attributedString.enumerateAttribute(.font, in: textRange, options: []) { (font, range, pointee) in
let newFont: UIFont
if let font = font as? UIFont {
let fontTraits = font.fontDescriptor.symbolicTraits
if fontTraits.contains(.traitItalic) {
newFont = UIFont.systemFont(ofSize: font.pointSize, weight: .regular)
} else {
newFont = UIFont.systemFont(ofSize: font.pointSize).italic()
}
textView.textStorage.addAttributes([.font : newFont], range: textRange)
}
}
}
}
How can I achieve the ability to check if the current text has the underlining attribute for my first function?
Code we have so far:
func isUnderlined(attrText: NSAttributedString) -> Bool {
var contains: ObjCBool = false
attrText.enumerateAttributes(in: NSRange(location: 0, length: attrText.length), options: []) { (dict, range, value) in
if dict.keys.contains(.underlineStyle) {
contains = true
}
}
return contains.boolValue
}
func underline() {
if let textRange = selectedRange {
let attributedString = NSMutableAttributedString(attributedString: textView.attributedText)
switch self.isUnderlined(attrText: attributedString) {
case true:
print("true")
textView.textStorage.removeAttribute(.underlineStyle, range: textRange)
case false:
print("remove")
textView.textStorage.addAttributes([.underlineStyle : NSUnderlineStyle.single.rawValue], range: textRange)
}
}
}
To check if a text is already underlined, you can simply run contains(_:)
on the attributes of the text, i.e.
func isUnderlined(attrText: NSAttributedString) -> Bool {
var contains: ObjCBool = false
attrText.enumerateAttributes(in: NSRange(location: 0, length: attrText.length), options: []) { (dict, range, value) in
if dict.keys.contains(.underlineStyle) {
contains = true
}
}
return contains.boolValue
}
Example:
let attrText1 = NSAttributedString(string: "This is an underlined text.", attributes: [.underlineStyle : NSUnderlineStyle.styleSingle.rawValue])
let attrText2 = NSAttributedString(string: "This is an underlined text.", attributes: [.font : UIFont.systemFontSize])
print(self.isUnderlined(attrText: attrText1)) //true
print(self.isUnderlined(attrText: attrText2)) //false
You can use the above logic in your UITextView
as per your requirement.
To remove the attribute,
1. first of all it must be an NSMutableAttributedString
.
2. Then to remove an attribute, use removeAttribute(_:range:)
method on attributed string.
let attrText1 = NSMutableAttributedString(string: "This is an underlined text.", attributes: [.underlineStyle : NSUnderlineStyle.styleSingle.rawValue])
print(self.isUnderlined(attrText: attrText1)) //true
if self.isUnderlined(attrText: attrText1) {
attrText1.removeAttribute(.underlineStyle, range: NSRange(location: 0, length: attrText1.string.count))
}
print(self.isUnderlined(attrText: attrText1)) //false
Handle textView
on button tap
@IBAction func onTapButton(_ sender: UIButton) {
if let selectedTextRange = self.textView.selectedTextRange {
let location = self.textView.offset(from: textView.beginningOfDocument, to: selectedTextRange.start)
let length = self.textView.offset(from: selectedTextRange.start, to: selectedTextRange.end)
let range = NSRange(location: location, length: length)
self.textView.attributedText.enumerateAttributes(in: range, options: []) { (dict, range, value) in
if dict.keys.contains(.underlineStyle) {
self.textView.textStorage.removeAttribute(.underlineStyle, range: range)
} else {
self.textView.textStorage.addAttributes([.underlineStyle : NSUnderlineStyle.styleSingle.rawValue], range: range)
}
}
}
}