Mac开发之重写NSSlider(比酷狗的播放进度条好看)
2018年03月15日
Mac开发如果觉得系统自带Slider不好看,可以通过重绘让自己软件的Slider变得好看一点。与iOS开发不同的是,Mac开发控件重绘没有那么直接,但也不算复杂。下面说一下怎么通过继承NSSlider和NSSliderCell重绘NSSlider。
新建一个Project,名字为:CustomSlider。打开Main.storyboard,从IB中拉几个slider,如图一所示:
图一
新建几个类,分别是CustomSlider、CustomSliderCell和NSColor+Hexa,下面是这几个类的详细内容:
CustomSlider.h
// CustomSlider.h
// Created by Chen Ling on 15/3/2018.
// Copyright © 2018 Chen Ling. All rights reserved.
#import <Cocoa/Cocoa.h>
@interface CustomSlider : NSSlider
@end
CustomSlider.m
#import "CustomSlider.h"
@interface CustomSlider()
@property (nonatomic, strong) NSTrackingArea *trackingArea;
@end
@implementation CustomSlider
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
}
return self;
}
#pragma mark - 设置mouse追踪区域
-(void)updateTrackingAreas
{
[super updateTrackingAreas];
if(_trackingArea != nil) {
[self removeTrackingArea:_trackingArea];
}
int opts = (NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways)
// 将设置追踪区域为控件大小
// 设置鼠标追踪区域,如果不设置追踪区域,mouseEntered和mouseExited会无效
_trackingArea = [ [NSTrackingArea alloc] initWithRect:[self bounds]
options:opts
owner:self
userInfo:nil];
[self addTrackingArea:_trackingArea];
}
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
// Drawing code here.
}
#pragma mark - 点击knob效果, 屏蔽就没有点下去的效果
- (BOOL)needsPanelToBecomeKey{
[super needsPanelToBecomeKey];
return YES;
}
//- (BOOL)becomeFirstResponder{
// [super becomeFirstResponder];
// return YES;
//}
#pragma mark - mouse action
- (void)mouseEntered:(NSEvent *)theEvent
{
[super mouseEntered:theEvent];
NSLog(@"mouseEntered");
self.highlighted = YES;
}
- (void)mouseExited:(NSEvent *)theEvent
{
[super mouseExited:theEvent];
NSLog(@"mouseExited");
self.highlighted = NO;
}
@end
CustomSliderCell.h
#import <Cocoa/Cocoa.h>
#define ColorWithRGB(colorCode) [NSColor colorWithDeviceRed:((colorCode>>16)&0xFF)/255.0 green:((colorCode>>8)&0xFF)/255.0 blue:((colorCode)&0xFF)/255.0 alpha:1.0];
#define SLIDER_PROGRESS_DEFAUT_COLOR ColorWithRGB(0x3399FF)
#define SLIDER_BACKGROUND_DEFAUT_COLOR ColorWithRGB(0x969696)
#define SLIDER_KNOB_DEFAUT_COLOR ColorWithRGB(0x3399FF)
#define SLIDER_DEFAUT_HEIGHT 3.0
#define SLIDER_DEFAUT_BAR_RADIUS 5.0
#define SLIDER_DEFAUT_KNOB_WIDTH 8.0
#define SLIDER_DEFAUT_KNOB_HEIGHT 8.0
IB_DESIGNABLE
@interface CustomSliderCell : NSSliderCell
@property (nonatomic, strong) IBInspectable NSColor *sliderProgressColor;
@property (nonatomic, strong) IBInspectable NSColor *sliderBackgroundColor;
@property (nonatomic, strong) IBInspectable NSColor *sliderKnobColor;
@property (nonatomic, assign) IBInspectable CGFloat sliderHeight;
@property (nonatomic, assign) IBInspectable CGFloat sliderBarRadius;
@property (nonatomic, assign) IBInspectable CGFloat sliderKnobWidth;
@property (nonatomic, assign) IBInspectable CGFloat sliderKnobHeight;
@end
CustomSliderCell.m
#import "CustomSliderCell.h"
#import "NSColor+Hexa.h"
#import "CustomSlider.h"
@interface CustomSliderCell()
@property (assign) NSRect leftBarRect;
@end
@implementation CustomSliderCell
-(instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
self.sliderProgressColor = SLIDER_PROGRESS_DEFAUT_COLOR;
self.sliderBackgroundColor = SLIDER_BACKGROUND_DEFAUT_COLOR;
self.sliderKnobColor = SLIDER_KNOB_DEFAUT_COLOR;
self.sliderHeight = SLIDER_DEFAUT_HEIGHT;
self.sliderBarRadius = SLIDER_DEFAUT_BAR_RADIUS;
self.sliderKnobWidth = SLIDER_DEFAUT_KNOB_WIDTH;
self.sliderKnobHeight = SLIDER_DEFAUT_KNOB_HEIGHT;
}
return self;
}
- (void)drawBarInside:(NSRect)rect flipped:(BOOL)flipped {
rect.size.height = self.sliderHeight;
CGFloat barRadius = self.sliderBarRadius;
CGFloat value = ([self doubleValue] - [self minValue]) / ([self maxValue] - [self minValue]);
CGFloat finalWidth = value * ([[self controlView] frame].size.width - self.sliderKnobWidth);
NSRect leftRect = rect;
leftRect.size.width = finalWidth;
self.leftBarRect = leftRect;
NSBezierPath* bg = [NSBezierPath bezierPathWithRoundedRect: rect xRadius: barRadius yRadius: barRadius];
[self.sliderBackgroundColor setFill];
[bg fill];
NSBezierPath* active = [NSBezierPath bezierPathWithRoundedRect: leftRect xRadius: barRadius yRadius: barRadius];
[self.sliderProgressColor setFill];
[active fill];
}
- (void)drawKnob:(NSRect)knobRect {
if (((CustomSlider *)self.controlView).highlighted) {
NSRect customKnobRect = NSMakeRect(_leftBarRect.size.width, _leftBarRect.origin.y + _leftBarRect.size.height / 2 - self.sliderKnobHeight / 2, self.sliderKnobWidth, self.sliderKnobHeight);
NSBezierPath* bg = [NSBezierPath bezierPathWithRoundedRect: customKnobRect xRadius: self.sliderKnobWidth / 2 yRadius: self.sliderKnobHeight / 2];
[self.sliderKnobColor setFill];
[bg fill];
}
}
@end
NSColor+Hexa.h
#import <Cocoa/Cocoa.h>
@interface NSColor (Hexa)
+ (NSColor *)colorFromHexadecimalValue:(NSString *)hexaString;
@end
NSColor+Hexa.m
#import "NSColor+Hexa.h"
@implementation NSColor (Hexa)
+ (NSColor *)colorFromHexadecimalValue:(NSString *)hexaString {
if ([hexaString hasPrefix:@"#"]) {
hexaString = [hexaString substringWithRange:NSMakeRange(1, [hexaString length] - 1)];
}
unsigned int colorCode = 0;
if (hexaString) {
NSScanner *scanner = [NSScanner scannerWithString:hexaString];
(void)[scanner scanHexInt:&colorCode];
}
return [NSColor colorWithDeviceRed:((colorCode>>16)&0xFF)/255.0 green:((colorCode>>8)&0xFF)/255.0 blue:((colorCode)&0xFF)/255.0 alpha:1.0];
}
@end
将前面那三个NSSlider的类分别设置为CustomSlider,将它们的NSSliderCell的类设置为CustomSliderCell,在NSSliderCell的Attributes Inspector中分别设置为如图二图三图四所示:
图二
图三
图四
运行效果: