一行核心程式碼做出類似tableViewHeaderView和AppStore裡的ScrollView懸浮條效果

穿山甲到底說了什麼發表於2018-12-19

現在主流app裡面經常需要有tableView上滑出現懸浮條樣式的設計,寶寶看了下appleStore裡面專題推薦用的scrollView 也有類似的懸浮效果,只不過它是下拉懸浮,而主流的是上滑懸浮!最終實現效果如以下gif圖。

上滑懸停條樣式
appStore下拉懸浮條效果
仿appStore效果
其實一看上去 表以為是兩種不同的方案,其實兩種思路方式完全一致,本來核心程式碼就一行,那麼要修改的程式碼豈不是半行,確實如此哦,寶寶已笑瘋???,好了啦,開個玩笑,進入正題! ##先別敲程式碼,想想懸浮條實現思路

  • ###上滑懸浮條樣式的思路 當你看到本文第一張的動圖的時候你想到了什麼,對,沒錯,上滑的時候高度在減小最後會減小到一個固定值,下滑的時候高度在增加最後也會增加到一個固定值,那麼簡單點描述就是懸浮條原點不變,讓其高度減小,在下面程式碼裡第二種實現方式就是這樣;其實我實現的是讓懸浮條的原點變化,高度不變,也是一樣的效果。
  • ###appStore下拉懸浮條樣式的思路 有了第一種思路,會發現下拉懸浮條的過程與上面驚人的相似,上滑的時候高度在減小,只不過最後會減為0,下滑的時候高度在增加最後也會增加到一個固定值。
  • ###技術實現 通過KVO監聽tableView的contentOffset屬性值或者在scrollView的代理方法中監聽contentOffset的值來讓懸浮條的原點變化,高度不變,或者原點不變,高度變化! 將自定義的headerView放在tabView 的上面,即headerView和tableView為平級關係,都新增到viewController的view上,然後設定tableView的contentInset為headerView的值,在tableView滑動的時候,動態改變view的位置或者大小,使這個headerView看起來就像是有了懸浮功能的tableView.tableHeaderView。
  • ###核心程式碼 寶寶真的沒有騙你們,真的只要一行,只要你看得懂就行???,因為我把五行if語句和成一行了,表問我為什麼! _yellowView.frame = offset.y < -64 ? (offset.y <= -200 ? CGRectMake(0, 0, 375, 200) : CGRectMake(0, -offset.y - 200, 375, 200)) : CGRectMake(0, 64 - 200, 375, 200);

##程式碼實現

- (void)viewDidLoad {

    [super viewDidLoad];
   //載入tableView到self.view上
    UITableView *tableView = [[UITableView alloc]  initWithFrame:CGRectMake(0, 0, 375, 667) style:UITableViewStylePlain];
    [tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"Cell"];
    tableView.dataSource = self;
    tableView.contentInset = UIEdgeInsetsMake(200, 0, 0, 0);
    [self.view addSubview:tableView];
  //利用KVO監聽tableView的contentOffset的屬性值,從而動態改變懸浮條yellowView的frame值
   [tableView addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];

  //載入yellowView作為懸浮條檢視到self.view上,因此tableView和yellowView是同一級關係,黃色檢視在tableView的上面
    TouchView *yellowView = [[TouchView alloc] initWithFrame:CGRectMake(0, 0, 375, 200)];
    yellowView.backgroundColor = [UIColor yellowColor];
  //將alpha設定成為0.7的透明度是為了看得更清楚底下的tableView隨時滾動的位置
    yellowView.alpha = 0.7;
    [self.view addSubview:yellowView];
  //設定成全域性的例項變數是為了在監聽方法中可以改變懸浮條yellowView的frame值
   _yellowView = yellowView;
 }

#pragma mark - KVO監聽tableView的contentOffset的屬性值變化
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {

     CGPoint offset = [change[NSKeyValueChangeNewKey] CGPointValue];

    //64為懸浮條最終停留的高度
    if (offset.y < -64) {
    
         //小於-64則有兩種情況,第一種小於-64是當懸浮條一進來時候,由於contentInset.top被我們調成了懸浮條的高度200,
       因此offset.y初始值是-200,當使用者上滑懸浮條的時候,會在 -200 <= offset.y <= -64 範圍內波動,我們需要
       調整懸浮條的Y座標為-offset.y - 200,如- (-180) - 200 = -20 ,代表tableView向上偏移了20各點,我們改變其原點,
       造成懸浮條懸停的假象;第二種小於-64是當懸浮條一進來時候,offset.y初始值是-200,當使用者下滑懸浮條的時候,offset.y會負的越多,
       在 offset.y < -200 範圍內波動,我們需要調整懸浮條的Y座標為固定值200,不讓其變大變小即可!!搞定
    
       //第一種方案,改變懸浮條的Y座標,而不改變其高度,這也是本文核心程式碼!!!!
       _yellowView.frame = offset.y <= -200 ?  CGRectMake(0, 0, 375, 200) : CGRectMake(0, -offset.y - 200, 375, 200);
    
       //第二種方案,改變懸浮條的高度,而不改變其原點
       //_yellowView.frame = offset.y < -200 ?  CGRectMake(0, 0, 375, 200) : CGRectMake(0, 0, 375, -offset.y);
    
       //下面這行程式碼沒必要新增,我看到網上很多人加了這句,實屬沒必要,tableView滾動本來就靠contentOffset,contentInset只是新增額外的滾動區域的
       //_tableView.contentInset = offset.y > -200 ? UIEdgeInsetsMake(-offset.y, 0, 0, 0) :UIEdgeInsetsMake(200, 0, 0, 0);
    }else {
    
       //大於等於-64就只有一種情況了,當到達臨界值的時候就會 懸停64的最小高度,該高度自己隨便寫啦,然後appStore那個效果是上滑到臨界值後,高度依然減小,
     那麼需要調整懸浮條的Y座標為-offset.y - 200,offset.y會越來越 大於-64,導致_yellowView.frame.origin.y負的越多,越來偏離螢幕原點,
     正好滿足需求看不見懸浮條啦!
    
      //appStore下拉懸浮條效果只需要改這行程式碼讓上滑的時候原點超過其高度200,這樣就看不到懸浮條了
      //_yellowView.frame = CGRectMake(0, -offset.y - 200, 375, 200);;
    
      //第一種方案,改變懸浮條的Y座標為懸停最小值64 - 200,這樣在使用者介面上就只顯示64的高度,而不改變其高度
      _yellowView.frame = CGRectMake(0, 64 - 200, 375, 200);
    
      //第二種方案,改變懸浮條的高度為最小值64,而不改變其原點
      //_yellowView.frame = CGRectMake(0, 0, 375, 64);
    
      //下面這行程式碼沒必要新增,我看到網上很多人加了這句,實屬沒必要,tableView滾動本來就靠contentOffset,contentInset只是新增額外的滾動區域的
      //_tableView.contentInset = UIEdgeInsetsMake(64, 0, 0, 0);
  }
}
複製程式碼

###自定義TouchView 如果有另外需求比如要是拖動黃色檢視,tableView要是也能滾動的話,那麼就需要自己攔截點選事件!!

@implementation TouchView

//這兩個方法隨便寫一個即可,為的是攔截響應鏈,不讓其捕獲到觸控事件,這樣使用者手指點選上面的黃色檢視,window分發觸控事件的時候,會認為使用者點選的那個點最遠的響應檢視是 tableView,因為tableView在黃色檢視的下方,這樣,當滑動黃色檢視的時候,tableView也跟著一起滾動了啦!!!
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
return NO;
}

//先呼叫hitTest:withEvent:此方法,再呼叫pointInside:withEvent:
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {

   UIView *view = [super hitTest:point withEvent:event];
   if ([view isKindOfClass:[self class]]) {
    
       return nil;
   }
   return nil;
 }

@end
複製程式碼

相關文章