iOS開發UIScrollView的底層實現

來源:互聯網
上載者:User

標籤:

起始

做開發也有一段時間了,經曆了第一次完成項目的激動,也經曆了天天調用系統的API的枯燥,於是就有了探索底層實現的想法。

關於scrollView的思考

在iOS開發中我們會大量用到scrollView這個控制項,我們使用的tableView/collectionview/textView都繼承自它。scrollView的頻繁使用讓我對它的底層實現產生了興趣,它到底是如何工作的?如何?一個scrollView?讀完本篇部落格,相信你一定也可以自己實現一個簡易的scrollView。

我們首先來思考以下幾個問題:

  • scrollView繼承自誰,它如何檢測到手指滑動?

  • scrollView如何?滾動?

  • scrollView裡的各種屬性是如何?的?如contentSize/contentOffSet......

通過一步步解決上邊的問題,我們就能實現一個自己的scrollView。

一步一步實現scrollView1.毫無疑問我們都知道scrollView繼承自UIView,檢測手指滑動應該是在view上放置了一個手勢識別,實現代碼如下:
1 - (instancetype)initWithFrame:(CGRect)frame {2     self = [super initWithFrame:frame];3     if (self) {4         UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] init];5         [panGesture addTarget:self action:@selector(panGestureAction:)];6         [self addGestureRecognizer:panGesture];7     }8     return self;9 }
2.要搞清楚第二個問題,首先我們必須理解frame和bounds的概念。

提到它們,大家都知道frame是相對於父視圖座標系來說自己的位置和尺寸,bounds相對於自身座標系來說的位置和尺寸,並且origin一般為(0,0)。但是bounds的origin有什麼用處?改變它會出現什麼效果呢?

當我們嘗試改變bounds的origin時,我們就會發現視圖本身沒有發生變化,但是它的子視圖的位置卻發生了變化,why???其實當我們改變bounds的origin的時候,視圖本身位置沒有變化,但是由於origin的值是基於自身的座標系,所以自身座標系的位置被我們改變了。而子視圖的frame正是基於父視圖的座標系,當我們更改父視圖bounds中origin的時候子視圖的位置就發生了變化,這就是實現scrollView的關鍵點!!!

是不是很好理解?
基於這點我們很容易實現一個簡單的最初級版本的scrollView,代碼如下:

 1 - (void)panGestureAction:(UIPanGestureRecognizer *)pan { 2     // 記錄每次滑動開始時的初始位置 3     if (pan.state == UIGestureRecognizerStateBegan) { 4         self.startLocation = self.bounds.origin; 5         NSLog(@"%@", NSStringFromCGPoint(self.startLocation)); 6     } 7  8     // 相對於初始觸摸點的位移量 9     if (pan.state == UIGestureRecognizerStateChanged) {10         CGPoint point = [pan translationInView:self];11         NSLog(@"%@", NSStringFromCGPoint(point));12         CGFloat newOriginalX = self.startLocation.x - point.x;13         CGFloat newOriginalY = self.startLocation.y - point.y;14 15         CGRect bounds = self.bounds;16         bounds.origin = CGPointMake(newOriginalX, newOriginalY);17         self.bounds = bounds;18     }19 }
3.理解了上邊內容的關鍵點,下邊我們將對我們實現的scrollView做一個簡單的最佳化。

通過contentSize限制scrollView的內部空間,實現代碼如下

 1 if (newOriginalX < 0) { 2     newOriginalX = 0; 3 } else { 4     CGFloat maxMoveWidth = self.contentSize.width - self.bounds.size.width; 5     if (newOriginalX > maxMoveWidth) { 6         newOriginalX = maxMoveWidth; 7     } 8 } 9 if (newOriginalY < 0) {10     newOriginalY = 0;11 } else {12     CGFloat maxMoveHeight = self.contentSize.height - self.bounds.size.height;13     if (newOriginalY > maxMoveHeight) {14         newOriginalY = maxMoveHeight;15     }16 }

通過contentOffset設定scrollView的初始位移量,相信大家已經懂了如何設定位移量了吧?沒錯我們只需設定view自身bounds的origin是實現代碼如下:

1  - (void)setContentOffset:(CGPoint)contentOffset {2     _contentOffset = contentOffset;3     CGRect newBounds = self.bounds;4     newBounds.origin = contentOffset;5     self.bounds = newBounds;6  }

防止scrollView的子視圖超出scrollView

 1 self.layer.masksToBounds = YES; 

總結

UIScrollView還有很多其它強大的功能,以上我們只是完成了一個特別簡單的scrollView,以後如果有時間我會對它進行完善。當然如果你有興趣,你完全可以對它進行擴充,放在這裡。同時我也會繼續研究UIKit中其它控制項的底層實現,歡迎您的持續關注!



文/PaulLi哥(簡書作者)
原文連結:http://www.jianshu.com/p/a9a1ca54ca54
著作權歸作者所有,轉載請聯絡作者獲得授權,並標註“簡書作者”。

iOS開發UIScrollView的底層實現

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.