Add several attributes to a NSMutableAttributedString - swift

I'm trying to add several attributes to a NSMutableAttributedString; i tried this:
let stringNumero: NSString = "\(squadra.cori.count)" //= two-digit number
var stringNumeroMutable = NSMutableAttributedString()
stringNumeroMutable = NSMutableAttributedString(string: stringNumero as! String, attributes: [NSFontAttributeName: UIFont(name: "Noteworthy-Light", size: 9)!,
NSForegroundColorAttributeName: UIColor(red: 110/255.0, green: 183/255.0, blue: 93/255.0, alpha: 1.0)])
cell.numeroCori.attributedText = stringNumeroMutable
now I'd like to add an other attributes and i'm going uso this code:
stringNumeroMutable.addAttribute(NSFontAttributeName, value: UIFont.boldSystemFontOfSize(8), range: NSRange(location: 0, length: 1))
but I'm incurring in a problem:
as you see, the bold effect is attributed only to the first digit and the previous attributed (such as the font) disappear. I think the the second NSFontAttributedName subscribe the previous one, so is there any way to set both font and bold?
Thanks a lot!!!
Edit: as suggested i tried to recall the method like this:
let stringNumero: NSString = "\(squadra.cori.count)" //= two-digit number
var stringNumeroMutable = NSMutableAttributedString()
stringNumeroMutable = NSMutableAttributedString(string: stringNumero as! String, attributes: [NSFontAttributeName: UIFont(name: "Noteworthy-Light", size: 9)!,
NSForegroundColorAttributeName: UIColor(red: 110/255.0, green: 183/255.0, blue: 93/255.0, alpha: 1.0)])
cell.numeroCori.attributedText = stringNumeroMutable
stringNumeroMutable.addAttribute(NSFontAttributeName, value: UIFont.boldSystemFontOfSize(9), range: NSRange(location: 0, length: stringNumeroMutable.length))
cell.numeroCori.atributedText = stringNumeroMutable
but the second call overwrite the first one (so the number loose the attributed font). How to solve this?

I think your problem, that you use method boldSystemFontOfSize
boldSystemFontOfSize:
Returns the font object used for standard interface items that are
rendered in boldface type in the specified size.
See my solution:
#IBOutlet weak var label: UILabel!
var mutableAttributedString: NSMutableAttributedString!
override func viewDidLoad() {
super.viewDidLoad()
let someString = "Hello World"
let attr: [String: AnyObject] = [NSFontAttributeName: UIFont(name: "Helvetica", size: 10)!,
NSForegroundColorAttributeName: UIColor(red: 0.18, green: 0.18, blue: 0.18, alpha: 1.0)]
mutableAttributedString = NSMutableAttributedString(string: someString, attributes: attr)
label.attributedText = mutableAttributedString
}
#IBAction func buttonPressed(sender: UIButton) {
let font = mutableAttributedString.attribute(NSFontAttributeName, atIndex: 1, effectiveRange: nil)!
mutableAttributedString.addAttribute(NSFontAttributeName, value: font.fontWithSize(20) , range: NSMakeRange(0, mutableAttributedString.length))
label.attributedText = mutableAttributedString
}
In buttonPressed function you define current font of mutableAttributedString and then you can change font's size with method fontWithSize.

Related

Change UILabel attributed String color multiple times

I am new to Swift 5 and am doing my first project.
I want to change the color of some texts inside my label, this is what my label looks like:
"You need to pay attention to blue texts, and even more to red texts. Try again so there is no more blue / red texts"
I want to change all the "blue" to blue, and all the "red" to red. Did some research and this is what I found:
extension NSMutableAttributedString {
func setColor(color: UIColor, forText stringValue: String) {
let range: NSRange = self.mutableString.range(of: stringValue, options: .caseInsensitive)
self.addAttribute(NSAttributedString.Key.foregroundColor, value: color, range: range)
}
}
let mainString = popUpLabel.text!
let blueString = "màu xanh" //(which is blue)
let redString = "màu đỏ" //(whish is red)
let attributedString = NSMutableAttributedString.init(string: mainString)
attributedString.setColor(color: UIColor(red: 0.24, green: 0.65, blue: 0.96, alpha: 1.00), forText: blueString)
attributedString.setColor(color: UIColor(red: 0.99, green: 0.20, blue: 0.20, alpha: 1.00), forText: redString)
popUpLabel.attributedText = attributedString
And also tried this:
let mainString = popUpLabel.text!
let blueString = "màu xanh" //(which is blue)
let redString = "màu đỏ" //(which is red)
let attributedString = NSMutableAttributedString.init(string: mainString)
let blueRange = (mainString as NSString).range(of: blueString)
let redRange = (mainString as NSString).range(of: redString)
attributedString.addAttributes([NSAttributedString.Key.foregroundColor : UIColor(red: 0.24, green: 0.65, blue: 0.96, alpha: 1.00)], range: blueRange)
attributedString.addAttributes([NSAttributedString.Key.foregroundColor : UIColor(red: 0.99, green: 0.20, blue: 0.20, alpha: 1.00)], range: redRange)
popUpLabel.attributedText = attributedString
Both works pretty well for the first blue and red, but the second blue and red stayed in black and I don't know why.
range(of: finds only the first occurrence of the search string.
A possible solution is Regular Expression, the pattern searches for both red and blue and changes the color accordingly.
let string = "You need to pay attention to blue texts, and even more to red texts. Try again so there is no more blue / red texts"
let pattern = "(red|blue)"
var attributedString = NSMutableAttributedString(string: string)
let regex = try! NSRegularExpression(pattern: pattern)
let matches = regex.matches(in: string, range: NSRange(string.startIndex..., in: string))
for match in matches {
let range = Range(match.range, in: string)!
let color : UIColor
if string[range] == "red" {
color = UIColor(red: 0.99, green: 0.20, blue: 0.20, alpha: 1.00)
} else {
color = UIColor(red: 0.24, green: 0.65, blue: 0.96, alpha: 1.00)
}
attributedString.addAttribute(.foregroundColor, value: color, range: match.range)
}
You also may use html-text converted to attributed string. Look at this answer.
Example from the answer:
let htmlText = "<medium><b><font color='#2f3744'>IPL Tamil Web Series Episode #3 | யாருடா Swetha ? | Tamil Comedy Web Series | Being Thamizhan</font></b></medium> has been succesfully scheduled on <medium><b><font color='#2f3744'>2018-05-23 08:51 PM</font></b></medium>"
let encodedData = htmlText.data(using: String.Encoding.utf8)!
var attributedString: NSAttributedString
do {
attributedString = try NSAttributedString(data: encodedData,
options:
[NSAttributedString.DocumentReadingOptionKey.documentType: NSAttributedString.DocumentType.html,
NSAttributedString.DocumentReadingOptionKey.characterEncoding: NSNumber(value: String.Encoding.utf8.rawValue)],
documentAttributes: nil)
} catch let error as NSError {
print(error.localizedDescription)
} catch {
print("error")
}

Why are NSAttributedString's attributes influencing other AttributedStrings?

In this case I'm setting a UILabel's attributedText with a combined NSAttributedString with different attributes for each line, and for some reason some AttributedString attributes influence the other AttributedStrings, but I thought their attributed are bound to the range of that particular AttributedString. I'm basically expecting that the NSAttributedString automatically gets its range set up when I append it to the NSMutableAttributedString.
Am I wrong?
In this case the shadows applies to the other appended AttributedStrings, in another case it is the bold font that overrides all fonts to come in the next AttributedStrings. Strange.
Here is some sample code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var textLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
textLabel.numberOfLines = 0
textLabel.lineBreakMode = .byWordWrapping
let myShadow = NSShadow()
myShadow.shadowBlurRadius = 3
myShadow.shadowOffset = CGSize(width: 3, height: 3)
myShadow.shadowColor = UIColor.gray
let attributedText = NSMutableAttributedString(string: "")
let fatString = NSMutableAttributedString(string: "Bold", attributes: [NSAttributedString.Key.foregroundColor : UIColor.green,
NSAttributedString.Key.font : UIFont.boldSystemFont(ofSize: 16),
NSAttributedString.Key.backgroundColor : UIColor.lightGray,
NSAttributedString.Key.shadow : myShadow])
let normalString = NSAttributedString(string: "\n\nNormal", attributes: [NSAttributedString.Key.foregroundColor : UIColor.white,
NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16)])
let italicString = NSAttributedString(string: "\n\nItalic", attributes: [NSAttributedString.Key.foregroundColor : UIColor.yellow,
NSAttributedString.Key.font : UIFont.italicSystemFont(ofSize: 16)])
let otherFontString = NSAttributedString(string: "\n\nBold + Italic", attributes: [NSAttributedString.Key.foregroundColor : UIColor.gray,
NSAttributedString.Key.font : UIFont(descriptor: UIFontDescriptor().withSymbolicTraits([.traitBold, .traitItalic])!, size: 16)])
let normalString2 = NSAttributedString(string: "\n\nUnderlined", attributes: [NSAttributedString.Key.underlineStyle : NSUnderlineStyle.single.rawValue,
NSAttributedString.Key.foregroundColor : UIColor.red,
NSAttributedString.Key.font : UIFont.systemFont(ofSize: 16)])
attributedText.append(fatString)
attributedText.append(normalString)
attributedText.append(italicString)
attributedText.append(otherFontString)
attributedText.append(normalString2)
textLabel.attributedText = attributedText
}
}

How to create random string with multiple formats?

I need to create a random string with format which can convert any string(including parenthesis() to another color on swift: for example:
Hey (Hey) : First part 'Hey' is fine, but I want to change : (Hey) to a different color
same goes if I choose another string
Hi (What's Up) ....
And tried the following
let label = UILabel(frame: CGRect(origin: .zero, size: CGSize(width: 200, height: 50)))
let color = UIColor(white: 0.2, alpha: 1)
let attributedTextCustom = NSMutableAttributedString(string: "(\(String())", attributes: [.font: UIFont(name:"AvenirNext-Medium", size: 16)!, .foregroundColor: color]))
attributedTextCustom.append(NSAttributedString(string: " (\(String())", attributes: [.font: UIFont(name: "AvenirNext-Regular", size: 12)!, .foregroundColor: UIColor.lightGray]))
label.attributedText = attributedTextCustom
Something like this is the behavior I am looking for (just for demostration...):
You can use a regex "\\((.*?)\\)" to find the range of the word between the parentheses and add the color attribute to a NSMutableAttributedString:
let label = UILabel(frame: CGRect(origin: .zero, size: CGSize(width: 200, height: 50)))
let sentence = "Hello (Playground)"
let mutableAttr = NSMutableAttributedString(string: sentence, attributes: [.font: UIFont(name:"AvenirNext-Medium", size: 16)!, .foregroundColor: UIColor.black])
if let range = sentence.range(of: "\\((.*?)\\)", options: .regularExpression) {
let color = UIColor(white: 0.2, alpha: 1)
let attributes: [NSAttributedString.Key: Any] = [.font: UIFont(name:"AvenirNext-Medium", size: 16)!, .foregroundColor: color]
mutableAttr.addAttributes(attributes, range: NSRange(range, in: sentence))
label.attributedText = mutableAttr
}

Trouble Referencing UIButton using NSMutableAttributedString in Instantiated View Controller

As soon as my view controller loads, I am presented with a button (gray background with white font) that displays the text “Sto 1”. This is called in viewWillLayoutSubviews and the title is set using a NSMutableAttributedString. “Sto” is short for store.
For my application, I would like the user to be able to select the Sto 1 button and be able to store a number that is presented on a UILabel. I am able to grab the current number being displayed but I’m unable to update the text inside my Sto 1 button using NSMutableAttributedString. In other words I want to go from the button showing “Sto 1” to displaying some number (e.g., 12).
Thank you all for any help you may be able to provide me. I am still relatively new to Swift and I have been trying to resolve this issue over the past week.
import UIKit
class ViewController: UIViewController {
var fontConstant = CGFloat()
var someRandomNumberDisplayedOnAUILabel = String(12)
#IBOutlet weak var myButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillLayoutSubviews() {
fontConstant = 1
let myString = "Sto 1"
let myAttributes = [NSAttributedString.Key.foregroundColor : UIColor(red: 251/255.0, green: 251/255.0, blue: 251/255.0, alpha: 1.0), NSAttributedString.Key.font : UIFont.systemFont(ofSize: 10 * fontConstant)]
let mutableAttributedString = NSMutableAttributedString(string: myString, attributes: myAttributes)
myButton.setAttributedTitle(mutableAttributedString, for: .normal)
myButton.backgroundColor = UIColor(red: 94/255.0, green: 94/255.0, blue: 94/255.0, alpha: 1.0)
}
#IBAction func myButtonAction(_ sender: Any) {
let myAttributes = [NSAttributedString.Key.foregroundColor : UIColor(red: 0/255.0, green: 0/255.0, blue: 0/255.0, alpha: 1.0), NSAttributedString.Key.font : UIFont.systemFont(ofSize: 10 * fontConstant)]
let mutableAttributedString = NSMutableAttributedString(string: someRandomNumberDisplayedOnAUILabel, attributes: myAttributes)
myButton.setAttributedTitle(mutableAttributedString, for: .normal)
myButton.backgroundColor = UIColor(red: 251/255.0, green: 251/255.0, blue: 251/255.0, alpha: 1.0)
}}
Normally, you would use setTitle(:for:) to change the text on a UIButton. But since you're working with an NSMutableAttributedString you will need the setAttributedTitle(:for:) function. I think this might be what you're looking for:
myButton.setAttributedTitle(myNSMutableAttributedString, for: .normal)
Heads up, though. You might need to call this function for the different control states and not just .normal otherwise you might see different text for an instant as the button is highlighted. Here is a list of the control states.
EDIT:I would try referencing the sender in the IBAction instead of myButton. This might be a quick fix:
#IBAction func myButtonAction(_ sender: Any) {
let myAttributes = [NSAttributedString.Key.foregroundColor : UIColor(red: 0/255.0, green: 0/255.0, blue: 0/255.0, alpha: 1.0), NSAttributedString.Key.font : UIFont.systemFont(ofSize: 10 * fontConstant)]
let mutableAttributedString = NSMutableAttributedString(string: someRandomNumberDisplayedOnAUILabel, attributes: myAttributes)
guard let button = sender as? UIButton else {
print("Error: sender was not a button")
return
}
button.setAttributedTitle(mutableAttributedString, for: .normal)
button.backgroundColor = UIColor(red: 251/255.0, green: 251/255.0, blue: 251/255.0, alpha: 1.0)
}
EDIT #2:If you're losing the reference to your IBOutlet you might be able to work around that by assigning a selector to the button before you lose it. Try this:
override func viewDidLoad() {
super.viewDidLoad()
// Add the action to the button rather than holding on to the IBOutlet
myButton.addTarget(self, action: #selector(RideInProgressViewController.myAction(sender:)), for: .touchUpInside)
}
#objc private func myAction(sender: Any) {
guard let button = sender as? UIButton else {
print("Error: sender was not a button")
return
}
let myAttributes = [NSAttributedString.Key.foregroundColor : UIColor(red: 0/255.0, green: 0/255.0, blue: 0/255.0, alpha: 1.0), NSAttributedString.Key.font : UIFont.systemFont(ofSize: 10 * fontConstant)]
let mutableAttributedString = NSMutableAttributedString(string: someRandomNumberDisplayedOnAUILabel, attributes: myAttributes)
button.setAttributedTitle(mutableAttributedString, for: .normal)
button.backgroundColor = UIColor(red: 251/255.0, green: 251/255.0, blue: 251/255.0, alpha: 1.0)
}

Can't change Paragraph attributes and String Attributes at the same time

I can get either the NSMutableParagraphStyle line spacing working OR the NSMutableAttributedString to be the correct font,color and size, but I can't get them both at the same time.
let style = NSMutableParagraphStyle()
style.lineSpacing = 5
let attributes = [NSParagraphStyleAttributeName : style]
aboutScrollable.attributedText = NSAttributedString(string: aboutScrollable.text, attributes:attributes)
let myString:NSString = aboutScrollable.text as NSString
var myMutableString = NSMutableAttributedString()
myMutableString = NSMutableAttributedString(string: myString as String, attributes: [NSFontAttributeName:UIFont(name: "Geomanist-regular", size: 12.0)!])
myMutableString.addAttribute(NSForegroundColorAttributeName, value: UIColor(red: 30/255, green: 30/255, blue: 30/255, alpha: 1), range: NSRange(location: 0,length: aboutScrollable.text.characters.count))
// set label Attribute
aboutScrollable.attributedText = myMutableString; NSAttributedString(string: aboutScrollable.text, attributes:attributes)