狀態
Idle 閒置狀態
pulling 松開就可以進行刷新的狀態
refreshing 正在刷新狀態
初始狀態:
Uploading 屏幕快照 2016-12-02 15.22.13_456523.png . . .]
header:Idle
footer:Idle
header的狀態變化
header在拖動中顯示出來:
屏幕快照 2016-12-02 15.38.59.png
拖拽下拉時使header開始出現在屏幕上
header開始出現 -> header完全出現在屏幕上:Idle -> Pulling
header完全出現 -> header部分出現在屏幕上:Pulling -> Idle
拖曳結束時(松手時):
屏幕快照 2016-12-02 15.39.03.png
這時是在Pulling狀態松手,所以狀態由:Pulling -> Refreshing
如果是在Idle狀態下松手,狀態不改變。
footer的狀態變化
屏幕快照 2016-12-02 15.39.09.png
將要出現footer,此時footer狀態為Idle
屏幕快照 2016-12-02 15.39.13.png
footer由開始出現 -> footer完全出現:Idle -> Pulling
footer由完全出現 -> footer部分出現:Pulling -> Idle
拖曳結束時(松手時):
屏幕快照 2016-12-02 15.39.18.png
這是在Pulling狀態下松的手,所以狀態由:Pulling -> Refreshing
如果是在Idle狀態下松的手,狀態不變。
header和footer的實現
初始位置確定:
從原始狀態可以看出:
header是scrollView的subView,它的frame.y為-header.frame.height。
footer也是scrollView的subView,它的frame.y分為兩種情況:
當contentSize.height > scrollView.frame.height時,footer.frame.y為contentSize.height
當contentSize.height < scrollView.frame.height時,footer.frame.y為scrollView.frame.height
總而言之footer一定在scrollView的下方。
上面是最簡單的情況分析,可是現實中往往是下面這樣:
屏幕快照 2016-12-02 17.18.42.png
以上的示意圖可以設想一下,一個tableViewController嵌入在navigationController中,navigationController又嵌入在tabbarController中。此時tableViewController的tableView的contentInset屬性為(64,0,49,0),contentOffset為(0,-64)。64是狀態欄加導航欄的高度,49是tabbar的高度。這是tableViewController默認設的一些值。
因此添加footer時要考慮oringinalInsets,把
當contentSize.height < scrollView.frame.height時,footer.frame.y為scrollView.frame.height
改為:
當contentSize.height < scrollView.frame.height時,footer.frame.y為scrollView.frame.height - originalInsets.top - originalInsets.bottom
下面臨界值的確定都要考慮originalInsets
臨界值確定
確定了初始的位置後,要確定狀態之間變換的臨界線,通過KVO,監聽scrollView的contentOffset的變化。
header臨界值:
contentOffset.y < -originalInsets.top時,才開始顯示header
contentOffset.y <= -originalInsets.top - header.frame.size.height時,才完全顯示header
footer的臨界值:
當contentSize.height大於showHeight時:
contentOffset.y > scrollView.frame.size.height - showHeight - originalInsets.top時才開始顯示footer。
contentOffset.y > scrollView.frame.size.height - showHeight - originalInsets.top + footer.frame.size.height時才完全顯示footer
當contentSize.height小於showHeight時:
contentOffset.y > -originalInsets.top時就開始顯示footer。
contentOffset.y > -originalInsets.top + footer.frame.size.height時才完全顯示footer。
懸浮狀態的實現
header的懸浮
scrollView.contentInsets.top = header.frame.size.height + originalInsets.top scrollView.contentOffset.y = - (scrollView.contentInsets.top)
header的隱藏
scrollView.contentInset.top = originalInsets.top scrollView.contentOffset.y = -(scrollView.contentInsets.top)
footer的懸浮
當contentSize.height大於showHeight時:
屏幕快照 2016-12-02 18.18.39.png
scrollView.contentInsets.bottom = footer.frame.size.height + originalInsets.bottom scrollView.contentOffset.y = contentSize.height - showHeight - originalInsets.top + footer.frame.size.height
當contentSize.height小於showHeight時:
屏幕快照 2016-12-02 18.01.38.png
scrollView.contentInsets.bottom = bottom + showHeight - contentSize.height
可以理解成初始的contentOffset.y為-originalInsets.top,然後向上移動了footer.frame.size.height:
scrollView.contentOffset.y = -originalInsets.top + footer.frame.size.height
footer的隱藏
scrollVIew.contentInsets.bottom = originalInsets.bottom
當contentSize.height > showHeight時:
scrollView.contentOffset.y = contentSize.height - showHeight - originalInsets.top
當contentSize.height < showHeight時:
scrollView.conentOffset.y = -originalInsets.top
UIScrollView屬性詳解:
坐標系正方向:
屏幕快照 2016-12-02 15.22.01.png
ContentOffset的表示:
屏幕快照 2016-12-02 15.22.13.png
屏幕快照 2016-12-02 15.22.06.png
ContentInsets的表示:
contentInsets並不影響contentSize:
屏幕快照 2016-12-02 15.22.54.png
contentInsets影響contentOffset:
屏幕快照 2016-12-02 15.23.11.png
上圖的contentOffset為
(0, 0)
屏幕快照 2016-12-02 15.23.16.png
上圖的contentOffset為
(-contentInsets.left, -contentInsets.top)
屏幕快照 2016-12-02 15.23.21.png
上圖的contentOffset為
(contentSize.width - scrollView.frame.size.width + contentInsets.right, contentSize.height - scrollView.frame.size.height + contentInsets.bottom)
其實contentInsets就是為scrollView提供了更多的可停留空間,切記要把彈簧效果開啟,否則在contentSize小於scrollView.frame時scrollView無法拉動。
scrollView.alwaysBounceVertical = YES; self.scrollView.alwaysBounceHorizontal = YES;
在沒有設置contentInsets的情況下,scrollView的停留范圍為:
contentOffset.x: 0 -> max(contentSize.width - scrollView.frame.width, 0) contentOffset.y: 0 -> max(contentSize.height - scrollView.frame.height, 0)
在有contentInsets的情況下,scrollView的停留范圍為:
contentOffset.x: -contentInsets.left -> max(contentSIze.width - scrollView.frame.size.width) + contentInsets.right contentOffset.y: -contentInsets.top -> max(contentSIze.height - scrollView.frame.size.height) + contentInsets.bottom
demo地址