前言
项目中比较可能会用到的自定义控件,也会去参考别人的第三方,解读他们解决问题的思路并加上自己的逻辑,记录下来方便以后阅读;
(一)图文验证码;
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