开发者

Reduce inner margins of the tokens of NSTokenField

I feel the tokens in NSTokenField have way too much inner margins, i.e. i believe the two semicircles (on the each side) should be closer to the text. The default setting wastes way too much space.

How do 开发者_StackOverflow社区I reduce these margins, and make the tokens more compact?


With the Objective-C runtime, there seems to be a way to accomplish this without subclassing a private class. This may not get around App Store requirements, though.

To use the Objective-C runtime, add

#import <objc/runtime.h>

to the top of the file where you want to make the modification to tokens. In an @implementation of a class or category in this file (perhaps a category on NSTokenField or NSTokenFieldCell), add

static NSSize (*kOriginalCellSizeForBounds)(id, SEL, NSRect);

NSSize cellSizeForBounds_override(id self, SEL _cmd, NSRect rect)
{
    NSSize size = kOriginalCellSizeForBounds(self, _cmd, rect);
    size.width -= 10;
    return size;
}

static NSRect (*kOriginalTitleRectForBounds)(id, SEL, NSRect);

NSRect titleRectForBounds_override(id self, SEL _cmd, NSRect rect)
{
    NSRect titleRect = kOriginalTitleRectForBounds(self, _cmd, rect);
    titleRect = NSInsetRect(rect, -5, 0);
    return titleRect;
}

+ (void)load
{
    Class tokenAttachmentCellClass = objc_getClass("NSTokenAttachmentCell");

    SEL selector = @selector(cellSizeForBounds:);
    Method originalMethod = class_getInstanceMethod(tokenAttachmentCellClass, selector);
    kOriginalCellSizeForBounds = (void *)method_getImplementation(originalMethod);
    if(!class_addMethod(tokenAttachmentCellClass, selector, (IMP)cellSizeForBounds_override, method_getTypeEncoding(originalMethod))) {
        method_setImplementation(originalMethod, (IMP)cellSizeForBounds_override);
    }

    selector = @selector(titleRectForBounds:);
    originalMethod = class_getInstanceMethod(tokenAttachmentCellClass, selector);
    kOriginalTitleRectForBounds = (void *)method_getImplementation(originalMethod);
    if(!class_addMethod(tokenAttachmentCellClass, selector, (IMP)titleRectForBounds_override, method_getTypeEncoding(originalMethod))) {
        method_setImplementation(originalMethod, (IMP)titleRectForBounds_override);
    }
}

What’s happening here is that we’re reducing the original width of the token (in cellSizeForBounds_override()) and proportionally increasing the width of the cell’s “title” (in titleRectForBounds_override()). The result is a token with reduced horizontal margins that still works properly in an NSTokenField. You can tinker with the amount of reduction in width to get the effect you want.

You can read more about method swizzling in Mike Ash’s article, “Method Replacement for Fun and Profit”; I’m using “Direct Override” method swizzling.


The answer is similar to this one: How do I set the column margins in an NSTableView?

You'll need to subclass NSTextFieldCell and override -titleRectForBounds: to pass a width-inset copy of the rectangle super returns.


I had to do some subclassing of a class which is not a part of the public API. I got an idea of what to do through the code of BWTokenField in BWToolkit. It works, but I would have wanted to do it without public APIs, as I might want to go on the Mac App Store one day. I have submitted a bug report asking for making the API public, and they marked the report as a duplicate. I'm not optimistic they'll do anything about it.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜