I recently updated XCode to 7.0 and I get this warning message:
Xcode.IDEInterfaceBuilder.Cocoa.NSObject.BroadSystemFontWeights
What does it mean and how do I get rid of it ?
I got the same error when I set the font weight to Semibold to a label with system font. This weight is available for the new system font (San Francisco) but not for the old Helvetica Neue, so I guess that that error means we won't get the right weight on older OS.
Changing the font to a weight available also for Helvetica Neue, Bold in my case, has fixed the error for me.
The problem is not with changing system font weight, the problem is with Xcode not handling this properly – contradicting statement, I know, see full blog post for details. There are three scenarios.
First – explicit typography is not important, regular weight is acceptable. Then stick with Marco's answer and use explicit regular weight.
Second – explicit typography is preferable, but can be compromised on older systems. This is the default behaviour right now, Xcode simply shows a warning and uses regular font on pre-10.11 targets. If you use adaptive layouts, everything should be fine. To get rid of the warning, you can simply set higher target in storyboard inspector:
Note, if your storyboard uses fallback features for earlier targets, they might become disabled, which will cause problems – I haven't come across any so far.
Third – explicit typography is a must. In this case you can use custom textfield with custom inspectable property. Open up identity inspector and set custom class to TextField, preferred font weight attribute will show up in attribute inspector, set the required value, build and enjoy the result.
import AppKit
#IBDesignable public class TextField: NSTextField
{
#IBInspectable public var preferredFontWeight: Int = 0
override public func awakeFromNib() {
if #available(OSX 10.11, *) {
return
}
guard
let weight: Int = self.preferredFontWeight where weight > 0,
let font: NSFont = self.font,
let name: String = font.familyName,
let manager: NSFontManager = NSFontManager.sharedFontManager() else {
return
}
// Full details here – https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSFontManager_Class/#//apple_ref/occ/instm/NSFontManager/convertWeight:ofFont:
//
// 1 – ultralight
// 2 – thin
// 3 – light, extralight
// 4 – book
// 5 – regular, display
// 6 – medium
// 7 – demi, demibold
// 8 – semi, semibold
// 9 – bold
// 10 – extra, extrabold
// 11 – heavy
// 12 – black
// 13 – ultrablack
// 14 – extrablack
if let font: NSFont = manager.fontWithFamily(name, traits: manager.traitsOfFont(font), weight: weight, size: font.pointSize) {
self.font = font
}
}
}
P.S. Bold weight works probably because it uses a slightly different logic – there's boldSystemFontOfSize(_:) that's available since OS X 10.0, unlike many other methods, which storyboard might rely upon.
Related
I'm looking for a way to disable scroll indicator background(of a TextEditor) in SwiftUI, macOS to only show the moving part of the indicator(like in popovers) look at examples below:
What I currently have:
What I'm looking for:
Updated to include feedback from OP that original soln proposed doesn't quite completely remove tint.
On Ventura with Xcode 14 beta 1 it's possible to come very close [0] to achieving the desired effect by adding a background modifier after the .scrollContentBackground(.hidden) extends the background under the scrollbar,
e.g.
TextEditor(text: $text)
.scrollContentBackground(.hidden)
.background(.cyan)
Where $text is a little snippet of Under Milk Wood gives
Beyond this, afaik SwiftUI's TextEditor doesnt have native api to get closer at the moment. As an alternative though - if working with underlying AppKit components and its trade-offs is acceptable - then nicer styling might be an option.
For example using the Introspect library to enable the more modern overlay scrollerStyle:
import Introspect // from https://github.com/siteline/SwiftUI-Introspect
struct ContentView: View {
#State var text = demoText
var body: some View {
TextEditor(text: $text)
.scrollContentBackground(.hidden)
.background(.cyan)
.introspectTextView { (nsV: NSTextView) in
DispatchQueue.main.async {
nsV.enclosingScrollView?.scrollerStyle = .overlay
}
}
}
}
Seems to give quite a nice effect
[0] The subtle tint that remains, is as can be seen - missable - or at least was for me :-/
I've created a custom segment control (Cocoa / macOS) by subclassing NSView (does not use any existing controls / buttons; it's an entirely custom view with a complex set of internal constraints) that has two modes:
Displays all segments horizontally by default: [ segment 1 ] [ segment 2 ] [ segment 3 ]
Displays a single segment as a drop down when all segments cannot fit in the window / current set of constraints (influenced by surrounding controls and their constraints): [ segment 1 🔽 ]
This works just fine, and I'm able to switch between / animate the two modes at runtime. However what I want to ultimately achieve is automatic expansion / compression based on the current window size (or switch between the two when the user is resizing the window). I want this control to be reusable without the window / view controller managing the switch, and trying to avoid switching between constraints based on 'rough' estimates from inside of a superview's layout call (which feels like a hack).
It seems NSSegmentControl, NSButton etc implement NSUserInterfaceCompression which should do what I am trying to achieve, however none of the methods in that protocol get called at any time during initial layout / intrinsic content size refresh / window resize etc. I also find the documentation lacking; the only useful information I found was inside the NSSegmentControl header files. The protocol seems to be exactly what I need - for the system to call the appropriate methods to determine a minimum / ideal size and ask the control to resize itself when space is at a premium.
For what it's worth, I've tried subclassing NSButton too (for various reasons, I need to stick to subclassing NSView) - however that did not trigger any of these methods either (i.e. from NSUserInterfaceCompression).
Any idea what I'm missing?
Curious... a little searching, and I can find very little information about NSUserInterfaceCompression?
Not sure what all you need to do, but something along the lines of this approach might work for you:
class SegTestView: NSView {
let segCtrl = NSSegmentedControl()
var curWidth: CGFloat = 0
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
addSubview(segCtrl)
segCtrl.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
segCtrl.topAnchor.constraint(equalTo: topAnchor),
segCtrl.leadingAnchor.constraint(equalTo: leadingAnchor),
segCtrl.trailingAnchor.constraint(equalTo: trailingAnchor),
segCtrl.bottomAnchor.constraint(equalTo: bottomAnchor),
])
}
override func layout() {
super.layout()
// only execute if bounds.width has changed
if curWidth != bounds.width {
curWidth = bounds.width
segCtrl.segmentCount = 3
segCtrl.setLabel("First", forSegment: 0)
segCtrl.setLabel("Second", forSegment: 1)
segCtrl.setLabel("Third", forSegment: 2)
if segCtrl.intrinsicContentSize.width > bounds.size.width {
segCtrl.segmentCount = 1
segCtrl.setLabel("Single 🔽", forSegment: 0)
} else {
// in case you want to do something else here...
}
}
}
}
It seems NSUserInterfaceCompression is a dead end. For now I've reported this as feedback / bug regarding the inadequate documentation (FB9062854).
The way I ended up solving this is by:
Setting the following content compression on the custom control:
let priorityToResistCompression = contentCompressionResistancePriority(for: .horizontal)
setContentCompressionResistancePriority(priorityToResistCompression, for: .horizontal)
The last segment (inner NSView subviews) within the control has a trailing anchor set with priority defaultLow to allow it to break so that the control can continue to stretch
Override setFrameSize and determine the best mode to display (compressed, single segment as a drop down, or all the segments if they can fit horizontally). Then call invalidateIntrinsicContentSize() for the content size to be recomputed.
Using the mode determined in the previous step, override intrinsicContentSize and offer the correct size (the minimum compressed version or the one where all segments can fit).
This way the control wraps all of this functionality into a single NSView subclass and relieves any superview / window hosting this control of setting the correct size as the window is resized.
I have charts with 15-17 lines and the cursor modifier cuts off the view at 6 on phones. It looks great on tablets, but on phones, as said, only shows six. Is there a way to decrease the text size in the modifier to fit more lines?
As for Android I would suggest to take a look on Custom Cursors example and use it as a base. In this example CustomXySeriesTooltip inherits from TextView which is used to render tooltip's text. So you can set font size for it like on regular TextView ( in ctor or internalUpdate override ):
public CustomXySeriesTooltip(Context context, XySeriesInfo seriesInfo) {
super(context, seriesInfo);
this.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
}
And the code for Xamarin.Android should be similar to Java one.
As for iOS - you can use dataStyle property provided by SCICursorModifierStyle which allows to specify font size:
SCITextFormattingStyle * textFormatting = [SCITextFormattingStyle new];
textFormatting.fontSize = 16;
textFormatting.fontName = #"Helvetica";
cursor.style.dataStyle = textFormatting;
Or you can create custom modifier like in this example.
El Capitan introduced San Francisco system font, which has proportional digits by default.
This makes numbers in table columns look jagged and hard to compare:
I'd like to enable fixed-width numbers option for the font, but keep using the default system font and keep backwards compatibility with earlier versions of OS X.
In Interface Builder selecting font > Font Panel > Typography > Monospaced Numbers does not affect the font (XIB file remains unchanged).
What's the right way to set monospaced numbers in OS X table view columns? (I suspect IB is unusable for this, so a programmatic solution is OK too).
Just use +[NSFont monospacedDigitSystemFontOfSize:weight:] when it's available. It's new in 10.11, but still not in the NSFont docs. It's in the headers and was discussed in the WWDC 2015 videos. So, something like:
if ([NSFont respondsToSelector:#selector(monospacedDigitSystemFontOfSize:weight:)])
textField.font = [NSFont monospacedDigitSystemFontOfSize:textField.font.pointSize weight:NSFontWeightRegular];
Here's a Swift extension that gives you a monospaced digits font with high legibility.
extension NSFont {
var legibleNumbersVariant: NSFont {
let features = [
[NSFontFeatureTypeIdentifierKey: kNumberSpacingType,
NSFontFeatureSelectorIdentifierKey: kMonospacedNumbersSelector],
[NSFontFeatureTypeIdentifierKey: kStylisticAlternativesType,
NSFontFeatureSelectorIdentifierKey: kStylisticAltSixOnSelector]
]
let descriptor = fontDescriptor.addingAttributes([NSFontFeatureSettingsAttribute: features])
return NSFont(descriptor: descriptor, size: pointSize) ?? self
}
}
Treat the following as pseudo-code, quickly done, not throughly tested, etc.
Given an NSFont which represents a font which has monospaced numbers as a feature the following method will produce another NSFont with that feature selected:
- (NSFont *) newMonospaceNumbersFont:(NSFont *)font
{
CTFontDescriptorRef origDesc = CTFontCopyFontDescriptor((__bridge CTFontRef)font);
CTFontDescriptorRef monoDesc = CTFontDescriptorCreateCopyWithFeature(origDesc, (__bridge CFNumberRef)#(kNumberSpacingType), (__bridge CFNumberRef)#(kMonospacedNumbersSelector));
CFRelease(origDesc);
CTFontRef monoFont = CTFontCreateWithFontDescriptor(monoDesc, font.pointSize, NULL);
CFRelease(monoDesc);
return (__bridge_transfer NSFont *)monoFont;
}
You can use this, say, to take the current font of a UI element and convert it to one with monospace numbers.
HTH
Variant for Swift
Assuming res is the NSTextField with the number to display:
let origDesc = CTFontCopyFontDescriptor(res.font!)
let monoDesc = CTFontDescriptorCreateCopyWithFeature(origDesc, kNumberSpacingType, kMonospacedNumbersSelector)
let monoFont = CTFontCreateWithFontDescriptor(monoDesc, res.font!.pointSize, nil)
res.font = monoFont
In my experience, the "font panel" functionality isn't well defined and I usually just ignore it whenever I'm messing with a XIB or Storyboard.
What should work is to go back to that "Font" attribute in the Text Field Cell attributes inspector and then select "User Fixed Pitch" from the Font drop down menu (the choice should automatically default to size 11).
If you bump the font size up a point, it'll magically switch to Monaco (the default fixed width font).
I have set font traits (bold, light) for several of my labels in an OSX app and now I get these warnings:
.../MainMenu.xib:9:
Xcode.IDEInterfaceBuilder.Cocoa.NSObject.BroadSystemFontWeights
without any explanation. Often the meant label isn't even selected when I click on the warnings. Can someone shed a light on what those warnings mean and how to get rid of them?
This is a warning shown starting from Xcode 7 when UI elements like a label or table view column header use a font style or variation that is not available on older OSes (and of course your project is still targeting them).
In my project a table view column header was using the system font with the "medium" font style variation instead of regular, in a project targeting OS X 10.9+.
The weird thing is I had to restart Xcode as Interface Builder refused to change the style of the control. Possibly a small glitch of this early 7.0.1 Xcode version.
Current answer almost gets it, but problem is not with font styles being unavailable in older targets, it's with Xcode not handling them properly, see full blog post for details.
If you want to keep your styles, use custom textfield with custom inspectable property. Open up identity inspector and set custom class to TextField, preferred font weight attribute will show up in attribute inspector, set the required value, build and enjoy the result.
import AppKit
#IBDesignable public class TextField: NSTextField
{
#IBInspectable public var preferredFontWeight: Int = 0
override public func awakeFromNib() {
if #available(OSX 10.11, *) {
return
}
guard
let weight: Int = self.preferredFontWeight where weight > 0,
let font: NSFont = self.font,
let name: String = font.familyName,
let manager: NSFontManager = NSFontManager.sharedFontManager() else {
return
}
// Full details here – https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSFontManager_Class/#//apple_ref/occ/instm/NSFontManager/convertWeight:ofFont:
//
// 1 – ultralight
// 2 – thin
// 3 – light, extralight
// 4 – book
// 5 – regular, display
// 6 – medium
// 7 – demi, demibold
// 8 – semi, semibold
// 9 – bold
// 10 – extra, extrabold
// 11 – heavy
// 12 – black
// 13 – ultrablack
// 14 – extrablack
if let font: NSFont = manager.fontWithFamily(name, traits: manager.traitsOfFont(font), weight: weight, size: font.pointSize) {
self.font = font
}
}
}
If you don't really care about the styles, use regular weight font for all text, it should solve the problem, see my earlier answer for available options.