Swift: Can I adjust attributed text with dynamic font size? - swift

I'm working with the collectionView
whose each cell has the content like this:
everything works pretty well until I test it on an iphone 5s:
"Ho Chi Minh City" was out of bounds.
Here is my code:
func configureNameLabel() {
nameLabel.numberOfLines = 2
let attributedText = NSMutableAttributedString(string: post?.name ?? "", attributes: [.font: UIFont.boldSystemFont(ofSize: 15)])
attributedText.append(NSAttributedString(string: "\nSaturday, December 1, 2018 ⦁ Ho Chi Minh City ⦁ ", attributes:
[.foregroundColor: UIColor.lightGray, .font: UIFont.preferredFont(forTextStyle: .caption1)]))
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 5
attributedText.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: attributedText.string.count))
let attachment = NSTextAttachment()
attachment.image = UIImage(named: "user-male")
attachment.bounds = CGRect(x: 0, y: -2, width: 12, height: 12)
attributedText.append(NSAttributedString(attachment: attachment))
nameLabel.attributedText = attributedText
}
I think this problem is due to the font size, can anybody fix this ?

Thanks guys, I solved it. "label.numberOfLines = 0" worked. the problem is at my constraints.

Related

Text Wrapping not happening as it should in NSTextField Attributed String

I am having an issue with text not wrapping correctly if there is a single quote, or macOS ASCII Extended Character #213 (shift+opt.+]) in a string.
Apple does not escape the media item title string when it is retrieved through the iTunesLibrary framework.
As you can see in the example below, the first string is exactly how it come from the iTunesLibrary using the framework API call. The second string is is the single quote is escaped, the third string is if I use macOS Extended ASCII Character code 213, and the fourth string is if I use a tilde. The tilde is not the right character to use in this situation, but it is the only one that correctly wraps the text in the cell.
I've been working on this for the past 6-8 hours to figure it out and I'm just throwing it out there to see if someone can help me.
ViewController.swift
import Cocoa
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.frame.size = NSSize(width: 616, height: 184)
// Strings
let string1 = "I Keep Forgettin' (Every Time You're Near)"
let string2 = "I Keep Forgettin\' (Every Time You're Near)"
let string3 = "I Keep Forgettin’ (Every Time You're Near)"
let string4 = "I Keep Forgettin` (Every Time You're Near)"
// Formatting
let foreground = NSColor.purple.cgColor
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .center
paragraphStyle.lineBreakMode = .byWordWrapping
paragraphStyle.tabStops = .none
paragraphStyle.baseWritingDirection = .leftToRight
guard let font = NSFont(name: "Helvetica", size: 28.0) else { return }
// Labels
let label1 = NSTextField(frame: NSRect(x: 20, y: self.view.frame.minY+20, width: 144, height: 144))
label1.cell = VerticallyCenteredTextFieldCell()
label1.wantsLayer = true
label1.layer?.borderColor = NSColor.purple.cgColor
label1.layer?.borderWidth = 0.5
label1.layer?.backgroundColor = NSColor.lightGray.cgColor
label1.alphaValue = 1
var fontSize = bestFontSize(attributedString: NSAttributedString(string: string1, attributes: [.font: font, .paragraphStyle: paragraphStyle]), size: CGSize(width: 136, height: 136))
label1.attributedStringValue = NSAttributedString(string: string1, attributes: [.font: font.withSize(fontSize), .foregroundColor: foreground, .paragraphStyle: paragraphStyle])
self.view.addSubview(label1)
let label2 = NSTextField(frame: NSRect(x: 164, y: self.view.frame.minY+20, width: 144, height: 144))
label2.cell = VerticallyCenteredTextFieldCell()
label2.wantsLayer = true
label2.layer?.borderColor = NSColor.purple.cgColor
label2.layer?.borderWidth = 0.5
label2.layer?.backgroundColor = NSColor.lightGray.cgColor
label2.alphaValue = 1
fontSize = bestFontSize(attributedString: NSAttributedString(string: string2, attributes: [.font: font, .paragraphStyle: paragraphStyle]), size: CGSize(width: 136, height: 136))
label2.attributedStringValue = NSAttributedString(string: string2, attributes: [.font: font.withSize(fontSize), .foregroundColor: foreground, .paragraphStyle: paragraphStyle])
self.view.addSubview(label2)
let label3 = NSTextField(frame: NSRect(x: 308, y: self.view.frame.minY+20, width: 144, height: 144))
label3.cell = VerticallyCenteredTextFieldCell()
label3.wantsLayer = true
label3.layer?.borderColor = NSColor.purple.cgColor
label3.layer?.borderWidth = 0.5
label3.layer?.backgroundColor = NSColor.lightGray.cgColor
label3.alphaValue = 1
fontSize = bestFontSize(attributedString: NSAttributedString(string: string3, attributes: [.font: font, .paragraphStyle: paragraphStyle]), size: CGSize(width: 136, height: 136))
label3.attributedStringValue = NSAttributedString(string: string3, attributes: [.font: font.withSize(fontSize), .foregroundColor: foreground, .paragraphStyle: paragraphStyle])
self.view.addSubview(label3)
let label4 = NSTextField(frame: NSRect(x: 452, y: self.view.frame.minY+20, width: 144, height: 144))
label4.cell = VerticallyCenteredTextFieldCell()
label4.wantsLayer = true
label4.layer?.borderColor = NSColor.purple.cgColor
label4.layer?.borderWidth = 0.5
label4.layer?.backgroundColor = NSColor.lightGray.cgColor
label4.alphaValue = 1
fontSize = bestFontSize(attributedString: NSAttributedString(string: string4, attributes: [.font: font, .paragraphStyle: paragraphStyle]), size: CGSize(width: 136, height: 136))
label4.attributedStringValue = NSAttributedString(string: string4, attributes: [.font: font.withSize(fontSize), .foregroundColor: foreground, .paragraphStyle: paragraphStyle])
self.view.addSubview(label4)
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
func bestFontSize(attributedString: NSAttributedString, size: CGSize) -> CGFloat {
// Create a property to hold the font and size
var font: NSFont?
// Get the font information from the string attibutes
attributedString.enumerateAttribute(.font, in: NSRange(0..<attributedString.length)) { value, range, stop in
if let attrFont = value as? NSFont {
font = attrFont
}
}
if font == nil {
return 0
}
// Get any paragraph styling attributes
var paragraphStyle: NSMutableParagraphStyle?
attributedString.enumerateAttribute(.paragraphStyle, in: NSMakeRange(0, attributedString.length)) { value, range, stop in
if let style = value as? NSMutableParagraphStyle {
paragraphStyle = style
}
}
if paragraphStyle == nil {
return 0
}
// Create a sorted list of words from the string in descending order of length (chars) of the word
let fragment = attributedString.string.split(separator: " ").sorted() { $0.count > $1.count }
// Create a bounding box size that will be used to check the width of the largest word in the string
var width = String(fragment[0]).boundingRect(with: CGSize(width: .greatestFiniteMagnitude, height: size.height), options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [.font: font!, .paragraphStyle: paragraphStyle!], context: nil).width.rounded(.up)
// Create a bounding box size that will be used to check the height of the string
var height = attributedString.string.boundingRect(with: CGSize(width: size.width, height: .greatestFiniteMagnitude), options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [.font: font!, .paragraphStyle: paragraphStyle!], context: nil).height.rounded(.up)
while height >= size.height || width >= size.width {
guard let pointSize = font?.pointSize else {
return 0
}
font = font?.withSize(pointSize-0.25)
width = String(fragment[0]).boundingRect(with: CGSize(width: .greatestFiniteMagnitude, height: size.height), options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [.font: font!, .paragraphStyle: paragraphStyle!], context: nil).width.rounded(.up)
height = attributedString.string.boundingRect(with: CGSize(width: size.width, height: .greatestFiniteMagnitude), options: [.usesLineFragmentOrigin, .usesFontLeading], attributes: [.font: font!, .paragraphStyle: paragraphStyle!], context: nil).height.rounded(.up)
}
return font!.pointSize
}
}
VerticallyCenteredTextFieldCell.swift
import Cocoa
class VerticallyCenteredTextFieldCell: NSTextFieldCell {
// https://stackoverflow.com/questions/11775128/set-text-vertical-center-in-nstextfield/33788973 - Sayanti Mondal
func adjustedFrame(toVerticallyCenterText rect: NSRect) -> NSRect {
// super would normally draw from the top of the cell
var titleRect = super.titleRect(forBounds: rect)
let minimumHeight = self.cellSize(forBounds: rect).height
titleRect.origin.y += (titleRect.height - minimumHeight) / 2
titleRect.size.height = minimumHeight
return titleRect
}
override func drawInterior(withFrame cellFrame: NSRect, in controlView: NSView) {
super.drawInterior(withFrame: adjustedFrame(toVerticallyCenterText: cellFrame), in: controlView)
}
}
This is the result I get:
Anyone else get the same result running this?

How can I center an image on a textView who is attached to a NSTextAttachment type?

I have a textView (programmatically) and I inserted an image appended to a NSTextAttachment and want it to be aligned to the center of the text view... but I haven't found any solutions yet...I was wondering if that task is possible to be done via code only...
let image = ResizeImage(image: UIImage(named: "logo")!, targetSize: size)
let image1Attachment = NSTextAttachment()
image1Attachment.image = image
let image1Attachments = NSAttributedString(attachment: image1Attachment)
My best approach to the solution on trying to achieve this was this code :
let image = ResizeImage(image: UIImage(named: "telepaint1")!,
targetSize: size)
//let textAttachment = NSTextAttachment()
let textAttachment = NSTextAttachment()
//let imageStyle = NSMutableParagraphStyle()
let imageStyle = NSMutableParagraphStyle()
//imageStyle.alignment = .center
imageStyle.alignment = .center
//textAttachment.image = image
textAttachment.image = image
let imageText = NSAttributedString(attachment:
textAttachment).mutableCopy() as! NSMutableAttributedString
//attempt to center image...
let attributedStringToAppend: NSMutableAttributedString =
NSMutableAttributedString(attributedString:
NSAttributedString(string: "\n\n"))
//attributedStringToAppend.addAttributes(attr, range: range)
let combination2: NSMutableAttributedString =
NSMutableAttributedString(attributedString:
NSAttributedString(string: "\n\n"))
combination2.append(lineBreak)
// combination2.addAttribute(attr, range: NSRange(location: 0,
length: length))
combination2.append(imageText)
//this text will display the "Walktrough" text on image
combination2.append(attributedText)
tv.attributedText = combination2
Answer(worked for me!):
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .center
attributedText.addAttribute(NSAttributedString.Key.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: length))
let size = CGSize(width: 100, height: 100)
//Here is an attempt to resize image and center it...
let image = ResizeImage(image: UIImage(named: "logo")!.withRenderingMode(.alwaysTemplate), targetSize: size)
let textAttachment = NSTextAttachment()
let imageStyle = NSMutableParagraphStyle()
imageStyle.alignment = .center
textAttachment.image = image
let imageText = NSAttributedString(attachment: textAttachment).mutableCopy() as! NSMutableAttributedString
let length2 = imageText.length
imageText.addAttribute(NSAttributedString.Key.paragraphStyle, value: imageStyle, range: NSRange(location: 0, length: length2))

SKLabelNode + NSMutableAttributedString = no shadow

let paragraph = NSMutableParagraphStyle()
paragraph.alignment = NSTextAlignment.right
paragraph.tailIndent = -3
let shadow = NSShadow()
shadow.shadowBlurRadius = 5
shadow.shadowColor = UIColor.gray
shadow.shadowOffset = CGSize(width: 2, height: -2)
let text = NSMutableAttributedString(string: "MY LABEL", attributes:
[NSShadowAttributeName : shadow,
NSStrokeColorAttributeName : UIColor.black,
NSStrokeWidthAttributeName : -3,
NSForegroundColorAttributeName: UIColor.white,
NSFontAttributeName : UIFont(name: "MyFont", size: 24) as Any,
NSParagraphStyleAttributeName : paragraph])
let node = SKLabelNode()
addChild(node)
node.attributedText = attributedString
It works but there is NO shadow. I can use the same NSMutableAttributedString on well-known ASAttributedLabel and it works nice and there IS a shadow, but i want to use SKLabelNode to achieve better performance on IOS11. ASAttributedLabel may lag on dynamic scenes :(

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'

Adding a stroke to a UILabel text in SWIFT

I am trying to add a stroke to a UILabel text in Swift. This is the text I have used:
var congratsLabel = UILabel(frame: CGRectMake(10, popUpView.frame.size.height * 0.540, popUpView.frame.size.width, 70))
congratsLabel.font = UIFont(name: Font.Daydreamer, size: 70)
congratsLabel.text = "Snap!"
congratsLabel.numberOfLines = 0
congratsLabel.textColor = UIColor.whiteColor()
congratsLabel.textAlignment = NSTextAlignment.Center
popUpView.addSubview(congratsLabel)
I have also tried using the NSMutableAttributedString althoough it is coming out a bit ropey and not how I want, the code I used for that also was:
var String = "Snap!"
var strokeString = NSMutableAttributedString(string: String, attributes: [NSFontAttributeName:UIFont(name: Font.Daydreamer, size: 70.0)!])
strokeString.addAttribute(NSForegroundColorAttributeName, value: UIColor.whiteColor(), range: NSRange(location:0,length:5))
strokeString.addAttribute(NSStrokeColorAttributeName, value: UIColor.blackColor(), range: NSRange(location: 0, length: 5))
strokeString.addAttribute(NSStrokeWidthAttributeName, value: 1, range: NSRange(location: 0, length: 5))
This is the look I am trying to recreate.
But it is coming out like this (The backgrounds are different) but as you can see the stroke is taking over the entire word: