iOS Attributed Text Setting Entire String on Editing End - ios

Swift 3.0. I'm highlighting certain words in a UITextField using attributed text.
let myRegex = "2 highlight"
for match in myRegex.matches(in: textField.text!, options: [], range: NSRange(location: 0, length: textField.text!.utf16.count)) as [NSTextCheckingResult] {
attributedString.addAttribute(NSBackgroundColorAttributeName, value: UIColor.yellow, range: match.range)
}
textField.attributedText = attributedString
For some reason though, once I get over a certain character limit, once I leave the text field, the entire thing gets highlighted. There is no way to unhighlight it once this happens.
http://www.giphy.com/gifs/3o6ZthPnWEvD7v5wT6
I've got nothing in my code that does anything like this, why is the whole string being attributed once I leave the field?

Related

Why does changing the text of a UILabel not reset text attributes on iOS 13?

On iOS 12, if one changes the text of a UILabel it resets the text attributes. On iOS 13 however, text attributes such as color, typeface, letter spacing, et cetera are kept when the text is changed. What has changed?
An example:
label.text = "Hello world"
let attributedString = NSMutableAttributedString(string: label.text ?? " ")
attributedString.addAttributes([.foregroundColor: UIColor.red], range: NSRange(location: 0, length: attributedString.length))
label.attributedText = attributedString
label.text = "What's up world" // Text is red on iOS 13, default black on iOS 12.
Seems like from iOS 13, if you set and attribute to the entire text, it will persist! If you don't apply the attribute on the entire range of the text, it behaves like before.
You have some options to get around it:
Not applying it on the entire range (Happens most of the times):
attributedString.addAttributes([
.foregroundColor: UIColor.red,
.backgroundColor: UIColor.green
], range: NSRange(location: 0, length: 3))
Perform a version check (Maybe with a little extension)
#available(iOS 13.0, *)
extension UILabel {
func setTextWithoutAttributes(_ text: String) {
// Get rid of the holding attributes instance as Asperi mentioned or in another way you like
self.attributedText = nil
// Set the text
self.text = text
}
}
You did not reset attributedText, but documentation says - if set, the label ignores the properties above (see below for UILabel.h interface, in obj-c it is more correctly visible):
#property(null_resettable, nonatomic,strong) UIColor *textColor UI_APPEARANCE_SELECTOR; // default is labelColor
...
// the underlying attributed string drawn by the label, if set, the label ignores the properties above.
#property(nullable, nonatomic,copy) NSAttributedString *attributedText API_AVAILABLE(ios(6.0)); // default is nil
so behaves as specified (before it might be a bug, that now is fixed)
The solution of your case should be
label.attributedText = attributedString
...
label.attributedText = nil // << reset to default !!
label.text = "What's up world"

Create custom NSAttributedString.Key

I'm trying to build a simple note app. At the moment, I'm focusing on the possibility to set the text with different text styles (e.g. body, headline, bold, italic, etc.). I used a NSAttributedString to set the different text styles. Now, I'd like to detect which style has been applied to the selected text.
I thought a good way to do it would have been to create a custom NSAttributedString.Key, so that I can assign it when setting the attributes (e.g. .textStyle: "headline", and read it when I need to detect it. I tried implementing it as an extension of NSAttributedString.Key but without success. What would be the correct way to do it?
Is there a better alternative?
You can simply create a TextStyle enumeration and set your cases "body, headline, bold, italic, etc" (You can assign any value to them if needed). Then you just need to create a new NSAttributedString key:
enum TextStyle {
case body, headline, bold, italic
}
extension NSAttributedString.Key {
static let textStyle: NSAttributedString.Key = .init("textStyle")
}
Playground Testing
let attributedString = NSMutableAttributedString(string: "Hello Playground")
attributedString.setAttributes([.textStyle: TextStyle.headline], range: NSRange(location: 0, length: 5))
attributedString.enumerateAttributes(in: NSRange(location: 0, length: attributedString.length), options: []) { attributes, range, stop in
print(attributes, range, stop )
print(attributedString.attributedSubstring(from: range))
}

Why my label is truncating even though the number of lines is 0 and no height constraint?

I have a label and I added a attributed string to it. The string is,
let nameText = "My name is Shreesha and Im an iOS developer. My name is Shreesha and Im an iOS developer."`My name is Shreesha and Im an iOS developer. My name is Shreesha and Im an iOS developer.`
In this text I'm trying to add a * in the beginning of the text so I used an attributed string and the code looks like this,
func attributedTextForFeeApplies() -> NSAttributedString {
let attributedText = NSMutableAttributedString(string: "* " + nameText)
attributedText.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor.blue, range: NSMakeRange(0, attributedText.length))
attributedText.addAttribute(NSAttributedStringKey.font, value: UIFont.systemFont(ofSize: 10), range: NSMakeRange(0, attributedText.length))
let superScriptString = "* "
attributedText.addAttribute(NSAttributedStringKey.baselineOffset, value: 2, range: NSMakeRange(0, superScriptString.characters.count))
attributedText.addAttribute(NSAttributedStringKey.font, value: UIFont.systemFont(ofSize: 9), range: NSMakeRange(0, superScriptString.characters.count))
attributedText.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor.blue, range: NSMakeRange(0, superScriptString.characters.count))
let superscriptAttributedString = attributedText
let paragraph = NSMutableParagraphStyle()
paragraph.lineBreakMode = .byTruncatingTail
superscriptAttributedString.addAttribute(NSAttributedStringKey.paragraphStyle, value: paragraph, range: NSMakeRange(0, superscriptAttributedString.length))
return superscriptAttributedString
}
I gave the constraints to the Label like this,
Even though I set number of lines to 0 and no height constraint the label is truncating like this,
But when I don't use this line of code attributedText.addAttribute(NSAttributedStringKey.baselineOffset, value: 2, range: NSMakeRange(0, superScriptString.characters.count)) it is working fine like this,
And also if add * in the middle (without removing attributedText.addAttribute(NSAttributedStringKey.baselineOffset, value: 2, range: NSMakeRange(0, superScriptString.characters.count))) it works fine but just doesn't work if I use it in the beginning of the text,
Screen shot:
And it also works if I increase the font size.
I think there is an issue with NSAttributedString and if not I want to know what is the issue. Could someone please help me.
Your problem is as soon as you assign attributed string you have to re calculate the height. but there is a quick fix which you don't need to calculate it your self. give the label a force line break at the end of your label in which case it has to re calculate the height automatically.
// see the \n at the end of your string that will cause the label to recalculate it's height.
let nameText = "My name is Shreesha and Im an iOS developer. My name is Shreesha and Im an iOS developer."`My name is Shreesha and Im an iOS developer. My name is Shreesha and Im an iOS developer. \n"

Programatically change some words' color in an UITextView

So I have an UITextView with some text and I want everytime the user inputs, for example, the word "while", after it has been written it should change into purple. Also, I have another UITextView just to display content, no user interaction enabled, and I want that everytime the view appears, all the "while" words to also be purple. How do I do this? Can you help me, please? Thank you!
Here's what I've tried so far:
let initialText = textView.text!
let string_to_color = "while"
let range = (initialText as NSString).range(of: string_to_color)
let attribute = NSMutableAttributedString.init(string: initialText)
attribute.addAttribute(NSForegroundColorAttributeName, value: UIColor.purple, range: range)
textView.attributedText = attribute
But it colors only the first word. This is for the user interaction disabled text field. I haven't figured out how to do it for the text field that contains the words that the user inputs.
You are not getting the range correctly, here is an example of using attributed strings in Swift 3.0:
// get initial text as a String type (you will get this from your textview)
let initialText = "Swift Attributed String"
// create an attribute for the text color, I chose blue color
let myAttribute = [ NSForegroundColorAttributeName: UIColor.blue ]
// create the attributed string and add the blue color attribute
let myString = NSMutableAttributedString(string: initialText, attributes: myAttribute )
// range starting at location 6 with a lenth of 10: "Attributed"
var myRange = NSRange(location: 6, length: 10)
// OR get range of specific string in initialText
let newRange = (initialText as NSString).range(of: "Attributed")
// change the range of the word "Attributed" to have red text color
myString.addAttribute(NSForegroundColorAttributeName, value: UIColor.red, range: newRange)
// create another attribute for highlighting
let anotherAttribute = [ NSBackgroundColorAttributeName: UIColor.yellow ]
// set the range of the "Attributed" part of the string to a yellow highlight
myString.addAttributes(anotherAttribute, range: newRange)
You can use this strategy to do whatever formatting you need to do with your string. Just make sure that the range that you are getting is correct.

I want my text strike through extend on whitespace

I use NSAttributeString to set strike through on my text, I want it extend on whitespace.
I already set the correct range, but the strike through only cover the text characters. The strike through is ignore the whitespace unless I add some non-empty text before and after it.
How can I make the strike through extend on whitespace without extra text?
I have come across the same problem today, and neither existing answers gave me a solution I felt was as straightforward as it should be. After some research, I found a more concise solution using Unicode characters.
Padding each side of your text with one or more non-breaking Unicode space characters (U+00A0) extends the line as desired:
let attributedText = NSAttributedString(
string: "\u{00A0}\(someText)\u{00A0}",
attributes: [.strikethroughStyle: NSUnderlineStyle.single.rawValue]
)
I haven't found any solution but with your last option means adding first and last character as . with space you can try one thing. Either set the NSForegroundColorAttributeName of that first and last character to your background color of label or set the NSFontAttributeName with UIFont.systemFont(ofSize: 0.1). So it will be goes like this. You haven't specify your answer language so i'm posting answer in latest Swift 3.
let attributedText = NSMutableAttributedString(string: self.lbl.text!)
attributedText.addAttributes([NSStrikethroughStyleAttributeName: 2], range: NSMakeRange(0, self.lbl.text!.characters.count))
self.lbl.attributedText = attributedText
Before using NSForegroundColorAttributeName & NSFontAttributeName
Now you can use either NSForegroundColorAttributeName or NSFontAttributeName to hide first and last dot(.) character.
let attributedText = NSMutableAttributedString(string: self.lbl.text!)
attributedText.addAttributes([NSStrikethroughStyleAttributeName: 2], range: NSMakeRange(0, self.lbl.text!.characters.count))
attributedText.addAttributes([NSForegroundColorAttributeName: UIColor.white], range: NSMakeRange(0, 1))
attributedText.addAttributes([NSForegroundColorAttributeName: UIColor.white], range: NSMakeRange(self.lbl.text!.characters.count - 1, 1))
//Or either Set NSFontAttributeName instead of NSForegroundColorAttributeName
//attributedText.addAttributes([NSFontAttributeName: UIFont.systemFont(ofSize: 0.1)], range: NSMakeRange(0, 1))
//attributedText.addAttributes([NSFontAttributeName: UIFont.systemFont(ofSize: 0.1)], range: NSMakeRange(self.lbl.text!.characters.count - 1, 1))
self.lbl.attributedText = attributedText
After using NSForegroundColorAttributeName or NSFontAttributeName
For people coming to this question who only want a horizontal line through whitespace, this is the way to do it (Swift 4):
let line = String(repeating: "\u{23AF}", count: 10)

Resources