1. 程式人生 > 實用技巧 >iOS開發之UIScrollView控制元件詳解

iOS開發之UIScrollView控制元件詳解

UIScrollView是一個非常重要的控制元件,其可以展示比裝置螢幕更大區域的內容,我們可以通過手指滑動來檢視內容檢視的每一部分內容,也可以通過手指捏合來對內容檢視進行縮放操作,我們每天開發中都不斷顯式或隱式地與UIScrollView打交道,下面給大家詳細介紹UIScrollView控制元件。

一、UIScrollView控制元件是什麼?

(1)移動裝置的螢幕⼤大⼩小是極其有限的,因此直接展⽰示在⽤使用者眼前的內容也相當有限

(2)當展⽰示的內容較多,超出⼀一個螢幕時,⽤使用者可通過滾動⼿手勢來檢視螢幕以外的內容

(3)普通的UIView不具備滾動功能,不能顯⽰示過多的內容

(4)UIScrollView是一個能夠滾動的檢視控制元件,可以⽤用來展⽰示⼤大量的內容,並且可以通過滾 動檢視所有的內容

(5) 舉例:手機上的“設定”、其他⽰示例程式

二、UIScrollView的簡單使用

(1)將需要展⽰的內容新增到UIScrollView中

(2)設定UIScrollView的contentSize屬性,告訴UIScrollView所有內容的尺⼨寸,也就是告訴 它滾動的範圍(能滾多遠,滾到哪⾥裡是盡頭)

注: 本文中所說的"內容檢視"在官方文件中稱作"content view",表示UIScrollView中可以用來展示內容的部分

三、屬性與方法

內容檢視相關

1 2 3 4 5 6 7 8 9 10 11 // 內容檢視的大小,預設為CGSizeZero @property(nonatomic) CGSize contentSize; // 為內容檢視周圍增加可滾動區域,預設為UIEdgeInsetsZero @property(nonatomic) UIEdgeInsets contentInset; // 內容檢視的原點相對於scrollView的原點的偏移量(左上方向偏移為正數),預設為CGPointZero
@property(nonatomic) CGPoint contentOffset; // 設定內容檢視的原點相對於scrollView的原點的偏移量 - (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated;

滑動相關

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 // 是否允許滑動,預設為YES @property(nonatomic,getter=isScrollEnabled)
BOOL scrollEnabled; // 是否只允許同時滑動一個方向,預設為NO,如果設定為YES,使用者在水平/豎直方向開始進行滑動,便禁止同時在豎直/水平方向滑動(注: 當用戶在對角線方向開始進行滑動,則本次滑動可以同時在任何方向滑動) @property(nonatomic, getter=isDirectionalLockEnabled) BOOL directionalLockEnabled; // 是否允許點選狀態列讓距離狀態列最近的scrollView滑動到頂部,預設為YES(注: 在iPhone中如果有多個將該屬性設定為YES的scrollView,則該方法無效;在iPad中則將距離狀態列最近的scrollView滑動到頂部) @property(nonatomic) BOOL scrollsToTop; // 是否按頁數進行滑動,預設為NO,如果設定為YES,則在滑動時只會停止在scrollView的bounds的倍數處 @property(nonatomic, getter=isPagingEnabled) BOOL pagingEnabled; // 是否有觸底反彈效果,預設為YES @property(nonatomic) BOOL bounces; // 是否總是有觸底反彈效果(即使內容檢視小於scrollView的大小),預設為NO(注: 生效的前提條件為bounces = YES) @property(nonatomic) BOOL alwaysBounceHorizontal; @property(nonatomic) BOOL alwaysBounceVertical; // 指定使用者手指離開屏幕後滑動減速的比率,預設為UIScrollViewDecelerationRateNormal(慢慢停止),其餘可選項為UIScrollViewDecelerationRateFast(快速停止) @property(nonatomic) CGFloat decelerationRate; // 將指定區域滑動到剛好可見處(即距離邊緣最近處) - (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated;

指示器相關

1 2 3 4 5 6 7 8 9 10 11 12 // 指示器樣式,預設為UIScrollViewIndicatorStyleDefault(黑內容白邊框,適用於任何背景),其餘可選項為UIScrollViewIndicatorStyleBlack(全黑)和UIScrollViewIndicatorStyleWhite(全白) @property(nonatomic) UIScrollViewIndicatorStyle indicatorStyle; // 為指示器周圍增加可滾動區域,預設為UIEdgeInsetsZero @property(nonatomic) UIEdgeInsets scrollIndicatorInsets; // 是否在滑動時指示器可見,預設為YES @property(nonatomic) BOOL showsHorizontalScrollIndicator; @property(nonatomic) BOOL showsVerticalScrollIndicator; // 閃一下指示器(注: 建議在scrollView展示給使用者時呼叫一下,以提醒使用者該處可滑動) - (void)flashScrollIndicators;

事件相關

UIScrollView處理觸控事件原理

當用戶在UIScrollView的一個子檢視上按下時,UIScrollView並不知道使用者是想要滑動內容檢視還是點選對應子檢視,所以在按下的一瞬間,事件UIEvent從UIApplication傳遞到UIScrollView後,其會先將該事件攔截而不會立即傳遞給對應的子檢視,同時開始一個150ms的倒計時,並監聽使用者接下來的行為

1、當倒計時結束前,如果使用者的手指發生了移動,則直接滾動內容檢視,不會將該事件傳遞給對應的子檢視;

2、當倒計時結束時,如果使用者的手指位置沒有改變,則呼叫自身的-touchesShouldBegin:withEvent:inContentView:方法詢問是否將事件傳遞給對應的子檢視(如果返回NO,則該事件不會傳遞給對應的子檢視,如果返回YES,則該事件會傳遞給對應的子檢視,預設為YES)

3、當事件被傳遞給子檢視後,如果手指位置又發生了移動,則呼叫自身的-touchesShouldCancelInContentView:方法詢問是否取消已經傳遞給子檢視的事件

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 // 返回是否使用者已經觸碰了內容檢視準備進行滑動(注: 該值被設定為YES的時候可能使用者只是觸碰了內容檢視,但是並沒有開始進行滑動) @property(nonatomic,readonly,getter=isTracking) BOOL tracking; // 返回是否使用者已經開始滑動內容檢視(注: 該值被設定為YES之前可能需要先滑動一段時間或距離) @property(nonatomic,readonly,getter=isDragging) BOOL dragging; // 返回是否處於減速狀態(即手指已經離開螢幕,但scrollView仍然處於滑動中) @property(nonatomic,readonly,getter=isDecelerating) BOOL decelerating; // 是否延遲事件傳遞,預設為YES,如果設定為NO,scrollView會立即呼叫-touchesShouldBegin:withEvent:inContentView:方法以進行下一步操作 @property(nonatomic) BOOL delaysContentTouches; // 是否可以取消內容檢視被觸控,預設為YES,如果設定為NO,則一旦開始跟蹤事件,即使手指進行移動也不會取消已經傳遞給子檢視的事件 @property(nonatomic) BOOL canCancelContentTouches; // 在UIScrollView的子類中重寫該方法,用於返回是否將事件傳遞給對應的子檢視,預設返回YES,如果返回NO,該事件不會傳遞給對應的子檢視 - (BOOL)touchesShouldBegin:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view; // 在UIScrollView的子類中重寫該方法,用於返回是否取消已經傳遞給子檢視的事件,預設當子檢視是UIControl時返回NO,否則返回YES(注: 該方法被呼叫的前提是canCancelContentTouches = YES) - (BOOL)touchesShouldCancelInContentView:(UIView *)view;

縮放相關

當用戶使用兩個手指進行縮放操作時,我們調整內容檢視的偏移量和縮放比例(注: 使用者兩個手指操作結束後,有可能仍然有一個手指在操作,這時不會將事件傳遞給子檢視)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 // 最小縮放比例,預設為1.0 @property(nonatomic) CGFloat minimumZoomScale; // 最大縮放比例,預設為1.0(必須大於minimumZoomScale才能正常工作) @property(nonatomic) CGFloat maximumZoomScale; // 縮放比例,預設為1.0 @property(nonatomic) CGFloat zoomScale; // 設定縮放比例 - (void)setZoomScale:(CGFloat)scale animated:(BOOL)animated; // 縮放到指定區域 - (void)zoomToRect:(CGRect)rect animated:(BOOL)animated; // 是否允許觸底反彈,預設為YES @property(nonatomic) BOOL bouncesZoom; // 返回是否正在縮放 @property(nonatomic,readonly,getter=isZooming) BOOL zooming; // 返回是否正在觸底反彈 @property(nonatomic,readonly,getter=isZoomBouncing) BOOL zoomBouncing;

鍵盤相關

1 2 // 隱藏鍵盤模式,預設為UIScrollViewKeyboardDismissModeNone(不隱藏鍵盤),其餘可選項為UIScrollViewKeyboardDismissModeOnDrag(當拖拽scrollView時隱藏鍵盤)和UIScrollViewKeyboardDismissModeInteractive(當拖拽鍵盤上方時隱藏鍵盤) @property(nonatomic) UIScrollViewKeyboardDismissMode keyboardDismissMode;

代理

滑動相關

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 // 當scrollView的contentOffset發生變化時呼叫 - (void)scrollViewDidScroll:(UIScrollView *)scrollView; // 將要開始拖拽時呼叫(注: 該方法可能需要先滑動一段時間或距離才會被呼叫) - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView; // 當用戶停止拖拽時呼叫(注: 應用程式可以通過修改targetContentOffset引數的值來調整內容檢視content view停止的位置) - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset; // 當用戶停止拖拽時呼叫(注: 如果內容檢視content view在停止拖拽後繼續移動,則decelerate引數為YES) - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate; // 將要開始減速時呼叫(僅當停止拖拽後繼續移動時才會被呼叫) - (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView; // 已經結束減速時呼叫(僅當停止拖拽後繼續移動時才會被呼叫) - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView; // 返回是否允許點選狀態列讓scrollView滑動到頂部,如果未實現該方法,則預設為YES(僅當scrollsToTop屬性為YES時才呼叫) - (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView; // 當scrollView已經滑動到頂部時呼叫(僅當點選狀態列讓scrollView滑動到頂部才呼叫) - (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView; // 當-setContentOffset:animated:/-scrollRectVisible:animated:方法動畫結束時呼叫(僅當animated設定為YES時才呼叫) - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView;

縮放相關

1 2 3 4 5 6 7 8 9 10 11 // 當縮放比例更改時呼叫 - (void)scrollViewDidZoom:(UIScrollView *)scrollView; // 參與縮放的子檢視 - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView; // 將要開始縮放時呼叫 - (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view; // 已經結束縮放時呼叫 - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale;

常見需求原理解析

導航欄半透明效果


原理解析:

預設情況下,在有UINavigationBar存在時,系統為了防止UIScrollView被遮擋,其contentInset和scrollIndicatorInsets屬性都會被設定為UIEdgeInsetsMake(64, 0, 0, 0);在有UITabBar存在時,系統為了防止UIScrollView被遮擋,其contentInset和scrollIndicatorInsets屬性都會被設定為UIEdgeInsetsMake(0, 0, 49, 0)

因此,為了使用此種半透明效果,可以直接將UIScrollView的frame設定為整個螢幕的大小

注1: 系統只在UIScrollView是控制器檢視的第0個子檢視時才會自動修改contentInset和scrollIndicatorInsets屬性

注2: 如果不想讓系統自動修改contentInset和scrollIndicatorInsets屬性,可以設定self.automaticallyAdjustsScrollViewInsets = NO;

控制元件懸停


原理解析:

方式一: 在懸停位置放置一個與待懸停控制元件相同的控制元件,通過-scrollViewDidScroll:代理方法跟蹤contentOffset的的變化,當不滿足懸停條件時,將該控制元件hidden屬性設定為YES;當滿足懸停條件時,將該控制元件hidden屬性設定為NO

方式二: 通過-scrollViewDidScroll:代理方法跟蹤contentOffset的的變化,當不滿足懸停條件時,待懸停控制元件屬於UIScrollView的子檢視,當滿足懸停條件時,待懸停控制元件屬於UIScrollView的父檢視的子檢視

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 // 以"方式二"為例 - (void)scrollViewDidScroll:(UIScrollView *)scrollView { if (scrollView.contentOffset.y >= 100) { CGRect rect = label.frame; rect.origin.y = 0; label.frame = rect; [self.view addSubview:label]; } else { CGRect rect = label.frame; rect.origin.y = 100; label.frame = rect; [scrollView addSubview:label]; } }

下拉頭部圖片放大


原理解析:

通過-scrollViewDidScroll:代理方法跟蹤contentOffset的的變化,根據contentOffset動態設定圖片的縮放比例

1 2 3 4 5 6 7 8 // 以"動態修改圖片縮放比例於1倍和2倍之間"為例 - (void)scrollViewDidScroll:(UIScrollView *)scrollView { CGFloat scale = 1 - (scrollView.contentOffset.y / 100); scale = (scale >= 1) ? scale : 1; scale = (scale <= 2) ? scale : 2; imageView.transform = CGAffineTransformMakeScale(scale, scale); }

圖片無限輪播


原理解析:

在已知圖片陣列有N個元素前提下,在UIScrollView中建立N+2個UIImageView,其中第1個至第N個圖片為真實內容,第0個與第N個一樣,第N+1個與第1個一樣,通過-scrollViewDidScroll:代理方法跟蹤contentOffset的的變化,在滑動到首尾兩個圖片處直接設定contentOffset到真實圖片處即可

總結

以上就是這篇文章的全部內容了,希望本文的內容對各位iOS開發者們能有所幫助,如果有疑問大家可以留言交流。