標籤:
起始
做開發也有一段時間了,經曆了第一次完成項目的激動,也經曆了天天調用系統的API的枯燥,於是就有了探索底層實現的想法。
關於scrollView的思考
在iOS開發中我們會大量用到scrollView這個控制項,我們使用的tableView/collectionview/textView都繼承自它。scrollView的頻繁使用讓我對它的底層實現產生了興趣,它到底是如何工作的?如何?一個scrollView?讀完本篇部落格,相信你一定也可以自己實現一個簡易的scrollView。
我們首先來思考以下幾個問題:
通過一步步解決上邊的問題,我們就能實現一個自己的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的底層實現