Question

I would like to make an NSSearchField, just like the one in Ecoute 3.0. The search icon in the search field should be clickable and expand and collapse if you click it.

enter image description here enter image description here

How can you achieve this? I have searched the documentation, but didn't come up with anything useful. I could implement the expanding with the animator proxy, so that is clear.

Another thing is that the background fades in/fades out as it expands/collapses. How can I achieve this?

I would be thankful for any advices, sample code, etc.

EDIT

Ok I found the first part out myself.

- (void) resetSearchButtonCell {
    NSButtonCell *c= [[NSButtonCell alloc] init];
    [c setButtonType:NSMomentaryChangeButton];  // configure the button
    [c setBezelStyle:NSRegularSquareBezelStyle];    // configure the button
    [c setBordered:NO];
    [c setBezeled:NO];
    [c setEditable:NO];
    [c setImagePosition:NSImageOnly];
    [c setImage:[NSImage imageNamed:@"search"]];
    [c setAlternateImage:[NSImage imageNamed:@"search-pushed"]];
    [c setTarget:self];
    [c setAction:@selector(_search:)];
    [self setSearchButtonCell:c];
}

Now, I have made a property isCollapsed, which I toggle in _search:. And in the setIsCollapsed: setter method I do the following:

NSRect newRect = self.frame;

if (_isCollapsed) {
    newRect.size.width = kCollapsedWidth;
} else {
    newRect.size.width = kEXpandedWidth;
}

[[self animator] setFrameSize:newRect.size];

But for some reason the frame is being reset to the initial size, the one set in Interface Builder, when the NSSearchField gets or looses focus.

Can anyone tell me why?

EDIT

I have solved that problem too. It was auto-layout, which messed up the animation. Now, the only thing remaining is the background-alpha, which should change. I could redraw the entire thing, but is there another solution where I could just modify the alpha of only the background and the cancel-button?

Was it helpful?

Solution 2

I ended up redrawing the background. Like this I was able to set the alpha of it.

The source code is available on Github:

https://github.com/iluuu1994/ITSearchField

OTHER TIPS

I wanted to do something similar. ITSearchField is very solid and nice. However, I desired to expand the search field once it gained focus. As one might find out quickly the NSSearchfield is comprised of many components and layers.

Here is what I wrote to get it to work... Note: It works on 10.7 or greater. Also you must hook-up the layout constraint of the search field control's width in IB to the outlet.

.h

@interface MySearchField : NSSearchField 
@end

.m

#pragma mark - Implementation: Search Field Control

@interface MySearchField()
@property (readwrite, nonatomic, getter=isExpanded) BOOL expand;
@property (readwrite, nonatomic, getter=hasFocusRing) BOOL focusRing;
@property (readwrite, strong, nonatomic) NSTimer *expandTimer;
@property (readwrite, strong, nonatomic) IBOutlet NSLayoutConstraint *widthConstraint;
@property (readwrite, nonatomic) CGFloat searchWidth;
@end

static NSTimeInterval const kSearchFieldTime = 0.5;

@implementation MySearchField

@synthesize expand, focusRing;
@synthesize expandTimer, searchWidth;
@synthesize widthConstraint;

#pragma mark Timer Methods:

- (void) resizeSearchField {


    if ( self.hasFocusRing ) {

        if ( !self.isExpanded ) {

            self.searchWidth = self.widthConstraint.constant;
            [[self.widthConstraint animator] setConstant:(self.searchWidth * 2)];
            self.expand = YES;

    } } else {

        if ( (self.isExpanded) &&
            ((!self.stringValue) || ([self.stringValue isEqualToString:@""])) ) {

            [[self.widthConstraint animator] setConstant:self.searchWidth];
            self.expand = NO;
    } }
}

- (void) stopTimer {

    if ( self.expandTimer )
        [self.expandTimer invalidate];

    self.expandTimer = nil;
}

- (void) startTimer {

    [self stopTimer];

    SEL resizeSelector = @selector(resizeSearchField);
    NSMethodSignature *expandSignature = [MySearchField instanceMethodSignatureForSelector:resizeSelector];
    NSInvocation *expandInvocation = [NSInvocation invocationWithMethodSignature:expandSignature];
    [expandInvocation setSelector:resizeSelector];
    [expandInvocation setTarget:self];

    self.expandTimer = [NSTimer scheduledTimerWithTimeInterval:kSearchFieldTime invocation:expandInvocation repeats:NO];
}

#pragma mark Override Methods:

- (void) noteFocusRingMaskChanged {

    self.focusRing = !NSEqualRects(NSZeroRect, self.focusRingMaskBounds);
    [self startTimer];
    [super noteFocusRingMaskChanged];
}

@end

Ok that's it really. You get notified when the focus ring changes and start a quick timer method. The timer is necessary for when you click the search field's cancel button which resets the control. The only thing the timer does is fire the resizeSearchField method.

Update: Oh yeah, don't forget to set the class of your Search Field control in IB to MySearchField or whatever you want to call it.

Enjoy!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top