iOS-常用的自定义控件

前言

项目中比较可能会用到的自定义控件,也会去参考别人的第三方,解读他们解决问题的思路并加上自己的逻辑,记录下来方便以后阅读;

 

(一)图文验证码;

demo展示的是简单的图文验证(没有干扰线);demo逻辑描述;

(1)NTAuthCodeType分为纯数字以及数字字符混合,可设置显示验证码的位数;

(2)sessionCode是当前的验证码信息,用于判断用户是否输入正确(若含字母,不区分大小写);

#import <UIKit/UIKit.h>

typedef NS_ENUM(NSInteger, NTAuthCodeType) {
    
    NTAuthCodeTypeNumber = 0,
    NTAuthCodeTypeNumberAndLetter
};

@interface NTAuthCodeView : UIView

@property (nonatomic, readonly) NSString *sessionCode;
- (instancetype)initViewWithType:(NTAuthCodeType)authCodeType authCodeCount:(NSInteger)authCodeCount;

@end


#import "NTAuthCodeView.h"

@interface NTAuthCodeView ()

@property (nonatomic, strong) NSArray *authCodeArray;
@property (nonatomic, strong) NSMutableString *authCodeStr;
@property (nonatomic, assign) NTAuthCodeType currentType;
@property (nonatomic, assign) NSInteger authCodeCount;
@property (nonatomic, readonly) UIColor *viewColor;

@end

@implementation NTAuthCodeView

- (instancetype)initViewWithType:(NTAuthCodeType)authCodeType authCodeCount:(NSInteger)authCodeCount {
    
    self = [super init];
    if (self) {
        
        self.currentType = authCodeType;
        self.authCodeCount = authCodeCount;
        self.backgroundColor = self.viewColor;
        [self excuteCode];
    }
    
    return self;
}

- (void)excuteCode {
    
    if (self.authCodeArray.count > 0) {
        
        _authCodeStr = [[NSMutableString alloc] initWithCapacity:_authCodeCount];
        for (int i = 0; i < _authCodeCount; i++) {
            
            NSInteger index = arc4random() %  (self.authCodeArray.count - 1);
            NSString *tempStr = self.authCodeArray[index];
            _authCodeStr = (NSMutableString *)[_authCodeStr stringByAppendingString:tempStr];
        }
    }
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    
    [self excuteCode];
    [self setNeedsDisplay];
}

- (void)drawRect:(CGRect)rect {
    
    [super drawRect:rect];
    self.backgroundColor = self.viewColor;
    //垂直居中显示;
    [self drawText:[NSString stringWithFormat:@"%@",_authCodeStr] inRect:rect font:25.0f textColor:[UIColor greenColor]];
    
    //随机位置;
//    NSString *authCodeText = [NSString stringWithFormat:@"%@",_authCodeStr];
//    CGSize cSize = [@"A" sizeWithAttributes:@{NSFontAttributeName: [UIFont systemFontOfSize:18.0f]}];
//    
//    CGFloat pX, pY;
//    CGPoint letterPoint;
//    
//    int width = (rect.size.width / authCodeText.length) - cSize.width;
//    int height = rect.size.height - cSize.height;
//    
//    for (int i = 0; i < authCodeText.length; i++) {
//        
//        pX = arc4random() % width + rect.size.width / authCodeText.length * i;
//        pY = arc4random() % height;
//        letterPoint = CGPointMake(pX, pY);
//        
//        unichar letter = [authCodeText characterAtIndex:i];
//        NSString *letterStr = [NSString stringWithFormat:@"%c",letter];
//        [letterStr drawAtPoint:letterPoint withAttributes:@{NSFontAttributeName: [UIFont systemFontOfSize:(arc4random() % 5 + 14.0f)], NSForegroundColorAttributeName: [UIColor blueColor]}];
//    }
}

- (NSString *)sessionCode {
    
    return [NSString stringWithFormat:@"%@",_authCodeStr].lowercaseString;
}

- (NSArray *)authCodeArray {
    
    if (!_authCodeArray) {

        if (_currentType == NTAuthCodeTypeNumber) {

            _authCodeArray = @[@"0", @"1", @"2", @"3", @"4", @"5", @"6", @"7", @"8", @"9"];
            
        } else if (_currentType == NTAuthCodeTypeNumberAndLetter) {

            _authCodeArray = @[@"0",@"1",@"2",@"3",@"4",@"5",@"6",@"7",@"8",@"9",@"A",@"B",@"C",@"D",@"E",@"F",@"G",@"H",@"I",@"J",@"K",@"L",@"M",@"N",@"O",@"P",@"Q",@"R",@"S",@"T",@"U",@"V",@"W",@"X",@"Y",@"Z",@"a",@"b",@"c",@"d",@"e",@"f",@"g",@"h",@"i",@"j",@"k",@"l",@"m",@"n",@"o",@"p",@"q",@"r",@"s",@"t",@"u",@"v",@"w",@"x",@"y",@"z"];
        }
    }
    
    return _authCodeArray;
}

- (UIColor *)viewColor {
    
    return [UIColor colorWithRed:(arc4random() % 255) / 255.0f green:(arc4random() % 255) / 255.0f blue:(arc4random() % 255) / 255.0f alpha:1.0f];
}

- (void)drawText:(NSString *)text inRect:(CGRect)contextRect font:(CGFloat)fontSize textColor:(UIColor *)textColor {
    
    UIFont *font = [UIFont systemFontOfSize:fontSize];
    CGFloat fontHeight = font.lineHeight;
    CGFloat yOffset = floor((contextRect.size.height - fontHeight) / 2.0) + contextRect.origin.y;
    CGRect textRect = CGRectMake(contextRect.origin.x, yOffset, contextRect.size.width, fontHeight);
    
    NSMutableParagraphStyle *paragraphStyle = [NSMutableParagraphStyle new];
    paragraphStyle.alignment = NSTextAlignmentCenter;
    
    //设置描边宽度以及颜色
    [text drawInRect:textRect withAttributes:@{NSForegroundColorAttributeName:[UIColor whiteColor], NSStrokeColorAttributeName:textColor, NSStrokeWidthAttributeName:@2, NSFontAttributeName:font, NSParagraphStyleAttributeName:paragraphStyle}];
}
@end

 

(二)发送验证码倒计时控件;

demo的逻辑;

(1)设置countDownButtonHandler添加按钮点击事件以及配置控件的初始属性和变化属性;

(2)设置startCountDownWithSecond,初始化倒计时秒数和_timer定时器配置;设置countDownChanging,改变button的title;设置countDownFinished,重新初始化button的title以及重新读秒计时;

(3)stopCountDown函数,可以主动停止_timer定时器;

#import <UIKit/UIKit.h>

@class JKCountDownButton;
typedef NSString* (^CountDownChanging)(JKCountDownButton *countDownButton, NSUInteger second);
typedef NSString* (^CountDownFinished)(JKCountDownButton *countDownButton, NSUInteger second);

typedef void (^TouchedCountDownButtonHandler)(JKCountDownButton *countDownButton, NSInteger tag);

@interface JKCountDownButton : UIButton {
    
    NSInteger _second;
    NSUInteger _totalSecond;
    
    NSTimer *_timer;
    NSDate *_startDate;
    
    CountDownChanging _countDownChanging;
    CountDownFinished _countDownFinished;
    TouchedCountDownButtonHandler _touchedCountDownButtonHandler;
}

@property(nonatomic,strong) id userInfo;

- (void)countDownButtonHandler:(TouchedCountDownButtonHandler)touchedCountDownButtonHandler;
- (void)countDownChanging:(CountDownChanging)countDownChanging;
- (void)countDownFinished:(CountDownFinished)countDownFinished;

- (void)startCountDownWithSecond:(NSUInteger)second;
- (void)stopCountDown;

@end


#import "JKCountDownButton.h"

@implementation JKCountDownButton

#pragma -mark touche action
- (void)countDownButtonHandler:(TouchedCountDownButtonHandler)touchedCountDownButtonHandler {
    
    _touchedCountDownButtonHandler = [touchedCountDownButtonHandler copy];
    [self addTarget:self action:@selector(touched:) forControlEvents:UIControlEventTouchUpInside];
}

- (void)touched:(JKCountDownButton *)sender {
    
    if (_touchedCountDownButtonHandler) {
        _touchedCountDownButtonHandler(sender, sender.tag);
    }
}

#pragma -mark count down method
- (void)startCountDownWithSecond:(NSUInteger)totalSecond {
    
    _totalSecond = totalSecond;
    _second = totalSecond;
    _timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(timerStart:) userInfo:nil repeats:YES];
    _startDate = [NSDate date];
    [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
}

- (void)timerStart:(NSTimer *)theTimer {
    
     double deltaTime = [[NSDate date] timeIntervalSinceDate:_startDate];
     _second = _totalSecond - (NSInteger)(deltaTime + 0.5) ;
    
    if (_second < 0.0) {
        [self stopCountDown];
        
    } else {
        
        if (_countDownChanging) {
            [self setTitle:_countDownChanging(self, _second) forState:UIControlStateNormal];
            [self setTitle:_countDownChanging(self, _second) forState:UIControlStateDisabled];

        } else {
            
            NSString *title = [NSString stringWithFormat:@"%zd秒倒计时", _second];
            [self setTitle:title forState:UIControlStateNormal];
            [self setTitle:title forState:UIControlStateDisabled];
        }
    }
}

- (void)stopCountDown {
    
    if (_timer) {
        if ([_timer respondsToSelector:@selector(isValid)]) {
            if ([_timer isValid]) {
                
                [_timer invalidate];
                _second = _totalSecond;
                
                if (_countDownFinished) {
                    [self setTitle:_countDownFinished(self, _totalSecond) forState:UIControlStateNormal];
                    [self setTitle:_countDownFinished(self, _totalSecond) forState:UIControlStateDisabled];

                } else {
                    
                    [self setTitle:@"获取验证码" forState:UIControlStateNormal];
                    [self setTitle:@"获取验证码" forState:UIControlStateDisabled];
                }
            }
        }
    }
}

#pragma -mark block
- (void)countDownChanging:(CountDownChanging)countDownChanging {
    
    _countDownChanging = [countDownChanging copy];
}
- (void)countDownFinished:(CountDownFinished)countDownFinished {
    
    _countDownFinished = [countDownFinished copy];
}
@end

(4)点击按钮,开启倒计时,关键语句[sender startCountDownWithSecond:60];配置倒计时总时长以及定时器;

_countDownCode = [JKCountDownButton buttonWithType:UIButtonTypeCustom];
_countDownCode.frame = CGRectMake(81, 200, 108, 32);
[_countDownCode setTitle:@"获取验证码" forState:UIControlStateNormal];
_countDownCode.backgroundColor = [UIColor blueColor];
_countDownCode.titleLabel.font = [UIFont systemFontOfSize:15.0f];
[self.view addSubview:_countDownCode];
    
__weak typeof(self) weakObject = self;
[_countDownCode countDownButtonHandler:^(JKCountDownButton *sender, NSInteger tag) {
        
    sender.enabled = NO;
    [sender startCountDownWithSecond:60];
    [sender countDownChanging:^NSString *(JKCountDownButton *countDownButton, NSUInteger second) {
            
        NSString *title = [NSString stringWithFormat:@"%zd秒倒计时", second];
        return title;
    }];
        
    [sender countDownFinished:^NSString *(JKCountDownButton *countDownButton, NSUInteger second) {
            
        countDownButton.enabled = YES;
        return @"获取验证码";
    }];

    [weakObject refresh];
}];

 

(三)横排按钮(中间竖线分割);

#import <UIKit/UIKit.h>

@interface ButtonsView : UIView

- (instancetype)initWithTitles:(NSArray *)titles buttonEvents:(NSArray *)events;

@end


#import "NTButtonsView.h"

@interface NTButtonsView () {
    
    UIView *lastView;
}

@property (nonatomic, strong) NSMutableArray *buttons;
@property (nonatomic, strong) NSMutableArray *lineViews;
@property (nonatomic, strong) NSArray *buttonEvents;

@end

@implementation NTButtonsView

- (instancetype)initWithTitles:(NSArray *)titles buttonEvents:(NSArray *)events {
    
    self = [super init];
    if (self) {
        
        _buttons = [NSMutableArray array];
        _buttonEvents = events;
        _lineViews = [NSMutableArray array];
        
        if (titles) {
            
            [titles enumerateObjectsUsingBlock:^(NSString *title, NSUInteger idx, BOOL * _Nonnull stop) {
                
                UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
                [button setTitle:title forState:UIControlStateNormal];
                button.titleLabel.font = [UIFont systemFontOfSize:14.0f];
                [button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
                button.tag = idx;
                [button addTarget:self action:@selector(clickButton:) forControlEvents:UIControlEventTouchUpInside];
                [self addSubview:button];
                
                [_buttons addObject:button];
            }];
        }
        
        [self initView];
        [self configView];
    }
    
    return self;
}

- (void)initView {
    
    for (int i = 0; i < _buttons.count - 1; i++) {
        
        UIView *lineView = [UIView new];
        lineView.backgroundColor = [UIColor whiteColor];
        [self addSubview:lineView];
        
        [_lineViews addObject:lineView];
    }
}

- (void)configView {
    
    [_buttons enumerateObjectsUsingBlock:^(UIButton *button, NSUInteger idx, BOOL * _Nonnull stop) {
        
        [button mas_makeConstraints:^(MASConstraintMaker *make) {
            
            make.width.mas_equalTo(@70);
            make.top.and.bottom.equalTo(self);
            
            if (lastView) {
                
                make.left.equalTo(lastView.mas_right);
                
            } else {
                
                make.left.equalTo(self);
            }
            
            lastView = button;
        }];
    }];
    
    [_lineViews enumerateObjectsUsingBlock:^(UIView *lineView, NSUInteger idx, BOOL * _Nonnull stop) {
        
        [lineView mas_makeConstraints:^(MASConstraintMaker *make) {
            
            make.top.equalTo(self).offset(5);
            make.bottom.equalTo(self).offset(-5);
            make.width.mas_equalTo(@1);
            make.centerX.equalTo(self);
        }];
    }];
    
    [self mas_makeConstraints:^(MASConstraintMaker *make) {
        
        make.right.equalTo(lastView.mas_right);
    }];
}


- (void)clickButton:(UIButton *)sender {
    
    void (^buttonEvent)() = _buttonEvents[sender.tag];
    
    if (buttonEvent) {
        
        buttonEvent();
    }
}
@end

 

(四)遮罩层高亮突出(UIBezierPath + CAShapeLayer),用于新手指引;

#import <UIKit/UIKit.h>

typedef NS_ENUM(NSInteger, NTMaskArrowDirectionType) {
    
    NTMaskArrowDirectionTypeUp = 0,
    NTMaskArrowDirectionTypeDown
};

typedef NS_ENUM(NSInteger, NTMaskViewImageAlignmentType) {
    
    NTMaskViewImageAlignmentTypeDefault = 0,
    NTMaskViewImageAlignmentTypeCenter
};

@interface NTMaskView : UIView

@property (nonatomic, strong, setter=_set_ButtonTitleAndBorgerColor:) UIColor *buttonTitleAndBorgerColor;
@property (nonatomic, strong, setter=_set_ButtonTitle:) NSString *buttonTitle;
@property (nonatomic, assign, setter=_set_ButtonCornerRadius:) CGFloat buttonCornerRadius;
@property (nonatomic, assign, setter=_set_FontSize:) CGFloat fontSize;

- (instancetype)initMaskViewRect:(CGRect)viewRect focusInsets:(UIEdgeInsets)focusInsets radius:(CGFloat)radius leftOffsetX:(CGFloat)leftOffsetX imageName:(NSString *)imageName directionType:(NTMaskArrowDirectionType)directionType;
- (instancetype)initMaskViewRect:(CGRect)viewRect focusInsets:(UIEdgeInsets)focusInsets radius:(CGFloat)radius leftOffsetX:(CGFloat)leftOffsetX imageName:(NSString *)imageName directionType:(NTMaskArrowDirectionType)directionType imageAlignmentType:(NTMaskViewImageAlignmentType)imageAlignmentType;

- (void)configViewWithExcuteCode:(void (^)(NTMaskView *weakView))excuteCode;

@end


#import "NTMaskView.h"
#import "UIImage+extension.h"

@interface NTMaskView () {
    
    UIButton *skipButton;
}

@property (nonatomic, assign) NTMaskArrowDirectionType directionType;
@property (nonatomic, assign) CGRect maskViewRect;
@property (nonatomic, copy) void (^excuteCode)(NTMaskView *);

@end

@implementation NTMaskView

- (instancetype)initMaskViewRect:(CGRect)viewRect focusInsets:(UIEdgeInsets)focusInsets radius:(CGFloat)radius leftOffsetX:(CGFloat)leftOffsetX imageName:(NSString *)imageName directionType:(NTMaskArrowDirectionType)directionType {
    
    return [self initMaskViewRect:viewRect focusInsets:focusInsets radius:radius leftOffsetX:leftOffsetX imageName:imageName directionType:directionType imageAlignmentType:NTMaskViewImageAlignmentTypeDefault];
}

- (instancetype)initMaskViewRect:(CGRect)viewRect focusInsets:(UIEdgeInsets)focusInsets radius:(CGFloat)radius leftOffsetX:(CGFloat)leftOffsetX imageName:(NSString *)imageName directionType:(NTMaskArrowDirectionType)directionType imageAlignmentType:(NTMaskViewImageAlignmentType)imageAlignmentType {
    
    self = [super init];
    if (self) {
        
        self.directionType = directionType;
        [self createMaskView:viewRect focusInsets:focusInsets radius:radius];
        [self createSkipButton];
        [self createImageViewByName:imageName imageAlignmentType:imageAlignmentType offsetX:leftOffsetX];
    }
    
    return self;
}

- (void)createMaskView:(CGRect)maskViewRect focusInsets:(UIEdgeInsets)focusInsets radius:(CGFloat)radius {
    
    self.frame = [UIScreen mainScreen].bounds;
    UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRect:[UIScreen mainScreen].bounds];
    bezierPath.usesEvenOddFillRule = YES;
    
    maskViewRect.origin.x -= focusInsets.left;
    maskViewRect.origin.y -= focusInsets.top;
    maskViewRect.size.width += focusInsets.left - focusInsets.right;
    maskViewRect.size.height += focusInsets.top - focusInsets.bottom;
    
    radius = maskViewRect.size.width / 2;
    self.maskViewRect = maskViewRect;
    
    UIBezierPath *maskViewBezier = [UIBezierPath bezierPathWithRoundedRect:maskViewRect cornerRadius:radius];
    [bezierPath appendPath:maskViewBezier];
    
    CAShapeLayer *shapeLayer = [CAShapeLayer layer];
    shapeLayer.frame = self.bounds;
    shapeLayer.fillRule = kCAFillRuleEvenOdd;
    shapeLayer.path = bezierPath.CGPath;
    
    shapeLayer.fillColor = [UIColor colorWithWhite:0 alpha:0.7].CGColor;
    [self.layer addSublayer:shapeLayer];
}

- (void)createImageViewByName:(NSString *)imageName imageAlignmentType:(NTMaskViewImageAlignmentType)imageAlignmentType offsetX:(CGFloat)offsetX {
    
    CGPoint imageCenter;
    UIImage *noteImage = [UIImage imageNamed:imageName];
    
    if (noteImage) {
        
        UIImageView *noteImageView = [[UIImageView alloc] initWithImage:noteImage];
        CGFloat imageViewWidth = SCREEN_WIDTH - 2 * offsetX;
        noteImageView.frame = CGRectMake(0, 0, imageViewWidth, noteImage.size.height / 2);
        
        CGFloat verticalSpacing = imageAlignmentType == NTMaskViewImageAlignmentTypeCenter ? 0 : 10;
        CGFloat imageVerticalSpacing = imageAlignmentType == NTMaskViewImageAlignmentTypeCenter ? CGRectGetHeight(_maskViewRect) / 2 : 0;
        
        if (_directionType == NTMaskArrowDirectionTypeUp) {
            
            imageCenter = CGPointMake(self.frame.size.width / 2, CGRectGetMaxY(_maskViewRect) + CGRectGetHeight(noteImageView.frame) / 2 + verticalSpacing - imageVerticalSpacing);
            
        } else {
            
            imageCenter = CGPointMake(self.frame.size.width / 2, CGRectGetMinY(_maskViewRect) - CGRectGetHeight(noteImageView.frame) / 2 - verticalSpacing + imageVerticalSpacing);
        }
        
        noteImageView.center = imageCenter;
        [self addSubview:noteImageView];
    }
}

- (void)createSkipButton {
    
    skipButton = [UIButton buttonWithType:UIButtonTypeCustom];
    self.buttonTitle = @"跳过";
    self.buttonTitleAndBorgerColor = [UIColor whiteColor];
    self.buttonCornerRadius = 15;
    self.fontSize = 14.0f;
    [skipButton addTarget:self action:@selector(clickSkipButton:) forControlEvents:UIControlEventTouchUpInside];
    [self addSubview:skipButton];
    
    [skipButton mas_makeConstraints:^(MASConstraintMaker *make) {
        
        make.top.equalTo(self).offset(30);
        make.right.equalTo(self).offset(-10);
        make.height.mas_equalTo(@30);
    }];
}

- (void)_set_ButtonTitleAndBorgerColor:(UIColor *)buttonTitleAndBorgerColor {
    
    if (buttonTitleAndBorgerColor) {
        
        skipButton.layer.borderWidth = 0.5f;
        skipButton.layer.borderColor = buttonTitleAndBorgerColor.CGColor;
        [skipButton setTitleColor:buttonTitleAndBorgerColor forState:UIControlStateNormal];
    }
}

- (void)_set_ButtonTitle:(NSString *)buttonTitle {
    
    if (buttonTitle) {
        
        [skipButton setTitle:buttonTitle forState:UIControlStateNormal];
    }
}

- (void)_set_ButtonCornerRadius:(CGFloat)buttonCornerRadius {
    
    if (buttonCornerRadius > 0) {
        
        skipButton.layer.cornerRadius = buttonCornerRadius;
    }
}

- (void)_set_FontSize:(CGFloat)fontSize {
    
    if (fontSize > 0) {
        
        skipButton.titleLabel.font = [UIFont systemFontOfSize:fontSize];
    }
}

- (void)configViewWithExcuteCode:(void (^)(NTMaskView *))excuteCode {
    
    self.excuteCode = excuteCode;
}

- (void)clickSkipButton:(UIButton *)sender {
    
    if (_excuteCode) {
        
        _excuteCode(self);
    }
}
@end

 

(五)星级评分控件,查看网络很多的实例都是通过多种状态的星级图片创建视图的,而且创建了背景视图和前景视图,通过计算前景视图的宽度,控制显示星级评分。缺点就是需要创建更多的subview,若在cell中数量多的话会影响滚动的体验;

我的设计思路;

(1)本文的demo只需要灰色背景图片,然后通过图片图层渲染与叠加填充评分后的星型颜色,即星型的颜色可以设置;

(2)减少subview创建;

(3)但是不支持动画;

 

下面是各个逻辑的设计;

图片渲染分类UIImage+BlendMode(保持原图的灰度值与透明度);

#import <UIKit/UIKit.h>

@interface UIImage (BlendMode)

- (UIImage *)imageWithTintColor:(UIColor *)tintColor;

- (UIImage *)imageWithBlendMode:(CGBlendMode)blendMode tintColor:(UIColor *)tintColor;

- (UIImage *)imageWithBlendMode:(CGBlendMode)blendMode tintColor:(UIColor *)tintColor progress:(CGFloat)progress;

@end


#import "UIImage+BlendMode.h"

@implementation UIImage (BlendMode)


- (UIImage *)imageWithTintColor:(UIColor *)tintColor {
    
    return [self imageWithBlendMode:kCGBlendModeDestinationIn tintColor:tintColor];
}

- (UIImage *)imageWithBlendMode:(CGBlendMode)blendMode tintColor:(UIColor *)tintColor {
    
    return [self imageWithBlendMode:blendMode tintColor:tintColor progress:1.0];
}

- (UIImage *)imageWithBlendMode:(CGBlendMode)blendMode tintColor:(UIColor *)tintColor progress:(CGFloat)progress {
    
    UIImage *image;
    
    UIGraphicsBeginImageContextWithOptions(self.size, NO, 0); // 开始图片上下文绘制
    
    [tintColor setFill]; // 填充颜色
    
    CGRect fillRect = CGRectMake(0, 0, self.size.width * progress, self.size.height);
    CGRect drawRect = CGRectMake(0, 0, self.size.width, self.size.height);
    
    UIRectFill(fillRect);
    
    [self drawInRect:drawRect blendMode:blendMode alpha:1.0]; // 设置绘画透明混合模式和透明度
    
    if (blendMode != kCGBlendModeDestinationIn) {
        
        [self drawInRect:drawRect blendMode:kCGBlendModeDestinationIn alpha:1.0]; // 能保留透明度信息
    }
    
    image = UIGraphicsGetImageFromCurrentImageContext(); // 结束图片上下文绘制
    
    UIGraphicsEndImageContext();
    
    return image;
}
@end

 

星级评分自定义view的布局,Masonry自适应布局,支持点击事件;

#import <UIKit/UIKit.h>

@interface NTScoreView : UIView

@property (nonatomic, assign) CGFloat starScore;

@property (nonatomic, assign) BOOL isClickScoreView;

- (instancetype)initWithNumberOfStars:(NSUInteger)numberOfStars;

- (instancetype)initWithNumberOfStars:(NSUInteger)numberOfStars fillColor:(UIColor *)fillColor;

- (void)configViewChangeStarScoreBlock:(void (^)(CGFloat starScore))changeStarScoreBlock;

@end


#import "NTScoreView.h"
#import <Masonry.h>
#import "UIImage+BlendMode.h"

@interface NTScoreView () {
    
    UIView *lastView;
}

@property (nonatomic, assign) NSInteger numberOfStars;

@property (nonatomic, strong) UIColor *fillColor;

@property (nonatomic, strong) UIView *starView;

@property (nonatomic, copy) void (^changeStarScoreBlock)(CGFloat starStore);

@end

@implementation NTScoreView

- (instancetype)init {
    
    return [self initWithNumberOfStars:5];
}

- (instancetype)initWithNumberOfStars:(NSUInteger)numberOfStars {
    
    return [self initWithNumberOfStars:numberOfStars fillColor:[UIColor orangeColor]];
}

- (instancetype)initWithNumberOfStars:(NSUInteger)numberOfStars fillColor:(UIColor *)fillColor {
    
    if (self = [super init]) {
        
        _numberOfStars = numberOfStars;
        _fillColor = fillColor;
        
        [self initView];
        
        [self configView];
        
        UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(userTapRateView:)];
        tapGesture.numberOfTapsRequired = 1;
        
        [self addGestureRecognizer:tapGesture];
    }
    
    return self;
}

- (void)initView {
    
    [self createStarImageViewByImageName:@"star_gray"];
}

- (void)configView {
    
    [self mas_makeConstraints:^(MASConstraintMaker *make) {
        
        make.width.and.height.equalTo(self.starView);
    }];
}


- (void)createStarImageViewByImageName:(NSString *)imageName {
    
    UIImage *image = [UIImage imageNamed:imageName];
    
    if (image) {
        
        for (int i = 0; i < _numberOfStars; i++) {
            
            UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
            imageView.contentMode = UIViewContentModeScaleAspectFit;
            [self.starView addSubview:imageView];
            
            [imageView mas_makeConstraints:^(MASConstraintMaker *make) {
                
                make.top.equalTo(self.starView);
                make.height.mas_equalTo(image.size.height);
                
                if (lastView) {
                    
                    make.left.equalTo(lastView.mas_right);
                    
                } else {
                    
                    make.left.equalTo(self.starView);
                }
                
                lastView = imageView;
            }];
        }
        
        [self.starView mas_updateConstraints:^(MASConstraintMaker *make) {
            
            make.top.and.left.equalTo(self);
            make.right.equalTo(lastView.mas_right);
            make.height.equalTo(lastView);
        }];
    }
}

// 显示star的视图;
- (UIView *)starView {
    
    if (!_starView) {
        
        UIView *starView = [[UIView alloc] init];
        starView.clipsToBounds = YES;
        
        _starView = starView;
        
        [self addSubview:starView];
    }
    
    return _starView;
}


// 设置评分,并刷新starView;
- (void)setStarScore:(CGFloat)starScore {
    
    _starScore = starScore;
    
    NSInteger starScoreInt = (NSInteger)starScore;
    CGFloat yushu = starScore - starScoreInt;
    NSInteger count = ceil(starScore);
    
    if (count > 0) {
        
        [self.starView.subviews enumerateObjectsUsingBlock:^(__kindof UIImageView * _Nonnull imageView, NSUInteger idx, BOOL * _Nonnull stop) {
            
            if (idx < count) {
                
                if (idx == count - 1 && yushu > 0) {
                    
                    imageView.image = [[UIImage imageNamed:@"star_gray"] imageWithBlendMode:kCGBlendModeOverlay tintColor:_fillColor progress:yushu];
                    
                } else {
                    
                    imageView.image = [[UIImage imageNamed:@"star_gray"] imageWithBlendMode:kCGBlendModeOverlay tintColor:_fillColor];
                }
                
            } else {
                
                imageView.image = [UIImage imageNamed:@"star_gray"];
            }
        }];
    }
}


// 点击事件
- (void)userTapRateView:(UITapGestureRecognizer *)tap {
    
    if (_isClickScoreView) {
        
        CGPoint tapPoint = [tap locationInView:self];
        CGFloat offsetX = tapPoint.x;
        self.starScore = (offsetX / self.bounds.size.width) * _numberOfStars;
        
        if (_changeStarScoreBlock) {
            
            _changeStarScoreBlock(_starScore);
        }
    }
}

- (void)configViewChangeStarScoreBlock:(void (^)(CGFloat))changeStarScoreBlock {
    
    _changeStarScoreBlock = changeStarScoreBlock;
}

@end

 

(六)导航栏选择控件,类似拉勾网APP中问题页面的导航栏下拉选择控件;

#import <UIKit/UIKit.h>

@interface NTDropListView : UIView

- (instancetype)initWithFrame:(CGRect)frame titles:(NSArray *)titles;

@end


#import "NTDropListView.h"
#import <Masonry.h>

static const CGFloat tableViewHeaderHeight = 100;

@interface NTDropListView () <UITableViewDelegate, UITableViewDataSource, UIGestureRecognizerDelegate>

@property (nonatomic, strong) NSArray *titles;

@property (nonatomic, strong) UIView *wrapperView;

@property (nonatomic, strong) UIView *backView;

@property (nonatomic, strong) UIButton *titleButton;

@property (nonatomic, strong) UITableView *tableView;

@property (nonatomic, assign) BOOL isShowMenu;

@end

@implementation NTDropListView

- (instancetype)initWithFrame:(CGRect)frame {
    
    return [self initWithFrame:frame titles:@[@"测试", @"测试二"]];
}

- (instancetype)initWithFrame:(CGRect)frame titles:(NSArray *)titles {
    
    if (self = [super initWithFrame:frame]) {
        
        _titles = titles;
        
        [self addSubview:self.titleButton];
        
        [self.wrapperView addSubview:self.backView];
        
        [self.wrapperView addSubview:self.tableView];
    }
    
    return self;
}

- (void)didMoveToWindow {
    
    if (self.window) {
        
        [self.window addSubview:self.wrapperView];
        
        [self.titleButton mas_remakeConstraints:^(MASConstraintMaker *make) {
            
            make.center.equalTo(self);
        }];
        
        [self.wrapperView mas_remakeConstraints:^(MASConstraintMaker *make) {
            
            make.top.equalTo(self.superview.mas_bottom);
            make.left.and.right.and.bottom.equalTo(self.window);
        }];
        
        [self.backView mas_remakeConstraints:^(MASConstraintMaker *make) {
            
            make.edges.equalTo(self.wrapperView);
        }];
        
        CGFloat tableViewHeight = _titles.count * 44;
        [self.tableView mas_remakeConstraints:^(MASConstraintMaker *make) {
            
            make.centerX.equalTo(self.wrapperView);
            make.width.equalTo(self.wrapperView.mas_width);
            make.top.equalTo(self.wrapperView.mas_top).offset(-tableViewHeight - tableViewHeaderHeight);
            make.bottom.equalTo(self.wrapperView.mas_bottom).offset(tableViewHeight + tableViewHeaderHeight);
        }];
        
        self.wrapperView.hidden = YES;
        
    } else {
        
        [self.wrapperView removeFromSuperview];
    }
}

- (void)handleTapOnTitleButton:(UIButton *)sender {
    
    if (!_isShowMenu) {
        
        [self showMenu];
        
    } else {
        
        [self hideMenu];
    }
}

- (void)showMenu {
    
    _titleButton.enabled = NO;
    UIView *headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, tableViewHeaderHeight)];
    headerView.backgroundColor = [UIColor lightGrayColor];
    self.tableView.tableHeaderView = headerView;
    [self.tableView layoutIfNeeded];
    
    [self.tableView mas_updateConstraints:^(MASConstraintMaker *make) {
        
        make.top.equalTo(self.wrapperView.mas_top).offset(-tableViewHeaderHeight);
        make.bottom.equalTo(self.wrapperView.mas_bottom).offset(tableViewHeaderHeight);
    }];
    
    self.wrapperView.hidden = NO;
    self.backView.alpha = 0.0;
    
    [UIView animateWithDuration:0.6
                          delay:0
         usingSpringWithDamping:0.7
          initialSpringVelocity:0.5
                        options:UIViewAnimationOptionCurveLinear
                     animations:^{
                         
                         [self.tableView layoutIfNeeded];
                         self.backView.alpha = 0.3;
                         
                     } completion:^(BOOL finished) {
                         
                         _isShowMenu = YES;
                         _titleButton.enabled = YES;
                     }];
}

- (void)hideMenu {
    
    _titleButton.enabled = NO;
    CGFloat tableViewHeight = _titles.count * 44;
    [self.tableView mas_updateConstraints:^(MASConstraintMaker *make) {
        
        make.top.equalTo(self.wrapperView.mas_top).offset(-tableViewHeight - tableViewHeaderHeight);
        make.bottom.equalTo(self.wrapperView.mas_bottom).offset(tableViewHeight + tableViewHeaderHeight);
    }];
    
    
    [UIView animateWithDuration:0.6
                          delay:0
         usingSpringWithDamping:0.7
          initialSpringVelocity:0.5
                        options:UIViewAnimationOptionCurveLinear
                     animations:^{
                         
                         [self.tableView layoutIfNeeded];
                         self.backView.alpha = 0.0;
                         
                     } completion:^(BOOL finished) {
                         
                         self.wrapperView.hidden = YES;
                         [self.tableView reloadData];
                         _isShowMenu = NO;
                         _titleButton.enabled = YES;
                     }];
}

- (UIView *)wrapperView {
    
    if (!_wrapperView) {
        
        UIView *wrapperView = [[UIView alloc] init];
        wrapperView.clipsToBounds = YES;
        
        UITapGestureRecognizer *wrapperViewTapGr = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(hideMenu)];
        wrapperViewTapGr.delegate = self;
        wrapperViewTapGr.numberOfTapsRequired = 1;
        [wrapperView addGestureRecognizer:wrapperViewTapGr];
        
        _wrapperView = wrapperView;
    }
    
    return _wrapperView;
}

- (UIView *)backView {
    
    if (!_backView) {
        
        UIView *backView = [[UIView alloc] init];
        backView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5];
        
        _backView = backView;
    }
    
    return _backView;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    
    return _titles.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"tableViewCell"];
    if (!cell) {
        
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"tableViewCell"];
    }
    
    cell.textLabel.text = [NSString stringWithFormat:@"%@ -- %ld",_titles[indexPath.row], (long)indexPath.row];
    
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    
    [_titleButton setTitle:_titles[indexPath.row] forState:UIControlStateNormal];
    [self hideMenu];
}

- (UITableView *)tableView {
    
    if (!_tableView) {
        
        UITableView *tableView = [[UITableView alloc] init];
        tableView.delegate = self;
        tableView.dataSource = self;
        tableView.bounces = NO;
        tableView.backgroundColor = [UIColor clearColor];
        tableView.tableFooterView = [[UIView alloc] init];
        tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine;
        [tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"tableViewCell"];
        
        _tableView = tableView;
    }
    
    return _tableView;
}

- (UIButton *)titleButton {
    
    if (!_titleButton) {
        
        UIButton *titleButton = [UIButton buttonWithType:UIButtonTypeCustom];
        [titleButton setTitle:_titles[0] forState:UIControlStateNormal];
        [titleButton setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
        [titleButton addTarget:self action:@selector(handleTapOnTitleButton:) forControlEvents:UIControlEventTouchUpInside];
        
        _titleButton = titleButton;
    }
    
    return _titleButton;
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
    
    if ([NSStringFromClass([touch.view class]) isEqualToString:@"UITableViewCellContentView"]) {
        return NO;
    }
    
    return YES;
}
@end

 

(7)多个条件筛选下拉控件(类似美团的下拉菜单),点击tab显示视图默认选中第一行内容,_isFirstShowLeftOrRight表示是否首次显示视图,_hadSelected表示是否点击左tableview的cell,_hadRightSelected表示是否点击右tableview的cell;然后在- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath通过这借个属性判断哪一行高亮或者是设置颜色。参考这个第三方控件,解读实现的逻辑以及加上自己的一些逻辑。

#import <UIKit/UIKit.h>

@interface NTIndexPath : NSObject

@property (nonatomic, assign) NSInteger column;
// 0 左边   1 右边
@property (nonatomic, assign) NSInteger leftOrRight;
// 左边行
@property (nonatomic, assign) NSInteger leftRow;
// 右边行
@property (nonatomic, assign) NSInteger row;

- (instancetype)initWithColumn:(NSInteger)column leftOrRight:(NSInteger)leftOrRight leftRow:(NSInteger)leftRow row:(NSInteger)row;
+ (instancetype)indexPathWithCol:(NSInteger)col leftOrRight:(NSInteger)leftOrRight leftRow:(NSInteger)leftRow row:(NSInteger)row;

@end

@class NTDropDownView;

@protocol NTDropDownViewDataDelegate <NSObject>

- (BOOL)displayByTableViewInColumn:(NSInteger)tapIndex; // 当前的index是否有右tableview;(未使用)

- (CGFloat)widthRatioOfLeftColumn:(NSInteger)column; // 有右tableview的情况下,获取左tableview的宽度比例;

- (BOOL)haveRightTableViewInColumn:(NSInteger)column; // 是否存在右tabelview;

- (NSInteger)currentLeftSelectedRowInColumn:(NSInteger)column; // 左tableview选中的row;

- (NSInteger)currentRightSelectedRowInColumn:(NSInteger)column; // 右tableview选中的row;

- (NSInteger)dropDownView:(NTDropDownView *)dropDownView numberOfRowsInColumn:(NSInteger)column leftOrRight:(NSInteger)leftOrRight leftRow:(NSInteger)leftRow; // 获取tableview需显示的行数;

- (NSString *)dropDownView:(NTDropDownView *)dropDownView titleForRowAtIndexPath:(NTIndexPath *)indexPath; // 获取tableview当前行显示的title;

@end

@protocol NTDropDownViewDelegate <NSObject>

- (void)dropDownView:(NTDropDownView *)dropDownView didSelectRowAtIndexPath:(NTIndexPath *)indexPath; // 点击某一行,保存响应的信息(当前的tapIndex,leftOrRight(0代表左tableview,1代表右tableview),_leftSelectedRow左tableview选中行,indexPath.row右tableview选中行)等;

@end


#import "NTDropDownView.h"
#import <Masonry.h>

@implementation NTIndexPath

- (instancetype)initWithColumn:(NSInteger)column leftOrRight:(NSInteger)leftOrRight  leftRow:(NSInteger)leftRow row:(NSInteger)row {
    
    self = [super init];
    if (self) {
        
        _column = column;
        _leftOrRight = leftOrRight;
        _leftRow = leftRow;
        _row = row;
    }
    
    return self;
}

+ (instancetype)indexPathWithCol:(NSInteger)col leftOrRight:(NSInteger)leftOrRight leftRow:(NSInteger)leftRow row:(NSInteger)row {
    
    return [[self alloc] initWithColumn:col leftOrRight:leftOrRight leftRow:leftRow row:row];
}
@end

#define WIDTH CGRectGetWidth([UIScreen mainScreen].bounds)
#define HEIGHT CGRectGetHeight([UIScreen mainScreen].bounds)

@interface NTDropDownView () <UITableViewDelegate, UITableViewDataSource> {
    
    UIView *lastView;
    BOOL _hadSelected; // 是否点击左tableview
    BOOL _isFirstShowLeftOrRight; // 首次显示;
    BOOL _hadRightSelected; // 是否点击右tableview
}

@property (nonatomic, strong) NSMutableArray *titleLabels;

@property (nonatomic, strong) UITableView *leftTableView;

@property (nonatomic, assign) NSInteger tapIndex;

@property (nonatomic, strong) UIView *backView;

@property (nonatomic, assign) BOOL show;

@property (nonatomic, assign) CGPoint origin;

@property (nonatomic, strong) UITableView *rightTableView;

@property (nonatomic, assign) NSInteger leftSelectedRow;

@end

@implementation NTDropDownView

// 初始化下拉界面,预先创建所有需要显示的视图;
- (instancetype)initWithOrigin:(CGPoint)origin andHeight:(CGFloat)height titles:(NSArray *)titles {
    
    if (self = [super initWithFrame:CGRectMake(origin.x, origin.y, WIDTH, height)]) {
        
        _tapIndex = -1;
        _origin = origin;
        _isFirstShowLeftOrRight = YES;
        
        if (titles) {
            
            [titles enumerateObjectsUsingBlock:^(NSString *title, NSUInteger idx, BOOL * _Nonnull stop) {
                
                UILabel *label = [UILabel new];
                label.text = title;
                label.tag = idx;
                label.userInteractionEnabled = YES;
                label.textAlignment = NSTextAlignmentCenter;
                
                UITapGestureRecognizer *handleTapOnTitleLabelGr = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapOnTitleLabel:)];
                [label addGestureRecognizer:handleTapOnTitleLabelGr];
                
                [self addSubview:label];
                
                [self.titleLabels addObject:label];
            }];
            
            [self configTitleLabels];
            
            [self createBackViewByOrigin:origin];
            
            [self createTableView];
        }
    }
    
    return self;
}

// 配置所有按钮
- (void)configTitleLabels {
    
    if (_titleLabels.count > 0) {
        
        CGFloat labelWidth = self.frame.size.width / _titleLabels.count;
        [_titleLabels enumerateObjectsUsingBlock:^(UILabel *label, NSUInteger idx, BOOL * _Nonnull stop) {
            
            [label mas_makeConstraints:^(MASConstraintMaker *make) {
                
                make.top.and.height.equalTo(self);
                make.width.mas_equalTo(labelWidth);
                
                if (lastView) {
                    
                    make.left.equalTo(lastView.mas_right);
                    
                } else {
                    
                    make.left.equalTo(self);
                }
                
                lastView = label;
            }];
        }];
    }
}

// 创建背景视图;
- (void)createBackViewByOrigin:(CGPoint)origin {
    
    _backView = [[UIView alloc] initWithFrame:CGRectMake(origin.x, origin.y + self.frame.size.height, WIDTH, HEIGHT)];
    _backView.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.0];
    _backView.opaque = NO;
}

// 创建tableview
- (void)createTableView {
    
    _leftTableView = ({
        
        UITableView *leftTableView = [[UITableView alloc] init];
        leftTableView.delegate = self;
        leftTableView.rowHeight = 44.0;
        leftTableView.dataSource = self;
        leftTableView.tableFooterView = [[UIView alloc] init];
        
        leftTableView;
    });
    
    _rightTableView = ({
        
        UITableView *rightTableView = [[UITableView alloc] init];
        rightTableView.delegate = self;
        rightTableView.rowHeight = 44.0;
        rightTableView.dataSource = self;
        rightTableView.tableFooterView = [[UIView alloc] init];
        
        rightTableView;
    });
}


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    
    NSInteger leftOrRight = 0;
    if ([tableView isEqual:_rightTableView]) {
        
        leftOrRight = 1;
    }
    
    if ([_dataSource respondsToSelector:@selector(dropDownView:numberOfRowsInColumn:leftOrRight:leftRow:)]) {
        
        return [_dataSource dropDownView:self numberOfRowsInColumn:_tapIndex leftOrRight:leftOrRight leftRow:_leftSelectedRow];
    }
    
    return 0;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    if (!cell) {
        
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
    }
    
    NSInteger leftOrRight = 0;
    if ([tableView isEqual:_rightTableView]) {
        
        leftOrRight = 1;
    }
    
    if ([_dataSource respondsToSelector:@selector(dropDownView:titleForRowAtIndexPath:)]) {
        
        cell.textLabel.text = [_dataSource dropDownView:self titleForRowAtIndexPath:[NTIndexPath indexPathWithCol:_tapIndex leftOrRight:leftOrRight leftRow:_leftSelectedRow row:indexPath.row]];
    }
    
    
    if (leftOrRight == 1) {
        
        NSInteger currentDataIndex = [_dataSource currentRightSelectedRowInColumn:_tapIndex];
        if (!_hadSelected && indexPath.row == currentDataIndex && (_isFirstShowLeftOrRight || _hadRightSelected)) {
            
            cell.textLabel.textColor = [UIColor blueColor];
            
        } else {
            
            cell.textLabel.textColor = [UIColor blackColor];
        }
        
    } else {
        
        if (_leftSelectedRow == indexPath.row) {
            
            cell.textLabel.textColor = [UIColor blueColor];
            
        } else {
            
            cell.textLabel.textColor = [UIColor blackColor];
        }
    }
    
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    
    NSInteger leftOrRight = 0;
    if ([tableView isEqual:_rightTableView]) {
        
        leftOrRight = 1;
        
    } else {
        
        _leftSelectedRow = indexPath.row;
    }
    
    if (_delegate && [_delegate respondsToSelector:@selector(dropDownView:didSelectRowAtIndexPath:)]) {
        
        BOOL haveRightTableView = [_dataSource haveRightTableViewInColumn:_tapIndex];
        if ((leftOrRight == 0 && !haveRightTableView) || leftOrRight == 1) {
            
            UILabel *curLabel = _titleLabels[_tapIndex];
            curLabel.text = [_dataSource dropDownView:self titleForRowAtIndexPath:[NTIndexPath indexPathWithCol:_tapIndex leftOrRight:leftOrRight leftRow:_leftSelectedRow row:indexPath.row]];
            
            if (!_hadRightSelected && leftOrRight == 1) {
                
                _hadRightSelected = YES;
            }
            
            [self animateBackGroundView:_backView show:NO complete:^{
                
                [self animateLeftTableView:_leftTableView rightTableView:_rightTableView show:NO complete:^{
                    
                    _show = NO;
                }];
            }];
        }
        
        if (leftOrRight == 0 && haveRightTableView) {
            
            if (!_hadSelected) {
                
                _hadSelected = YES;
            }
            
            _isFirstShowLeftOrRight = NO;
            _hadRightSelected = NO;
            [_leftTableView reloadData];
            [_rightTableView reloadData];
        }
        
        [_delegate dropDownView:self didSelectRowAtIndexPath:[NTIndexPath indexPathWithCol:_tapIndex leftOrRight:leftOrRight leftRow:_leftSelectedRow row:indexPath.row]];
    }
}

- (void)handleTapOnTitleLabel:(UITapGestureRecognizer *)gesture {
    
    NSInteger tapIndex = gesture.view.tag;
    BOOL haveRightTableView = [_dataSource haveRightTableViewInColumn:tapIndex];
    UITableView *rightTableView = nil;
    
    if (haveRightTableView) {
        
        rightTableView = _rightTableView;
        // 修改左右tableview显示比例
    }

    if (tapIndex == _tapIndex && _show) {
        
        [self animateBackGroundView:_backView show:NO complete:^{
            
            [self animateLeftTableView:_leftTableView rightTableView:_rightTableView show:NO complete:^{
                
                _tapIndex = tapIndex;
                _show = NO;
            }];
        }];
        
    } else {
        
        _hadSelected = NO;
        _tapIndex = tapIndex;
        
        if ([_dataSource respondsToSelector:@selector(currentLeftSelectedRowInColumn:)]) {
            
            _leftSelectedRow = [_dataSource currentLeftSelectedRowInColumn:_tapIndex];
        }
        
        if (rightTableView) {
            
            [rightTableView reloadData];
        }
        
        [_leftTableView reloadData];
        
        [self animateBackGroundView:_backView show:YES complete:^{
            
            [self animateLeftTableView:_leftTableView rightTableView:_rightTableView show:YES complete:^{
                
                _show = YES;
            }];
        }];
    }
}

// 控制背景视图的显示与隐藏;
- (void)animateBackGroundView:(UIView *)view show:(BOOL)show complete:(void(^)())complete {
    
    if (show) {
        
        [self.superview addSubview:view];
        [view.superview addSubview:self];
        
        [UIView animateWithDuration:0.2 animations:^{
            
            view.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.3];
        }];
        
    } else {
        
        [UIView animateWithDuration:0.2 animations:^{
            
            view.backgroundColor = [UIColor colorWithWhite:0.0 alpha:0.0];
            
        } completion:^(BOOL finished) {
            
            [view removeFromSuperview];
        }];
    }
    
    complete();
}

// 控制tableview的显示与隐藏;
- (void)animateLeftTableView:(UITableView *)leftTableView rightTableView:(UITableView *)rightTableView show:(BOOL)show complete:(void(^)())complete {
    
    CGFloat ratio = [_dataSource widthRatioOfLeftColumn:_tapIndex];
    
    if (show) {
        
        CGFloat leftTableViewHeight = 0;
        if (leftTableView) {
            
            [self.superview addSubview:leftTableView];
            [leftTableView mas_remakeConstraints:^(MASConstraintMaker *make) {
                
                make.left.equalTo(self);
                make.top.equalTo(self.mas_bottom);
                make.width.equalTo(self.mas_width).multipliedBy(ratio);
                make.height.mas_equalTo(leftTableViewHeight);
            }];
            
            [leftTableView layoutIfNeeded];
        }
        
        if (rightTableView) {
            
            [self.superview addSubview:rightTableView];
            [rightTableView mas_remakeConstraints:^(MASConstraintMaker *make) {
                
                make.left.equalTo(leftTableView.mas_right);
                make.top.equalTo(self.mas_bottom);
                make.width.equalTo(self.mas_width).multipliedBy(1-ratio);
                make.height.mas_equalTo(leftTableViewHeight);
            }];
            
            [rightTableView layoutIfNeeded];
        }
        
        CGFloat tableViewHeight = 200;
        
        if (leftTableView) {
            
            [leftTableView mas_updateConstraints:^(MASConstraintMaker *make) {
                
                make.height.mas_equalTo(tableViewHeight);
            }];
        }
        
        if (rightTableView) {
            
            [rightTableView mas_updateConstraints:^(MASConstraintMaker *make) {
                
                make.height.mas_equalTo(tableViewHeight);
            }];
        }
        
        [UIView animateWithDuration:0.2 animations:^{
            
            if (leftTableView) {

                [leftTableView layoutIfNeeded];
            }
            
            if (rightTableView) {
                
                [rightTableView layoutIfNeeded];
            }
        }];
        
    } else {
        
        if (leftTableView) {
            
            [leftTableView mas_updateConstraints:^(MASConstraintMaker *make) {
                
                make.height.mas_equalTo(@0);
            }];
        }
        
        if (rightTableView) {
            
            [rightTableView mas_updateConstraints:^(MASConstraintMaker *make) {
                
                make.height.mas_equalTo(@0);
            }];
        }
        
        [UIView animateWithDuration:0.2 animations:^{
            
            if (leftTableView) {
                
                [leftTableView layoutIfNeeded];
            }
            
            if (rightTableView) {
                
                [rightTableView layoutIfNeeded];
            }
            
        } completion:^(BOOL finished) {
            
            if (leftTableView) {
                
                [leftTableView removeFromSuperview];
            }
            
            if (rightTableView) {
                
                [rightTableView removeFromSuperview];
            }
        }];
    }
    
    complete();
}

// 数组,保存所有的labels;
- (NSMutableArray *)titleLabels {
    
    if (!_titleLabels) {
        
        _titleLabels = [NSMutableArray array];
    }
    
    return _titleLabels;
}
@end

 

(8)仿QQ头像裁剪(UIScrollView + UIImageView),借鉴网络上的代码并加以修改,UIScrollview用于缩放UIImageView视图,支持拖动,裁剪图片指定区域;

#import <UIKit/UIKit.h>

@interface NTCropImgaeView : UIView

@property (nonatomic, strong) UIImage *outputImage;

@property (nonatomic, strong) UIImage *inputImage;
@property (nonatomic, assign) CGSize cropSize;

@end


#import "NTCropImgaeView.h"

#define SCREEN_WIDTH [[UIScreen mainScreen] bounds].size.width
#define SCREEN_HEIGHT [[UIScreen mainScreen] bounds].size.height

@interface NTCropImgaeView () <UIScrollViewDelegate> {
    
    UIImageView * _sourceImage;
    UIScrollView * _scrollView;
    UIView * _maskView;
    
    CGFloat _addWidth;
    CGFloat _addHeight;
}

@end

@implementation NTCropImgaeView

// 先裁剪图片;
- (UIImage *)outputImage {
    
    CGFloat w_faktor = _sourceImage.image.size.width / _sourceImage.frame.size.width;
    CGFloat h_faktor = _sourceImage.image.size.height / _sourceImage.frame.size.height;
    
    CGFloat x = _scrollView.contentOffset.x * w_faktor;
    CGFloat y = _scrollView.contentOffset.y * h_faktor;
    CGRect myImageRect = CGRectMake(x, y, _cropSize.width * w_faktor, _cropSize.height * h_faktor);
    
    CGImageRef sourceImageRef = [_sourceImage.image CGImage];
    CGImageRef newImageRef = CGImageCreateWithImageInRect(sourceImageRef, myImageRect);
    UIImage *newImage = [UIImage imageWithCGImage:newImageRef];
    CGImageRelease(newImageRef);
    
    newImage = [self tesWithImage:newImage];
    
    return newImage;
}


// 切圆形图片;
- (UIImage *)tesWithImage:(UIImage *)cropImage {
    
    UIGraphicsBeginImageContextWithOptions(cropImage.size, NO, [UIScreen mainScreen].scale);
    
    UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, cropImage.size.width, cropImage.size.height)];
    
    [path addClip];
    [cropImage drawAtPoint:CGPointZero];
    
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    
    UIGraphicsEndImageContext();
    
    return [self cropImage:newImage withSize:_cropSize];
}


// 压缩图片;
- (UIImage *)cropImage:(UIImage *)cropImage withSize:(CGSize)newSize {
        
        UIGraphicsBeginImageContextWithOptions(newSize, NO, [UIScreen mainScreen].scale);
        
        CGRect rect = CGRectMake(0, 0, newSize.width, newSize.height);
        [cropImage drawInRect:rect];
        
        UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
        
        UIGraphicsEndImageContext();
        
        return newImage;
}

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    
    return _scrollView;
}

- (void)layoutSubviews {
    
    [super layoutSubviews];
    _scrollView = [[UIScrollView alloc] init];
    //_scrollView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.6];
    // scrollview的frame这里只是暂时设置
    _scrollView.frame = CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
    _scrollView.clipsToBounds = NO;
    _scrollView.showsVerticalScrollIndicator = NO;
    _scrollView.showsHorizontalScrollIndicator = NO;
    _scrollView.delegate = self;
    _scrollView.minimumZoomScale = 0.5f;
    _scrollView.maximumZoomScale = 2.0f;
    [_scrollView setZoomScale:1.0f];
    [self addSubview:_scrollView];
    
    CGFloat resultHeight, resultWidth;
    // 把图片进行压缩,但要比例不变
    
    NSLog(@"origin image size == %@", NSStringFromCGSize(_inputImage.size));
    
    if (_inputImage.size.width > SCREEN_WIDTH) {
        
        if (_inputImage.size.width > _inputImage.size.height) {
            
            CGFloat faktor = _inputImage.size.width / SCREEN_WIDTH;
            resultWidth = SCREEN_WIDTH;
            resultHeight = _inputImage.size.height / faktor;
        }
        
    } else if (_inputImage.size.height > SCREEN_HEIGHT) {
        
        if (_inputImage.size.height > _inputImage.size.width) {
            
            CGFloat faktor = _inputImage.size.height / SCREEN_HEIGHT;
            resultHeight = SCREEN_HEIGHT;
            resultWidth = _inputImage.size.width / faktor;
        }
        
    } else {
        
        resultWidth = _inputImage.size.width;
        resultHeight = _inputImage.size.height;
    }
    
    // 计算位置,也要居中
    CGFloat x = (_scrollView.frame.size.width - resultWidth) / 2;
    CGFloat y = (_scrollView.frame.size.height - resultHeight) / 2;
    
    // 补差,因为裁剪的显示位置也会居中,那如何让image的各个部分都可以滚动到裁剪位置内呢,这就需要补差
    // 举个例子就是,当scrollview的contentOffset为CGPointZero时,裁剪图片的开始位置也是CGPointZero;
    // 也就是说可以根据scrollview的contentOffset属性确定裁剪图片的起始位置;
    CGFloat addWidth = 0, addHeight = 0;
    if (resultWidth > _cropSize.width) {
        
        addWidth = (resultWidth - _cropSize.width) / 2;
    }
    
    if (resultHeight > _cropSize.height) {
        
        addHeight = (resultHeight - _cropSize.height) / 2;
    }
    
    _addWidth = addWidth;
    _addHeight = addHeight;
    
    _scrollView.frame = CGRectMake(x, y, resultWidth, resultHeight);
    _scrollView.contentSize = CGSizeMake(resultWidth + 2 * addWidth, resultHeight + 2 * addHeight);
    
    _sourceImage = [[UIImageView alloc] initWithImage:_inputImage];
    _sourceImage.backgroundColor = [UIColor brownColor];
    _sourceImage.frame = CGRectMake(addWidth, addHeight, resultWidth, resultHeight);
    [_scrollView addSubview:_sourceImage];
    
    _scrollView.contentOffset = CGPointMake(addWidth, addHeight);
    
    // 最上层的view,用来作为遮罩
    _maskView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)];
    _maskView.backgroundColor = [UIColor blackColor];
    _maskView.alpha = 0.5;
    _maskView.userInteractionEnabled = NO;
    [self addSubview:_maskView];
    
    x = (SCREEN_WIDTH - _cropSize.width) / 2;
    y = (SCREEN_HEIGHT - _cropSize.height) / 2;
    
    // 显示出裁剪位置尺寸
    UIBezierPath *path = [UIBezierPath bezierPathWithRect:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT)];
    path.usesEvenOddFillRule = YES;
    // 这个方法在ios7下有问题,会变成三角形,具体原因不知,替代方法是换成圆角矩形,把圆角角度设为0
    // [path appendPath:[[UIBezierPath bezierPathWithRoundedRect:CGRectMake(x, y, _cropSize.width, _cropSize.height) byRoundingCorners:0 cornerRadii:CGSizeZero] bezierPathByReversingPath]];
    //UIBezierPath bezierPathWithRoundedRect:<#(CGRect)#> cornerRadius:<#(CGFloat)#>
    
    [path appendPath:[[UIBezierPath bezierPathWithRoundedRect:CGRectMake(x, y, _cropSize.width, _cropSize.height) cornerRadius:_cropSize.width / 2] bezierPathByReversingPath]];
    
    CAShapeLayer *shapeLayer = [CAShapeLayer layer];
    shapeLayer.path = path.CGPath;
    shapeLayer.fillRule = kCAFillRuleEvenOdd;
    
    _maskView.layer.mask = shapeLayer;
    
    // 屏幕中心点位置,画圆,边框白色;
    UIBezierPath *circlePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2) radius:_cropSize.width / 2 startAngle:0 endAngle:M_PI * 2 clockwise:YES];
    
    CAShapeLayer *borderLayer = [CAShapeLayer layer];
    borderLayer.lineWidth = 2;
    borderLayer.fillColor = [UIColor clearColor].CGColor;
    borderLayer.strokeColor = [UIColor whiteColor].CGColor;
    borderLayer.path = circlePath.CGPath;
    
    [_maskView.layer addSublayer:borderLayer];
}

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
    
    return _sourceImage;
}

- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale {
    
    CGSize recontentSize = CGSizeMake(_scrollView.contentSize.width + _addWidth * 2, _scrollView.contentSize.height + _addHeight * 2);
    _scrollView.contentSize = recontentSize;
}
@end

 

转载于:https://my.oschina.net/u/1450995/blog/759786

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值