了解UIScrollView

本篇翻譯至 --Understanding UIScrollView衅胀,夾雜了部分自己的理解

坐標(biāo)系統(tǒng)

每個(gè)視圖都定義了自己的坐標(biāo)系〕牛看起來(lái)如下圖掸掏,x軸指向右側(cè),y軸指向下:


一個(gè)View的坐標(biāo)系

請(qǐng)注意阅束,這個(gè)邏輯坐標(biāo)系不關(guān)心視圖的寬度和高度。它沒(méi)有邊界蝇更,在四個(gè)方向上無(wú)限延伸『襞瑁現(xiàn)在讓我們?cè)谶@個(gè)坐標(biāo)系中展示一些項(xiàng)目(又稱(chēng)子視圖)。每個(gè)彩色矩形代表一個(gè)子視圖:


將子視圖添加到坐標(biāo)系厨幻。
UIView *redView = [[UIView alloc] initWithFrame:CGRectMake(20, 20, 100, 100)];
redView.backgroundColor = [UIColor colorWithRed:0.815 green:0.007
    blue:0.105 alpha:1];

UIView *greenView = [[UIView alloc] initWithFrame:CGRectMake(150, 160, 150, 200)];
greenView.backgroundColor = [UIColor colorWithRed:0.494 green:0.827
    blue:0.129 alpha:1];

UIView *blueView = [[UIView alloc] initWithFrame:CGRectMake(40, 400, 200, 150)];
blueView.backgroundColor = [UIColor colorWithRed:0.29 green:0.564
    blue:0.886 alpha:1];

UIView *yellowView = [[UIView alloc] initWithFrame:CGRectMake(100, 600, 180, 150)];
yellowView.backgroundColor = [UIColor colorWithRed:0.972 green:0.905
    blue:0.109 alpha:1];

[mainView addSubview:redView];
[mainView addSubview:greenView];
[mainView addSubview:blueView];
[mainView addSubview:yellowView];

邊界

UIViewbounds屬性是在自己的坐標(biāo)系中描述視圖的位置和大小况脆。

一個(gè)視圖可以被認(rèn)為是一個(gè)窗口或視口到由其坐標(biāo)系定義的平面的矩形區(qū)域。viewbounds表示這個(gè)矩形的位置和大小格了。

假設(shè)我們視圖的bounds矩形的寬度和高度為320×480點(diǎn),其原點(diǎn)是默認(rèn)的(0, 0)弹惦。視圖成為坐標(biāo)系平面的視口悄但,顯示整個(gè)平面的一小部分。界外的一切仍然存在檐嚣,只有隱藏:
實(shí)際上,除非clipsToBounds == YES(默認(rèn)是NO)报咳,否則邊界矩形外的子視圖將保持可見(jiàn)挖藏。盡管如此厢漩,該視圖并沒(méi)有檢測(cè)到界限之外的觸摸。

主視圖提供了一個(gè)子視圖到由其坐標(biāo)系定義的平面中宵膨。主視圖的邊界矩形描述了可見(jiàn)區(qū)域的位置和大小

接下來(lái)炸宵,我們將修改邊界矩形的原點(diǎn):

CGRect bounds = mainView.bounds;
bounds.origin = CGPointMake(0, 100);
mainView.bounds = bounds;

邊界矩形的起源現(xiàn)在在(0, 100)我們的場(chǎng)景如下所示:


修改邊界矩形的原點(diǎn)等同于移動(dòng)視口

看起來(lái)好像這個(gè)view已經(jīng)下降了100個(gè)點(diǎn),事實(shí)上這個(gè)view對(duì)于它自己的坐標(biāo)系是正確的捎琐。視圖在屏幕上的實(shí)際位置(或者在其父視圖中)仍然是固定的裹匙,由它決定的frame并沒(méi)有改變:

frame在其父視圖的坐標(biāo)系中描述視圖的位置和大小。

由于視圖的位置是固定的(從它自己的角度來(lái)看)籽御,所以把坐標(biāo)系平面看作是我們可以拖動(dòng)的一片透明膠片,將view看作是我們正在查看的固定窗口技掏。調(diào)整bounds原點(diǎn)相當(dāng)于移動(dòng)透明薄膜,使其另一部分通過(guò)視圖變得可見(jiàn):

修改邊界矩形的原點(diǎn)相當(dāng)于在相反的方向上移動(dòng)坐標(biāo)系劲阎,而視圖的位置保持固定涧衙,因?yàn)槠淇蚣懿粫?huì)改變

而這正是UIScrollView的滾動(dòng)。請(qǐng)注意雁比,從用戶(hù)的角度來(lái)看撤嫩,盡管視圖的子視圖在視圖的坐標(biāo)系統(tǒng)(換言之,其幀)方面的位置保持不變序攘,但看起來(lái)好像視圖的子視圖正在移動(dòng)。

我們來(lái)構(gòu)建UIScrollView

滾動(dòng)視圖不需要不斷更新其子視圖的坐標(biāo)以使其滾動(dòng)丈牢。它所要做的就是調(diào)整邊界的起點(diǎn)瞄沙。有了這些知識(shí),實(shí)現(xiàn)一個(gè)非常簡(jiǎn)單的滾動(dòng)視圖是微不足道的距境。我們?cè)O(shè)置了一個(gè)手勢(shì)識(shí)別器來(lái)檢測(cè)用戶(hù)的平移手勢(shì),為了響應(yīng)手勢(shì)师幕,我們bounds通過(guò)拖動(dòng)量來(lái)轉(zhuǎn)換視圖:

// CustomScrollView.h
@import UIKit;

@interface CustomScrollView : UIView

@property (nonatomic) CGSize contentSize;

@end

// CustomScrollView.m
#import "CustomScrollView.h"

@implementation CustomScrollView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self == nil) {
        return nil;
    }
    UIPanGestureRecognizer *gestureRecognizer = [[UIPanGestureRecognizer alloc]
        initWithTarget:self action:@selector(handlePanGesture:)];
    [self addGestureRecognizer:gestureRecognizer];
    return self;
}

- (void)handlePanGesture:(UIPanGestureRecognizer *)gestureRecognizer
{
    CGPoint translation = [gestureRecognizer translationInView:self];
    CGRect bounds = self.bounds;

    // Translate the view's bounds, but do not permit values that would violate contentSize
    CGFloat newBoundsOriginX = bounds.origin.x - translation.x;
    CGFloat minBoundsOriginX = 0.0;
    CGFloat maxBoundsOriginX = self.contentSize.width - bounds.size.width;
    bounds.origin.x = fmax(minBoundsOriginX, fmin(newBoundsOriginX, maxBoundsOriginX));

    CGFloat newBoundsOriginY = bounds.origin.y - translation.y;
    CGFloat minBoundsOriginY = 0.0;
    CGFloat maxBoundsOriginY = self.contentSize.height - bounds.size.height;
    bounds.origin.y = fmax(minBoundsOriginY, fmin(newBoundsOriginY, maxBoundsOriginY));

    self.bounds = bounds;
    [gestureRecognizer setTranslation:CGPointZero inView:self];
}

@end

就像真實(shí)一樣UIScrollView霹粥,我們有一個(gè)contentSize必須從外面設(shè)置的屬性來(lái)定義可滾動(dòng)區(qū)域的范圍疼鸟。當(dāng)我們調(diào)整邊界時(shí),我們確保只允許有效值愚臀。

結(jié)果:

我們的自定義滾動(dòng)視圖在行動(dòng)矾利。請(qǐng)注意馋袜,它缺乏動(dòng)力滾動(dòng),彈跳和滾動(dòng)指標(biāo)察皇。

結(jié)論

由于UIKit中坐標(biāo)系的嵌套本質(zhì)泽台,它用不到30行的代碼來(lái)重新實(shí)現(xiàn)UIScrollView。當(dāng)然怀酷,還有更多的事情是真實(shí)的UIScrollView。動(dòng)量滾動(dòng)桅锄,彈跳样眠,滾動(dòng)指標(biāo),縮放和委托方法只是我們?cè)谶@里沒(méi)有實(shí)現(xiàn)的一些功能檐束。

代碼現(xiàn)在在GitHub上可用

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末茶没,一起剝皮案震驚了整個(gè)濱河市晚碾,隨后出現(xiàn)的幾起案子喂急,更是在濱河造成了極大的恐慌,老刑警劉巖糕簿,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件狡孔,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡苗膝,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)离唐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人完沪,你說(shuō)我怎么就攤上這事嵌戈。” “怎么了熟呛?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵惰拱,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我偿短,道長(zhǎng),這世上最難降的妖魔是什么降传? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任勾怒,我火速辦了婚禮,結(jié)果婚禮上笔链,老公的妹妹穿的比我還像新娘。我一直安慰自己赞枕,他們只是感情好坪创,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著柠掂,像睡著了一般依沮。 火紅的嫁衣襯著肌膚如雪枪狂。 梳的紋絲不亂的頭發(fā)上肩狂,一...
    開(kāi)封第一講書(shū)人閱讀 51,692評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音傻谁,去河邊找鬼。 笑死谈飒,一個(gè)胖子當(dāng)著我的面吹牛态蒂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播钾恢,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼瘩蚪,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了疹瘦?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤邓嘹,失蹤者是張志新(化名)和其女友劉穎险胰,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體起便,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缨睡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年陈辱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沛贪。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖水评,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情寇甸,我是刑警寧澤疗涉,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站咱扣,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏沪铭。R本人自食惡果不足惜偏瓤,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望硼补。 院中可真熱鬧,春花似錦离钝、人聲如沸褪储。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至碘橘,卻和暖如春吱肌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背氮墨。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工吐葵, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留桥氏,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓凤藏,卻偏偏與公主長(zhǎng)得像祥款,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子刃跛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

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