NSTextAttachment image in attributed text with foreground colour - swift

When I add an image attachment to an UITextView with a foreground colour set, the image is blanked out with the set colour:
let attrString = NSMutableAttributedString(string: rawText, attributes: [.font: UIFont.systemFont(ofSize: 17), .foregroundColor: UIColor.black])
let attachment = NSTextAttachment(image: image)
let imgStr = NSMutableAttributedString(attachment: attachment)
attrString.append(imgStr)
textview.attributedText = attrString
When I removed .foregroundColor: UIColor.black, the image is displayed correctly, but I need to be able to set the attributed text colour.
I tried to explicitly remove the .foregroundColor attribute after adding the image attachment with no luck. I also tried to remove the .foregroundColor attribute from most of the text and it still wouldn't work, only removing the attribute from the entire string works:
attrString.removeAttribute(.foregroundColor, range: NSRange(location: attrString.length-1, length: 1)) // does not work
// -------
attrString.removeAttribute(.foregroundColor, range: NSRange(location: 1, length: attrString.length-1)) // does not work
// -------
attrString.removeAttribute(.foregroundColor, range: NSRange(location: 0, length: attrString.length)) // works but no text colour
This is developed on Xcode 11.0, iOS 13. Is this a UITextView/iOS bug or an expected behaviour (which, I don't think is likely)? How do I display the image correctly with the text colour set?

It looks like there is a bug with the NSTextAttachment(image:) constructor (on iOS 13, at the time of this answer), the following image attachment construction works correctly:
let attachment = NSTextAttachment()
attachment.image = image

Related

How to change character background opacity?

I am using Swift 5.
My first test was to change the background of my label and use the opacity. It is ok, but now I would like to try to change the character's background opacity only and not the full background label.
For this, I have no idea where to start.
Any hints?
Thanks
you can try this, change AlphaComponent value according to you preference:
let text = "your label text"
let attributedString = NSMutableAttributedString(string: text)
attributedString.addAttribute(NSAttributedStringKey.foregroundColor, value: UIColor.red.withAlphaComponent(0.5), range: NSRange(location: 0, length: attributedString.length))
label.attributedText = attributedString
You can use
sender.alpha = 0.5

How do I make NSMutableAttributedString work for links?

I'm using NSMutableAttributedString to format text strings. It works fine, even with the links. But for some reason the font size for the links won't change even though I have specified the size. My function looks like this: (There are comments in the code to explain everything)
func formatfunc2(chapter: String, boldStart: Int, boldLength: Int, italicsStart: Int, italicsLength: Int, link: [String], linkStart: [Int], linkEnd: [Int]) -> NSAttributedString {
let bold = UIFont.boldSystemFont(ofSize: 17)
let italics = UIFont.italicSystemFont(ofSize: 17)
//BELOW IS MY FORMAT FOR THE HYPERLINK
let hyperlink = UIFont.boldSystemFont(ofSize: 17)
let attributedString = NSMutableAttributedString.init(string: chapter, attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 17)])
//BELOW ARE FORMAT FOR BOLD AND ITALICS - AND THEY WORK FINE
attributedString.addAttribute(.font, value: bold, range: NSRange.init(location: boldStart, length: boldLength))
attributedString.addAttribute(.font, value: italics, range: NSRange.init(location: italicsStart, length: italicsLength))
//THE LOOP GOES THROUGH ALL LINK ADRESSES AND THEIR POSITIONS
for i in 0...link.count - 1 {
//HERE I ADD THE FONT SIZE TO THE SAME POSITION AS THE LINKS, BUT IT DOESN'T WORK
attributedString.addAttribute(.font, value: hyperlink, range: NSRange.init(location: linkStart[i], length: linkEnd[i]))
let url = URL(string: link[i]) as! URL
//MY THEORY IS THAT THE CODE BELOW OVERRIDES THE PREVIOUS FONT SIZE
attributedString.setAttributes([.link: url], range: NSMakeRange(linkStart[i], linkEnd[i]))
}
return attributedString
}
So the format works fine for all other text but not for the links. Can I add font size in the last code part instead? :
attributedString.setAttributes([.link: url], range: NSMakeRange(linkStart[i], linkEnd[i]))

Multiline multicolor attributed text in swift

How can I build something like this using attributed text? I tried adding line feed (\n), it didn't work, just displayed first line and cropped next two, removing \n will display two parts in single line:
let mutableAttributedString = NSMutableAttributedString()
let regularAttribute = [NSAttributedStringKey.font: UIFont(name: "Avenir-Light", size: 45.0), NSAttributedStringKey.foregroundColor: UIColor.yellow]
let boldAttribute = [NSAttributedStringKey.font: UIFont(name: "Avenir-Light", size: 25.0), NSAttributedStringKey.foregroundColor: UIColor.white]
let mutableAttributedString2 = NSMutableAttributedString()
let boldAttributedString = NSAttributedString(string: "person", attributes: boldAttribute)
let regularAttributedString = NSAttributedString(string: "\(self.requestCount)", attributes: regularAttribute)
mutableAttributedString2.append(boldAttributedString)
mutableAttributedString2.append(NSAttributedString(string: "\n"))
mutableAttributedString2.append(regularAttributedString)
self.btnStatsPeople.setAttributedTitle(mutableAttributedString2, for: .normal)
UILabel has one line by default.
This property controls the maximum number of lines to use in order to
fit the label’s text into its bounding rectangle. The default value
for this property is 1. To remove any maximum limit, and use as many
lines as needed, set the value of this property to 0.
UIButton creates default UILabel. So using 0 lines is a solution for your problem:
self.btnStatsPeople.titleLabel?.numberOfLines = 0

How to make the text as a hyperlink in NSTextView programmatically? Swift 4, Xcode 9.4

How to make the text as a hyperlink in NSTextView programmatically?
Like this:
Just click here to register
or like this:
Just http://example.com to register
I found this solution but it works only for iOS, not for macOS
Try this:
let attributedString = NSMutableAttributedString(string: "Just click here to register")
let range = NSRange(location: 5, length: 10)
let url = URL(string: "https://www.apple.com")!
attributedString.setAttributes([.link: url], range: range)
textView.textStorage?.setAttributedString(attributedString)
// Define how links should look like within the text view
textView.linkTextAttributes = [
.foregroundColor: NSColor.blue,
.underlineStyle: NSUnderlineStyle.styleSingle.rawValue
]
If you needed set Font Size for links use this ->
let input = "Your string with urls"
let detector = try! NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
let matches = detector.matches(in: input, options: [], range: NSRange(location: 0, length: input.utf16.count))
let attributes = [NSAttributedString.Key.font: NSFont.systemFont(ofSize: 15)]
let attributedString = NSMutableAttributedString(string: input, attributes: attributes)
if matches.count > 0{
for match in matches {
guard let range = Range(match.range, in: input) else { continue }
let url = input[range]
attributedString.setAttributes([.link: url, .font: NSFont.systemFont(ofSize: 15)], range: match.range)
}
}
youTextView.textStorage?.setAttributedString(attributedString)
Swift 5 / macOS 12 solution to make the link clickable and have the same font any NSTextField's you display in the same window:
Define some NSTextView IBOutlet (mine is called tutorialLink):
#IBOutlet weak var tutorialLink : NSTextView!
Then here's the styling code in one function:
fileprivate func initTutorialLink() {
let attributedString = NSMutableAttributedString(string: "Here's a tutorial on how to set up a Digital Ocean S3 account.")
// Set font for entire string
let fontAttributes = [NSAttributedString.Key.font: NSFont.systemFont(ofSize: 13)]
let range2 = NSRange(location: 0, length: 61)
attributedString.setAttributes(fontAttributes, range: range2)
// Set url and same font for just the link range
let range = NSRange(location: 21, length: 40)
let url = URL(string: tutorialUrl)!
attributedString.setAttributes([.link: url, NSAttributedString.Key.font:NSFont.systemFont(ofSize: 13)], range: range)
// Store the attributed string
tutorialLink.textStorage?.setAttributedString(attributedString)
// Mark how link text should be colored and underlined
tutorialLink.linkTextAttributes = [
.foregroundColor: NSColor.systemBlue,
.underlineStyle: NSUnderlineStyle.single.rawValue
]
// Give non-linked text same color as other labels
tutorialLink.textColor = .labelColor
}
In IB, you obviously need to connect your NSTextView (called Scrollable Text View in the Objects Library) to the IBOutlet. Less obviously, you need to click the box to make the NSTextView Selectable. I don't know why exactly, but if it's not Selectable then your link will not react when clicked.

Attributed string cutting and displaying single word into two lines on UILabel

I'm creating attributed text using following attributes,
func attributedString(font: UIFont, contentColor: UIColor, alignment: NSTextAlignment) -> NSAttributedString {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 0.6
paragraphStyle.lineHeightMultiple = 0.8
paragraphStyle.alignment = alignment
paragraphStyle.lineBreakMode = .byWordWrapping
let lineSpacingAttribute: [NSAttributedStringKey: Any] = [NSAttributedStringKey.paragraphStyle: paragraphStyle, NSAttributedStringKey.font: font, NSAttributedStringKey.foregroundColor: contentColor]
let attributedString = NSAttributedString(string: self, attributes: lineSpacingAttribute)
return attributedString
}
I'm displaying this text on UILabel inside a custom tableViewCell. But, it is cutting a single word into two (cutting last letter of a word and displaying it on next line). I've set the numberOfLines to zero, and preferredMaxLayoutWidth for label. And I'm using a custom font.
This problem is happening on small screens only, iPhone SE and iPhone 5S simulators. But, on other simulators it is displaying correctly. Could you please help me to figure out what is wrong in this?
Thanks!
From Apple doc:
var preferredMaxLayoutWidth: CGFloat
This property affects the size of the label when layout constraints are applied to it. During layout, if the text extends beyond the width specified by this property, the additional text flows to one or more new lines, increasing the height of the label.
Depending on your text you can use the adjustsFontSizeToFitWidth property with the minimumScaleFactor property