Swift attribute for new line in NSAttributedString - swift

I have a UITextView with a lightGray text.
I would like the color to always be set to black with each new line so that I can be sure that the new line is always black regardless of the color of the previous line
For example
This is first line --> foregroundColor.lightgray
\n
_ --> this new empty line must have foregroundColor.black
To do this I tried to change the foregroundColor attribute every time I hit enter (new line \n)
In Coordinator of UITextView
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if text == "\n" {
let customAttrributes : [NSAttributedString.Key: Any] = [
NSAttributedString.Key.foregroundColor: UIColor.black
]
if textView.selectedTextRange != nil {
textView.textStorage.addAttributes(customAttrributes, range: NSRange(location: textView.selectedRange.location, length: 1))
This return an "Out of bounds" error and I don't understand why. I suppose it is due to the fact that since there is no character, length 1 is considered non-existent. But then is it possible to set the new blank line already with the black text attribute or do you have to wait for some characters to be inserted?
Thanks!

One of easiest way to do it is to set typingAttributes. You can detect a new line using this answer for example. So let's say what we started printing with .lightGray color. Firstly we set: textView.typingAttributes = [.foregroundColor: UIColor. lightGray]. We should switch to the black color on a start of a new line.
var previousRect = CGRect.zero
func textViewDidChange(_ textView: UITextView) {
let pos = textView.endOfDocument
let currentRect = textView.caretRect(for: pos)
self.previousRect = self.previousRect.origin.y == 0.0 ? currentRect : self.previousRect
if currentRect.origin.y > self.previousRect.origin.y {
textView.typingAttributes = [.foregroundColor: UIColor.black]
}
self.previousRect = currentRect
}

This is an example for two line with different text and textColor in NSAttributedString:
let attributedString = NSMutableAttributedString(attributedString: NSAttributedString(string: "First Line", attributes: [.font: UIFont.systemFont(ofSize: 16, weight: .semibold), .foregroundColor: UIColor.lightGray]))
attributedString.append(NSAttributedString(string: "Second Line", attributes: [.foregroundColor: UIColor.black, .font: UIFont.systemFont(ofSize: 16, weight: .semibold)]))
textView.attributedText = attributedString

Related

NSAttributedString background color is not solid when other attributes are applied

I want to make several changes to UILabel text style by using NSAttributedString:
Part of the string must have a solid backgroundColor;
Some parts of the string must have a different font;
Some parts of the string must have a different foregroundColor;
But when i made these changes, i ran into the problem that backgroundColor has some spacing (vertical lines in the image) between (i think) other attributes (font/foregroundColor).
(See the full size image)
How can i fixed that?
Swift Playground code:
import PlaygroundSupport
import UIKit
let label = UILabel()
label.backgroundColor = .black
let text = "Hello + World!"
let nsText = NSString(string: text)
let attributedString = NSMutableAttributedString(string: text)
attributedString.addAttribute(.backgroundColor, value: UIColor.systemCyan, range: nsText.range(of: "Hello + Wo"))
attributedString.addAttribute(.font, value: UIFont.systemFont(ofSize: 100.0), range: nsText.range(of: "Hello"))
attributedString.addAttribute(.foregroundColor, value: UIColor.systemPurple, range: nsText.range(of: " + "))
attributedString.addAttribute(.font, value: UIFont.systemFont(ofSize: 80.0), range: nsText.range(of: " + "))
attributedString.addAttribute(.foregroundColor, value: UIColor.white, range: nsText.range(of: "World!"))
attributedString.addAttribute(.font, value: UIFont.systemFont(ofSize: 44.0), range: nsText.range(of: "World!"))
label.attributedText = attributedString
label.sizeToFit()
PlaygroundPage.current.liveView = label
There is an attribute called expansion that adjusts the font's expansion factor using a float value. The problem only occurs where two ranges with different font sizes meet, and this is a graphical rendering issue that cannot be overridden. But if you explicitly zero out the expansion factor, or even apply a small negative value, the gaps will not be noticeable.
attributedString.addAttribute(.expansion, value: NSNumber(value: 0), range: nsText.range(of: "Hello + World!"))
https://developer.apple.com/documentation/foundation/nsattributedstring/key/1524652-expansion?language=objc__5

Set a word under a word in UILabel/UITextView Swift

I'm trying to understand ways to implement the placement of a word under a word in a UILabel or UITextView.
I know that there is an offset on the y axis in the attributes of the string, but not on the x axis. Therefore, I did not find a solution in this.
Just in case, the code that changes the position on the Y axis. Its disadvantage is also that it shifts the neighboring word to the right.
extension String {
func setSuperscript(text: String) -> NSMutableAttributedString {
let attributedString = NSMutableAttributedString(string: self)
let foundRange = attributedString.mutableString.range(of: text)
let font = UIFont.systemFont(ofSize: 12)
if foundRange.location != NSNotFound {
attributedString.addAttribute(.font, value: font, range: foundRange)
attributedString.addAttribute(.baselineOffset, value: -10, range: foundRange)
attributedString.addAttribute(.foregroundColor, value: UIColor.red, range: foundRange)
}
return attributedString
}
}
It looks like this:
Does anyone have any ideas?

Add a label after a String

I need to create a line of text with two colors
There will Be a text, which is string in black and there will be one $ Symbol and it should be red.
lazy var dollarSmb: UILabel = {
let smb = UILabel()
smb.text = "$"
smb.font = UIFont.systemFont(ofSize: 17, weight: .regular)
smb.textColor = UIColor.red
smb.baselineAdjustment = .alignCenters
smb.translatesAutoresizingMaskIntoConstraints = false
return smb
}()
And I want to add it after the label string, something like that:
Var label = "This is a test" + " \(dollarSmb)"
It's not working
Could anyone help me the best way to do that?
Many thanks
You could use NSAttributedString to construct a single string with a different color.
let attributedString = NSMutableAttributedString(string: "This is a test ")
let dollarSymbl = NSAttributedString(string: "$", attributes: [.foregroundColor: UIColor.red])
attributedString.append(dollarSymbl)
// Then assign it to a UILabel
label.attributedText = attributedString
Use an NSAttributedString and instead of setting smb.text, set smb.attributedText.
Something like
smb.attributedText = NSAttributedString(string: "$", attributes [.foregroundColor: UIColor.red])

SKLabelNode Border and Bounds Issue

I am attempting to create text that has an outline. I am currently using SKLabelNode with NSAttributedString, which you can now do in SpriteKit as of iOS 11. The problem is, if the stroke width is too thick, then the outline gets cut off by what appears to be the bounding rectangle of the SKLabelNode. Please see below for the image and code.
extension SKLabelNode {
func addStroke(_ strokeColor:UIColor) {
let font = UIFont(name: self.fontName!, size: self.fontSize)
let attributes:[NSAttributedStringKey:Any] = [.strokeColor: strokeColor, .strokeWidth: 20.0, .font: font!]
let attributedString = NSMutableAttributedString(string: " \(self.text!) ", attributes: attributes)
let label1 = SKLabelNode()
label1.horizontalAlignmentMode = self.horizontalAlignmentMode
label1.text = self.text
label1.zPosition = -1
label1.attributedText = attributedString
self.addChild(label1)
}
}
I looked at expanding the frame of the SKLabelNode serving as the border text, but that is a get-only property. I tried to add leading/trailing spaces, but they appear to be automatically trimmed. Using a negative value for strokeWidth works but creates an inner stroke, I'd prefer to have an outer stroke.
Any ideas? Thanks in advance for the help!
Mike
You shouldn't need to create a separate node for the stroke.
Use negative width values to only render the stroke without fill.
Use .foregroundColor to fill.
You should first check to see if an attributed string is already present to ensure you do not clobber it.
Here is the code:
extension SKLabelNode {
func addStroke(color:UIColor, width: CGFloat) {
guard let labelText = self.text else { return }
let font = UIFont(name: self.fontName!, size: self.fontSize)
let attributedString:NSMutableAttributedString
if let labelAttributedText = self.attributedText {
attributedString = NSMutableAttributedString(attributedString: labelAttributedText)
} else {
attributedString = NSMutableAttributedString(string: labelText)
}
let attributes:[NSAttributedStringKey:Any] = [.strokeColor: color, .strokeWidth: -width, .font: font!, .foregroundColor: self.fontColor!]
attributedString.addAttributes(attributes, range: NSMakeRange(0, attributedString.length))
self.attributedText = attributedString
}
}

center and bold TextView swift

Hi all I am trying to set a bold and centre to my text view I have 3 different colours in the view but I want the 2 colours that change to be centred. Is there a way to do this? below is my code.
let myString:String = DisplayableContents.texts[10]
var myMutableString = NSMutableAttributedString()
myMutableString = NSMutableAttributedString(string: myString, attributes: [NSFontAttributeName:UIFont(name: "Calibri", size: 17.0)!])
myMutableString.addAttribute(NSForegroundColorAttributeName, value: UIColor.red, range: NSRange(location:74, length:96))
myMutableString.addAttribute(NSForegroundColorAttributeName, value: UIColor.gray, range: NSRange(location:96, length:19))
// set label Attribute
infoBox.attributedText = myMutableString
Try with
let myString:String = DisplayableContents.texts[10]
var myMutableString = NSMutableAttributedString()
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = NSTextAlignment.Center
myMutableString = NSMutableAttributedString(string: myString, attributes: [NSParagraphStyleAttributeName:paragraphStyle, NSFontAttributeName:UIFont(name: "Calibri", size: 17.0)!])
myMutableString.addAttribute(NSForegroundColorAttributeName, value: UIColor.red, range: NSRange(location:74, length:96))
myMutableString.addAttribute(NSForegroundColorAttributeName, value: UIColor.gray, range: NSRange(location:96, length:19))
// set label Attribute
infoBox.attributedText = myMutableString
But 'Calibri' doesn't exist in iOS. You can check fonts in http://iosfonts.com/. If you are adding the font to your project check its bold variation, probably 'Calibri-Bold'