• 易购平台官网

  • 易购平台官网

  • 易购平台官网

  • 易购平台官网

易购平台官网

作者︰  發布日期︰2020-02-20 15:25:33
Tag標簽︰首頁  
  • 很(hen)久以前,一(yi)個學弟的曾問(wen)shi)wo)如何wen)迪職ban)糖iOS版(ban)本首頁效(xiao)果,我(wo)當bi)幣yi)看覺(jue)得這個效(xiao)果挺酷(ku)炫(xuan),然後去github上搜了一(yi)下,很(hen)多(duo)自稱是仿半(ban)糖首頁的mo) wo)下載(zai)之後發現其(qi)實很(hen)多(duo)代(dai)碼(ma)都沒(mei)有實現主要的代(dai)碼(ma)。有些代(dai)碼(ma)也做了一(yi)些簡單的嘗試,但是qin)詈蠖擠牌耍 運嫡飧魴xiao)果還是沒(mei)有很(hen)好(hao)的實現。我(wo)于是打算研究(jiu)一(yi)下這個有趣的效(xiao)果,經過(guo)工作之余一(yi)段wen)奔jian)的研究(jiu)。有時候(hou)路(lu)上也會想一(yi)想,做了很(hen)多(duo)的嘗試,一(yi)點(dian)一(yi)點(dian)的把遇(yu)到的問(wen)題解決了。于是寫(xie)下這篇文章,把自己(ji)的一(yi)些嘗試和想法(fa)與大家分享(xiang)。

    有的開發者可能會覺(jue)得這麼簡單的東西別拿來忽悠我(wo),可以自己(ji)親(qin)自嘗試去做一(yi)個,並(bing)沒(mei)有想象的那麼簡單。

    易购平台官网

    這一(yi)步是首頁效(xiao)果的基礎,實現這一(yi)步後,才有繼(ji)續其(qi)它(ta)步的必要,這里面難xun)炔皇嗆hen)大,關鍵是要想到一(yi)個好(hao)方法(fa)不容易(yi)。下面就(jiu)具體講講cai)僑綰問(wen)迪值摹/p>

    有一(yi)點(dian)可以確(que)定的是,使(shi)用的肯定是KVO的做法(fa)。通過(guo)監听contentOffset的變化來進行相(xiang)應的處理。但是具體怎麼做,怎麼chun)椿 植憒ci),真的是一(yi)個讓人腦殼痛的問(wen)題。

    怎麼下手呢(ne),開始真的毫無思緒(xu),然後想到了一(yi)個利器,Reveal。不管(guan)別的mo) 扔eveal看看圖(tu)層結構再說。關于Reveal的使(shi)用,在(zai)我(wo)的另外一(yi)篇文章里面有。使(shi)用Reveal查(cha)看任wo)OS App的圖(tu)層結構。通過(guo)圖(tu)層查(cha)看後,下面是一(yi)個ScrollView,上面是幾個TableView。于是這個立刻把我(wo)帶入了坑,很(hen)多(duo)網上的Demo都是這樣嘗試,把TableView放到ScrollView上面,然後對它(ta)們的contentOffset都ji)砑jia)監听。通過(guo)判斷偏移(yi)量來禁用TableView或是Scrollew的手勢。經過(guo)無數次(ci)的嘗試最後還是放棄了,手勢沖突這個問(wen)題不可能這樣很(hen)好(hao)的解決。

    然後我(wo)搜了資料,有人說可以使(shi)用contentInset這個屬性,這是用來設(she)定ScrollView及其(qi)子(zi)類的內容顯示(shi)區(qu)域(yu),通過(guo)改變這個屬性的值,達(da)到類似的滑動的效(xiao)果。也就(jiu)是在(zai)KVO的實現方法(fa)里面不斷的改變contentInset的值,然後模(mo)擬上推的效(xiao)果。沒(mei)有試過(guo)這個屬性的可以嘗試一(yi)下。我(wo)使(shi)用之後,出現的問(wen)題就(jiu)是卡頓(dun),特(te)別卡,而且很(hen)難控(kong)制值,這就(jiu)造成了完(wan)全沒(mei)有流暢性可言。間(jian)接(jie)說明了使(shi)用這個根本沒(mei)法(fa)達(da)到這個效(xiao)果。

    然後我(wo)實在(zai)是想不到什麼好(hao)辦法(fa),打算用UISwipGesturer,不過(guo)想想這就(jiu)算了吧。太愚蠢(chun)了,而且也會很(hen)麻(ma)煩(fan)。像我(wo)這樣懶的mo) 芟 儺xie)幾行xie)ma)。怎麼辦呢(ne),再想想。有一(yi)天在(zai)地鐵上拿出半(ban)糖的APP來研究(jiu),突然靈光一(yi)閃,想到了。因為(wei)既然這麼流暢,那一(yi)定是使(shi)用了原(yuan)生的UITableView,然後再使(shi)用scrollIndicatorInsets這個屬性就(jiu)可以偽裝出tableView是從下面開始的效(xiao)果,然後我(wo)的tableView從坐標(0,0)的位置開始。上面添加(jia)一(yi)個空白的View把內容往下面撐(cheng)即可實現類似的效(xiao)果。如圖(tu)(為(wei)了便于看清楚布局,我(wo)給每個視圖(tu)留了一(yi)個邊(bian)距)

    這里寫(xie)圖(tu)片描(miao)述

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

    易购平台官网

    給TableView添加(jia)監听,然後在(zai)如下lu)椒fa)里面根據contentOffset的值改變輪播和分類選擇控(kong)價的坐標。

     (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); }}

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

    為(wei)了模(mo)塊的劃分清晰一(yi)些,我(wo)把上面的搜素框單獨(du)的劃分到了JQHeaderView里面。所以需要把外面的tableView傳到里面去。然後在(zai)里面同(tong)樣進行了監听然後事件處理。

     (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]; }]; }}

    做完(wan)以上工作後,我(wo)們應該可以看到的是這樣的效(xiao)果。

    這里寫(xie)圖(tu)片描(miao)述


    易购平台官网

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

     (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;//兩(liang)個偏移(yi)量切換一(yi)張圖(tu)片  [self hideImageViewExcept:imageCount]; } }else if (tableViewoffsetY <136){ }}

    把這里面使(shi)用的圖(tu)片是我(wo)自己(ji)用PS做的mo) 鑰雌鵠春hen)丑,實現後的效(xiao)果如下
    這里寫(xie)圖(tu)片描(miao)述

    易购平台官网

    首先是單純的實現左(zuo)右yi)  男xiao)果,這里我(wo)使(shi)用了簡單的ScrollView來實現這個效(xiao)果。上面的分類選擇是一(yi)個ScrollView,下面的也cai)crlloView,在(zai)切換時候(hou)修改對應的偏移(yi)量即可。當然實現方式很(hen)多(duo),網上也有很(hen)多(duo)的框架,不過(guo)該項目的分類選擇控(kong)件需要實現上下滑動,所以我(wo)yi)故親(qin)約ji)實現了。實現原(yuan)理很(hen)簡單

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

     [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); }];

    然後們在(zai)滑動下面的ScrollView的時候(hou)滑動在(zai)代(dai)理方法(fa)里面分類選擇控(kong)件也跟著進行滑動即可。

     [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); } }];

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

    實現後的基本效(xiao)果如下
    這里寫(xie)圖(tu)片描(miao)述

    易购平台官网

    到這一(yi)步感覺(jue)這也cai)且yi)件非常費腦子(zi)的事情,每一(yi)個分類都要能滑動上面的分類控(kong)件和推薦控(kong)件,而且在(zai)滑動到任wo) 恢彌 螅 謝環擲 蠡剮枰 芄患ji)續滑動視圖(tu)。

    首先我(wo)們來解決第一(yi)個問(wen)題,如何讓所有的分類都可以有類似的效(xiao)果呢(ne)。其(qi)實很(hen)簡單,弄(long)一(yi)個數組(zu),把所有的控(kong)制器里面的TableView放到里面去,然後給所有的TableView的contentOffset都ji)砑jia)KVO就(jiu)好(hao)了。當然基本思路(lu)就(jiu)是這樣,具體實踐的時候(hou)可能會遇(yu)到很(hen)多(duo)的問(wen)題,讀者可以自行嘗試研究(jiu)。當然我(wo)們的下拉(la)刷新控(kong)件也需要添加(jia)到每一(yi)個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];  //下拉(la)刷新動畫  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]; }

    這樣的話,我(wo)在(zai)切換之後其(qi)它(ta)的控(kong)制器也bu)梢允迪稚匣 男xiao)果。不過(guo)如果我(wo)yi) 艘yi)部(bu)分沒(mei)有到最上面或是qin)釹旅嫻幕埃 jiu)會出現錯亂的情況。要保持各個控(kong)制器能夠連續滑動。我(wo)們可以記錄下上一(yi)次(ci)的偏移(yi)量,然後在(zai)切換的時候(hou)對其(qi)它(ta)的tableView也設(she)置同(tong)樣的偏移(yi)量,這樣就(jiu)可以保證都保持統一(yi)的偏移(yi)量,當然我(wo)做了類似半(ban)糖的判斷,就(jiu)是當偏移(yi)量大于最大值當bi)焙hou)設(she)置為(wei)最大值,小于最小值的時候(hou)設(she)置為(wei)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); } }

    最後實現的效(xiao)果如下所示(shi),感覺(jue)還可以吧。
    這里寫(xie)圖(tu)片描(miao)述

    易购平台官网

    里面的點(dian)擊事件我(wo)都沒(mei)有添加(jia),這些都ji)hen)簡單。。不想浪費時間(jian)在(zai)這上面。

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

    如果你(ni)博客給你(ni)了一(yi)些幫助(zhu),希望(wang)點(dian)一(yi)波CSDN的關注(zhu)。最後附上Demo地址JSDBanTangHomeDem,如果覺(jue)得滿意,請給個Satr。

About IT165 -廣告服務 -隱私聲明 -版(ban)權(quan)申明 -免責(ze)條款(kuan) -網站地圖(tu) -網友投(tou)稿 -聯系方式
本站內容來自于互(hu)聯網,僅供用于網絡技術學習,學習中請遵循相(xiang)關法(fa)律法(fa)規
易购平台官网 | 下一页