iOS文檔補完計劃--UIScrollView

目錄主要分爲以下幾個樣式:
常用、會用、瞭解

目錄

  • 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;

deceleratebool值、表示是否有繼續滾動的減速過程。
這個值、取決於WillEndDragging方法中velocity是否爲CGPointZero

需要注意的是:
  1. didEndDragging 之後,如果有減速過程,UIScrollviewdragging屬性 並不會立即置爲 NO,而是要等到減速結束之後。
  2. 通過代碼的方式(- 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;
這裏有一種特殊情況需要注意:

當一次減速動畫尚未結束的時候再次拖動 UIScrollViewdidEndDecelerating 先不會被調用,並且這時 UIScrollViewdraggingdecelerating 屬性都是 YES

  1. 新的拖動如果有加速度
    那麼 willBeginDecelerating 會再一次被調用,然後纔是 didEndDecelerating
  2. 新的拖動如果沒有加速度
    雖然 willBeginDecelerating 不會被調用,但前一次留下的 didEndDecelerating 會被調用。

縮放管理

  • viewForZoomingInScrollView:

在縮放手勢發生時、返回需要變化的View。不允許縮放可以返回nil

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView;

你還需要其他屬性進行配合才能實現縮放功能
Demo的話可以看一個帖子《iOS開發UI篇—UIScrollView控件實現圖片縮放功能》

此外有幾點需要注意:

  1. 返回的View並不必須爲UIScrollView的子視圖
  2. 系統會根據手勢修改該Viewtransform進而完成縮放。
  • 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是UITableViewUITextView的父類。

響應原理

當用戶手指觸摸UIScrollView及其子類時,其屬性 isTracking 設置爲YES,同時內部會開啓一個NSTimer, 在timer的一個極短的時間週期內,如果手指發生了較大距離的移動,UIScrollView接收這個事件開始滾動到相應的位置, isTracking 設置爲NO。 如果手指沒有發生較大距離的移動,而touch位置正好位於子視圖上,並且其子視圖接受touch事件,那麼就觸發子視圖的touch事件。

我們可以通過以下三個方法來影響它處理滾動手勢的方式:
touchesShouldBegin:withEvent:inContentView:pagingEnabledtouchesShouldCancelInContentView:

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;

需要注意UIScrollViewDelegatescrollViewDidChangeAdjustedContentInset也能捕獲這個動作、當然、他是給外界用的。


配置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;
這裏不同的控件的默認值會有些差異
  1. UIScrollViewUICollectionView
    默認情況下垂直alwaysBounceVertical和水平alwaysBounceHorizontal都是NO;只有當內容視圖的尺寸超過了自己的bounds的尺寸的時候,相應方向上反彈屬性纔會自動設置爲YES;
  2. 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;
  1. 慣性時 >> YES
  2. 回彈時 >> NO
  3. 代碼設置滾動 >> 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;
  1. 寬高不能爲0
  2. 如果已經可以展示則不再移動

觸摸管理

能否響應某處子視圖、事件傳遞相關

  • - 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;

默認情況下有兩種情況

  1. 如果視圖不是UIControl對象
    返回值爲YES
  2. 如果是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必須介於minimumZoomScalemaximumZoomScale之間。

該方法的主要用於雙擊縮放圖片的效果:《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 圖片縮放

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章