前言ios
項目中比較可能會用到的自定義控件,也會去參考別人的第三方,解讀他們解決問題的思路並加上本身的邏輯,記錄下來方便之後閱讀;git
(一)圖文驗證碼;github
demo展現的是簡單的圖文驗證(沒有干擾線);demo邏輯描述;數組
(1)NTAuthCodeType分爲純數字以及數字字符混合,可設置顯示驗證碼的位數;網絡
(2)sessionCode是當前的驗證碼信息,用於判斷用戶是否輸入正確(若含字母,不區分大小寫);session
#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
(二)發送驗證碼倒計時控件;app
demo的邏輯;dom
(1)設置countDownButtonHandler添加按鈕點擊事件以及配置控件的初始屬性和變化屬性;ide
(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