《macOS开发》自定义控件之NSButton
2017-08-18 本文已影响211人
九剑仙
控件功能简介:
1. 可设置(默认/选中)背景(图片/颜色)
2. 可设置(默认/选中)文字(内容/颜色/字体样式)
4. 可设置文字下划线样式
5. 可设置文字对齐格式
6. 可设置任意圆角
7. 当鼠标移动到控件位置时,鼠标变为"小手"
8. 当鼠标移动出控件,鼠标变为"箭头"
9. 基本的点击功能
#import <Cocoa/Cocoa.h>
typedef enum {
LLRectCornerTopLeft = 1 << 0,
LLRectCornerTopRight = 1 << 1,
LLRectCornerBottomLeft = 1 << 2,
LLRectCornerBottomRight = 1 << 3,
LLRectCornerAllCorners = ~0UL
} LLRectCorner;
typedef enum {
LLTextAlignmentLeft = 0, //左对齐
LLTextAlignmentCenter, //居中
LLTextAlignmentRight //右对齐
}LLTextAlignment;
typedef enum {
LLTextUnderLineStyleNone = 0, //无下划线
LLTextUnderLineStyleSingle, //单下划线
LLTextUnderLineStyleDouble, //双下划线
LLTextUnderLineStyleDeleteSingle, //单删除线
LLTextUnderLineStyleDeleteDouble //双删除线
}LLTextUnderLineStyle;
@interface LLCustomBtn : NSView
@property (nullable, weak) id target;
@property (nullable) SEL action;
///当鼠标移动到控件时,是否显示"小手"
@property (nonatomic, assign) BOOL isHandCursor;
///圆角
@property (nonatomic, assign) CGFloat radius;
@property (nonatomic, assign) LLRectCorner rectCorners;
///按钮文字
@property (nonatomic, nullable, strong) NSString *defaultTitle;
@property (nonatomic, nullable, strong) NSString *selectedTitle;
///按钮文字对齐方式
@property (nonatomic, assign) LLTextAlignment textAlignment;
///按钮文字下划线样式
@property (nonatomic, assign) LLTextUnderLineStyle textUnderLineStyle;
///按钮文字颜色
@property (nonatomic, nullable, strong) NSColor *defaultTitleColor;
@property (nonatomic, nullable, strong) NSColor *selectedTitleColor;
///按钮字体
@property (nonatomic, nullable, strong) NSFont *defaultFont;
@property (nonatomic, nullable, strong) NSFont *selectedFont;
///当背景图片存在时,背景色无效
@property (nonatomic, nullable, strong) NSImage *defaultBackgroundImage;
@property (nonatomic, nullable, strong) NSImage *selectedBackgroundImage;
///当背景图片不存在时,显示背景色
@property (nonatomic, nullable, strong) NSColor *defaultBackgroundColor;
@property (nonatomic, nullable, strong) NSColor *selectedBackgroundColor;
@end
#import "LLCustomBtn.h"
#import <objc/message.h>
#define LLMsgSend(...) ((void (*)(void *, SEL, id))objc_msgSend)(__VA_ARGS__)
#define LLMsgTarget(target) (__bridge void *)(target)
@interface LLCustomBtn () {
NSTrackingArea *_trackingArea;
}
@property (nonatomic,assign) BOOL mouseDown;
@end
@implementation LLCustomBtn
- (void)setMouseDown:(BOOL)mouseDown {
if (_mouseDown == mouseDown) return;
_mouseDown = mouseDown;
[self setNeedsDisplay];
}
///圆角
- (void)setRectCorners:(LLRectCorner)rectCorners {
if (_rectCorners == rectCorners) return;
_rectCorners = rectCorners;
[self setNeedsDisplay];
}
///半径
- (void)setRadius:(CGFloat)radius {
if (_radius == radius) return;
_radius = radius;
[self setNeedsDisplay];
}
///按钮文字
- (void)setDefaultTitle:(NSString *)defaultTitle {
if ([_defaultTitle isEqualToString:defaultTitle]) return;
_defaultTitle = defaultTitle;
[self setNeedsDisplay];
}
- (void)setSelectedTitle:(NSString *)selectedTitle {
if ([_selectedTitle isEqualToString:selectedTitle]) return;
_selectedTitle = selectedTitle;
[self setNeedsDisplay];
}
///按钮文字对齐方式
- (void)setTextAlignment:(LLTextAlignment)textAlignment {
if (_textAlignment == textAlignment) return;
_textAlignment = textAlignment;
[self setNeedsDisplay];
}
///按钮文字下划线样式
- (void)setTextUnderLineStyle:(LLTextUnderLineStyle)textUnderLineStyle {
if (_textUnderLineStyle == textUnderLineStyle) return;
_textUnderLineStyle = textUnderLineStyle;
[self setNeedsDisplay];
}
///按钮文字颜色
- (void)setDefaultTitleColor:(NSColor *)defaultTitleColor {
if (_defaultTitleColor == defaultTitleColor) return;
_defaultTitleColor = defaultTitleColor;
[self setNeedsDisplay];
}
- (void)setSelectedTitleColor:(NSColor *)selectedTitleColor {
if (_selectedTitleColor == selectedTitleColor) return;
_selectedTitleColor = selectedTitleColor;
[self setNeedsDisplay];
}
///按钮字体
- (void)setDefaultFont:(NSFont *)defaultFont {
if (_defaultFont == defaultFont) return;
_defaultFont = defaultFont;
[self setNeedsDisplay];
}
- (void)setSelectedFont:(NSFont *)selectedFont {
if (_selectedFont == selectedFont) return;
_selectedFont = selectedFont;
[self setNeedsDisplay];
}
///当背景图片存在时,背景色无效
- (void)setDefaultBackgroundImage:(NSImage *)defaultBackgroundImage {
if (_defaultBackgroundImage == defaultBackgroundImage) return;
_defaultBackgroundImage = defaultBackgroundImage;
[self setNeedsDisplay];
}
- (void)setSelectedBackgroundImage:(NSImage *)selectedBackgroundImage {
if (_selectedBackgroundImage == selectedBackgroundImage) return;
_selectedBackgroundImage = selectedBackgroundImage;
[self setNeedsDisplay];
}
///当背景图片不存在时,显示背景色
- (void)setDefaultBackgroundColor:(NSColor *)defaultBackgroundColor {
if (_defaultBackgroundColor == defaultBackgroundColor) return;
_defaultBackgroundColor = defaultBackgroundColor;
[self setNeedsDisplay];
}
- (void)setSelectedBackgroundColor:(NSColor *)selectedBackgroundColor {
if (_selectedBackgroundColor == selectedBackgroundColor) return;
_selectedBackgroundColor = selectedBackgroundColor;
[self setNeedsDisplay];
}
- (void)setNeedsDisplay {
if (self.superview) {
[self setNeedsDisplay:YES];
}
}
-(void)updateTrackingAreas {
if (_trackingArea == nil) {
_trackingArea = [[NSTrackingArea alloc] initWithRect:self.bounds
options:NSTrackingMouseEnteredAndExited|NSTrackingActiveInKeyWindow
owner:self
userInfo:nil];
[self addTrackingArea:_trackingArea];
}
}
-(void)mouseEntered:(NSEvent *)theEvent{
if (_isHandCursor == NO) return;
[[NSCursor pointingHandCursor] set];
}
-(void)mouseExited:(NSEvent *)theEvent{
if (_isHandCursor == NO) return;
[[NSCursor arrowCursor] set];
}
- (void)mouseDown:(NSEvent *)event {
NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
if (CGRectContainsPoint(self.bounds, point)) {
self.mouseDown = YES;
}
}
- (void)mouseUp:(NSEvent *)event {
if (self.mouseDown) {
self.mouseDown = NO;
[self setNeedsDisplay:YES];
NSPoint point = [self convertPoint:[event locationInWindow] fromView:nil];
if (CGRectContainsPoint(self.bounds, point)) {
if (self.target && self.action && [self.target respondsToSelector:self.action]) {
LLMsgSend(LLMsgTarget(self.target), self.action, self);
}
}
}
}
- (void)drawRect:(NSRect)dirtyRect {
NSString *title = nil;
NSFont *font = nil;
NSColor *titleColor = nil;
NSColor *backgroundColor = nil;
NSImage *backgroundImage = nil;
if (self.mouseDown) {
title = self.selectedTitle;
font = self.selectedFont;
titleColor = self.selectedTitleColor;
backgroundColor = self.selectedBackgroundColor;
backgroundImage = self.selectedBackgroundImage;
if (title == nil) {
title = self.defaultTitle;
}
if (font == nil) {
font = self.defaultFont;
}
if (titleColor == nil) {
titleColor = self.defaultTitleColor;
}
if (backgroundColor == nil) {
backgroundColor = self.defaultBackgroundColor;
}
if (backgroundImage == nil) {
backgroundImage = self.defaultBackgroundImage;
}
}
else {
title = self.defaultTitle;
font = self.defaultFont;
titleColor = self.defaultTitleColor;
backgroundColor = self.defaultBackgroundColor;
backgroundImage = self.defaultBackgroundImage;
}
if (title == nil) {
title = @"按钮";
}
if (font == nil) {
font = [NSFont systemFontOfSize:17];
}
if (titleColor == nil) {
titleColor = [NSColor blackColor];
}
if (backgroundImage) {
backgroundColor = [NSColor colorWithPatternImage:backgroundImage];
}
else {
if (backgroundColor == nil) {
backgroundColor = [NSColor clearColor];
}
}
if (_rectCorners) {
NSBezierPath *bezierPath;
if (_rectCorners == LLRectCornerAllCorners) {
bezierPath = [NSBezierPath bezierPathWithRoundedRect:dirtyRect xRadius:_radius yRadius:_radius];
}
else {
bezierPath = [NSBezierPath bezierPath];
CGFloat topRightRadius = 0.0, topLeftRadius = 0.0, bottomLeftRadius = 0.0, bottomRightRadius = 0.0;
if (_rectCorners & LLRectCornerTopRight) {
topRightRadius = _radius;
}
if (_rectCorners & LLRectCornerTopLeft) {
topLeftRadius = _radius;
}
if (_rectCorners & LLRectCornerBottomLeft) {
bottomLeftRadius = _radius;
}
if (_rectCorners & LLRectCornerBottomRight) {
bottomRightRadius = _radius;
}
//右上
CGPoint topRightPoint = CGPointMake(dirtyRect.origin.x+dirtyRect.size.width, dirtyRect.origin.y+dirtyRect.size.height);
topRightPoint.x -= topRightRadius;
topRightPoint.y -= topRightRadius;
[bezierPath appendBezierPathWithArcWithCenter:topRightPoint radius:topRightRadius startAngle:0 endAngle:90];
//左上
CGPoint topLeftPoint = CGPointMake(dirtyRect.origin.x, dirtyRect.origin.y+dirtyRect.size.height);
topLeftPoint.x += topLeftRadius;
topLeftPoint.y -= topLeftRadius;
[bezierPath appendBezierPathWithArcWithCenter:topLeftPoint radius:topLeftRadius startAngle:90 endAngle:180];
//左下
CGPoint bottomLeftPoint = dirtyRect.origin;
bottomLeftPoint.x += bottomLeftRadius;
bottomLeftPoint.y += bottomLeftRadius;
[bezierPath appendBezierPathWithArcWithCenter:bottomLeftPoint radius:bottomLeftRadius startAngle:180 endAngle:270];
//右下
CGPoint bottomRightPoint = CGPointMake(dirtyRect.origin.x+dirtyRect.size.width, dirtyRect.origin.y);
bottomRightPoint.x -= bottomRightRadius;
bottomRightPoint.y += bottomRightRadius;
[bezierPath appendBezierPathWithArcWithCenter:bottomRightPoint radius:bottomRightRadius startAngle:270 endAngle:360];
}
[backgroundColor setFill];
[bezierPath fill];
}
else {
[backgroundColor setFill];
NSRectFill(dirtyRect);
}
if (title) {
//绘制文字
NSMutableAttributedString *attTitle = [[NSMutableAttributedString alloc] initWithString:title];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc]init];
paragraphStyle.lineSpacing = 1;
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
NSDictionary *attributes = @{NSFontAttributeName:font,
NSParagraphStyleAttributeName:paragraphStyle,
NSForegroundColorAttributeName:titleColor};
[attTitle addAttributes:attributes range:NSMakeRange(0, attTitle.length)];
if (self.textUnderLineStyle == LLTextUnderLineStyleSingle) {
NSUnderlineStyle style = NSUnderlineStyleSingle;
[attTitle addAttributes:@{NSUnderlineStyleAttributeName:@(style)} range:NSMakeRange(0, attTitle.length)];
[attTitle addAttributes:@{NSUnderlineColorAttributeName:titleColor} range:NSMakeRange(0, attTitle.length)];
}
else if (self.textUnderLineStyle == LLTextUnderLineStyleDouble) {
NSUnderlineStyle style = NSUnderlineStyleDouble;
[attTitle addAttributes:@{NSUnderlineStyleAttributeName:@(style)} range:NSMakeRange(0, attTitle.length)];
[attTitle addAttributes:@{NSUnderlineColorAttributeName:titleColor} range:NSMakeRange(0, attTitle.length)];
}
else if (self.textUnderLineStyle == LLTextUnderLineStyleDeleteSingle) {
[attTitle addAttributes:@{NSStrikethroughStyleAttributeName:@(NSUnderlinePatternSolid|NSUnderlineStyleSingle),
NSStrikethroughColorAttributeName:titleColor}
range:NSMakeRange(0, attTitle.length)];
}
else if (self.textUnderLineStyle == LLTextUnderLineStyleDeleteDouble) {
[attTitle addAttributes:@{NSStrikethroughStyleAttributeName:@(NSUnderlinePatternSolid|NSUnderlineStyleDouble),
NSStrikethroughColorAttributeName:titleColor}
range:NSMakeRange(0, attTitle.length)];
}
CGSize titleSize = [attTitle.string boundingRectWithSize:CGSizeMake(self.bounds.size.width, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:attributes context:nil].size;
CGRect titleRect;
if (self.textAlignment == LLTextAlignmentLeft) {
titleRect = CGRectMake(0,
(self.bounds.size.height-titleSize.height)/2.0,
titleSize.width,
titleSize.height);
}
else if (self.textAlignment == LLTextAlignmentCenter) {
titleRect = CGRectMake((self.bounds.size.width-titleSize.width)/2.0,
(self.bounds.size.height-titleSize.height)/2.0,
titleSize.width,
titleSize.height);
}
else {
titleRect = CGRectMake((self.bounds.size.width-titleSize.width),
(self.bounds.size.height-titleSize.height)/2.0,
titleSize.width,
titleSize.height);
}
[attTitle drawInRect:titleRect];
}
}
- (void)removeFromSuperview {
if (_trackingArea) {
[self removeTrackingArea:_trackingArea];
}
[super removeFromSuperview];
}
@end
//使用方法
LLCustomBtn *btn = [[LLCustomBtn alloc] initWithFrame:CGRectMake(200, 200, 100, 20)];
btn.isHandCursor = YES;
btn.defaultTitle = @"未选中";
btn.selectedTitle = @"已选中";
btn.defaultTitleColor = [NSColor whiteColor];
btn.selectedTitleColor = [NSColor blackColor];
btn.defaultFont = [NSFont systemFontOfSize:10];
btn.selectedFont = [NSFont systemFontOfSize:10];
btn.defaultBackgroundColor = [NSColor greenColor];
btn.selectedBackgroundColor = [NSColor blueColor];
btn.defaultBackgroundImage = [NSImage imageNamed:@""];
btn.selectedBackgroundImage = [NSImage imageNamed:@""];
btn.rectCorners = LLRectCornerTopLeft|LLRectCornerBottomLeft;
btn.radius = 15;
btn.textAlignment = LLTextAlignmentLeft;
btn.textUnderLineStyle = LLTextUnderLineStyleDeleteDouble;
[btn setTarget:self];
[btn setAction:@selector(btnCilck:)];
[self.view addSubview:btn];
我们是伟大的程序员,我们天生爱分享!