無(wú)限滾動(dòng)視圖XBoundlessScrollView

日常效果圖


show.gif

支持兩個(gè)以上的view無(wú)限展示

項(xiàng)目中剛好有這個(gè)需求缚态,封裝一下當(dāng)一個(gè)組件蟋滴,方便童鞋方便自己粹庞。
github源碼地址:https://github.com/orangeLong/XBoundlessScrollView 求star
已提交cocoapods
podfile添加 pod 'XBoundlessScrollView', '~> 0.0.1' 后pod install 即可使用 如找不到 則需要pod repo update一發(fā) 美滋滋

思路如下 scrollView的contentSize的寬為視圖寬度的三倍浩淘,scrollView的contentOffset始終在中間位置矾利,使向左向右都可以滑動(dòng)。當(dāng)1左滑到2時(shí)馋袜,改變相應(yīng)view的frame并將contenOffset置為中間位置男旗。如果是兩個(gè)view,需要在滾動(dòng)的時(shí)候檢測(cè)滾動(dòng)方向欣鳖,然后改變不在視圖中間view的frame察皇,這樣就可以無(wú)限左滑或者右滑了。


image.png

h文件如下 繼承于scrollView

#import <UIKit/UIKit.h>

typedef NS_ENUM(NSUInteger, CHDLQBoundlessBlockType) {
    CHDLQBoundlessBlockTypeScroll,      //滾動(dòng)事件
    CHDLQBoundlessBlockTypeClick,       //點(diǎn)擊事件
};

@interface XBoundlessScrollView : UIScrollView

/**< 自動(dòng)滾動(dòng)時(shí)間間隔 默認(rèn)為3s 值小于等于0時(shí)不自動(dòng)滾 */
@property (nonatomic) NSTimeInterval scrollInterval;
/**< showView 把所需要展示的view數(shù)據(jù)源給過(guò)來(lái)就可以展示 frame自動(dòng)變?yōu)楫?dāng)前frame大小*/
@property (nonatomic, strong) NSArray<UIView *> *showViews;
/**< 滾動(dòng)到到某個(gè)頁(yè)面或者點(diǎn)擊的時(shí)候的回調(diào)*/
@property (nonatomic, copy) void(^boundlessBlock)(CHDLQBoundlessBlockType blockType, NSInteger index);
/**< 設(shè)置delegate會(huì)使相關(guān)方法失靈 暫時(shí)不支持設(shè)置delegate*/
- (void)setDelegate:(id<UIScrollViewDelegate>)delegate __attribute__((unavailable("delegate暫時(shí)不允許使用")));

@end

初始化 設(shè)置一些基本參數(shù)

- (void)baseInit
{
    self.bounces = NO;
    self.pagingEnabled = YES;
    [self setValue:self forKey:@"delegate"];
    self.showsVerticalScrollIndicator = NO;
    self.showsHorizontalScrollIndicator = NO;
    [self setContentOffset:CGPointMake(kSelfWidth, 0)];
    self.scrollInterval = 3.f;
}

設(shè)置需要展示的視圖泽台,設(shè)置之前移除上次添加的相關(guān)視圖什荣,我這里是把所有視圖都先添加上來(lái)。

- (void)setShowViews:(NSArray *)showViews
{
    _showViews = showViews;
    [self.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
    if (!showViews.count) {
        return;
    }
    [showViews enumerateObjectsUsingBlock:^(UIView *  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSInteger index = idx + 1;
        if (index == showViews.count && index != 1) {
            index = 0;
            //把最后一個(gè)view加載在3的位置 如果只有一個(gè)view  還是老老實(shí)實(shí)的待在1的位置
        }
        obj.frame = CGRectMake(kSelfWidth * index, 0, kSelfWidth, kSelfHeight);
        obj.userInteractionEnabled = YES;
        [self addSubview:obj];
    }];
    self.contentSize = CGSizeMake(kSelfWidth * 3, 0);
    [self setContentOffset:CGPointMake(kSelfWidth, 0)];
    self.scrollEnabled = showViews.count != 1;
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapClick)];
    [self addGestureRecognizer:tap];
    self.currentIndex = 0;
    [self resetTimer];
}

因?yàn)橛凶詣?dòng)滾動(dòng) 需要設(shè)置一下timer怀酷,在重新設(shè)置滾動(dòng)時(shí)間間隔和設(shè)置showViews的時(shí)候調(diào)用稻爬,需要停止上一個(gè)timer

- (void)resetTimer
{
    [self.scrollTimer invalidate];
    self.scrollTimer = nil;
    if (self.subviews.count < 2 || self.scrollInterval <= 0) {
        return;
    }
    __weak typeof(self) weakself = self;
    self.scrollTimer = [NSTimer scheduledTimerWithTimeInterval:self.scrollInterval repeats:YES block:^(NSTimer * _Nonnull timer) {
        __strong typeof(weakself) strongself = weakself;
        CGPoint point = strongself.contentOffset;
        point.x += kSelfWidth;
        [strongself setContentOffset:point animated:YES];
    }];
    [[NSRunLoop mainRunLoop] addTimer:self.scrollTimer forMode:NSRunLoopCommonModes];
//把timer添加到commonModes上 這樣其他scrollView滑動(dòng)的時(shí)候就不會(huì)影響timer
}

監(jiān)聽(tīng)scrollView滾動(dòng)代理,為了判斷滾動(dòng)方向蜕依,當(dāng)只有兩個(gè)視圖時(shí)能夠及時(shí)的改變未展示視圖的frame

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if (self.showViews.count != 2) {
        return;
    }
    UIView *subView = [self.showViews objectAtIndex:[self realIndex:self.currentIndex + 1]];
    CGFloat x = 0;
//因?yàn)閏ontentOffset始終在中間位置 如果大于一個(gè)width的寬度就是左滑 否則就是右滑
    if (scrollView.contentOffset.x > scrollView.frame.size.width) {
        x = kSelfWidth * 2;
    }
    if (!doubleEqual(x, subView.frame.origin.x)) {
        CGRect frame = scrollView.bounds;
        frame.origin.x = x;
        subView.frame = frame;
    }
}

- (NSInteger)realIndex:(NSInteger)index
{
//左滑index+1 右滑index-1
  //防止數(shù)組越界 index過(guò)大時(shí)自動(dòng)變?yōu)榈谝粡?index過(guò)小時(shí)變?yōu)樽詈笠粡?使view能夠連起來(lái)
    NSInteger realIndex = index;
    if (realIndex < 0) {
        realIndex = self.showViews.count - 1;
    } else if (realIndex > self.showViews.count - 1) {
        realIndex = 0;
    }
    return realIndex;
}

當(dāng)手動(dòng)拖動(dòng)到某一個(gè)視圖或者自動(dòng)滾動(dòng)到某個(gè)視圖的時(shí)候調(diào)用下面的方法

- (void)setCurrentIndex:(NSInteger)currentIndex
{
    NSInteger realIndex = [self realIndex:currentIndex];
    if (_currentIndex == realIndex) {
        return;
    }
    _currentIndex = realIndex;
    for (int i = 0; i < 3; i++) {
        [self bringShowViewToFront:i];
    }
}

- (void)bringShowViewToFront:(NSInteger)result
{
//result分別是0 1 2代表312三張圖 因?yàn)閏urrentIndex是當(dāng)前視圖 需要-1獲取上一個(gè)視圖
    NSInteger realIndex = [self realIndex:self.currentIndex + result - 1];
    UIView *showView = [self.showViews objectAtIndex:realIndex];
    CGRect frame = showView.frame;
    frame.origin.x = frame.size.width * result;
    showView.frame = frame;
    [self bringSubviewToFront:showView];
//最后把這三個(gè)視圖設(shè)置到最前面就可以了
}

有疑問(wèn)或者有bug可以在??提

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末桅锄,一起剝皮案震驚了整個(gè)濱河市琉雳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌友瘤,老刑警劉巖翠肘,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異辫秧,居然都是意外死亡束倍,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門盟戏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)绪妹,“玉大人,你說(shuō)我怎么就攤上這事柿究∮士酰” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵笛求,是天一觀的道長(zhǎng)廊移。 經(jīng)常有香客問(wèn)我,道長(zhǎng)探入,這世上最難降的妖魔是什么狡孔? 我笑而不...
    開(kāi)封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮蜂嗽,結(jié)果婚禮上苗膝,老公的妹妹穿的比我還像新娘。我一直安慰自己植旧,他們只是感情好辱揭,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著病附,像睡著了一般问窃。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上完沪,一...
    開(kāi)封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天域庇,我揣著相機(jī)與錄音,去河邊找鬼覆积。 笑死听皿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的宽档。 我是一名探鬼主播尉姨,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼吗冤!你這毒婦竟也來(lái)了又厉?” 一聲冷哼從身側(cè)響起九府,我...
    開(kāi)封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎馋没,沒(méi)想到半個(gè)月后昔逗,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體降传,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡篷朵,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了婆排。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片声旺。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖段只,靈堂內(nèi)的尸體忽然破棺而出腮猖,到底是詐尸還是另有隱情,我是刑警寧澤赞枕,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布澈缺,位于F島的核電站,受9級(jí)特大地震影響炕婶,放射性物質(zhì)發(fā)生泄漏姐赡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一柠掂、第九天 我趴在偏房一處隱蔽的房頂上張望项滑。 院中可真熱鬧,春花似錦涯贞、人聲如沸枪狂。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)州疾。三九已至,卻和暖如春皇拣,著一層夾襖步出監(jiān)牢的瞬間严蓖,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工审磁, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留谈飒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓态蒂,卻偏偏與公主長(zhǎng)得像杭措,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子钾恢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345