WyhPageControl
A customizable pageControl,support many styles including custom image、tintcolor ,etc
Github
https://github.com/XiaoWuTongZhi/WyhPageControl
CocoaPods Support
pod search WyhPageControl
pod 'WyhPageControl', '~> 1.0.0'
前段時(shí)間項(xiàng)目整體UI及交互大改,需要準(zhǔn)備封裝很多UI組件,借著這個(gè)機(jī)會(huì)封裝了不少實(shí)用組件,這里給大家介紹一款支持自定義的pageControl
吧(其實(shí)我就是來騙star的,github上很少有人去封裝pageControl,有一個(gè)上千star的pageControl也已經(jīng)好多年沒維護(hù)了,因?yàn)樘?jiǎn)單了,但是今天介紹的不僅僅是一個(gè)第三方組件,更多的教大家如何去封裝一個(gè)自定義UI的方式方法)。
傳統(tǒng)意義的UIPageControl
想必根本滿足不了廣大開發(fā)同胞的使用了,項(xiàng)目越來越遠(yuǎn)離Native
頁面,也著實(shí)讓我們很頭疼!
PageControl
封裝起來很簡(jiǎn)單,因?yàn)樗墓δ芤埠芎?jiǎn)單,無非是多一些系統(tǒng)沒有的自定義小點(diǎn)點(diǎn)的功能罷了,不過封裝思想很重要。從工作至今,封裝過很多組件,其實(shí)思想都大同小異,下面我們來分析一波代碼?。ㄇ煤诎辶藠W)
封裝思想
這里僅談?wù)?code>UI組件的封裝,日后我還會(huì)出一些針對(duì)業(yè)務(wù)模塊的封裝思想。
UI組件封裝都大同小異,像Native原生
的tableView
就是一個(gè)很好的例子,支持自定義最大化的組件往往并不是暴露很多的自定義屬性,而是直接用代理回調(diào)的方式,讓使用者去自定義這個(gè)組件的樣式,而不是已定的樣式。這一點(diǎn)是很重要的,你要清楚你所暴露的自定義屬性永遠(yuǎn)沒有辦法滿足所有人的需求,因此代理回調(diào)很重要。讓我們通過分析WyhPageControl
來理解如何通過代理回調(diào)來自定義UI組件:
WyhPageControl代碼分析
WyhPageControl
設(shè)計(jì)之初就是想自定義pageControl的小圓點(diǎn)樣式、間距、圓點(diǎn)尺寸等,那么完全可以仿照tableView
的代理模式,將小圓點(diǎn)作為cell
,通過代理回調(diào)的方式,讓用戶去自定義,當(dāng)然還是要暴露一些自定義屬性的,最好維持UIPageControl
的屬性不變,起碼使用起來更舒服。
WyhPageControl
作為主體View,WyhPageControlDot
作為cell,內(nèi)部實(shí)現(xiàn)一點(diǎn)要分清楚哪些是支持reload
的方法:
初始化方法,采用block回調(diào)自定義配置使代碼塊更聚合,block內(nèi)無需考慮循環(huán)引用,dataSource
和delegate
一定要分清,參考UITableView
。
- (instancetype)initWithDataSource:(id<WyhPageControlDataSource>)dataSource
Delegate:(id<WyhPageControlDelegate>)delegate
WithConfiguration:(void (^)(WyhPageControl *))configuration {
if (self = [self init]) {
if(configuration) configuration(self);
_dataSource = dataSource;
_delegate = delegate;
[self reloadUI];
}
return self;
}
創(chuàng)建協(xié)議代理,來高度自定義你的dot
,dataSource
和delegate
一定要分清并分開,結(jié)構(gòu)一定要嚴(yán)格規(guī)范,為了可拓展性和便于維護(hù):
@class WyhPageControl;
@protocol WyhPageControlDataSource <NSObject>
@optional
- (WyhPageControlDot *)pageControl:(WyhPageControl *)pageControl dotForIndex:(NSInteger)index;
@end
@protocol WyhPageControlDelegate <NSObject>
@optional
- (void)pageControl:(WyhPageControl *)pageControl didClickForIndex:(NSInteger)index;
@end
初始化一些屬性,確保所有屬性都有默認(rèn)值。
- (void)initializeConfig {
self.clipsToBounds = YES;
_numberOfPages = 0;
_currentPage = 0;
_hidesForSinglePage = NO;
_pageIndicatorTintColor = [UIColor lightGrayColor];
_currentPageIndicatorTintColor = [UIColor darkGrayColor];
_backgroundColor = [UIColor clearColor];
_borderWidth = 0.f;
_cornerRadius = 0.f;
_borderColor = [UIColor darkGrayColor];
_backgroundImage = nil;
_showReloadActivityIndicator = YES;
// const
_dotLeftMargin = 15.f;
_dotTopMargin = 8.f;
_dotSpace = 8.f;
// ui
_coverView = [[UIView alloc]init];
_coverImageView = [[UIImageView alloc]init];
_indicator = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:(UIActivityIndicatorViewStyleGray)];
[_indicator sizeToFit];
[self addSubview:_coverView];
[self addSubview:_coverImageView];
[self addSubview:_indicator];
}
并重寫這些屬性的setter
方法,使其重新set的時(shí)候會(huì)發(fā)生樣式的變化。
#pragma mark - Setter
- (void)setNumberOfPages:(NSUInteger)numberOfPages {
_numberOfPages = numberOfPages;
}
- (void)setHidesForSinglePage:(BOOL)hidesForSinglePage {
_hidesForSinglePage = hidesForSinglePage;
}
- (void)setBackgroundColor:(UIColor *)backgroundColor {
_backgroundColor = backgroundColor;
_coverView.backgroundColor = backgroundColor;
}
- (void)setPageIndicatorTintColor:(UIColor *)pageIndicatorTintColor {
_pageIndicatorTintColor = pageIndicatorTintColor;
}
- (void)setCurrentPageIndicatorTintColor:(UIColor *)currentPageIndicatorTintColor {
_currentPageIndicatorTintColor = currentPageIndicatorTintColor;
}
- (void)setDotLeftMargin:(CGFloat)dotLeftMargin {
_dotLeftMargin = dotLeftMargin;
}
- (void)setDotTopMargin:(CGFloat)dotTopMargin {
_dotTopMargin = dotTopMargin;
}
- (void)setDotSpace:(CGFloat)dotSpace {
_dotSpace = dotSpace;
}
- (void)setBorderWidth:(CGFloat)borderWidth {
_borderWidth = borderWidth;
self.layer.borderWidth = borderWidth;
}
- (void)setBorderColor:(UIColor *)borderColor {
_borderColor = borderColor;
self.layer.borderColor = borderColor.CGColor;
}
- (void)setBackgroundImage:(UIImage *)backgroundImage {
_backgroundImage = backgroundImage;
_coverImageView.image = backgroundImage;
}
- (void)setCornerRadius:(CGFloat)cornerRadius {
_cornerRadius = cornerRadius;
self.layer.cornerRadius = cornerRadius;
}
通過dataSource
指定的樣式來創(chuàng)建dot
,visibleDots
是用來存放所有dot
的數(shù)組,這里一定要判斷代理人是否給回調(diào)了dot
,如果沒有自定創(chuàng)建一個(gè)默認(rèn)的dot
,并通過用戶設(shè)置的間距等屬性,設(shè)置dot
的位置,并添加點(diǎn)擊手勢(shì)。(這里要注意的是,這個(gè)initDots方法一定是一個(gè)支持reload的,使用者可能會(huì)根據(jù)不同情況返回不同的dot
,這點(diǎn)必須清楚)
- (void)initDots {
[self.visibleDots makeObjectsPerformSelector:@selector(removeFromSuperview)];
self.visibleDots = [NSMutableArray new];
WyhPageControlDot *lastDot ;
for (int i = 0; i < _numberOfPages; i++) {
WyhPageControlDot *dot = nil;
if (![self.dataSource respondsToSelector:@selector(pageControl:dotForIndex:)]) {
dot = [[WyhPageControlDot alloc]init];
dot.unSelectTintColor = _pageIndicatorTintColor;
dot.selectTintColor = _currentPageIndicatorTintColor;
}else {
dot = [self.dataSource pageControl:self dotForIndex:i];
}
dot.hidden = _isReloading;
// frame
CGFloat dotX = (!lastDot)?_dotLeftMargin:CGRectGetMaxX(lastDot.frame)+_dotSpace;
dot.frame = CGRectMake(dotX, 0, dot.size.width, dot.size.height);
// gesture
UITapGestureRecognizer *tapges = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(pageControlDotTapAction:)];
[dot addGestureRecognizer:tapges];
[self addSubview:dot];
[self.visibleDots addObject:dot];
lastDot = dot;
}
[self bringSubviewToFront:self.indicator];
[self configDotsUI];
[self autoConfigBounds];
[self configAllDotsCenterY]; // must after autoConfigBounds.
}
增加與之對(duì)應(yīng)的reload
方法拋給用戶,同tableView一樣,這個(gè)reload
方法執(zhí)行后,會(huì)重新調(diào)用所有代理回調(diào)及自定義屬性,保證更新機(jī)制,這也是整個(gè)UI自定義組件封裝最重要的環(huán)節(jié)?。╓yhPageControl增加了一個(gè)轉(zhuǎn)圈圈的菊花,用戶可以定義顯示與隱藏當(dāng)在reload時(shí))
- (void)reloadData {
[self reloadUI];
}
- (void)reloadUI {
[self showActivity:YES];
[self checkHiddenIfNeeded];
[self configPageControlUI];
[self initDots];
[self showActivity:NO];
}
最后就是當(dāng)點(diǎn)擊dot
時(shí),回調(diào)代理方法:
- (void)pageControlDotTapAction:(UITapGestureRecognizer *)tapGes {
WyhPageControlDot *dot = (WyhPageControlDot *)tapGes.view;
NSInteger index = [self.visibleDots indexOfObject:dot];
if (index == NSNotFound) {
NSAssert(NO, @"Can't found this tap dot !");
return;
}
[self moveToIndex:index];
// call back
if ([self.delegate respondsToSelector:@selector(pageControl:didClickForIndex:)]) {
[self.delegate pageControl:self didClickForIndex:index];
}
}
每一個(gè)dot
有兩種狀態(tài)對(duì)應(yīng)兩種UI樣式,選中和未選中,目前僅支持自定義 選中/未選中 顏色、背景圖片。
@interface WyhPageControlDot : UIView
@property(nonatomic, strong) UIColor *unSelectTintColor;
@property(nonatomic, strong) UIColor *selectTintColor;
@property (nonatomic, strong) UIImage *unselectImage;
@property (nonatomic, strong) UIImage *selectImage;
@property (nonatomic, assign) CGSize size; // default is (20,20)
@property (nonatomic, strong) UIColor *borderColor; //defult is nil;
@property (nonatomic, assign) CGFloat borderWidth; // default is 0.f;
@property (nonatomic, assign) CGFloat conerRadius; //default is 10.f
- (void)setSelected:(BOOL)selected;
@end
dot
如果不滿足你的需求,同cell一樣,你也可以自定義繼承這個(gè)dot
的Base類,來自定義你的圓點(diǎn),這里就不舉例子了。
使用方法請(qǐng)大家去demo
中自行查看,很簡(jiǎn)單,同tableView
類似。
總結(jié)
通過分析這個(gè)簡(jiǎn)單的組件,希望朋友們對(duì)于UI組件封裝思想能更加理解,最后希望喜歡的朋友們到GitHub
幫點(diǎn)個(gè)star
,歡迎各種好朋友,一起來探討、研究,接下來我會(huì)出一些其他方面的,不只是UI層次的,簡(jiǎn)書這個(gè)平臺(tái)挺好,(但就是有時(shí)太懶),大家共勉吧。
開啟傳送門:WyhPageControl