If it's not possible to access a group container from a Dock Tile plugin, what would be a supported way to share data between a sandboxed app and its Dock Tile plugin?
Thans,
Marc
Post
Replies
Boosts
Views
Activity
Ok, I figured it out. The following code gives me the correct bounds of the attributed string:
- (CGRect)typographicBounds
{
CGRect usedRect = CGRectZero;
CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)self);
if (line) {
CGContextRef context = [[NSGraphicsContext currentContext] CGContext];
usedRect = CTLineGetImageBounds(line, context);
CFRelease(line);
}
return usedRect;
}
and the following code draws it exactly into the view:
NSRect stringRect = [_attributedString typographicBounds];
// draw the string
CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)_attributedString);
if (line) {
CGContextRef context = [[NSGraphicsContext currentContext] CGContext];
CGContextSetTextPosition(
context,
((NSWidth([self frame]) - NSWidth(stringRect)) / 2) - stringRect.origin.x,
((NSHeight([self frame]) - NSHeight(stringRect)) / 2) - stringRect.origin.y
);
CTLineDraw(line, context);
CFRelease(line);
}
@DTS Engineer Thank you for your patience and for showing me the right direction.
This is what I have so far:
CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)self);
if (line) {
CGContextRef context = [[NSGraphicsContext currentContext] CGContext];
CTLineDraw(line, context);
CFRelease(line);
}
Looks quite good…
Just have to figure out how to align it horizontally and vertically in the rect.
Ok. I think I also have to use Core Text for drawing the string. Right? Would be something like CGContextShowGlyphsAtPositions a good way to go?
@DTS Engineer
Thanks a lot. Seems this is what I was looking for. This allows me to figure out the best font size for a text to fit in a given rect. The only issue still see is related to drawing the string. For whatever reason half of the string is missing:
Here's the code:
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
NSDictionary *attributes = [NSDictionary dictionaryWithObject:[NSFont systemFontOfSize:10] forKey:NSFontAttributeName];
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:@"test" attributes:attributes];
// get the font size to make the string fit into the text view
CGFloat fontSize = [attrString fontSizeToFitInRect:[self bounds]
minimumFontSize:10
maximumFontSize:0
];
// change the attributed string's font size
[attrString addAttribute:NSFontAttributeName
value:[[NSFontManager sharedFontManager] convertFont:[attrString font] toSize:fontSize]
range:NSMakeRange(0, [attrString length])
];
// draw the string
[attrString drawInRect:[self frame]];
}
And here are the extensions I made to the NSAttributedString class to calculate the string's bounds:
- (CGFloat)fontSizeToFitInRect:(NSRect)rect
minimumFontSize:(CGFloat)minFontSize
maximumFontSize:(CGFloat)maxFontSize
{
CGFloat fontSize = (maxFontSize > minFontSize) ? maxFontSize : NSHeight(rect) * 2;
CGFloat textHeight = CGFLOAT_MAX;
CGFloat textWidth = CGFLOAT_MAX;
while ((textHeight > NSHeight(rect) || textWidth > NSWidth(rect)) && fontSize >= minFontSize) {
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithAttributedString:self];
[attrString addAttribute:NSFontAttributeName
value:[[NSFontManager sharedFontManager] convertFont:[self font] toSize:--fontSize]
range:NSMakeRange(0, [[self string] length])
];
CGRect usedRect = [attrString bounds];
textHeight = ceil(NSHeight(usedRect));
textWidth = ceil(NSWidth(usedRect));
}
return fontSize;
}
- (CGRect)bounds
{
CGRect usedRect = CGRectZero;
CTLineRef line = CTLineCreateWithAttributedString((CFAttributedStringRef)self);
NSArray *ctRuns = (__bridge NSArray*)CTLineGetGlyphRuns(line);
for (id ctRun in ctRuns) {
NSDictionary *attributes = (__bridge NSDictionary *)CTRunGetAttributes((CTRunRef)ctRun);
id font = attributes[(NSString *)kCTFontAttributeName];
if (font) {
size_t runGlyphsCount = CTRunGetGlyphCount((CTRunRef)ctRun);
CGGlyph glyphs[runGlyphsCount];
CTRunGetGlyphs((CTRunRef)ctRun, CFRangeMake(0, 0), glyphs);
CGRect runRects[runGlyphsCount];
CTFontGetBoundingRectsForGlyphs((CTFontRef)font, kCTFontOrientationDefault, glyphs, runRects, runGlyphsCount);
for (size_t i = 0; i < runGlyphsCount; i++) {
CGRect rect = runRects[i];
CGFloat width = usedRect.size.width + rect.size.width;
CGFloat height = MAX(usedRect.size.height, rect.size.height);
CGFloat y = MIN(usedRect.origin.y, rect.origin.y);
usedRect = CGRectMake(usedRect.origin.x, y, width, height);
}
} else {
break;
}
}
return usedRect;
}
The string rect is a bit smaller than the rectangle of the view it should be drawn into. So I wonder why the string is cut off. With upper and lower case characters it looks like this:
Any idea? Thank you very much.
Regards,
Marc
@DTS Engineer
Thanks, again. I tried your code but it returns more or less the same rect as boundingRectWithSize:options:context:. So it returns the exact same height for a lower case "x" and an upper case "X":
x -> {{-66, 0}, {132, 303.63671875}}
X -> {{-82.88671875, 0}, {165.7734375, 303.63671875}}
Any idea? Thanks…
@DTS Engineer
Thank you! This looks very promising. In the sample code it works as expected but if I try to create a NSTextLineFragment from an attributed string,typographicBounds always returns {{0, 0}, {0, 0}}…
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:@"test"];
[attrString addAttribute:NSFontAttributeName
value:[NSFont systemFontOfSize:90]
range:NSMakeRange(0, [[attrString string] length])
];
NSTextLineFragment *fragment = [[NSTextLineFragment alloc] initWithAttributedString:attrString
range:NSMakeRange(0, [[attrString string] length])
];
CGRect usedRect = [fragment typographicBounds];
For me it looks like boundingRectWithSize:options:context: just uses the attributed string's font (and its attributes) to calculate a bounding rectangle that could hold any combination of characters with the specific font and font size. The height of the bounding rect is more or less the same, regardless of what combinations of characters I use in the string. So the question is how to get the real bounding rect… 🤷🏻♂️
This is how I try to get the font size at the moment but it seems the value is always a bit too small…
- (CGFloat)fontSizeToFitInRect:(NSRect)rect
minimumFontSize:(CGFloat)minFontSize
maximumFontSize:(CGFloat)maxFontSize
{
CGFloat fontSize = (maxFontSize > minFontSize) ? maxFontSize : NSHeight(rect);
CGFloat textHeight = CGFLOAT_MAX;
CGFloat textWidth = CGFLOAT_MAX;
while ((textHeight > NSHeight(rect) || textWidth > NSWidth(rect)) && fontSize >= minFontSize) {
NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithAttributedString:self];
[attrString addAttribute:NSFontAttributeName
value:[[NSFontManager sharedFontManager] convertFont:[self font] toSize:--fontSize]
range:NSMakeRange(0, [[self string] length])
];
CGRect usedRect = [attrString boundingRectWithSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)
options:0
context:nil
];
textHeight = ceil(NSHeight(usedRect));
textWidth = ceil(NSWidth(usedRect));
}
return fontSize;
}
@Claude31 This is more or less what I am actually doing but the returned bounding rect seems to have some sort of spacing around the text.
@DTS Engineer
I don't think that I misuse NSUserDefaults. In the app the user can save different configurations and they will stored in NSUserDefaults as an array of dictionaries. This works as expected. But now I got the request to allow the user to set one of the configurations as the default configuration. So I just wanted to add an additional key to one of the dictionaries in the array to mark it as the default.
I also tried "mutableArrayForKeyPath" but even if using this, the user defaults are not updated…
FB14511046
@DTS Engineer
It fails in Xcode 16 beta 3.
Also tried your sample project from here: https://developer.apple.com/documentation/security/constraining_a_tool_s_launch_environment but as soon as I upload it for notarizing the constraints also disappear. So the issue seems not to be related to my project.
@DTS Engineer
I tried this with Xcode 16 beta 3.
Thank you, @DTS Engineer. The correct syntax seems to be:
<dict>
<key>$or-array</key>
<array>
<array>
<string>$or</string>
<dict>
<key>launch-type</key>
<dict>
<key>$lt</key>
<integer>3</integer>
</dict>
</dict>
</array>
<array>
<string>$or</string>
<dict>
<key>launch-type</key>
<dict>
<key>$gt</key>
<integer>3</integer>
</dict>
</dict>
</array>
</array>
</dict>
Regards,
Marc