Vikas Bansal
Vikas Bansal

Reputation: 11760

COCOA: How to create a round button. It should be clickable only in its boundary

I am trying to create a round button which is only clickable in its boundaries.

What I have done

// imported QuartzCore
#import <QuartzCore/QuartzCore.h>

//created an IBOutlet for the button
IBOutlet NSButton* btn;

//defined the button height width (same)
#define ROUND_BUTTON_WIDTH_HEIGHT 150

//set corner radius
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // Insert code here to initialize your application

    btn.frame = CGRectMake(0, 0, ROUND_BUTTON_WIDTH_HEIGHT, ROUND_BUTTON_WIDTH_HEIGHT);
    btn.layer.cornerRadius = ROUND_BUTTON_WIDTH_HEIGHT/2.0f;
}

//set view layer to YES

enter image description here

Problem

  1. The button is clickable outside its boundaries.
  2. When I am setting its position and when I am resizing the window it is getting back to its right position (the actual positon of the button is center of the window)

enter image description here

I have also tried to subclass NSButton and assign the class to the button but results are the same.

#import "roundBtn.h"
#import <QuartzCore/QuartzCore.h>

@implementation roundBtn

#define ROUND_BUTTON_WIDTH_HEIGHT 142

- (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];

    // Drawing code here.


    self.frame = CGRectMake(0, 0, ROUND_BUTTON_WIDTH_HEIGHT, ROUND_BUTTON_WIDTH_HEIGHT);
    self.layer.cornerRadius = ROUND_BUTTON_WIDTH_HEIGHT/2.0f;
}

@end

Upvotes: 0

Views: 346

Answers (1)

Lucas Derraugh
Lucas Derraugh

Reputation: 7049

You can either reimplement the button behavior using a custom NSView and then it will respect the mouseDown events of a masksToBounds layer. NSButton doesn't work this way so the only way to tell your subclassed button that it won't be hit outside of the circle is to override the -hitTest: method to detect hits within the circle:

- (NSView *)hitTest:(NSPoint)aPoint {
    NSPoint p = [self convertPoint:aPoint fromView:self.superview];
    CGFloat midX = NSMidX(self.bounds);
    CGFloat midY = NSMidY(self.bounds);
    if (sqrt(pow(p.x-midX, 2) + pow(p.y-midY, 2)) < self.bounds.size.width/2.0) {
        return self;
    }
    return nil;
}

In overriding this, you are telling the button that it will only register a hit if it is within the circle (using a little trigonometry). Returning self indicates the button was hit, returning nil indicates it was not hit.

Upvotes: 1

Related Questions