iOS開發(fā)UIScrollView的底層實現(xiàn)-你也可以自己實現(xiàn)一個scrollView

起始

做開發(fā)也有一段時間了,經(jīng)歷了第一次完成項目的激動弟灼,也經(jīng)歷了天天調(diào)用系統(tǒng)的API的枯燥邑飒,于是就有了探索底層實現(xiàn)的想法喊暖。

關(guān)于scrollView的思考

在iOS開發(fā)中我們會大量用到scrollView這個控件辜王,我們使用的tableView/collectionview/textView都繼承自它劈狐。scrollView的頻繁使用讓我對它的底層實現(xiàn)產(chǎn)生了興趣,它到底是如何工作的誓禁?如何實現(xiàn)一個scrollView懈息?讀完本篇博客,相信你一定也可以自己實現(xiàn)一個簡易的scrollView摹恰。

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

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

  • scrollView如何實現(xiàn)滾動俗慈?

  • scrollView里的各種屬性是如何實現(xiàn)的姑宽?如contentSize/contentOffSet......

通過一步步解決上邊的問題,我們就能實現(xiàn)一個自己的scrollView闺阱。

一步一步實現(xiàn)scrollView

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

提到它們,大家都知道frame是相對于父視圖坐標系來說自己的位置和尺寸赊豌,bounds相對于自身坐標系來說的位置和尺寸扛或,并且origin一般為(0,0)碘饼。但是bounds的origin有什么用處熙兔?改變它會出現(xiàn)什么效果呢?

當我們嘗試改變bounds的origin時艾恼,我們就會發(fā)現(xiàn)視圖本身沒有發(fā)生變化住涉,但是它的子視圖的位置卻發(fā)生了變化,why钠绍?舆声??其實當我們改變bounds的origin的時候柳爽,視圖本身位置沒有變化媳握,但是由于origin的值是基于自身的坐標系,所以自身坐標系的位置被我們改變了泻拦。而子視圖的frame正是基于父視圖的坐標系毙芜,當我們更改父視圖bounds中origin的時候子視圖的位置就發(fā)生了變化,這就是實現(xiàn)scrollView的關(guān)鍵點U铡R钢唷晦雨!

是不是很好理解?
基于這點我們很容易實現(xiàn)一個簡單的最初級版本的scrollView隘冲,代碼如下:

- (void)panGestureAction:(UIPanGestureRecognizer *)pan {
    // 記錄每次滑動開始時的初始位置
    if (pan.state == UIGestureRecognizerStateBegan) {
        self.startLocation = self.bounds.origin;
        NSLog(@"%@", NSStringFromCGPoint(self.startLocation));
    }
    
    // 相對于初始觸摸點的偏移量
    if (pan.state == UIGestureRecognizerStateChanged) {
        CGPoint point = [pan translationInView:self];
        NSLog(@"%@", NSStringFromCGPoint(point));
        CGFloat newOriginalX = self.startLocation.x - point.x;
        CGFloat newOriginalY = self.startLocation.y - point.y;
        
        CGRect bounds = self.bounds;
        bounds.origin = CGPointMake(newOriginalX, newOriginalY);
        self.bounds = bounds;
    }
}
3.理解了上邊內(nèi)容的關(guān)鍵點闹瞧,下邊我們將對我們實現(xiàn)的scrollView做一個簡單的優(yōu)化。

通過contentSize限制scrollView的內(nèi)部空間展辞,實現(xiàn)代碼如下

if (newOriginalX < 0) {
   newOriginalX = 0;
} else {
   CGFloat maxMoveWidth = self.contentSize.width - self.bounds.size.width;
   if (newOriginalX > maxMoveWidth) {
       newOriginalX = maxMoveWidth;
   }
}
if (newOriginalY < 0) {
   newOriginalY = 0;
} else {
   CGFloat maxMoveHeight = self.contentSize.height - self.bounds.size.height;
   if (newOriginalY > maxMoveHeight) {
       newOriginalY = maxMoveHeight;
   }
}

通過contentOffset設(shè)置scrollView的初始偏移量奥邮,相信大家已經(jīng)懂了如何設(shè)置偏移量了吧?沒錯我們只需設(shè)置view自身bounds的origin是實現(xiàn)代碼如下:

- (void)setContentOffset:(CGPoint)contentOffset {
   _contentOffset = contentOffset;
   CGRect newBounds = self.bounds;
   newBounds.origin = contentOffset;
   self.bounds = newBounds;
}

防止scrollView的子視圖超出scrollView

self.layer.masksToBounds = YES;

總結(jié)

UIScrollView還有很多其它強大的功能罗珍,以上我們只是完成了一個特別簡單的scrollView洽腺,以后如果有時間我會對它進行完善。當然如果你有興趣覆旱,你完全可以對它進行擴展,下載地址放在這里蘸朋。同時我也會繼續(xù)研究UIKit中其它控件的底層實現(xiàn),歡迎您的持續(xù)關(guān)注扣唱!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末藕坯,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子噪沙,更是在濱河造成了極大的恐慌炼彪,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件正歼,死亡現(xiàn)場離奇詭異辐马,居然都是意外死亡,警方通過查閱死者的電腦和手機朋腋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門齐疙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來膜楷,“玉大人旭咽,你說我怎么就攤上這事《奶” “怎么了穷绵?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長特愿。 經(jīng)常有香客問我仲墨,道長,這世上最難降的妖魔是什么揍障? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任目养,我火速辦了婚禮,結(jié)果婚禮上毒嫡,老公的妹妹穿的比我還像新娘癌蚁。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布努释。 她就那樣靜靜地躺著碘梢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪伐蒂。 梳的紋絲不亂的頭發(fā)上煞躬,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天,我揣著相機與錄音逸邦,去河邊找鬼恩沛。 笑死,一個胖子當著我的面吹牛缕减,可吹牛的內(nèi)容都是我干的复唤。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼烛卧,長吁一口氣:“原來是場噩夢啊……” “哼佛纫!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起总放,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤呈宇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后局雄,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體甥啄,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年炬搭,在試婚紗的時候發(fā)現(xiàn)自己被綠了蜈漓。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡宫盔,死狀恐怖融虽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情灼芭,我是刑警寧澤有额,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站彼绷,受9級特大地震影響巍佑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜寄悯,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一萤衰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧猜旬,春花似錦脆栋、人聲如沸胳螟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽糖耸。三九已至,卻和暖如春丘薛,著一層夾襖步出監(jiān)牢的瞬間嘉竟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工洋侨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留舍扰,地道東北人。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓希坚,卻偏偏與公主長得像边苹,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子裁僧,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容