OC版寫一個(gè)快速集成網(wǎng)易新聞,騰訊視頻莺褒,頭條首頁(yè)的ZJScrollPageView,實(shí)現(xiàn)視圖聯(lián)動(dòng)


最終效果

oc版滾動(dòng)示例.gif

oc版滾動(dòng)示例2.gif

oc版滾動(dòng)示例3.gif

oc版滾動(dòng)示例4.gif

oc版滾動(dòng)示例5.gif

oc版滾動(dòng)示例6.gif

oc版滾動(dòng)示例7.gif

oc版滾動(dòng)示例8.gif
圖片在左邊

圖片在右邊.gif

圖片在上面.gif

只顯示圖片.gif

一.構(gòu)思部分:

打算分為三個(gè)部分, 滑塊部分View, 內(nèi)容顯示部分View, 包含滑塊View和顯示內(nèi)容View的View,以便于可以靈活的使用

1. 滑塊部分View

1.1 要實(shí)現(xiàn)滑塊可以滾動(dòng), 考慮可以直接使用collectionView, 但是這里還是直接使用scrollView方便里面的控件布局

1.2 要實(shí)現(xiàn)滑塊的點(diǎn)擊, 可以直接使用UIButton, 但是經(jīng)過嘗試, 要讓button的frame隨著文字的寬度來自適應(yīng)實(shí)現(xiàn)比較麻煩, 所以選擇了使用UILabel添加點(diǎn)擊手勢(shì)來實(shí)現(xiàn)點(diǎn)擊事件,這里使用了closures來實(shí)現(xiàn)(可以使用代理模式)

1.3 實(shí)現(xiàn)對(duì)應(yīng)的滾動(dòng)條和遮蓋同步移動(dòng)的效果,文字顏色漸變功能(在點(diǎn)擊的時(shí)候直接使用一個(gè)動(dòng)畫就可以簡(jiǎn)單的完成了)

2. 內(nèi)容顯示部分View

2.1 用來作為包含子控制器的view的容器, 并且實(shí)現(xiàn)可以分頁(yè)滾動(dòng)的效果

2.2 要實(shí)現(xiàn)分頁(yè)滾動(dòng), 可以使用UIScrollView來實(shí)現(xiàn), 但是這樣就要考慮UIScrollView上的各個(gè)view的復(fù)用的問題, 其中細(xì)節(jié)還是很麻煩, 所以直接使用了UICollectionView(利用他的重用機(jī)制)來實(shí)現(xiàn)

2.3 將每一個(gè)子控制器的view直接添加到對(duì)應(yīng)的每一個(gè)cell的contentView中來展示, 所以這里需要注意cell重用可能帶來的內(nèi)容顯示不正常的問題, 這里采用了每次添加contentView的內(nèi)容時(shí)移除所有的subviews(也可以直接給每個(gè)cell用不同的reuseIdentifier實(shí)現(xiàn))

2.4 實(shí)現(xiàn)實(shí)時(shí)監(jiān)控滾動(dòng)的進(jìn)度提供給滑塊部分來同步調(diào)整滾動(dòng)條和遮蓋,文字顏色的漸變, 并且在每次滾動(dòng)完成的時(shí)候可以通知給滑塊來調(diào)整他的內(nèi)容

3. 包含滑塊View和顯示內(nèi)容View的View

3.1 因?yàn)榛瑝K部分View和內(nèi)容顯示部分View是相對(duì)獨(dú)立的部分, 在這里只需要實(shí)現(xiàn)兩者的通信即可

3.2 可以自定義滑塊部分View和內(nèi)容顯示部分View的frame

a. 滑塊部分

1. 基本屬性

// 滾動(dòng)條
@property (weak, nonatomic) UIView *scrollLine;
// 遮蓋
@property (weak, nonatomic) UIView *coverLayer;
// 滾動(dòng)scrollView
@property (weak, nonatomic) UIScrollView *scrollView;
// 背景ImageView
@property (weak, nonatomic) UIImageView *backgroundImageView;
// 附加的按鈕
@property (weak, nonatomic) UIButton *extraBtn;
/// 所有的title設(shè)置 -> 使用了一個(gè)class ZJSegmentStyle, 將可以自定義的部分全部暴露了出來, 使用的時(shí)候就可以比較方便的自定義很多屬性  -> 初始化時(shí)傳入
@property (strong, nonatomic) ZJSegmentStyle *segmentStyle;
// 所有的標(biāo)題
@property (strong, nonatomic) NSArray *titles;
// 用于懶加載計(jì)算文字的rgb差值, 用于顏色漸變的時(shí)候設(shè)置
@property (strong, nonatomic) NSArray *deltaRGB;
@property (strong, nonatomic) NSArray *selectedColorRgb;
@property (strong, nonatomic) NSArray *normalColorRgb;
/** 緩存所有標(biāo)題label */
@property (nonatomic, strong) NSMutableArray *titleLabels;
// 緩存計(jì)算出來的每個(gè)標(biāo)題的寬度
@property (nonatomic, strong) NSMutableArray *titleWidths;
// 響應(yīng)標(biāo)題點(diǎn)擊
@property (copy, nonatomic) TitleBtnOnClickBlock titleBtnOnClick;


邏輯處理

#pragma mark - life cycle
- (instancetype)initWithFrame:(CGRect )frame segmentStyle:(ZJSegmentStyle *)segmentStyle titles:(NSArray *)titles titleDidClick:(TitleBtnOnClickBlock)titleDidClick {
    if (self = [super initWithFrame:frame]) {
        self.segmentStyle = segmentStyle;
        self.titles = titles;
        self.titleBtnOnClick = titleDidClick;
        _currentIndex = 0;
        _oldIndex = 0;
        _currentWidth = frame.size.width;
        
        if (!self.segmentStyle.isScrollTitle) { // 不能滾動(dòng)的時(shí)候就不要把縮放和遮蓋或者滾動(dòng)條同時(shí)使用, 否則顯示效果不好
            
            self.segmentStyle.scaleTitle = !(self.segmentStyle.isShowCover || self.segmentStyle.isShowLine);
        }
        
        // 這個(gè)函數(shù)里面設(shè)置了基本屬性中的titles, labelsArray, titlesWidthArray,并且添加了label到scrollView上
        [self setupTitles];
        // 這個(gè)函數(shù)里面設(shè)置了遮蓋, 滾動(dòng)條,和label的初始化位置
        [self setupUI];

    }
    
    return self;
}

#pragma mark - button action
  -> 處理點(diǎn)擊title的時(shí)候?qū)崿F(xiàn)標(biāo)題的切換,和遮蓋,滾動(dòng)條...的位置調(diào)整, 同時(shí)執(zhí)行了響應(yīng)點(diǎn)擊的block, 以便于外部相應(yīng)點(diǎn)擊方法
- (void)titleLabelOnClick:(UITapGestureRecognizer *)tapGes {
    
    ZJCustomLabel *currentLabel = (ZJCustomLabel *)tapGes.view;
    
    if (!currentLabel) {
        return;
    }
    
    _currentIndex = currentLabel.tag;
    // 同步更改UI
    [self adjustUIWhenBtnOnClickWithAnimate:true];
}

- (void)adjustTitleOffSetToCurrentIndex:(NSInteger)currentIndex  -> 更改scrollview的contentOffSet來居中顯示title


     // 手動(dòng)滾動(dòng)時(shí)需要提供動(dòng)畫效果
- (void)adjustUIWithProgress:(CGFloat)progress oldIndex:(NSInteger)oldIndex currentIndex:(NSInteger)currentIndex -> 提供給外部來執(zhí)行標(biāo)題切換之間的動(dòng)畫效果(注意這個(gè)方法里面進(jìn)行了一些簡(jiǎn)單的數(shù)學(xué)計(jì)算以便于"同步" 滾動(dòng)滾動(dòng)條和cell )
    這里以滑塊的位置x變化為例, 其他類似
    
    CGFloat xDistance = currentLabel.zj_x - oldLabel.zj_x;
    這個(gè)xDistance就是滑塊將要從一個(gè)label下面移動(dòng)到下一個(gè)label下面所需要移動(dòng)的路程,
    
    這個(gè)progress是外界提供來的, 表示當(dāng)前已經(jīng)移動(dòng)的百分比(0 --- 1)是多少了,所以可以改變當(dāng)前滑塊的x為之前的x + 已經(jīng)完成滾動(dòng)的距離(xDistance * progress)
    scrollLine?.frame.origin.x = oldLabel.frame.origin.x + xDistance * progress
    
    這樣就達(dá)到了滑塊的位置隨著提供的progress同步移動(dòng)

    其他的遮蓋 和顏色漸變的處理類似

b 內(nèi)容顯示部分View

基本屬性

// 用于處理重用和內(nèi)容的顯示
@property (weak, nonatomic) UICollectionView *collectionView;
// collectionView的布局
@property (strong, nonatomic) UICollectionViewFlowLayout *collectionViewLayout;
/** 避免循環(huán)引用*/
@property (weak, nonatomic) ZJScrollSegmentView *segmentView;

@property (weak, nonatomic) UIButton *extraBtn;
// 父類 用于處理添加子控制器  使用weak避免循環(huán)引用
@property (weak, nonatomic) UIViewController *parentViewController;
// 當(dāng)這個(gè)屬性設(shè)置為YES的時(shí)候 就不用處理 scrollView滾動(dòng)的計(jì)算
@property (assign, nonatomic) BOOL forbidTouchToAdjustPosition;
// 所有的子控制器
@property (strong, nonatomic) NSArray *childVcs;

邏輯處理

#pragma mark - life cycle 

- (instancetype)initWithFrame:(CGRect)frame childVcs:(NSArray *)childVcs segmentView:(ZJScrollSegmentView *)segmentView parentViewController:(UIViewController *)parentViewController {
    
    if (self = [super initWithFrame:frame]) {
        self.childVcs = childVcs;
        self.parentViewController = parentViewController;
        self.segmentView = segmentView;
        _oldIndex = 0;
        _currentIndex = 1;
        _oldOffSetX = 0.0;
        self.forbidTouchToAdjustPosition = NO;
        // 觸發(fā)懶加載
        self.collectionView.backgroundColor = [UIColor whiteColor];
        [self commonInit];
    }
    return self;
}

/** 給外界可以設(shè)置ContentOffSet的方法 比如點(diǎn)擊了標(biāo)題的時(shí)候需要更改內(nèi)容顯示調(diào)用*/
- (void)setContentOffSet:(CGPoint)offset animated:(BOOL)animated

/** 給外界刷新視圖的方法  用于動(dòng)態(tài)設(shè)置子控制器和標(biāo)題*/
- (void)reloadAllViewsWithNewChildVcs:(NSArray *)newChileVcs {
  // 這種處理是結(jié)束子控制器和父控制器的關(guān)系
    for (UIViewController *childVc in self.childVcs) {
        [childVc willMoveToParentViewController:nil];
        [childVc.view removeFromSuperview];
        [childVc removeFromParentViewController];
    }

}


#pragma mark - UICollectionViewDelegate --- UICollectionViewDataSource

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
    return 1;
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return self.childVcs.count;
}

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath];
    // 移除subviews 避免重用內(nèi)容顯示錯(cuò)誤
    [cell.contentView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
    // 這里建立子控制器和父控制器的關(guān)系  -> 當(dāng)然在這之前已經(jīng)將對(duì)應(yīng)的子控制器添加到了父控制器了, 只不過還沒有建立完成
    UIViewController *vc = (UIViewController *)self.childVcs[indexPath.row];
    vc.view.frame = self.bounds;
    [cell.contentView addSubview:vc.view];
    [vc didMoveToParentViewController:self.parentViewController];
    
    return cell;
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView  -> 這個(gè)代理方法中處理了滾動(dòng)的下標(biāo)和進(jìn)度  

3. "ZJScrollPageView.h"

這一部分是將segmentView和ContentView封裝在一起的, 便于使用者直接使用, 但是您也可以參照這里面的實(shí)現(xiàn), 任意組合segmentView和ContentView的位置, 在demo中也有示例


詳細(xì)請(qǐng)移步OC源碼, swift源碼,里面都有詳細(xì)的Demo使用示例 如果您覺得有幫助,不妨給個(gè)star鼓勵(lì)一下, 歡迎關(guān)注


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末白粉,一起剝皮案震驚了整個(gè)濱河市传泊,隨后出現(xiàn)的幾起案子鼠渺,更是在濱河造成了極大的恐慌,老刑警劉巖眷细,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拦盹,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡溪椎,警方通過查閱死者的電腦和手機(jī)普舆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來校读,“玉大人沼侣,你說我怎么就攤上這事〉叵ǎ” “怎么了华临?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)端考。 經(jīng)常有香客問我雅潭,道長(zhǎng),這世上最難降的妖魔是什么却特? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任扶供,我火速辦了婚禮,結(jié)果婚禮上裂明,老公的妹妹穿的比我還像新娘椿浓。我一直安慰自己,他們只是感情好闽晦,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布扳碍。 她就那樣靜靜地躺著,像睡著了一般仙蛉。 火紅的嫁衣襯著肌膚如雪笋敞。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天荠瘪,我揣著相機(jī)與錄音夯巷,去河邊找鬼。 笑死哀墓,一個(gè)胖子當(dāng)著我的面吹牛趁餐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播篮绰,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼后雷,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起喷面,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤星瘾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后惧辈,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體琳状,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年盒齿,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了念逞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡边翁,死狀恐怖翎承,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情符匾,我是刑警寧澤叨咖,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站啊胶,受9級(jí)特大地震影響甸各,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜焰坪,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一趣倾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧某饰,春花似錦儒恋、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至炬守,卻和暖如春箱锐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背劳较。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留浩聋,地道東北人观蜗。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像衣洁,于是被迫代替她去往敵國(guó)和親墓捻。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容