I'm working on a project to apply text attributes to UIlabel text. I'm using the common NSAttributedString keys to basically apply strokeColor and strokeWidth and foregroundColor to generate the desired outlining effect. The problem appears on non-latin characters, such as Arabic, where letters are individual highlighted instead of the entire work. The letter typically are connected in Arabic, unlink english were letters are spaced. I'm attaching the example I'm working on with a screenshot of the issue and the desired outcome. I would appreciate your support and suggestions.
let quote = "الكتابة على الصور بخطوط جميلة"
//let font = UIFont.systemFon.selft(ofSize: 50)
let attributes: [NSAttributedString.Key: Any] = [
.strokeColor: UIColor.green,
.strokeWidth: -3.0,
.foregroundColor: UIColor.red,
.font: UIFont(name: "Georgia-Bold", size: 40)!,
]
let attributedQuote = NSAttributedString(string: quote, attributes: attributes)
TextLabel.attributedText = attributedQuote
outcome with issue:
desired outcome:
Taking a quick look at this - and comparing it to similar output from css - I think you'll need to use two labels layers on top of each other.
Give this a try:
let backLabel = UILabel()
let frontLabel = UILabel()
backLabel.translatesAutoresizingMaskIntoConstraints = false
frontLabel.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(backLabel)
view.addSubview(frontLabel)
NSLayoutConstraint.activate([
backLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: 100.0),
backLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20.0),
backLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20.0),
frontLabel.topAnchor.constraint(equalTo: backLabel.topAnchor),
frontLabel.leadingAnchor.constraint(equalTo: backLabel.leadingAnchor),
frontLabel.trailingAnchor.constraint(equalTo: backLabel.trailingAnchor),
])
let quote = "الكتابة على الصور بخطوط جميلة"
var attributes: [NSAttributedString.Key: Any] = [
.strokeColor: UIColor.green,
.strokeWidth: 12.0,
.foregroundColor: UIColor.red,
.font: UIFont(name: "Georgia-Bold", size: 40)!,
]
var attributedQuote = NSAttributedString(string: quote, attributes: attributes)
backLabel.attributedText = attributedQuote
attributes = [
.strokeColor: UIColor.green,
.strokeWidth: 0,
.foregroundColor: UIColor.red,
.font: UIFont(name: "Georgia-Bold", size: 40)!,
]
attributedQuote = NSAttributedString(string: quote, attributes: attributes)
frontLabel.attributedText = attributedQuote
Result:
Related
I have used NSMutableAttributedString/NSAttributedString here and there but don't have extensive experience with them. I have a code block that repeats itself and was wondering how would I go about refactoring it? I've been working on a few extensions to refactor this but haven't had any luck.
The attributes goes into a UILabel variable closure.
let attributes = NSMutableAttributedString(string: "ID: \n",
attributes: [NSAttributedString.Key.foregroundColor : UIColor.black,
NSAttributedString.Key.backgroundColor : UIColor.clear,
NSAttributedString.Key.font : UIFont(name: "Helvetica", size: 15)!])
attributes.append(NSMutableAttributedString(string: "\(nameID)",
attributes: [NSAttributedString.Key.foregroundColor : UIColor.white,
NSAttributedString.Key.backgroundColor : UIColor.customBlue(),
NSAttributedString.Key.font : UIFont(name: "Helvetica", size: 15)!]))
attributes.append(NSMutableAttributedString(string: "\nDate Created: \n",
attributes: [NSAttributedString.Key.foregroundColor : UIColor.black,
NSAttributedString.Key.backgroundColor : UIColor.clear,
NSAttributedString.Key.font : UIFont(name: "Helvetica", size: 15)!]))
attributes.append(NSMutableAttributedString(string: "TEST",
attributes: [NSAttributedString.Key.foregroundColor : UIColor.white,
NSAttributedString.Key.backgroundColor : UIColor.customBlue(),
NSAttributedString.Key.font : UIFont(name: "Helvetica", size: 15)!]))
attributes.append(NSMutableAttributedString(string: "\nDate Last Used: \n",
attributes: [NSAttributedString.Key.foregroundColor : UIColor.black,
NSAttributedString.Key.backgroundColor : UIColor.clear,
NSAttributedString.Key.font : UIFont(name: "Helvetica", size: 15)!]))
attributes.append(NSMutableAttributedString(string: "TEST",
attributes: [NSAttributedString.Key.foregroundColor : UIColor.white,
NSAttributedString.Key.backgroundColor : UIColor.customBlue(),
NSAttributedString.Key.font : UIFont(name: "Helvetica", size: 15)!]))
Try looking at your code and seeing if there's a pattern. What I see is creating multiple NSAttributedStrings with String values, setting foreground and background, and the same font over and over. So this is ripe for the refactor picking.
First, set up your data:
// one note here is that you actually have a newline char for every entry, so it's probably better practice t simply drop them and apply them in your loop which we'll get to
let values = ["ID:", nameID /*I assume this is already a `String`*/, "Date Created: ", "TEST", "Date Last Used:", "Test"]
Colors seem to follow their own pattern: titles are black on clear and values are white on custom blue. We can use this in our loop, too. Let's set up our attributes so they're easy to reference:
let titleAttributes: [NSAttributedString.Key: Any] = [
.foregroundColor: UIColor.black,
.backgroundColor: UIColor.clear,
.font: UIFont(name: "Helvetica", size: 15)!
]
let valueAttributes: [NSAttributedString.Key: Any] = [
.foregroundColor: UIColor.white,
.backgroundColor: UIColor.customBlue(),
.font: UIFont(name: "Helvetica", size: 15)!
]
Now, let's loop:
let mutableString = NSMutableAttributedString()
for (index, currentValue) in values.enumerated() {
// if we're on an even number, we have a title. If it's odd, it's a value
let attributes = index % 2 == 0 ? titleAttributes : valueAttributes
// if we aren't on the last index, add a newline to the value
let value = index < values.count - 1 ? "\(currentValue)\n" : currentValue
mutableString.appendAttributedString(string: value, attributes: attributes)
}
I'm currently using it with MDCTextInputControllerOutlined.
I already tried
self.emailTextField.attributedPlaceholder = NSAttributedString(string: "Email", attributes: [.font: UIFont.ralewayFont(ofSize: 18.0, weight: .semibold), .foregroundColor: UIColor.white])
but not working
Found it!
set UIColor to inlinePlaceholderColor for MDCTextInputControllerOutlined
let emailTextField = MDCTextField()
let emailTextFieldController = MDCTextInputControllerOutlined(textInput: emailTextField)
emailTextFieldController.inlinePlaceholderColor = .white
I have a label that uses a NSMutableAttributedString to write out the text as:
What I want to do is lower the asterisk's top padding so that it's midY is even with the word Cuisine like below:
How can I add padding using a NSMutableAttributedString?
I know I can create a separate label with the asterisk alone and use anchors w/ a constant to center it but I want to see how this is possible using a NSMutableAttributedString
let cuisineLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
let attributedText = NSMutableAttributedString(string: "Cuisine ", attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 17), NSAttributedStringKey.foregroundColor: UIColor.lightGray])
attributedText.append(NSAttributedString(string: "*", attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 24), NSAttributedStringKey.foregroundColor: UIColor.red]))
label.attributedText = attributedText
return label
}()
The baselineOffset attribute key is used for this purpose.
let cuisine = NSMutableAttributedString(string: "Cuisine")
let asterisk = NSAttributedString(string: "*", attributes: [.baselineOffset: -3])
cuisine.append(asterisk)
Obviously, you will have to calculate the offset using the font size of the rest of the text. This is why I believe that using a full width asterisk (*) is easier.
Result with full width asterisk (you might want its font size to be a proportion of the font size of the rest of the string):
As Code Different points out, you can do this with baselineOffset attribute. A value of -8 should work for your case:
import UIKit
import PlaygroundSupport
class MyViewController : UIViewController {
override func loadView() {
let view = UIView()
view.backgroundColor = .white
self.view = view
let cuisineLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.frame = CGRect(x: 150, y: 200, width: 200, height: 20)
let attributedText = NSMutableAttributedString(string: "Cuisine ", attributes: [
NSAttributedStringKey.font: UIFont.systemFont(ofSize: 17),
NSAttributedStringKey.foregroundColor: UIColor.lightGray])
attributedText.append(NSAttributedString(string: "*", attributes: [
NSAttributedStringKey.font: UIFont.systemFont(ofSize: 24),
NSAttributedStringKey.baselineOffset: -8,
NSAttributedStringKey.foregroundColor: UIColor.red]))
label.attributedText = attributedText
return label
}()
view.addSubview(cuisineLabel)
}
}
// Present the view controller in the Live View window
PlaygroundPage.current.liveView = MyViewController()
If you're struggling with line height offsets being messed up because of the new baseline and you're using a multi-line label, try playing with lineHeightMultiple:
let lineStyle = NSParagraphStyle()
lineStyle.lineHeightMultiple = 0.8
...
NSAttributedStringKey.paragraphStyle = style
If not (and you're using multiple labels stacked on top of one another) then you probably just need to adjust the frame of each label in the series to compensate.
Dears,
I'm learning swift and I'm trying to color a textfield text, the issue now is that the color is not applied to the text. please find the code next:
let textStyle:[String:Any] = [
NSAttributedStringKey.strokeColor.rawValue: UIColor.black,
NSAttributedStringKey.foregroundColor.rawValue: UIColor.white,
NSAttributedStringKey.font.rawValue: UIFont (name: "Impact", size: 50)!,
NSAttributedStringKey.strokeWidth.rawValue: 3.5
]
Thanks
The key appears to be the .strokeWidth... If you want stroke and fill, the stroke width needs to be a negative value.
Give this a try:
let textStyle = [
NSAttributedStringKey.strokeColor: UIColor.black,
NSAttributedStringKey.foregroundColor: UIColor.white,
NSAttributedStringKey.font: UIFont(name: "Impact", size: 50)!,
NSAttributedStringKey.strokeWidth: -3.5
]
can you try this...
myTextField.attributedText = NSAttributedString(string: "text",
attributes: [NSAttributedStringKey.foregroundColor: UIColor.white])
I'm building a dictionary with a big UITextView with multiple NSAttributedString for each word. I'm trying to set a fixed space before lines like below but I can't set heading on the text starting by "L'ensemble" as it's not a paragraph (not starting with \n ).
Do you have an idea on to achieve this?
Here is my code so far which doesn't work as valueParaStyle doesn't do anything because valueTextdoesn't start with \n.
Thanks.
let senseNumberParaStyle = NSMutableParagraphStyle()
senseNumberParaStyle.paragraphSpacingBefore = 20
let valueParaStyle = NSMutableParagraphStyle()
valueParaStyle.firstLineHeadIndent = 20
valueParaStyle.headIndent = 20
for i in 0..<senses.count {
let senseNumberText = NSAttributedString(string: "\n\(i + 1).", attributes: [
NSFontAttributeName: UIFont(name: "AvenirNext-Bold", size: 14)!,
NSForegroundColorAttributeName: UIColor(red: 1, green: 0.275, blue: 0.294, alpha: 1),
NSParagraphStyleAttributeName: senseNumberParaStyle
])
wordText.appendAttributedString(senseNumberText)
if let value = senses[i].value {
let valueText = NSAttributedString(string: " \(value)", attributes: [
NSFontAttributeName: UIFont(name: "AvenirNext-Regular", size: 15)!,
NSForegroundColorAttributeName: UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 1),
NSParagraphStyleAttributeName: valueParaStyle
])
wordText.appendAttributedString(valueText)
}
}
You are on the right track. You just need to add a "\t" at your first line:
var paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.headIndent = 20
label.attributedText = NSAttributedString(string: "1.\tHere comes your text. Indent as you really want it", attributes:[NSParagraphStyleAttributeName : paragraphStyle])
Without the "\t":
With "\t":