其實(shí)這個(gè)問(wèn)題是這樣的:
在一個(gè)需求里,看到類似跑馬燈上下輪播的一個(gè)需求,要是只有文字還好說(shuō),我直接github上down一個(gè)輪子立馬就可以套用啊,當(dāng)然具體實(shí)現(xiàn)方式或者基本原理得懂一遍,不然做的啥都不知道,要有這么一點(diǎn)點(diǎn)責(zé)任心传泊。搞不好是給自己挖坑呢, 可需求是多變的,絕不僅僅是只有文字辣么簡(jiǎn)單,這次增加了一個(gè)icon圖標(biāo)也要跟著文字一起滾動(dòng),要是下次在增加一個(gè)按鈕還是啥的呢.... 將永無(wú)止境了,我們改起來(lái)也忙的一筆,焦頭爛額的感覺(jué)砍鸠。基于此,我想這個(gè)玩意一定是可以定制的,比如把輪播這個(gè)組件化,分離出去,里面的子視圖是根據(jù)數(shù)據(jù)源來(lái)創(chuàng)建,然后輪播的邏輯是輪播組件自身攜帶,只要遵守了協(xié)議就可以定制豐富多彩的子視圖進(jìn)行輪播,目的是想要打造這樣一款組件舆逃。夢(mèng)想還是要有的,萬(wàn)一實(shí)現(xiàn)了呢,從簡(jiǎn)單的一步步開始,慢工出細(xì)活。
操作步驟
- 一開始寫最丑的代碼和難看的UI,先實(shí)現(xiàn)基本功能先
- 改進(jìn)代碼,優(yōu)化UI的細(xì)節(jié)
- 定制協(xié)議接口方法
- 細(xì)節(jié)調(diào)整
- 滾動(dòng)方向,創(chuàng)建一個(gè)枚舉
typedef NS_ENUM(NSInteger,WGBScrollDirectionType){
WGBScrollDirectionTypeHorizontal = 0,
WGBScrollDirectionTypeVertical
};
- 數(shù)據(jù)源以及協(xié)議方法
@class WGBScrollContainerView;
@protocol WGBScrollContainerViewDataSourceDelegate <NSObject>
@required
///時(shí)間
- (NSTimeInterval)wgb_autoScrollDuration;
///滾動(dòng)的方向
- (WGBScrollDirectionType)wgb_ScrollDirectionType;
///有多少個(gè)子控件
- (NSInteger)wgb_numberOfRowsInWithContainerView:(WGBScrollContainerView *) containerView ;
///子控件
- (UIView *)wgb_subContentViewWithContainerView:(WGBScrollContainerView *) containerView subViewForRowAtIndex:(NSInteger)index;
@optional
///點(diǎn)擊事件
- (void)wgb_containerView:(WGBScrollContainerView *)containerView didSelectRowAtIndex:(NSInteger)index;
@end
- InterFace
@interface WGBScrollContainerView : UIView
@property (nonatomic,weak) id<WGBScrollContainerViewDataSourceDelegate> wgbDataSourceDalegate;
@property (nonatomic,assign,readonly) NSTimeInterval duration;
@property (nonatomic,assign,readonly) WGBScrollDirectionType directionType;
- (void)start;
- (void)stop;
- (void)pause;
- (void)reloadData ;
- (void)clickItemViewWithIndex:(void(^)(NSInteger index))clickBlock;
@end
- imp
#import "WGBScrollContainerView.h"
@interface WGBScrollContainerView ()<UIScrollViewDelegate>
@property (nonatomic,strong) UIScrollView *bgScrollView;
@property (nonatomic,strong) NSTimer *timer;
@property (nonatomic,assign) NSInteger flagIndex;
@property (nonatomic,copy) void(^clickIndex) (NSInteger index);
@property (nonatomic,assign,readwrite) NSTimeInterval duration;
@property (nonatomic,assign,readwrite) WGBScrollDirectionType directionType;
@end
@implementation WGBScrollContainerView
- (UIScrollView *)bgScrollView{
if (!_bgScrollView) {
_bgScrollView = [[UIScrollView alloc] initWithFrame:self.bounds];
_bgScrollView.delegate = self;
_bgScrollView.backgroundColor = [UIColor whiteColor];
// _bgScrollView.userInteractionEnabled = NO;
_bgScrollView.showsVerticalScrollIndicator = NO;
_bgScrollView.showsHorizontalScrollIndicator = NO;
_bgScrollView.bounces = NO;
_bgScrollView.pagingEnabled = YES;
[self addSubview: _bgScrollView];
}
return _bgScrollView;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
#pragma mark - 即將進(jìn)入窗口
- (void)willMoveToWindow:(UIWindow *)newWindow
{
[super willMoveToWindow:newWindow];
[self reloadData];
}
- (void)reloadData{
[self stop];
///沒(méi)有設(shè)置數(shù)據(jù)源 return
if (self.wgbDataSourceDalegate == nil) {
return;
}
if (![self.wgbDataSourceDalegate respondsToSelector:@selector(wgb_autoScrollDuration)]) {
@throw [NSException exceptionWithName:@"WGBError" reason:@"未實(shí)現(xiàn)(wgb_autoScrollDuration:)" userInfo:nil];
}
if (![self.wgbDataSourceDalegate respondsToSelector:@selector(wgb_ScrollDirectionType)]) {
@throw [NSException exceptionWithName:@"WGBError" reason:@"未實(shí)現(xiàn)(wgb_ScrollDirectionType:)" userInfo:nil];
}
if (![self.wgbDataSourceDalegate respondsToSelector:@selector(wgb_numberOfRowsInWithContainerView:)]) {
@throw [NSException exceptionWithName:@"WGBError" reason:@"未實(shí)現(xiàn)(wgb_numberOfRowsInWithContainerView:)" userInfo:nil];
}
if (![self.wgbDataSourceDalegate respondsToSelector:@selector(wgb_subContentViewWithContainerView:subViewForRowAtIndex:)]) {
@throw [NSException exceptionWithName:@"WGBError" reason:@"未實(shí)現(xiàn)(wgb_subContentViewWithContainerView:subViewForRowAtIndex:)" userInfo:nil];
}
self.duration = [self.wgbDataSourceDalegate wgb_autoScrollDuration];
self.directionType = [self.wgbDataSourceDalegate wgb_ScrollDirectionType];
[self setup];
[self start];
}
- (void)setDirectionType:(WGBScrollDirectionType)directionType{
_directionType = directionType;
}
- (void)setDuration:(NSTimeInterval)duration{
_duration = duration;
self.timer = [NSTimer scheduledTimerWithTimeInterval: duration target:self selector:@selector(changeContent) userInfo:nil repeats:YES];
[[NSRunLoop mainRunLoop] addTimer: self.timer forMode:NSRunLoopCommonModes];
self.flagIndex = 0;
}
- (void)setup{
CGRect frame = self.bounds;
CGFloat width = frame.size.width;
CGFloat height = frame.size.height;
CGFloat count = [self.wgbDataSourceDalegate wgb_numberOfRowsInWithContainerView: self];
for (NSInteger i = 0; i < count ; i += 1) {
UIView *subView = [self.wgbDataSourceDalegate wgb_subContentViewWithContainerView:self subViewForRowAtIndex: i];
if (self.directionType == WGBScrollDirectionTypeVertical) {
subView.frame = CGRectMake(0, height*i , width , height);
self.bgScrollView.contentSize = CGSizeMake(width, height*count);
}else{
subView.frame = CGRectMake(width*i, 0, width , height);
self.bgScrollView.contentSize = CGSizeMake(width*count, height);
}
subView.tag = i;
subView.userInteractionEnabled = YES;
UITapGestureRecognizer *tapGes = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(clickItemViewIndex:)];
[subView addGestureRecognizer: tapGes];
[self.bgScrollView addSubview: subView];
}
[self.bgScrollView setContentOffset:CGPointMake(0, 0)];
}
- (void)clickItemViewIndex:(UITapGestureRecognizer *)tap{
if (self.clickIndex) { ///之前是用block實(shí)現(xiàn)的
self.clickIndex(tap.view.tag);
}
if ([self.wgbDataSourceDalegate respondsToSelector:@selector(wgb_containerView:didSelectRowAtIndex:)]) {
[self.wgbDataSourceDalegate wgb_containerView:self didSelectRowAtIndex:tap.view.tag];
}
}
- (void)clickItemViewWithIndex:(void (^)(NSInteger index))clickBlock{
self.clickIndex = clickBlock;
}
//// 這里的做法是將數(shù)據(jù)源的第一條放置到了最后一條 ,等輪完一輪的時(shí)候,重新回到第一條時(shí),這里需要一個(gè)等待時(shí)間,這個(gè)時(shí)間間隔沒(méi)處理,一輪結(jié)束休息一波,也是人之常情,科學(xué)都源自于生活,我覺(jué)得這一點(diǎn)也是沒(méi)有毛病的
- (void)changeContent{
CGRect frame = self.bounds;
CGFloat width = frame.size.width;
CGFloat height = frame.size.height;
CGFloat count = [self.wgbDataSourceDalegate wgb_numberOfRowsInWithContainerView: self];
if (self.flagIndex == count) {
self.flagIndex = 0;
[self.bgScrollView setContentOffset:CGPointMake(0, 0)];
}
if (self.directionType == WGBScrollDirectionTypeVertical) {
[self.bgScrollView setContentOffset:CGPointMake(0, height*self.flagIndex) animated:YES];
}else{
[self.bgScrollView setContentOffset:CGPointMake(width *self.flagIndex, 0) animated:YES];
}
self.flagIndex++;
}
- (void)start{
[self.timer fire];
}
- (void)stop{
[self.timer invalidate];
self.timer = nil;
}
- (void)pause{
[self.timer setFireDate:[NSDate distantPast]];
}
- (void)dealloc{
[self stop];
}
///這一步是關(guān)鍵點(diǎn),取消scrollView的手動(dòng)滑動(dòng)手勢(shì),只保留定時(shí)器自動(dòng)滾動(dòng)
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
self.bgScrollView.panGestureRecognizer.enabled = NO;
}
@end
遇到的困難
1. 禁用UIScrollView的Pan滑動(dòng)手勢(shì) [已解決]
一開始也是沒(méi)有頭緒,關(guān)掉人機(jī)交互,,但是又要點(diǎn)擊事件,還想到過(guò)用穿透視圖來(lái)攔截,仍然沒(méi)有DidScroll: 這個(gè)方法方便
2. 視圖輪播完一輪回到初始位置的時(shí)間間隔的處理 [待定]
暫時(shí)沒(méi)處理,輪播完一波就休息一會(huì)兒
3. 子視圖復(fù)用的問(wèn)題 [待定]
子視圖需要設(shè)計(jì)一種像創(chuàng)建cell那樣注冊(cè)或者復(fù)用的方案,但是目前一點(diǎn)頭緒也沒(méi)有...