美文网首页macOS开发iOS之MAC端开发程序员
《macOS开发》自定义控件之NSButton

《macOS开发》自定义控件之NSButton

作者: 九剑仙 | 来源:发表于2017-08-18 19:38 被阅读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];
    

    我们是伟大的程序员,我们天生爱分享!

    相关文章

      网友评论

      • 邓高鹃:分享代码呀
        邓高鹃:@九剑仙 加我QQ:1852022504
        九剑仙:@邓高鹃 直接复制代码就行了,上面的代码就是所有的,你创建文件,粘进去就好了
      • 云力太心:正是我需要的
        九剑仙:@云力太心 添加了一些新功能,可以在添加完控件之后重新赋值某个属性,建议更新
        九剑仙:@云力太心 已更新,修复了部分bug
      • 九剑仙:如有雷同,纯属巧合!

      本文标题:《macOS开发》自定义控件之NSButton

      本文链接:https://www.haomeiwen.com/subject/xyiarxtx.html