Set a word under a word in UILabel/UITextView Swift - 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?

Related

Swift attribute for new line in NSAttributedString

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

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

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
}
}

How to set the UIColor for each letter in a string

Is there a simple way to set the colour for each letter in a UILabel in swift? (Or any other text for that matter!)
For example
let label = UILabel(frame CGRectMake(100,100,100,100))
label.text = "spell"
label.textColor = UIColor.whiteColor()
But I want the S to be red, the p to be blue, the e to be yellow etc.
Thanks in advance if anyone knows how to do this easily...
I got this to work with help from those below. And added the attributed string to a UIButton title.
let mainspring = "spell"
let spellAttrString: NSMutableAttributedString = NSMutableAttributedString(string: mainString)
spellAttrString.addAttribute(NSForegroundColorAttributeName, value: UIColor.blueColor(), range: NSRange(location: 0,length: 1))
spellAttrString.addAttribute(NSForegroundColorAttributeName, value: UIColor.greenColor(), range: NSRange(location: 1, length: 1))
spellAttrString.addAttribute(NSForegroundColorAttributeName, value: UIColor.yellowColor(), range: NSRange(location: 2, length: 1))
spellAttrString.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: NSRange(location: 3, length: 1))
spellAttrString.addAttribute(NSForegroundColorAttributeName, value: UIColor.magentaColor(), range: NSRange(location: 4, length: 1))
[spellButton .setAttributedTitle(spellAttrString, forState: .Normal)]
For creating of the colorful string you should use NSMutableAttributedString.
Small example:
var attrString: NSMutableAttributedString = NSMutableAttributedString(string: "Some text")
attrString.addAttribute(NSForegroundColorAttributeName, value: UIColor.redColor(), range: NSMakeRange(0, 3))

is it possible to set the UILabel distance between the line?

Is it possible to set a UILabel's distance between the line, as i had a UILabel contain 3 lines, and linebreakmode is wordwrap?
If you're referring to "leading", which refers to the gap between lines of type - you cannot change this on a UILabel. That is inferred from the front of the label itself. Some people have tried to create categories to override the "leading" property of the UIFont for the label but it doesn't actually work when rendering.
If you really need to control the vertical spacing between lines of text then your best bet is to programmatically drop 1 UILabel per line of fixed width and control the vertical gap yourself.
Here is how you can set line spacing using interface builder and programatically as well.
From Interface Builder:
Programmatically:
SWift 4
Using label extension
extension UILabel {
// Pass value for any one of both parameters and see result
func setLineSpacing(lineSpacing: CGFloat = 0.0, lineHeightMultiple: CGFloat = 0.0) {
guard let labelText = self.text else { return }
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = lineSpacing
paragraphStyle.lineHeightMultiple = lineHeightMultiple
let attributedString:NSMutableAttributedString
if let labelattributedText = self.attributedText {
attributedString = NSMutableAttributedString(attributedString: labelattributedText)
} else {
attributedString = NSMutableAttributedString(string: labelText)
}
// Line spacing attribute
attributedString.addAttribute(NSAttributedStringKey.paragraphStyle, value:paragraphStyle, range:NSMakeRange(0, attributedString.length))
self.attributedText = attributedString
}
}
Now call extension function
let label = UILabel()
let stringValue = "is\nit\npossible\nto\nset\nthe\nUILabel\ndistance\nbetween\nthe\nline?"
// Pass value for any one argument - lineSpacing or lineHeightMultiple
label.setLineSpacing(lineSpacing: 2.0) . // try values 1.0 to 5.0
// or try lineHeightMultiple
//label.setLineSpacing(lineHeightMultiple = 2.0) // try values 0.5 to 2.0
Or using label instance (Just copy & execute this code to see result)
let label = UILabel()
let stringValue = "is\nit\npossible\nto\nset\nthe\nUILabel\ndistance\nbetween\nthe\nline?"
let attrString = NSMutableAttributedString(string: stringValue)
var style = NSMutableParagraphStyle()
style.lineSpacing = 24 // change line spacing between paragraph like 36 or 48
style.minimumLineHeight = 20 // change line spacing between each line like 30 or 40
// Line spacing attribute
attrString.addAttribute(NSAttributedStringKey.paragraphStyle, value: style, range: NSRange(location: 0, length: stringValue.characters.count))
// Character spacing attribute
attrString.addAttribute(NSAttributedStringKey.kern, value: 2, range: NSMakeRange(0, attrString.length))
label.attributedText = attrString