下拉重新整理和UITableView的section headerView衝突的原因分析與解決方案

NSTopGun發表於2013-11-28

UITableView:下拉重新整理和上拉載入更多

【轉載請註明出處】

 

本文將說明具有多個section的UITableView在使用下拉重新整理機制時會遇到的問題及其解決方案。

工程地址在帖子最下方,只需要程式碼的直拉到底即可。

 

【目錄】

1、現象分析;

2、解決方案;

3、工程地址。 

 

1、現象分析 
當前的大多數支援下拉重新整理的庫,都是通過修改UIScrollView的contentInset實現的。

(可參見我的這篇帖子:UITableView:下拉重新整理和上拉載入更多

使用這種方法帶來的問題是,當UITableView具有多個section時,依照上述方式使用下拉重新整理,在載入過程中上滑,會出現樣式跑版:

 

 

為了分析出問題所在,首先在控制檯列印出正常狀態下UITableView的所有子View :

 

比較各行的frame.origin.y可以看出,UITableView的section headerView是和cell一起,按順序放入基類UIScrollView的內容View中的。

再看載入中的情況:

 從Pull2RefreshView的frame可以看出,此時UITableView的contentInset已經被修改為(65, 0, 0, 0)。而section headerView和各個cell的frame.origin.y不受影響,和預期一致。

最後看看在載入中狀態下上滑時的情況,將UITableView上滑至上方圖中所示情境,即第一行的cell剛剛好越過section headerView:

可以看到,section headerView的frame.origin.y發生了變化,它和第二個cell一起被放在了第一個cell的下方,即:

section headerView.frame.origin.y == firstCell.frame.origin.y + cellHeight;

繼續上滑,可以看到section headerView.frame.origin.y不斷變化,但在介面顯示上,始終位於距離UITableView.frame.origin.y為65(即修改後contengInset.top的值)的位置,直到下一個section headerView出現時,才隨著cell向上移動,如圖:
 

 

注意:不論在任何情況下,第一個cell的frame.origin.y始終為section headerView的高度,在本例中為23,即使它已經處於section headerView的上方】 

 

2、解決方案 

 由此,可以粗略得出以下結論:

A、 section headerView和cell之間並不是簡單的順序擺放的關係,它們之間可以發生重疊;

B、 section headerView在介面上的顯示位置由UITableView.contentInset.top決定,直到被下一個section headerView替代。 

如此,想要在滑動時修改section headerView的顯示位置,令其和cell一起移動,只需要動態地修改UITableView.contentInset.top即可,如下:

 1  - (void)scrollViewDidScroll:(UIScrollView *)scrollView
 2   {
 3       //added 2013.11.28 動態修改headerView的位置
 4       if (headerRefreshing)
 5       {
 6           if (scrollView.contentOffset.y >= -scrollView.contentInset.top
 7               && scrollView.contentOffset.y < 0)
 8           {
 9               //注意:修改scrollView.contentInset時,若使當前介面顯示位置發生變化,會觸發scrollViewDidScroll:,從而導致死迴圈。
10              //因此此處scrollView.contentInset.top必須為-scrollView.contentOffset.y
11              scrollView.contentInset = UIEdgeInsetsMake(-scrollView.contentOffset.y, 000);
12          }
13          else if (scrollView.contentOffset.y == 0)//到0說明headerView已經在tableView最上方,不需要再修改了
14          {
15              scrollView.contentInset = UIEdgeInsetsZero;
16          }
17      }
18      
19      //other code here...
20  }

 

Added 2014.7.24:

對於不需要提交到AppStore的應用,還有一個更簡單的辦法,即覆蓋UITableView的私有方法。

- (BOOL)allowsHeaderViewsToFloat
{
    return NO;
}

- (BOOL)allowsFooterViewsToFloat
{
    return NO;
}

 

3、工程地址

使用iOS 6.1 SDK編譯,使用ARC。
地址:https://github.com/cDigger/CDPullToRefreshDemo

 

 

 

【參考】

1、Section Headers in UITableView when inset of tableview is changed

http://stackoverflow.com/questions/5466097/section-headers-in-uitableview-when-inset-of-tableview-is-changed 

2、Change Default Scrolling Behavior of UITableView Section Header

http://stackoverflow.com/questions/664781/change-default-scrolling-behavior-of-uitableview-section-header 

相關文章