How to change UIButton attributed text colour programatically? - ios

I have a subclass (KeyButton) of UIButton where I am applying certain styles for the button. The following code adds the attributed text for buttons in the ViewController.
func superScriptText(text: String, button: KeyButton, fontSize: Int) {
let font:UIFont? = UIFont.systemFont(ofSize: 22, weight: UIFont.Weight.light)
let fontSuper:UIFont? = UIFont(name: "Helvetica", size:CGFloat(fontSize))
let attString:NSMutableAttributedString = NSMutableAttributedString(string: text, attributes: [.font:font!])
attString.setAttributes([.font:fontSuper!,.baselineOffset:15], range: NSRange(location:1,length:1))
button.setAttributedTitle(attString, for: .normal)
}
How can I change the color of attributed text for the button in the class?

Just change:
let attString:NSMutableAttributedString =
NSMutableAttributedString(string: text, attributes: [.font:font!])
to:
let attString:NSMutableAttributedString =
NSMutableAttributedString(string: text, attributes: [.font:font!, .foregroundColor: UIColor.red])
NSAttributedStringKey.foregroundColor is used for the text color, see more options in docs.

You have to add the .foregroundColor key with a UIColor object as the value to the NSAttributedStrings attributes dictionary.
Example (assuming you have added a custom button in storyboard):
class CustomButton: UIButton {
override func awakeFromNib() {
super.awakeFromNib()
let text = "CustomButton"
let font = UIFont.systemFont(ofSize: UIFont.buttonFontSize)
let textColor = UIColor.orange
let attributes: [NSAttributedStringKey: Any] = [
.font: font,
.foregroundColor: textColor
]
let attributedText = NSAttributedString(string: text, attributes: attributes)
self.setAttributedTitle(attributedText, for: .normal)
}
}

I couldn't do this through UIButton subclass. I created the subclass of NSAttributtedText and add the following method:
var textColor: UIColor?
func setSuperScript(text: String, button: KeyButton, fontSize: Int) {
let font:UIFont? = UIFont.systemFont(ofSize: 22, weight: UIFont.Weight.light)
let fontSuper:UIFont? = UIFont(name: "Helvetica", size:CGFloat(fontSize))
let attString:NSMutableAttributedString = NSMutableAttributedString(string: text, attributes: [.font:font!, .foregroundColor: textColor!])
attString.setAttributes([.font:fontSuper!,.baselineOffset:15, .foregroundColor: textColor!,], range: NSRange(location:1,length:1))
button.setAttributedTitle(attString, for: .normal)
}
I am setting the color based on the logic I have and then set the attributed string color accordingly.

Related

NSAttributedText alignment right and left doesn't work for the same object

I have a simple extension for NSAttributedString:
convenience init(
string: String,
strikeThrough: Bool = false,
color: UIColor? = nil,
font: UIFont? = nil,
alignment: NSTextAlignment? = nil
) {
var attributes = [NSAttributedString.Key: Any]()
if strikeThrough {
attributes[.strikethroughStyle] = NSUnderlineStyle.single.rawValue
}
if let color = color {
attributes[.foregroundColor] = color
}
if let font = font {
attributes[.font] = font
}
if let alignment = alignment {
let style = NSMutableParagraphStyle()
style.alignment = alignment
attributes[.paragraphStyle] = style
}
self.init(string: string, attributes: attributes)
}
And this is how I use it in code:
let baseString = "Collectives™ helps you find trusted answers faster, engage with product experts, and share knowledge around the technologies you use most."
let baseColor: UIColor.mineShaft
let baseFont = UIFont.poppinsRegular.withSize(16)
let attributedString = NSMutableAttributedString(string: baseString, color: baseColor, font: baseFont)
attributedString.append(NSAttributedString(string: "\n"))
let color = baseColor.withAlphaComponent(0.5)
let font = baseFont.withSize(12)
let originalString = NSAttributedString(string: baseString, color: color, font: font, alignment: .right)
attributedString.append(originalString)
And the results is the following:
Everything is left aligned. Why? The second part should be right aligned.
I set two style for attributes and the result is ok, try to set like this:
let style = NSMutableParagraphStyle()
style.alignment = .left
let style2 = NSMutableParagraphStyle()
style2.alignment = .right
let attributedString = NSMutableAttributedString(string: "Collectives™ helps you find trusted answers faster, engage with product experts, and share knowledge around the technologies you use most.", attributes: [.paragraphStyle: style, .foregroundColor: UIColor.black, .font: UIFont.systemFont(ofSize: 14, weight: .bold)])
attributedString.append(NSAttributedString(string: "\nCollectives™ helps you find trusted answers faster, engage with product experts, and share knowledge around the technologies you use most.", attributes: [.paragraphStyle: style2, .foregroundColor: UIColor.gray, .font: UIFont.systemFont(ofSize: 12, weight: .semibold)]))
yourTextContainer.attributedText = attributedString
The result:

UIButton Change Title Color Partially

enter image description here
I want to make button like this picture.
In order to implement it like a picture, the color of the button title needs to be partially changed, but I don't know what to do.
Here is my code.
private let signUpButton = UIButton().then {
$0.setTitleColor(.black, for: .normal)
$0.titleLabel?.font = .boldSystemFont(ofSize: 12)
}
Use NSMutableAttributedString for this purpose
private let signUpButton = UIButton().then {
// Creating gray text part
let grayButtonText = NSAttributedString(string: "FirstPart", attributes: [.font: UIFont.systemFont(ofSize: 12), .foregroundColor: UIColor.gray ])
// Creating black text part
let blackButtonText = NSAttributedString(string: "SecondPart", attributes: [.font: UIFont.systemFont(ofSize: 12), .foregroundColor: UIColor.black ])
// Merging two parts together
let buttonTitle = NSMutableAttributedString(attributedString: grayButtonText)
buttonTitle.append(blackButtonText)
// Set it as a title for ".normal" state
$0.setAttributedTitle(buttonTitle, for: .normal)
}

Make half of the TextView's text color Different than other 50% of the text SWIFT

I've got a large text in my UITextView and I want to make the 50% of the text's color red and the other 50% green . I've added NSMutableAttributedString in the TextView but it works's for the full range of the text . How to divide the textView's text into two sections and color them into red and green ?
let strNumber: NSString = self.text as NSString // TextView Text
let range = (strNumber).range(of: strNumber as String)
let attribute = NSMutableAttributedString.init(string: strNumber as String)
attribute.addAttributes([NSAttributedString.Key.font : UIFont.systemFont(ofSize: 14) , NSAttributedString.Key.foregroundColor : UIColor.red], range: range)
self.attributedText = attribute
It seems you have an extension to UITextView. The following extension function will make the existing attributed text of a text view be half red and half green. All other existing attributes, if any, will remain.
extension UITextView {
func makeHalfRedGreen() {
if let text = self.text {
let half = text.count / 2
let halfIndex = text.index(text.startIndex, offsetBy: half)
let firstRange = NSRange(..<halfIndex, in: text)
let secondRange = NSRange(halfIndex..., in: text)
let attrTxt = NSMutableAttributedString(attributedString: attributedText)
attrTxt.addAttribute(.foregroundColor, value: UIColor.red, range: firstRange)
attrTxt.addAttribute(.foregroundColor, value: UIColor.green, range: secondRange)
attributedText = attrTxt
}
}
}
Try to use function like below
text_lbl.attributedText = self.decorateText(txt1: "Red Color", txt2: “Blue Color”)
func decorateText(txt1:String, txt2:String)->NSAttributedString{
let textAttributesOne = [NSAttributedStringKey.foregroundColor: UIColor.red, NSAttributedStringKey.font: UIFont(name: "Poppins-Regular", size: 12.0)!] as [NSAttributedStringKey : Any]
let textAttributesTwo = [NSAttributedStringKey.foregroundColor: UIColor.blue, NSAttributedStringKey.font: UIFont(name: "Poppins-SemiBold", size: 14.0)!] as [NSAttributedStringKey : Any]
let textPartOne = NSMutableAttributedString(string: txt1, attributes: textAttributesOne)
let textPartTwo = NSMutableAttributedString(string: txt2, attributes: textAttributesTwo)
let textCombination = NSMutableAttributedString()
textCombination.append(textPartOne)
textCombination.append(textPartTwo)
return textCombination
}

textfield placeholder centered and also change color

I want to make my UITextField placeholder to center aligned and also change the placeholderColor.
I have already make text center aligned using this ↓
extension String {
func attributedString(aligment: NSTextAlignment) -> NSAttributedString {
return NSAttributedString(text: self, aligment: aligment)
}
}
extension NSAttributedString {
convenience init(text: String, aligment: NSTextAlignment) {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = aligment
self.init(string: text, attributes: [NSAttributedStringKey.paragraphStyle: paragraphStyle])
}
}
and call above extension method like below
textField.attributedPlaceholder = NSAttributedString(text: text, aligment: .left)
But I am not able to change the color of placeholder. How should I change my placeholder color? Any help is appreciated.
Try using modified extension like below..
extension NSAttributedString
{
convenience init(text: String, aligment: NSTextAlignment, color:UIColor) {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = aligment
self.init(string: text, attributes: [NSAttributedStringKey.paragraphStyle: paragraphStyle, NSAttributedStringKey.foregroundColor:color])
}
}
Usage
textField.attributedPlaceholder = NSAttributedString(text: text, aligment: .left, color:UIColor.red)
Simply try this code
Swift 3.0
myTextField.textAlignment = .center
myTextField.attributedPlaceholder = NSAttributedString(string: "PlaceHolder", attributes: [NSForegroundColorAttributeName: UIColor.green])

Swift - UIButton with two lines of text

I was wondering if it is possible to create a UIButton with two lines of text. I need each line to have a different font size. The first line will be 17 point and the second will be 11 point. I've tried messing with putting two labels inside of a UIButton, but I can't get them to stay inside the bounds of the button.
I'm attempting to do all of this in the ui builder, and not programmatically.
Thanks
There are two questions.
I was wondering if it is possible to create a UIButton with two lines
of text
This is possible through using the storyboard or programmatically.
Storyboard:
Change the 'Line Break Mode' to Character Wrap or Word Wrap and use Alt/Option + Enter key to enter a new line in the UIButton's Title field.
Programmatically:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
btnTwoLine?.titleLabel?.lineBreakMode = NSLineBreakMode.ByWordWrapping;
}
I need each line to have a different font size
1
The worst case is, you can use a custom UIButton class and add two labels within it.
The better way is, make use of NSMutableAttributedString. Note that,this can be achieved through only programmatically.
Swift 5:
#IBOutlet weak var btnTwoLine: UIButton?
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
//applying the line break mode
textResponseButton?.titleLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping;
let buttonText: NSString = "hello\nthere"
//getting the range to separate the button title strings
let newlineRange: NSRange = buttonText.range(of: "\n")
//getting both substrings
var substring1 = ""
var substring2 = ""
if(newlineRange.location != NSNotFound) {
substring1 = buttonText.substring(to: newlineRange.location)
substring2 = buttonText.substring(from: newlineRange.location)
}
//assigning diffrent fonts to both substrings
let font1: UIFont = UIFont(name: "Arial", size: 17.0)!
let attributes1 = [NSMutableAttributedString.Key.font: font1]
let attrString1 = NSMutableAttributedString(string: substring1, attributes: attributes1)
let font2: UIFont = UIFont(name: "Arial", size: 11.0)!
let attributes2 = [NSMutableAttributedString.Key.font: font2]
let attrString2 = NSMutableAttributedString(string: substring2, attributes: attributes2)
//appending both attributed strings
attrString1.append(attrString2)
//assigning the resultant attributed strings to the button
textResponseButton?.setAttributedTitle(attrString1, for: [])
}
Older Swift
#IBOutlet weak var btnTwoLine: UIButton?
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
//applying the line break mode
btnTwoLine?.titleLabel?.lineBreakMode = NSLineBreakMode.ByWordWrapping;
var buttonText: NSString = "hello\nthere"
//getting the range to separate the button title strings
var newlineRange: NSRange = buttonText.rangeOfString("\n")
//getting both substrings
var substring1: NSString = ""
var substring2: NSString = ""
if(newlineRange.location != NSNotFound) {
substring1 = buttonText.substringToIndex(newlineRange.location)
substring2 = buttonText.substringFromIndex(newlineRange.location)
}
//assigning diffrent fonts to both substrings
let font:UIFont? = UIFont(name: "Arial", size: 17.0)
let attrString = NSMutableAttributedString(
string: substring1 as String,
attributes: NSDictionary(
object: font!,
forKey: NSFontAttributeName) as [NSObject : AnyObject])
let font1:UIFont? = UIFont(name: "Arial", size: 11.0)
let attrString1 = NSMutableAttributedString(
string: substring2 as String,
attributes: NSDictionary(
object: font1!,
forKey: NSFontAttributeName) as [NSObject : AnyObject])
//appending both attributed strings
attrString.appendAttributedString(attrString1)
//assigning the resultant attributed strings to the button
btnTwoLine?.setAttributedTitle(attrString, forState: UIControlState.Normal)
}
Output
I was looking for nearly the same topic, except that I don't need two different font sizes. In case someone is looking for a simple solution:
let button = UIButton()
button.titleLabel?.numberOfLines = 0
button.titleLabel?.lineBreakMode = .byWordWrapping
button.setTitle("Foo\nBar", for: .normal)
button.titleLabel?.textAlignment = .center
button.sizeToFit()
button.addTarget(self, action: #selector(rightBarButtonTapped), for: .allEvents)
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: button)
I have notice an issue in most of the solutions which is while making line break mode to "Character Wrap" the second line will be left aligned to the first line
To make all the lines centered.
just change the title From Plain to Attributed and then you can make each line centered
change line break to character wrap , select your button and in attribute inspector go to line break and change it to character wrap
SWIFT 3 Syntax
let str = NSMutableAttributedString(string: "First line\nSecond Line")
str.addAttribute(NSFontAttributeName, value: UIFont.systemFont(ofSize: 17), range: NSMakeRange(0, 10))
str.addAttribute(NSFontAttributeName, value: UIFont.systemFont(ofSize: 12), range: NSMakeRange(11, 11))
button.setAttributedTitle(str, for: .normal)
I have fixed this and my solution it was only in the Storyboard.
Changes:
It added in Identity Inspector -> User Defined Runtime Attributes (these KeyPaths):
numberOfLines = 2
titleLabel.textAlignment = 1
User Defined Runtime Attributes
I added this in attributes inspector:
line break = word wrap
Word wrap
You need to do some of this in code. you can't set 2 different fonts in IB. In addition to changing the line break mode to character wrap, you need something like this to set the title,
override func viewDidLoad() {
super.viewDidLoad()
var str = NSMutableAttributedString(string: "First line\nSecond Line")
str.addAttribute(NSFontAttributeName, value: UIFont.systemFontOfSize(17), range: NSMakeRange(0, 10))
str.addAttribute(NSFontAttributeName, value: UIFont.systemFontOfSize(12), range: NSMakeRange(11, 11))
button.setAttributedTitle(str, forState: .Normal)
}
New with Xcode 13 (iOS 15)
Starting with Xcode 13, the button's title and subtitle may have their attributes set separately.
Using Storyboard:
In the Attribute Inspector for the button, select "Attributed" by Title. Then change font size of the title and the subtitle.
Or Programmatically:
// Create Title
let titleSettings = AttributeContainer.font( UIFont(name: "HelveticaNeue-Italic", size: 17)! )
yourButton.configuration?.attributedTitle = AttributedString("Button's Title", attributes: titleSettings)
// Create Subtitle
let subtitleSettings = AttributeContainer.font( UIFont(name: "HelveticaNeue-Italic", size: 11)! )
yourButton.configuration?.attributedSubtitle = AttributedString("Button's Subtitle", attributes: subtitleSettings)
One way to do it is with labels, I guess. I did this, and it seems to work ok. I could create this as a UIButton and then expose the labels, I guess. I don't know if this makes any sense.
let firstLabel = UILabel()
firstLabel.backgroundColor = UIColor.lightGrayColor()
firstLabel.text = "Hi"
firstLabel.textColor = UIColor.blueColor()
firstLabel.textAlignment = NSTextAlignment.Center
firstLabel.frame = CGRectMake(0, testButton.frame.height * 0.25, testButton.frame.width, testButton.frame.height * 0.2)
testButton.addSubview(firstLabel)
let secondLabel = UILabel()
secondLabel.backgroundColor = UIColor.lightGrayColor()
secondLabel.textColor = UIColor.blueColor()
secondLabel.font = UIFont(name: "Arial", size: 12)
secondLabel.text = "There"
secondLabel.textAlignment = NSTextAlignment.Center
secondLabel.frame = CGRectMake(0, testButton.frame.height * 0.5, testButton.frame.width, testButton.frame.height * 0.2)
testButton.addSubview(secondLabel)
The suggested solutions unfortunately did not work out for me when I wanted to have a mutliline button inside a CollectionView. Then a colleague showed me a workaround which I wanted to share in case someone has the same problem - hope this helps! Create a class which inherits from UIControl and extend it with a label, which will then behave similar like a button.
class MultilineButton: UIControl {
let label: UILabel = {
$0.translatesAutoresizingMaskIntoConstraints = false
$0.numberOfLines = 0
$0.textAlignment = .center
return $0
}(UILabel())
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(label)
NSLayoutConstraint.activate([
label.leadingAnchor.constraint(equalTo: layoutMarginsGuide.leadingAnchor),
label.trailingAnchor.constraint(equalTo: layoutMarginsGuide.trailingAnchor),
label.topAnchor.constraint(equalTo: layoutMarginsGuide.topAnchor),
label.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor)
])
}
override var isHighlighted: Bool {
didSet {
backgroundColor = backgroundColor?.withAlphaComponent(isHighlighted ? 0.7 : 1.0)
label.textColor = label.textColor.withAlphaComponent(isHighlighted ? 0.7 : 1.0)
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
my way:
func setButtonTitle(title: String, subtitle: String, button: UIButton){
//applying the line break mode
button.titleLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping;
let title = NSMutableAttributedString(string: title, attributes: Attributes.biggestLabel)
let subtitle = NSMutableAttributedString(string: subtitle, attributes: Attributes.label)
let char = NSMutableAttributedString(string: "\n", attributes: Attributes.biggestLabel)
title.append(char)
title.append(subtitle)
button.setAttributedTitle(title, for: .normal)
}

Resources