Use SwiftUI's Font.largeTitle when NSFont is expected - swift

I am still learning SwiftUI & dev in general. Anything NSWhatever related still throws me off.
And I know that my issue is just a lack of understanding :D
Rn, I am building a SwiftUI macOS 11 app & it works pretty well... but SwiftUI has its limits.
Now, I want to use a NSTextField instead of SwiftUI's TextField(), bc custom UI etc...
My code:
func makeNSView(context: Context) -> NSTextField {
let textField = FocusAwareTextField()
textField.placeholderAttributedString = NSAttributedString(
string: placeholder,
attributes: [
NSAttributedString.Key.foregroundColor: NSColor(Color.secondary),
NSAttributedString.Key.font: NSFont(name: "SF Pro", size: 32)!
]
)
textField.isBordered = false
textField.delegate = context.coordinator
textField.backgroundColor = NSColor.clear
...
...
...
My issue:
NSAttributedString.Key.font: expects a NSFont.
And the above code builds fine but I'd rather stick to SwiftUI's Dynamic Font System and use Font.largeTitle.bold() instead of manually defining to use SF Pro and a font size.
I know how to convert Color.black to NSColor(Color.black) but didn't find a working example for Fonts.
Also, I'd appreciate it if someone could actually explain what is going on so I can understand, instead of just doing copy & paste.
Thank you so much!

Related

NSMenuItem with attributedTitle containing an NSFont object draws the title with baseline shift

I'm tying to create an NSPopUpButton with the list of fonts available in the system. Seemed pretty obvious task but I've failed. I guess, I'm missing something so obvious that I've completely forgot about it.
The code is pretty straight:
let button = NSPopUpButton()
button.menu = NSMenu()
NSFontManager.shared.availableFonts.forEach { fontNameString in
let item = NSMenuItem()
let font = NSFont(name: fontNameString, size: 14)!
let attrs: [NSAttributedString.Key: Any] = [.font: font]
item.attributedTitle = NSAttributedString(string: fontNameString, attributes: attrs)
button.menu?.addItem(item)
}
But that just creates the NSMenu with items having shifted baselines. I've tried to calculate the baseline offset and add it as an attribute but I've failed. Haven't found an algorhythm to satisfy all the font available in the system.
Besides, adding the baseline offset resizes the corresponding NSMenuItem which does not have a fixed size, by the way - the height of an item is different on every font.
To analyse the situation I've added the .backgroundColor attribute and set it to red NSColor. And that brought even more confusing. It appears that some font somehow not drawing in bounds.
How can I center the attributed title vertically? Please, help!
Probably, that is NSAttributedString's issue.
To workaround, I created a custom view and drew a string in it with a trick.
Then set it to NSMenuItem.view.
Get more details, see my codes below.
https://github.com/bluedome/FontSelectionView/blob/main/FontSelectionView.swift
Hope it help, if you're still having the trouble...

Strikethrough not working in CoreText on macOS

Strikethrough is not being displayed, but underline does. The code is as below, its fairly straight forward. When I comment out the underline the text is displayed without a strikethrough, when I comment out the strikethrough and display the underline it works fine. I've tried everything — I must be missing something obvious, the docs say strikeout should work.
I'm running macOS 10.13.6 and Xcode 10.1.
import AppKit
import PlaygroundSupport
class CustomView: NSView {
override func draw(_ dirtyRect: NSRect) {
let attrString = NSAttributedString(
string: "Hello",
attributes: [
//NSAttributedString.Key.underlineStyle: NSUnderlineStyle.thick.rawValue,
NSAttributedString.Key.strikethroughStyle: NSUnderlineStyle.thick.rawValue
]
)
let line = CTLineCreateWithAttributedString(attrString)
// Set text position and draw the line into the graphics context.
let context = (NSGraphicsContext.current?.cgContext)!
context.translateBy(x: 10, y: 10)
CTLineDraw(line, context)
}
}
let customView = CustomView(frame: NSRect(x: 0, y: 0, width: 400, height: 400))
PlaygroundPage.current.liveView = customView
Playground settings. To run this in a Xcode Playground just be sure to change the platform to macOS in the inspector settings (all new playgrounds are set to iOS by default).
CoreText does not have native support for the strikethrough typographic text decoration (but supports underlines just fine as you noted in your question). The full list of string attributes supported by CoreText is described here.
This old, Objective-C based blog post provides the necessary details to implement strikethrough manually using CoreText APIs. Unfortunately, not a quick fix at all.
The higher-level TextKit framework does provide strikethrough support though. To quickly see that working just replace this line in your original code:
CTLineDraw(line, context)
with this instead:
attrString.draw(at: .zero)
And, by the way, that somehow helps validate that there was nothing wrong with your original NSAttributedString to begin with ;)
Of course, depending on how much your code relies on CoreText, switching over to TextKit might be a non-trivial task, so you need to keep that in mind as well.

Blurry font rendering on non-retina Macs when using NSAttributedString drawing

I'm using an NSAttributedString's draw(at:) function inside of an NSView to render some text in a custom font inside my window.
However, the font looks weirdly blurry and "too heavy" when run on a non-retina MacBook (both on the internal display & on an external LCD).
Since I'm able to perfectly reproduce the desired outcome in Sketch on the same machine, I'm assuming this to be an issue with my code.
Here's my code so far:
import Cocoa
class StepNameLabel: NSView {
// Im aware that this is not the intended way of loading Apple's system fonts
// However, this was the best way I could find to make sure that both Sketch
// and the app were using the exact same fonts.
var font = NSFont(name: "SF Pro Text Semibold", size: 22)
override func draw(_ dirtyRect: NSRect) {
super.draw(dirtyRect)
let text = "Choose your Images"
let attributes: [NSAttributedString.Key: Any] = [
.font: font,
.foregroundColor: NSColor.white
]
print(font?.fontName)
let drawableString = NSAttributedString(string: text, attributes: attributes)
frame.size = drawableString.size()
drawableString.draw(at: NSPoint(x: 0, y: 0))
}
}
And here's a screenshot showing the difference between the Sketch-File & the app running on the same display (left: Graphic in Sketch app, right: The output of above code):
The app's code & the Sketch graphic both use Apple's "SF Pro Text" font with a font-weight of "Semibold" at a size of 22 units.
Any help in finding out what's going wrong here would be greatly appreciated.
This might be the infamous 'half pixel' problem. Try:
drawableString.draw(at: NSPoint(x: 0.5, y: 0.5))
There is some information about this here (search the page for 'Points and Pixels').

Changing WKInterfaceLabel Text

I currently have:
#IBOutlet var label: WKInterfaceLabel!
and
let myString = "Swift Attributed String"
let myAttribute = [ NSAttributedStringKey.foregroundColor: UIColor.blue ]
let myAttrString = NSAttributedString(string: myString, attributes: myAttribute)
label.setAttributedText(myAttrString)
Currently this code does not change the text of the label. Is there something obvious I am missing? Thanks for any help!
Your code is working perfectly fine but for only textcolor
You do not need to use attributed property you can directly use setTextColor() property.
label.setTextColor(UIColor.blue)
label.setText(myString)
You can refer WKInterfaceLabel and setTextColor

Changing the font size of an NSButton doesn't change anything

I'm working on my first Swift project (hooray), which is an app for Mac OS. What I want to do now is create some buttons, with a white text color and a custom font. Because I needed a new attribute on that button to hold some data, I made a child class of NSButton.
Now, I know that I can set the font of a button like this:
super.font = NSFont(name: "NB-International-Pro", size: 40)
Which worked like a charm. After that I tried to change the color of the text by doing this:
let pstyle = NSMutableParagraphStyle()
pstyle.alignment = .center
super.attributedTitle = NSAttributedString(string: (device?.name)!, attributes: [ NSForegroundColorAttributeName : NSColor.white, NSParagraphStyleAttributeName : pstyle ])
Which did work, and it did change the color of the text, however, now the font and font size are back to default. Is there an option to do both?
Thanks.
Instead of setting the font first, include the font name and size in your attributes when creating your NSAttributedString by using the NSFontAttributeName and NSFontSizeAttribute keys:
attributedTitle = NSAttributedString(string: (device?.name)!, attributes: [ NSForegroundColorAttributeName : NSColor.white, NSParagraphStyleAttributeName : pstyle, NSFontAttributeName: "NB-International-Pro", NSFontSizeAttribute: 40])
Beyond that there's probably some improvements you can make to the architecture of your code. Subclassing NSButton is not really needed just for changing text style (can be done either in .xib or your view controller).