目錄主要分爲以下幾個樣式:
常用、會用、瞭解
目錄
- UIScrollViewDelegate
-
滾動&&拖動
- scrollViewDidScroll:
- scrollViewWillEndDragging:withVelocity:targetContentOffset:
- scrollViewDidEndDragging:willDecelerate:
- scrollViewShouldScrollToTop:
- scrollViewDidScrollToTop:
- scrollViewWillBeginDecelerating:
- scrollViewDidEndDecelerating:
- 縮放管理
- viewForZoomingInScrollView:
- scrollViewWillBeginZooming:withView:
- scrollViewDidEndZooming:withView:atScale:
- scrollViewDidZoom:
- UIScrollView
-
滾動動畫
- scrollViewDidEndScrollingAnimation:
-
adjustedContentInset更改
- scrollViewDidChangeAdjustedContentInset:
- UIScrollView
-
內容大小&&偏移量
- contentSize
- contentOffset
- setContentOffset:animated:
-
內容嵌入
- adjustedContentInset(iOS11+)
- contentInset(iOS11-)
- contentInsetAdjustmentBehavior
- adjustedContentInsetDidChange
-
配置UIScrollView
- scrollEnabled
- directionalLockEnabled
- pagingEnabled
- scrollsToTop
- bounces
- alwaysBounceVertical
- alwaysBounceHorizontal
-
獲得滾動狀態
- tracking
- dragging
- decelerating
- decelerationRate
-
滾動條 && 刷新控件
- indicatorStyle
- scrollIndicatorInsets
- showsHorizontalScrollIndicator
- showsVerticalScrollIndicator
- flashScrollIndicators
- refreshControl
-
滾動到指定區域
- scrollRectToVisible:animated:
-
觸摸管理
- touchesShouldBegin:withEvent:inContentView:
- touchesShouldCancelInContentView:
- canCancelContentTouches
- delaysContentTouches
-
縮放和移動
- panGestureRecognizer
- pinchGestureRecognizer
- zoomToRect:animated:
- setZoomScale:animated:
- maximumZoomScale
- minimumZoomScale
- zoomBouncing
- zooming
- bouncesZoom
-
鍵盤管理
- keyboardDismissMode
-
索引
- indexDisplayMode
- 參考資料
UIScrollViewDelegate
通過協議方法響應滾動、縮放、滾動內容的減速和滾動動畫等操作。
滾動&&拖動
-
scrollViewDidScroll:
視圖滾動(contentOffset改變)時觸發
- (void)scrollViewDidScroll:(UIScrollView *)scrollView;
這個方法在任何方式觸發 contentOffset 變化的時候都會被調用(包括用戶拖動,減速過程,直接通過代碼設置等),可以用於監控 contentOffset 的變化,並根據當前的 contentOffset 對其他 view 做出隨動調整。
-
scrollViewWillEndDragging:withVelocity:targetContentOffset:
用戶手指離開屏幕(滾動完成)時觸發
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
withVelocity:(CGPoint)velocity
targetContentOffset:(inout CGPoint *)targetContentOffset;
velocity
表示慣性速度、targetContentOffset
表示將會在哪裏完全靜止。
當velocity
不爲CGPointZero
時,UIScrollview
會以 velocity
爲初速度,減速直到 targetContentOffset
。
需要注意的是:
這裏的 targetContentOffset 是個指針,沒錯,你可以改變減速運動的目的地
-
scrollViewDidEndDragging:willDecelerate:
用戶手指離開屏幕(滾動完成)時觸發
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView
willDecelerate:(BOOL)decelerate;
decelerate
爲bool值
、表示是否有繼續滾動的減速過程。
這個值、取決於WillEndDragging
方法中velocity
是否爲CGPointZero
。
需要注意的是:
- 在
didEndDragging
之後,如果有減速過程,UIScrollview
的dragging
屬性 並不會立即置爲NO
,而是要等到減速結束之後。 - 通過代碼的方式(
- setContentOffset:animated:
)並不會觸發。
-
scrollViewShouldScrollToTop:
當觸摸狀態欄時、是否允許UIScrollView滾動到頂部。默認返回YES
- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView;
這個效果依賴scrollsToTop
屬性、當然他默認也是YES
。
-
scrollViewDidScrollToTop:
UIScrollView滾動到頂部之後的回調
- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView;
-
scrollViewWillBeginDecelerating:
UIScrollView開始自動減速時觸發
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView;
-
scrollViewDidEndDecelerating:
UIScrollView減速結束時觸發
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;
這裏有一種特殊情況需要注意:
當一次減速動畫尚未結束的時候再次拖動 UIScrollView
,didEndDecelerating
先不會被調用,並且這時 UIScrollView
的 dragging
和 decelerating
屬性都是 YES
。
- 新的拖動如果有加速度
那麼willBeginDecelerating
會再一次被調用,然後纔是didEndDecelerating
- 新的拖動如果沒有加速度
雖然willBeginDecelerating
不會被調用,但前一次留下的didEndDecelerating
會被調用。
縮放管理
-
viewForZoomingInScrollView:
在縮放手勢發生時、返回需要變化的View。不允許縮放可以返回nil
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView;
你還需要其他屬性進行配合才能實現縮放功能
Demo的話可以看一個帖子《iOS開發UI篇—UIScrollView控件實現圖片縮放功能》
此外有幾點需要注意:
- 返回的
View
並不必須爲UIScrollView
的子視圖 - 系統會根據手勢修改該
View
的transform
進而完成縮放。
-
scrollViewWillBeginZooming:withView:
縮放開始時觸發
- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView
withView:(UIView *)view;
此時View的狀態爲縮放之前
-
scrollViewDidEndZooming:withView:atScale:
縮放完成時觸發
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView
withView:(UIView *)view
atScale:(CGFloat)scale;
此時View的狀態爲縮放之後
-
scrollViewDidZoom:
發生縮放時觸發
- (void)scrollViewDidZoom:(UIScrollView *)scrollView;
實時觸發、和scrollViewDidScroll
一樣
滾動動畫
-
scrollViewDidEndScrollingAnimation:
滾動結束時觸發
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView;
在通過代碼的方式(- setContentOffset:animated:
)令UIScrollView
滾動結束時觸發。
adjustedContentInset更改
-
scrollViewDidChangeAdjustedContentInset:
adjustedContentInset
屬性改變時觸發
- (void)scrollViewDidChangeAdjustedContentInset:(UIScrollView *)scrollView;
UIScrollView
一個允許對其包含視圖進行滾動、縮放操作的視圖
繼承關係
UIScrollView是UITableView和UITextView的父類。
響應原理
當用戶手指觸摸UIScrollView及其子類時,其屬性 isTracking 設置爲YES,同時內部會開啓一個NSTimer, 在timer的一個極短的時間週期內,如果手指發生了較大距離的移動,UIScrollView接收這個事件開始滾動到相應的位置, isTracking 設置爲NO。 如果手指沒有發生較大距離的移動,而touch位置正好位於子視圖上,並且其子視圖接受touch事件,那麼就觸發子視圖的touch事件。
我們可以通過以下三個方法來影響它處理滾動手勢的方式:
touchesShouldBegin:withEvent:inContentView:
、 pagingEnabled
、 touchesShouldCancelInContentView:
UIScrollView還可以處理內容的縮放和平移:
- 當手勢正在進行時,滾動視圖不會向子視圖發送任何跟蹤調用。
- UIScrollView類可以有一個必須採用UIScrollViewDelegate協議的委託。要使縮放和平移工作,委託必須實現
viewForZoomingInScrollView:
和scrollViewDidEndZooming:withView:atScale:
- 最大變焦比例尺和最小變焦比例尺必須不同。
狀態恢復
如果你實現了restorationIdentifier
、那麼它會試圖在應用程序啓動之間保存與滾動相關的信息(zoomScale、contentInset和contentOffset)。
內容大小&&偏移量
-
contentSize
可以滾動的內容大小
@property(nonatomic) CGSize contentSize;
-
contentOffset
偏移量
@property(nonatomic) CGPoint contentOffset;
-
-setContentOffset:animated:
允許通過動畫的方式設置
contentOffset
- (void)setContentOffset:(CGPoint)contentOffset
animated:(BOOL)animated;
內容嵌入
主要控制內容展示的範圍、frame(0.0)的位置。
十分重要、因爲iOS11之後安全區域以及adjustedContentInset
的引入。
-
adjustedContentInset
允許內容到邊緣的距離(決定iOS11以上)
@property(nonatomic, readonly) UIEdgeInsets adjustedContentInset;
在iOS11中替代了contentInset
起到最終決定作用、其值和contentInset
以及contentInsetAdjustmentBehavior
有關
以MJRefresh內的代碼爲例
- (UIEdgeInsets)mj_inset
{
#ifdef __IPHONE_11_0
if (respondsToAdjustedContentInset_) {
return self.adjustedContentInset;
}
#endif
return self.contentInset;
}
contentInsetAdjustmentBehavior
的類型決定是否在調整中包含安全區域、具體的數值可以參考《本文》
-
contentInset
允許內容到邊緣的距離(決定iOS11以下)
@property(nonatomic) UIEdgeInsets contentInset;
使用此屬性可擴展內容和內容視圖邊緣之間的空間。
-
contentInsetAdjustmentBehavior
決定調整後的
adjustedContentInset
具體值、默認UIScrollViewContentInsetAdjustmentAutomatic
@property(nonatomic) UIScrollViewContentInsetAdjustmentBehavior contentInsetAdjustmentBehavior;
UIScrollViewContentInsetAdjustmentAutomatic:如果scrollview在一個automaticallyAdjustsScrollViewInsets = YES的controller上,並且這個Controller包含在一個navigation controller中,這種情況下會設置在top & bottom上 adjustedContentInset = safeAreaInset + contentInset不管是否滾動。其他情況下與UIScrollViewContentInsetAdjustmentScrollableAxes相同
UIScrollViewContentInsetAdjustmentScrollableAxes: 在可滾動方向上adjustedContentInset = safeAreaInset + contentInset,在不可滾動方向上adjustedContentInset = contentInset;依賴於scrollEnabled和alwaysBounceHorizontal / vertical = YES,scrollEnabled默認爲yes,所以大多數情況下,計算方式還是adjustedContentInset = safeAreaInset + contentInset
UIScrollViewContentInsetAdjustmentNever: adjustedContentInset = contentInset
UIScrollViewContentInsetAdjustmentAlways: adjustedContentInset = safeAreaInset + contentInset
通常、如果我們想要屏蔽系統的安全區域(比如我們想滿屏顯示、甚至被狀態欄遮擋)
if (@available(iOS 11.0, *)) {
self.tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
}
-
adjustedContentInsetDidChange
adjustedContentInset
值改變時觸發的放啊
- (void)adjustedContentInsetDidChange;
需要注意UIScrollViewDelegate
的scrollViewDidChangeAdjustedContentInset
也能捕獲這個動作、當然、他是給外界用的。
配置UIScrollView
能否滾動、滾動方向、分頁、迴歸頂部、彈簧效果等
-
scrollEnabled
是否能夠滾動
@property(nonatomic, getter=isScrollEnabled) BOOL scrollEnabled;
-
directionalLockEnabled
滾動時是否鎖定另一座標軸上的滾動
@property(nonatomic, getter=isDirectionalLockEnabled) BOOL directionalLockEnabled;
如果爲YES、那麼在同一次操作中你將只能在一個座標軸上滾動。
但是官方文檔上指出、如果對角(45°)滑動這個屬性將失效。
有興趣可以參閱《Stackoverflow》中的討論。
-
pagingEnabled
其否啓用分頁滑動
@property(nonatomic, getter=isPagingEnabled) BOOL pagingEnabled;
每頁的寬/高度爲scrollview的bounds
-
scrollsToTop
是否允許(當觸摸狀態欄時)滾動到頂部、默認YES
@property(nonatomic) BOOL scrollsToTop;
對於一個scrollsToTop
爲YES的UIScrollView
、你可以通過scrollViewShouldScrollToTop
代理的返回值控制具體是否可以迴歸頂部。
需要注意的是如果有多個UIScrollView
該值爲YES、那麼他們都將失效。
-
bounces
彈簧效果、默認YES
@property(nonatomic) BOOL bounces;
在滾動超出內容後、會反彈回來。
-
alwaysBounceVertical
垂直方向否有彈簧效果
@property(nonatomic) BOOL alwaysBounceVertical;
-
alwaysBounceHorizontal
水平方向否有彈簧效果
@property(nonatomic) BOOL alwaysBounceHorizontal;
這裏不同的控件的默認值會有些差異
-
UIScrollView
和UICollectionView
默認情況下垂直alwaysBounceVertical
和水平alwaysBounceHorizontal
都是NO;只有當內容視圖的尺寸超過了自己的bounds的尺寸的時候,相應方向上反彈屬性纔會自動設置爲YES; -
UITableView
默認情況下垂直alwaysBounceVertical
是YES,水平alwaysBounceHorizontal
是NO;
獲得滾動狀態
開始觸碰、拖拽、減速、停止等一些列屬性
-
tracking
用戶是否已經觸摸了內容
@property(nonatomic, readonly, getter=isTracking) BOOL tracking;
如果用戶僅僅是觸摸、但並未拖動、也是YES。
比如我們在手指離開時爲YES、在慣性滾動之後爲NO。
2018-10-11 11:23:34.580538+0800 DocumentDemo[19889:1060480] scrollViewDidEndDragging - 1
2018-10-11 11:23:36.916507+0800 DocumentDemo[19889:1060480] scrollViewDidEndDecelerating - 0
-
dragging
獲取用戶是否開始拖動視圖
@property(nonatomic, readonly, getter=isDragging) BOOL dragging;
- 慣性時 >> YES
- 回彈時 >> NO
- 代碼設置滾動 >> NO
-
decelerating
獲取視圖是否開始減速(用戶停止拖動但視圖仍在滾動)
@property(nonatomic, readonly, getter=isDecelerating) BOOL decelerating;
-
decelerationRate
慣性速度的衰減速率
@property(nonatomic) UIScrollViewDecelerationRate decelerationRate;
UIScrollViewDecelerationRate
本身是一個浮點型、範圍是0-1。系統爲我們提供了兩個參考值(當然你也可以自己設置):
UIScrollViewDecelerationRateNormal:0.998(默認值)
UIScrollViewDecelerationRateFast:0.99
滾動條 && 刷新控件
-
indicatorStyle
設置滑動條樣式
@property(nonatomic) UIScrollViewIndicatorStyle indicatorStyle;
具體值爲一個枚舉
typedef NS_ENUM(NSInteger, UIScrollViewIndicatorStyle) {
UIScrollViewIndicatorStyleDefault, //默認
UIScrollViewIndicatorStyleBlack, //黑色風格
UIScrollViewIndicatorStyleWhite //白色風格
};
-
scrollIndicatorInsets
滑動條的位置(長短)。默認
UIEdgeInsetsZero
@property(nonatomic) UIEdgeInsets scrollIndicatorInsets;
需要注意的是UIEdgeInsets
結構體中有4個值。正好對應右側、下方兩個滑動條。
支付寶這種滑動條從屏幕中央開始的效果就是用了這個屬性(我猜)
-
showsHorizontalScrollIndicator
-
showsVerticalScrollIndicator
水平/垂直的滑動條是否會被顯示
@property(nonatomic) BOOL showsHorizontalScrollIndicator;
@property(nonatomic) BOOL showsVerticalScrollIndicator;
在滑動發生時、是否會顯示出來。並不是一直顯示
-
flashScrollIndicators
當你的滑動條短暫的顯示一下
- (void)flashScrollIndicators;
你想讓用戶知道目前的位置時、可以用一下、而不是偷偷滾動一點點距離。
-
refreshControl
刷新控件(iOS10+)
@property(nonatomic, strong) UIRefreshControl *refreshControl;
你可以用UIRefreshControl
自己實現刷新的控件、這個我沒看
滾動到指定區域
-
- scrollRectToVisible:animated:
將
UIScrollView
座標系中的指定Rect
展示出來
- (void)scrollRectToVisible:(CGRect)rect
animated:(BOOL)animated;
- 寬高不能爲0
- 如果已經可以展示則不再移動
觸摸管理
能否響應某處子視圖、事件傳遞相關
-
- touchesShouldBegin:withEvent:inContentView:
自定義手指觸碰到顯示內容時的默認行爲
- (BOOL)touchesShouldBegin:(NSSet<UITouch *> *)touches
withEvent:(UIEvent *)event
inContentView:(UIView *)view;
當點擊到了一個子View會觸發這個方法。
如果返回YES(默認)、UIScrollView會把當前事件分發到子view去處理(調用對應touchesBegan
方法)、如果NO就不分發此事件。
需要注意的是:
只有系統識別到點擊、而不是拖動動作纔會下發。具體識別方法可以看本文中《UIScrollView >> 響應原理》處的解釋。
-
- touchesShouldCancelInContentView:
是否取消與子視圖相關的觸摸、並恢復拖動
- (BOOL)touchesShouldCancelInContentView:(UIView *)view;
默認情況下有兩種情況
- 如果視圖不是UIControl對象
返回值爲YES - 如果是UIControl對象
返回值爲NO
也就是說默認情況下、我們長按一個UIButton只有、就不能繼續滾動了。如果返回NO、那麼一旦我們繼續滾動、UIButton的事件則會被取消。
-
canCancelContentTouches
當長按後繼續滾動時、是否給子視圖傳遞取消動作的消息
@property(nonatomic) BOOL canCancelContentTouches;
默認設置爲YES。
對於子View、長按後令UIScrollView
繼續滾動、會收到touchesCancelled
消息。如果這個值爲NO、則不會收到該消息。
-
delaysContentTouches
設置視圖是否延遲處理觸摸事件。默認YES
@property(nonatomic) BOOL delaysContentTouches;
不等待識別是否爲拖動手勢、直接將消息先下發給內容控件(touchesBegan
方法)。
縮放和移動
拖動、捏合的手勢獲取。縮放相關
-
panGestureRecognizer
返回ScrollView的拖動手勢識別器
@property(nonatomic, readonly) UIPanGestureRecognizer *panGestureRecognizer;
主要用於處理手勢衝突、比如右滑返回等等需要獲取具體手勢的時候。
-
pinchGestureRecognizer
返回ScrollView的捏合手勢識別器
@property(nonatomic, readonly) UIPinchGestureRecognizer *pinchGestureRecognize
-
- zoomToRect:animated:
縮放到特定區域、使其可見
- (void)zoomToRect:(CGRect)rect
animated:(BOOL)animated;
這個rect必須處在viewForZoomingInScrollView:
方法返回的View之中、也就是允許對該View進行縮放。
大於當前rect則縮小、否則放大。
該方法的主要用於雙擊縮放圖片的效果:《iOS 圖片縮放》
-
- setZoomScale:animated:
設置當前的縮放比例
- (void)setZoomScale:(CGFloat)scale
animated:(BOOL)animated;
新的scale
必須介於minimumZoomScale
和 maximumZoomScale
之間。
該方法的主要用於雙擊縮放圖片的效果:《iOS 圖片縮放》
-
maximumZoomScale
-
minimumZoomScale
允許縮放的最大/最小值
@property(nonatomic) CGFloat maximumZoomScale;
@property(nonatomic) CGFloat minimumZoomScale;
默認值都爲1、如果想實現縮放效果必須設置。
-
zoomBouncing
當前的縮放比例是否超出設置的峯值
@property(nonatomic, readonly, getter=isZoomBouncing) BOOL zoomBouncing;
bounce
也就是意味着將會有一個回彈效果出現
-
zooming
是否正在進行縮放
@property(nonatomic, readonly, getter=isZooming) BOOL zooming;
-
bouncesZoom
當縮放超過閾值、是否有會回彈動畫發生。默認YES
@property(nonatomic) BOOL bouncesZoom;
鍵盤管理
-
keyboardDismissMode
當開始滾動時、鍵盤消失的方式
@property(nonatomic) UIScrollViewKeyboardDismissMode keyboardDismissMode;
具體爲一個枚舉值、包含三個樣式
typedef NS_ENUM(NSInteger, UIScrollViewKeyboardDismissMode) {
UIScrollViewKeyboardDismissModeNone,
UIScrollViewKeyboardDismissModeOnDrag, //手指滑動視圖鍵盤就會消失
UIScrollViewKeyboardDismissModeInteractive, //表示鍵盤可以隨着手指下滑而移出屏幕
};
這個鍵盤的控件、並不必須是scrollView的子視圖。
索引
-
indexDisplayMode
滾動時索引的顯示方式
@property(nonatomic) UIScrollViewIndexDisplayMode indexDisplayMode;
爲UIScrollViewIndexDisplayMode
類型的枚舉、有兩種方式
typedef NS_ENUM(NSInteger, UIScrollViewIndexDisplayMode) {
UIScrollViewIndexDisplayModeAutomatic, // 索引將根據需要自動顯示或隱藏
UIScrollViewIndexDisplayModeAlwaysHidden, // 索引將永遠不會顯示
} API_AVAILABLE(tvos(10.2));
TVOS專用
最後
本文主要是自己的學習與總結。如果文內存在紕漏、萬望留言斧正。如果願意補充以及不吝賜教小弟會更加感激。
參考資料
官方文檔-UIScrollView
UIScrollView 實踐經驗
scrollViewDidEndScrollingAnimation和scrollViewDidEndDecelerating的區別
iOS 圖片縮放