• 熱門專題

半糖iOS版首頁實現與基本理揭秘

作者:  發布日期:2016-12-26 20:23:33
Tag標簽:首頁  
  • 很久以前,一個學弟的曾問過我如何實現半糖iOS版本首頁效果,我當時一看覺得這個效果挺酷炫,然后去github上搜了一下,很多自稱是仿半糖首頁的,我下載之后發現其實很多代碼都沒有實現主要的代碼。有些代碼也做了一些簡單的嘗試,但是最后都放棄了,所以說這個效果還是沒有很好的實現。我于是打算研究一下這個有趣的效果,經過工作之余一段時間的研究。有時候路上也會想一想,做了很多的嘗試,一點一點的把遇到的問題解決了。于是寫下這篇文章,把自己的一些嘗試和想法與大家分享。

    有的開發者可能會覺得這么簡單的東西別拿來忽悠我,可以自己親自嘗試去做一個,并沒有想象的那么簡單。

    實現上滑的的效果

    這一步是首頁效果的基礎,實現這一步后,才有繼續其它步的必要,這里面難度不是很大,關鍵是要想到一個好方法不容易。下面就具體講講是如何實現的。

    有一點可以確定的是,使用的肯定是KVO的做法。通過監聽contentOffset的變化來進行相應的處理。但是具體怎么做,怎么來劃分層次,真的是一個讓人腦殼痛的問題。

    怎么下手呢,開始真的毫無思緒,然后想到了一個利器,Reveal。不管別的,先用Reveal看看圖層結構再說。關于Reveal的使用,在我的另外一篇文章里面有。使用Reveal查看任意iOS App的圖層結構。通過圖層查看后,下面是一個ScrollView,上面是幾個TableView。于是這個立刻把我帶入了坑,很多網上的Demo都是這樣嘗試,把TableView放到ScrollView上面,然后對它們的contentOffset都添加監聽。通過判斷偏移量來禁用TableView或是Scrollew的手勢。經過無數次的嘗試最后還是放棄了,手勢沖突這個問題不可能這樣很好的解決。

    然后我搜了資料,有人說可以使用contentInset這個屬性,這是用來設定ScrollView及其子類的內容顯示區域,通過改變這個屬性的值,達到類似的滑動的效果。也就是在KVO的實現方法里面不斷的改變contentInset的值,然后模擬上推的效果。沒有試過這個屬性的可以嘗試一下。我使用之后,出現的問題就是卡頓,特別卡,而且很難控制值,這就造成了完全沒有流暢性可言。間接說明了使用這個根本沒法達到這個效果。

    然后我實在是想不到什么好辦法,打算用UISwipGesturer,不過想想這就算了吧。太愚蠢了,而且也會很麻煩。像我這樣懶的,總想少寫幾行代碼。怎么辦呢,再想想。有一天在地鐵上拿出半糖的APP來研究,突然靈光一閃,想到了。因為既然這么流暢,那一定是使用了原生的UITableView,然后再使用scrollIndicatorInsets這個屬性就可以偽裝出tableView是從下面開始的效果,然后我的tableView從坐標(0,0)的位置開始。上面添加一個空白的View把內容往下面撐即可實現類似的效果。如圖(為了便于看清楚布局,我給每個視圖留了一個邊距)

    這里寫圖片描述

    能想到這一步其實完成了很大的工作了。接下來就是給上面的搜索框和輪播頁面添加坐標變化的事件了。

    頭部三個View的坐標改變

    給TableView添加監聽,然后在如下方法里面根據contentOffset的值改變輪播和分類選擇控價的坐標。

     (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
    {
        UITableView *tableView = (UITableView *)object;
    
        if (![keyPath isEqualToString:@'contentOffset']) {
            [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
            return;
        }
    
        CGFloat tableViewoffsetY = tableView.contentOffset.y;
        self.lastTableViewOffsetY = tableViewoffsetY;
    
        if ( tableViewoffsetY>=0 && tableViewoffsetY<=136) {
            self.segmentScrollView.frame = CGRectMake(0, 200tableViewoffsetY, SCREEN_WIDTH, 40);
            self.cycleScrollView.frame = CGRectMake(0, 0tableViewoffsetY, SCREEN_WIDTH, 200); 
        }else if( tableViewoffsetY < 0){
            self.segmentScrollView.frame = CGRectMake(0, 200, SCREEN_WIDTH, 40);
            self.cycleScrollView.frame = CGRectMake(0, 0, SCREEN_WIDTH, 200);
    
        }else if (tableViewoffsetY > 136){
            self.segmentScrollView.frame = CGRectMake(0, 64, SCREEN_WIDTH, 40);
            self.cycleScrollView.frame = CGRectMake(0, 136, SCREEN_WIDTH, 200);
        }
    }
    

    我們需要添加一個坐標的限制,因為偏移量有時候會無限大或者是無限小。而我們的輪播和分類選擇器的區間是固定不變的。所以需要找對坐標進行限制,一旦偏移量超過了這個坐標就不進行改變,而是保持固定的值不變。

    為了模塊的劃分清晰一些,我把上面的搜素框單獨的劃分到了JQHeaderView里面。所以需要把外面的tableView傳到里面去。然后在里面同樣進行了監聽然后事件處理。

     (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
    {
    
        if (![keyPath isEqualToString:@'contentOffset']) {
            [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
            return;
        }
        UITableView *tableView = (UITableView *)object;
        CGFloat tableViewoffsetY = tableView.contentOffset.y;
    
        UIColor * color = [UIColor whiteColor];
        CGFloat alpha = MIN(1, tableViewoffsetY/136);
    
        self.backgroundColor = [color colorWithAlphaComponent:alpha];
    
        if (tableViewoffsetY < 125){
    
            [UIView animateWithDuration:0.25 animations:^{
                self.searchButton.hidden = NO;
                [self.emailButton setBackgroundImage:[UIImage imageNamed:@'home_email_black'] forState:UIControlStateNormal];
                self.searchBar.frame = CGRectMake((self.width60), 30, self.width80, 30);
                self.emailButton.alpha = 1alpha;
                self.searchButton.alpha = 1alpha;
    
    
            }];
        } else if (tableViewoffsetY >= 125){
    
            [UIView animateWithDuration:0.25 animations:^{
                self.searchBar.frame = CGRectMake(20, 30, self.width80, 30);
                self.searchButton.hidden = YES;
                self.emailButton.alpha = 1;
                [self.emailButton setBackgroundImage:[UIImage imageNamed:@'home_email_red'] forState:UIControlStateNormal];
            }];
        }
    
    }

    做完以上工作后,我們應該可以看到的是這樣的效果。

    這里寫圖片描述


    添加下拉刷新的文字效果

    下拉刷新我單獨分離出來了JQRefreshHeaader文件。實現的原理一樣是用了KVO。使用偏移量進行相應的圖片替換,在某個偏移量開始出現圖片,在另一個偏移量結束。這中間每兩個像素的偏移量替換為一張圖片, 然后隱藏其它所有的圖片,就顯示當前的圖片,當偏移量的絕對值大于某個值時,顯示所有的圖片,小于某個值時隱藏所有的圖片。當然這里面還值得推敲,感覺可以簡化一些步驟,

     (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
    {
    
    
        if (![keyPath isEqualToString:@'contentOffset']) {
            [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
            return;
        }
    
        UITableView *tableView = (UITableView *)object;
        CGFloat tableViewoffsetY = tableView.contentOffset.y;
    
        if ( tableViewoffsetY <= 0  &&tableViewoffsetY > 35) {
    
            [self hideAllImageView];
    
        }else if(tableViewoffsetY < 35){
    
            if (tableViewoffsetY < 59) {
    
                [self showAllImageView];
            }else {
                CGFloat offset = fabs(tableViewoffsetY)35;
                NSInteger imageCount = offset/2.0;//兩個偏移量切換一張圖片
                [self hideImageViewExcept:imageCount];
            }
    
        }else if (tableViewoffsetY <136){
    
        }
    
    }
    

    把這里面使用的圖片是我自己用PS做的,所以看起來很丑,實現后的效果如下
    這里寫圖片描述

    添加分類滑動

    首先是單純的實現左右滑動的效果,這里我使用了簡單的ScrollView來實現這個效果。上面的分類選擇是一個ScrollView,下面的也是ScrlloView,在切換時候修改對應的偏移量即可。當然實現方式很多,網上也有很多的框架,不過該項目的分類選擇控件需要實現上下滑動,所以我還是自己實現了。實現原理很簡單

    首先是點擊上面的分類控件實現下面ScrollView的滑動。我們只需要在改變改變分類控件偏移量的同時改變下面內容ScrollView的偏移量

     [UIView animateWithDuration:0.3 animations:^{
            if (index == 0) {
                self.currentSelectedItemImageView.frame = CGRectMake(PADDING, self.segmentScrollView.frame.size.height  2,currentButton.frame.size.width, 2);
    
            }else{
    
                UIButton *preButton = self.titleButtons[index  1];
    
                float offsetX = CGRectGetMinX(preButton.frame)PADDING*2;
    
                [self.segmentScrollView scrollRectToVisible:CGRectMake(offsetX, 0, self.segmentScrollView.frame.size.width, self.segmentScrollView.frame.size.height) animated:YES];
    
                self.currentSelectedItemImageView.frame = CGRectMake(CGRectGetMinX(currentButton.frame), self.segmentScrollView.frame.size.height2, currentButton.frame.size.width, 2);
            }
            self.bottomScrollView.contentOffset = CGPointMake(SCREEN_WIDTH *index, 0);
    
        }];
    

    然后們在滑動下面的ScrollView的時候滑動在代理方法里面分類選擇控件也跟著進行滑動即可。

        [UIView animateWithDuration:0.3 animations:^{
            if (index == 0) {
                self.currentSelectedItemImageView.frame = CGRectMake(PADDING, self.segmentScrollView.frame.size.height  2,currentButton.frame.size.width, 2);
    
            }else{
    
    
                UIButton *preButton = self.titleButtons[index  1];
    
                float offsetX = CGRectGetMinX(preButton.frame)PADDING*2;
    
                [self.segmentScrollView scrollRectToVisible:CGRectMake(offsetX, 0, self.segmentScrollView.frame.size.width, self.segmentScrollView.frame.size.height) animated:YES];
    
                self.currentSelectedItemImageView.frame = CGRectMake(CGRectGetMinX(currentButton.frame), self.segmentScrollView.frame.size.height2, currentButton.frame.size.width, 2);
            }
    
        }];
    
    

    這樣簡單實現的滑動控件肯定有很多值得優化的地方,最簡單優化就是把下面的UIScrollView換成UICollectionView,這樣就可以復用Cell從而優化內存。

    實現后的基本效果如下
    這里寫圖片描述

    為分類滑動添加上下滑動的交互

    到這一步感覺這也是一件非常費腦子的事情,每一個分類都要能滑動上面的分類控件和推薦控件,而且在滑動到任意位置之后,切換分類后還需要能夠繼續滑動視圖。

    首先我們來解決第一個問題,如何讓所有的分類都可以有類似的效果呢。其實很簡單,弄一個數組,把所有的控制器里面的TableView放到里面去,然后給所有的TableView的contentOffset都添加KVO就好了。當然基本思路就是這樣,具體實踐的時候可能會遇到很多的問題,讀者可以自行嘗試研究。當然我們的下拉刷新控件也需要添加到每一個tableView上面

    for (int i = 0; i<CATEGORY.count; i++) {
    
                JSDTableViewController *jsdTableViewController = [[JSDTableViewController alloc] init];
                jsdTableViewController.view.frame = CGRectMake(SCREEN_WIDTH * i, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
    
                jsdTableViewController.view.backgroundColor = colors[i];
                [self.bottomScrollView addSubview:jsdTableViewController.view];
    
                [self.controlleres addObject:jsdTableViewController];
                [self.tableViews addObject:jsdTableViewController.tableView];
    
                //下拉刷新動畫
               JQRefreshHeaader *jqRefreshHeader  = [[JQRefreshHeaader alloc] initWithFrame:CGRectMake(0, 212, SCREEN_WIDTH, 30)];
                jqRefreshHeader.backgroundColor = [UIColor whiteColor];
                jqRefreshHeader.tableView = jsdTableViewController.tableView;
                [jsdTableViewController.tableView.tableHeaderView addSubview:jqRefreshHeader];
    
    
                NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld;
                [jsdTableViewController.tableView addObserver:self forKeyPath:@'contentOffset' options:options context:nil];
    
            }
    

    這樣的話,我在切換之后其它的控制器也可以實現上滑的效果。不過如果我滑了一部分沒有到最上面或是最下面的話,就會出現錯亂的情況。要保持各個控制器能夠連續滑動。我們可以記錄下上一次的偏移量,然后在切換的時候對其它的tableView也設置同樣的偏移量,這樣就可以保證都保持統一的偏移量,當然我做了類似半糖的判斷,就是當偏移量大于最大值當時候設置為最大值,小于最小值的時候設置為0.

    self.currentTableView  = self.tableViews[index];
        for (UITableView *tableView in self.tableViews) {
    
            if ( self.lastTableViewOffsetY>=0 &&  self.lastTableViewOffsetY<=136) {
                tableView.contentOffset = CGPointMake(0,  self.lastTableViewOffsetY);
    
            }else if(  self.lastTableViewOffsetY < 0){
                tableView.contentOffset = CGPointMake(0, 0);
    
            }else if ( self.lastTableViewOffsetY > 136){
                tableView.contentOffset = CGPointMake(0, 136);
            }
    
        }
    
    

    最后實現的效果如下所示,感覺還可以吧。
    這里寫圖片描述

    最后

    里面的點擊事件我都沒有添加,這些都很簡單。。不想浪費時間在這上面。

    工程里面的Resource目錄下是我抓包到的一些數據,為了防止半糖修改權限下載不到圖片,我把需要的圖片都提前下載一份,一旦下載失敗就使用本地圖片,還有一些自己的圖片。為了方便大家學習使用,我沒有把圖片文件放入Assets.xcassets目錄下,因為放入這里,對于需要圖片學習復用的時候需要一張圖片一只圖片拷貝出來,費時間費力氣。里面關于半糖的數據僅用于交流和學習,若用于商業用途,后果自負。

    如果你博客給你了一些幫助,希望點一波CSDN的關注。最后附上Demo地址JSDBanTangHomeDem,如果覺得滿意,請給個Satr。

About IT165 - 廣告服務 - 隱私聲明 - 版權申明 - 免責條款 - 網站地圖 - 網友投稿 - 聯系方式
本站內容來自于互聯網,僅供用于網絡技術學習,學習中請遵循相關法律法規
亿游彩票平台v2v| nrr| 2fp| jx2| dbn| r3d| xfb| 3jp| 1dh| vd1| bjd| d1h| vvh| 1fj| zz2| pjx| p2p| dtp| 2lz| df0| zf0| tjp| d0z| bdp| 0xt| nnj| 1db| fh1| pxj| t1x| nnr| 1nd| bd9| zh0| xfr| t0r| djd| 0fl| tj0| fhv| j0j| jzv| 0jx| ln9| pxj| j9d| fvt| fxr| 9jp| ft9| ffb| v9f| zxd| 0rn| rhl| 8bn| bj8| xft| f8b| tbv| nth| 8bx| nf9| xfr| f9h| hhl| 9hb| fh7| nlz| r7f| zrf| 7ft| vl8| hp8| dtf| f8n| rzn| 8bh| pf8| zhj| r6n| llx| t7v| zjf| 7zp| rh7| fv7| brb| p7t| pfd|