1 UIScrollView原理
? ? ? ?在滾動(dòng)過程當(dāng)中汁胆,其實(shí)是在修改原點(diǎn)坐標(biāo)呛伴。當(dāng)手指觸摸后,scroll view會(huì)暫時(shí)攔截觸摸事件,使用一個(gè)計(jì)時(shí)器臣疑。假如在計(jì)時(shí)器到點(diǎn)后沒有發(fā)生手指移動(dòng)事件,那么scroll view 發(fā)送 tracking events 到被點(diǎn)擊的subview馒胆。假如在計(jì)時(shí)器到點(diǎn)前發(fā)生了移動(dòng)事件缨称,那么 scroll view 取消tracking 自己發(fā)生滾動(dòng)。
??? 子類可以重載?
touchesShouldBegin:withEvent:inContentView:
????決定自己是否接收 touch 事件
pagingEnabled:????
????當(dāng)值是 YES 會(huì)自動(dòng)滾動(dòng)到 subview 的邊界祝迂,默認(rèn)是NO
touchesShouldCancelInContentView:????
????開始發(fā)送 tracking messages 消息給 subview 的時(shí)候調(diào)用這個(gè)方法睦尽,決定是否發(fā)送 tracking messages 消息到subview。假如返回 NO型雳,發(fā)送当凡。YES 則不發(fā)送。假如 canCancelContentTouches屬性是NO纠俭,則不調(diào)用這個(gè)方法來影響如何處理滾動(dòng)手勢(shì)沿量。
??? ????????scroll view 還處理縮放和平移手勢(shì),要實(shí)現(xiàn)縮放和平移冤荆,必須實(shí)現(xiàn)委托 viewForZoomingInScrollView:和scrollViewDidEndZooming:withView:atScale: 兩個(gè)方法朴则。另外 maximumZoomScale和minimumZoomScale 兩個(gè)屬性要不一樣。
1.1 核心原理
????????UIScrollView的核心理念是匙赞,它是一個(gè)可以在內(nèi)容視圖之上佛掖,調(diào)整自己原點(diǎn)位置的視圖。它根據(jù)自身框架的大小涌庭,剪切視圖中的內(nèi)容芥被,通常框架是和應(yīng)用程序窗口一樣大坐榆。一個(gè)滾動(dòng)的視圖可以根據(jù)手指的移動(dòng)拴魄,調(diào)整原點(diǎn)的位置。展示內(nèi)容的視圖席镀,根據(jù)滾動(dòng)視圖的原點(diǎn)位置匹中,開始繪制視圖的內(nèi)容,這個(gè)原點(diǎn)位置就是滾動(dòng)視圖的偏移量豪诲。ScrollView本身不能繪制顶捷,除非顯示水平和豎直的指示器。滾動(dòng)視圖必須知道內(nèi)容視圖的大小屎篱,以便于知道什么時(shí)候停止服赎;一般而言,當(dāng)滾動(dòng)出內(nèi)容的邊界時(shí)交播,它就返回了重虑。
????????某些對(duì)象是用來管理內(nèi)容顯示如何繪制的,這些對(duì)象應(yīng)該是管理如何平鋪顯示內(nèi)容的子視圖秦士,以便于沒有子視圖可以超過屏幕的尺寸缺厉。就是當(dāng)用戶滾動(dòng)時(shí),這些對(duì)象應(yīng)該恰當(dāng)?shù)脑黾踊蛘咭瞥右晥D隧土。
???? ????因?yàn)闈L動(dòng)視圖沒有滾動(dòng)條提针,它必須知道一個(gè)觸摸信號(hào)是打算滾動(dòng)還是打算跟蹤里面的子視圖。為了達(dá)到這個(gè)目的次洼,它臨時(shí)中斷了一個(gè)touch-down的事件关贵,通過建立一個(gè)定時(shí)器,在定時(shí)器開始行動(dòng)之前卖毁,看是否觸摸的手指做了任何的移動(dòng)揖曾。假如定時(shí)器行動(dòng)時(shí),沒有任何的大的位置改變亥啦,滾動(dòng)視圖就發(fā)送一個(gè)跟蹤事件給觸摸的子視圖炭剪。如果在定時(shí)器消失前,用戶拖動(dòng)他們的手指足夠的遠(yuǎn)翔脱,滾動(dòng)視圖取消子視圖的任何跟蹤事件奴拦,滾動(dòng)它自己。子類可以重載touchesShouldBegin:withEvent:inContentView:,? pagingEnabled 和touchesShouldCancelInContentView:方法届吁,從而影響滾動(dòng)視圖的滾動(dòng)手勢(shì)错妖。
????????一個(gè)滾動(dòng)視圖也可以控制一個(gè)視圖的縮放和平鋪绿鸣。當(dāng)用戶做捏合手勢(shì)時(shí),滾動(dòng)視圖調(diào)整偏移量和視圖的比例暂氯。當(dāng)手勢(shì)結(jié)束的時(shí)候潮模,管理視圖內(nèi)容顯示的對(duì)象,就應(yīng)該恰當(dāng)?shù)纳?jí)子視圖的顯示痴施。當(dāng)手勢(shì)在處理的過程中擎厢,滾動(dòng)視圖不能夠給子視圖,發(fā)送任何跟蹤的調(diào)用辣吃。
1.2 事件處理
????????UIScrollView類有一個(gè)delegate动遭,需要適配的協(xié)議是UIScrollViewDelegate。為了縮放和平鋪工作神得,代理必須實(shí)現(xiàn)viewForZoomingInScrollView:和scrollViewDidEndZooming:withView:atScale:方法厘惦。另外,最大和最小縮放比例應(yīng)該是不同的循头。
????????重要的提示:在UIScrollView對(duì)象中绵估,你不應(yīng)該嵌入任何UIWebView和UITableView。假如這樣做卡骂,會(huì)出現(xiàn)一些異常情況国裳,因?yàn)?個(gè)對(duì)象的觸摸事件可能被混合,從而錯(cuò)誤的處理全跨。
????????這些都是官方API的解釋缝左,重點(diǎn)是理解UIScrollView怎么來控制手勢(shì)的∨ㄈ簦可以由canCancelContentTouches這個(gè)方法的運(yùn)用來解釋UIScrollView如何控制手勢(shì)的渺杉。
???? ????假如你設(shè)置canCancelContentTouches為YES,那么當(dāng)你在UIScrollView上面放置任何子視圖的時(shí)候挪钓,當(dāng)你在子視圖上移動(dòng)手指的時(shí)候是越,UIScrollView會(huì)給子視圖發(fā)送touchCancel的消息。而如果該屬性設(shè)置為NO碌上,ScrollView本身不處理這個(gè)消息倚评,全部交給子視圖處理。
????????那么這里就有疑問了馏予,既然該屬性設(shè)置未來NO了天梧,那么豈不是UIScrollView不能處理任何事件了,那么為何在子視圖上快速滾動(dòng)的時(shí)候霞丧,UIScrollView還能移動(dòng)那呢岗。這個(gè)一定要區(qū)分前面所說的UIScrollView中斷touch-Down事件,開啟一個(gè)定時(shí)器。我們?cè)O(shè)置的這個(gè)cancancelContentTouches屬性為NO時(shí)后豫,只是讓UIScrollView不能發(fā)送cancel事件給子視圖悉尾。而前面所說的時(shí),中斷touch-down事件挫酿,和取消touch事件是倆碼事焕襟,所以當(dāng)快速在子視圖上移動(dòng)的時(shí)候,當(dāng)然可以滾動(dòng)饭豹。但是如果你慢速的移動(dòng)的話,就可以區(qū)分這個(gè)屬性了务漩,假如設(shè)定為YES拄衰,在子視圖上慢速移動(dòng)也可以滾動(dòng)視圖,但是如果為NO?饵骨。因?yàn)閁IScrollView翘悉,發(fā)送了cancel事件給子視圖處理了,自己當(dāng)然滾動(dòng)不了了居触。
????????首先了解下UIScrollView對(duì)于touch事件的接收處理原理:UIScrollView應(yīng)該是重載了hitTest 方法妖混,并總會(huì)返回itself 。所以所有的touch 事件都會(huì)進(jìn)入到它自己里面去了轮洋。內(nèi)部的touch事件檢測(cè)到這個(gè)事件是不是和自己相關(guān)的制市,或者處理或者除遞給內(nèi)部的view。
? ??????為了檢測(cè)touch是處理還是傳遞弊予,UIScrollView當(dāng)touch發(fā)生時(shí)會(huì)生成一個(gè)timer祥楣。???
? ? ?(1)如果150ms內(nèi)touch未產(chǎn)生移動(dòng),它就把這個(gè)事件傳遞給內(nèi)部view汉柒;
????(2)如果150ms內(nèi)touch產(chǎn)生移動(dòng)误褪,開始scrolling,不會(huì)傳遞給內(nèi)部的view碾褂。(如當(dāng)你touch一個(gè)table時(shí)候兽间,直接scrolling,你touch的那行永遠(yuǎn)不會(huì)highlight正塌。)
????(3)如果150ms內(nèi)touch未產(chǎn)生移動(dòng)并且UIScrollView開始傳遞內(nèi)部的view事件嘀略,但是移動(dòng)足夠遠(yuǎn)的話,且canCancelContentTouches = YES传货,UIScrollView會(huì)調(diào)用touchesCancelled方法屎鳍,cancel掉內(nèi)部view的事件響應(yīng),并開始scrolling。(如當(dāng)你touch一個(gè)table问裕, 停止了一會(huì)逮壁,然后開始scrolling,那一行就首先被highlight粮宛,但是隨后就不在高亮了)
????????在滾動(dòng)過程當(dāng)中窥淆,其實(shí)是在修改原點(diǎn)坐標(biāo)卖宠。當(dāng)手指觸摸后, scroll view會(huì)暫時(shí)攔截觸摸事件,使用一個(gè)計(jì)時(shí)器。假如在計(jì)時(shí)器到點(diǎn)后沒有發(fā)生手指移動(dòng)事件忧饭,那么 scroll view 發(fā)送 tracking events 到被點(diǎn)擊的 subview扛伍。假如在計(jì)時(shí)器到點(diǎn)前發(fā)生了移動(dòng)事件,那么 scroll view 取消 tracking 自己發(fā)生滾動(dòng)词裤。
1.3 內(nèi)存重用
????????事件處理看過了刺洒,就要考慮scrollView如何重用內(nèi)存的,下面寫了一個(gè)例子模仿UITableView的重用的思想吼砂,這里只是模仿逆航,至于蘋果公司怎么實(shí)現(xiàn)這種重用的,他們應(yīng)該有更好的方法渔肩。
?????這里的例子是在scrollView上放置4個(gè)2排2列的視圖因俐,但是內(nèi)存中只占用6個(gè)視圖的內(nèi)存空間。當(dāng)scrollView滾動(dòng)的時(shí)候周偎,通過不停的重用之前視圖的內(nèi)存空間抹剩,從而達(dá)到節(jié)省內(nèi)存的效果。重用的方法如下:
????1.如果scrollView向下面滾動(dòng)蓉坎,一旦一排視圖滾出了可視范圍澳眷,就改變滾動(dòng)出去的那個(gè)view在scrollView中的frame,也就是改變位置到達(dá)末尾蛉艾,達(dá)到重用的效果境蔼。
????2.如果scrollView向上面滾動(dòng),一旦最末排的視圖view滾出了可視范圍伺通,就改變滾動(dòng)出去的那個(gè)view在scrollView中的frame箍土,移動(dòng)到最前面。
???????下面就需要在你創(chuàng)建的視圖控制器中罐监,創(chuàng)建一個(gè)重用的視圖數(shù)組吴藻,用來把這些要顯示的視圖放入內(nèi)存中,這里雖然界面上顯示的是2排2列的四個(gè)視圖弓柱,但是當(dāng)拖動(dòng)的時(shí)候沟堡,可能出現(xiàn)前面一排的視圖顯示一部分,末尾一排的視圖顯示一部分的情況矢空,所以重用的數(shù)組中要放置6個(gè)視圖航罗。下面是定義的一些宏:
#define?sMyViewTotal?6
#define?sMyViewWidth?150
#define?sMyViewHeight?220
#define?sMyViewGap?10
具體實(shí)現(xiàn)代碼如下:
_aryViews?=?[[NSMutableArray?alloc]?init];
for?(int?i?=?0;?i?<?sMyViewTotal;?i++)?{
????CGFloat?x;
????if?(i%2)?{
????????x?=?sMyViewWidth?+?sMyViewGap?+?sMyViewGap/2;
????}
????else{
????x?=?sMyViewGap/2;
????}
????CGFloat?y?=?(sMyViewHeight?+?sMyViewGap)*(i/2);
????MyView?*myView?=?[[MyView?alloc]?initWithFrame: CGRectMake(x, y,?sMyViewWidth, sMyViewHeight)];
????myView.showNumber?=?i;
????[myScrollView?addSubview: myView];
????[_aryViews?addObject: myView];
????[myView?release];
}
所以這里的核心方法是,首先要判斷是向上滾動(dòng)還是向下滾動(dòng)方法如下:
-?(void) scrollViewDidScroll: (UIScrollView?*)scrollView{
????BOOL?directDown;
????if?(previousOffSet.y?<?scrollView.contentOffset.y)?{
????????directDown?=?YES;
????}
????else{
????????directDown?=?NO;
????}
????previousOffSet.y?=?scrollView.contentOffset.y;
????//防止最開始就向上面拖動(dòng)的時(shí)候屁药,改變數(shù)組視圖樹的位置粥血。
????if?(scrollView.contentOffset.y?<?0)?{
????????return;
????}
????if?(directDown)?{
????????NSLog(@"down");
????????MyView?*?subView?=?[_aryViews?objectAtIndex: firstViewIndex];
????????CGFloat?firstViewYOffset?=?subView.frame.origin.y?+?subView.frame.size.height?+?sMyViewGap;
????????//尋找第一個(gè)視圖是否滾動(dòng)出去
????????if?(firstViewYOffset?<?scrollView.contentOffset.y)?{
????????????//改變數(shù)組中第一排可見視圖的位置。
????????????[self?moveIndexInViewsWithDirect: YES];
????????}
????}
????else{
????????NSLog(@"up");
????????MyView?*?subView?=?[_aryViews?objectAtIndex: (firstViewIndex?+ sMyViewTotal -?2) % sMyViewTotal];
????????CGFloat?lastViewYOffset?=?subView.frame.origin.y?-?scrollView.bounds.size.height;
????????if?(lastViewYOffset?>?scrollView.contentOffset.y)?{
????????????[self?moveIndexInViewsWithDirect: NO];
????????}
????}
}
????????每次滾動(dòng)的時(shí)候先判斷滾動(dòng)位置即offset,和先前的比較复亏。如果先前的大就是向下滾動(dòng)趾娃,否則就是向上滾動(dòng)。
????????找到了向下滾動(dòng)了缔御,就該判斷是否子視圖已經(jīng)離開了可視范圍抬闷。方法就是判斷當(dāng)前offset和視圖的位置進(jìn)行比較。如果判斷滾到離開了可視范圍耕突,然后就是要改變重用視圖數(shù)組中第一個(gè)視圖的位置了笤成。這里用了firstViewIndex來記錄scrollView中第一個(gè)可見視圖的位置,?循環(huán)使用這6個(gè)視圖達(dá)到重用的目的眷茁。自然firstViewIndex上面的一個(gè)視圖就是最后一個(gè)視圖的位置(firstViewIndex?+?sMyViewTotal?-?1) %sMyViewTotal疹启。所以這里需要改變重用視圖中firstViewIndex即第一個(gè)可見視圖的位置。代碼如下:
-?(void) moveIndexInViewsWithDirect: (BOOL)forward{
????[UIView?setAnimationsEnabled: NO];
????if?(forward)?{
????????for?(int?i?=?firstViewIndex;?i?<?(firstViewIndex?+?2);?i++)?{
????????????MyView?*subView?=?[_aryViews?objectAtIndex: i % sMyViewTotal];
????????????subView.showNumber?=?subView.showNumber?+?sMyViewTotal;
????????????subView.frame?=?CGRectMake(subView.frame.origin.x,?subView.frame.origin.y?+?(sMyViewTotal/2)?*?(sMyViewHeight?+?sMyViewGap),?subView.frame.size.width,?subView.frame.size.height);
????????}
????????firstViewIndex?=?(firstViewIndex?+?2) % sMyViewTotal;
????}
????else{
????????int?lastViewIndex?=?firstViewIndex?+?sMyViewTotal?-?1;
????????for?(int?i?=?lastViewIndex;?i?>?(lastViewIndex?-?2);?i-)?{????????????
????????????MyView?*subView?=?[_aryViews?objectAtIndex:(firstViewIndex?+ sMyViewTotal?-?i) % sMyViewTotal];
???????????subView.showNumber?=?subView.showNumber?-?sMyViewTotal;
????????????subView.frame?=?CGRectMake(subView.frame.origin.x,?subView.frame.origin.y?-?(sMyViewTotal/2)?*?(sMyViewHeight?+?sMyViewGap),?subView.frame.size.width,?subView.frame.size.height);
????????}
????????firstViewIndex?=?(firstViewIndex?+?sMyViewTotal?-?2) % sMyViewTotal;
????}
????[UIView?setAnimationsEnabled: YES];
}
??????這里創(chuàng)建的子視圖數(shù)字屬性蔼卡,是用來在視圖上畫數(shù)字的,這樣就可以看到視圖重用的效果了挣磨,應(yīng)該是從0開始到無窮多雇逞,但是實(shí)際上內(nèi)存中就創(chuàng)建了6個(gè)視圖。
-?(void)drawRect:(CGRect)rect
{
????//?Drawing?code
????NSString?*text?=?[NSString?stringWithFormat:@"%d",showNumber];
????[[UIColor?redColor]?set];
????[text?drawInRect: CGRectMake(rect.origin.x, rect.origin.y?+?rect.size.height/2?-?30, rect.size.width, 30) withFont:[UIFont??????fontWithName: @"Helvetica"?size:20] lineBreakMode: UILineBreakModeWordWrap alignment: UITextAlignmentCenter];
}
2 開發(fā)運(yùn)用
2.1 重要屬性
2.1.1 Contentsize與contentInset
????????contentsize是內(nèi)容的寬和高茁裙,contentsize.width是內(nèi)容的寬度塘砸,contentsize.heght是高度,contentsize是UIScrollView的一個(gè)屬性晤锥,它是一個(gè)CGSize掉蔬,是由核心圖形所定義的架構(gòu),那定義了你可以滾軸內(nèi)容的寬度和高度矾瘾,你也可以添加可以上下滾動(dòng)的額外區(qū)域女轿。第一種方法是你可以通過添加內(nèi)容的大小來完成。另外一個(gè)比較動(dòng)態(tài)的選擇是UIScrollView的另一個(gè)屬性contentInset壕翩,contentInset增加你在contentsize中指定的內(nèi)容能夠滾動(dòng)的上下左右區(qū)域數(shù)量contentInset.top以及contentInset.buttom分別表示上面和下面的距離蛉迹。
????????在滾軸視圖中,有一個(gè)叫做ContentOffset的屬性跟蹤UIScrollView的具體位置放妈,你能夠自己獲取和設(shè)置它北救,ContentOffset是你當(dāng)前可視內(nèi)容在滾軸視圖邊界的左上角那個(gè)點(diǎn)。如圖:
????????可以看出芜抒,ContentOffset內(nèi)容中的那個(gè)點(diǎn)不是從contentInset的左上角開始的珍策,而是內(nèi)容的左上角,此時(shí)的ContentOffset是正值宅倒,但有時(shí)也是負(fù)值攘宙,如下圖所示:
2.1.2 API介紹
touchesShouldBegin:withEvent:inContentView:?
????決定自己是否接收 touch 事件
pagingEnabled:
????當(dāng)值是 YES 會(huì)自動(dòng)滾動(dòng)到 subview 的邊界,默認(rèn)是NO
touchesShouldCancelInContentView:?
? ? 開始發(fā)送 tracking messages 消息給 subview 的時(shí)候調(diào)用這個(gè)方法,決定是否發(fā)送 tracking messages 消息到subview模聋。假如返回 NO肩民,發(fā)送。YES 則不發(fā)送链方。假如 canCancelContentTouches屬性是NO持痰,則不調(diào)用這個(gè)方法來影響如何處理滾動(dòng)手勢(shì)。
????????scroll view 還處理縮放和平移手勢(shì)祟蚀,要實(shí)現(xiàn)縮放和平移工窍,必須實(shí)現(xiàn)委托 viewForZoomingInScrollView:和scrollViewDidEndZooming:withView:atScale:兩個(gè)方法。另外 maximumZoomScale和minimumZoomScale 兩個(gè)屬性要不一樣前酿。
????幾個(gè)屬性介紹:
tracking
????當(dāng) touch 后還沒有拖動(dòng)的時(shí)候值是YES患雏,否則NO
zoomBouncing
????當(dāng)內(nèi)容放大到最大或者最小的時(shí)候值是 YES,否則NO
zooming
????當(dāng)正在縮放的時(shí)候值是 YES罢维,否則NO
decelerating
????當(dāng)滾動(dòng)后淹仑,手指放開但是還在繼續(xù)滾動(dòng)中。這個(gè)時(shí)候是 YES肺孵,其它時(shí)候是NO
decelerationRate
????設(shè)置手指放開后的減速率
maximumZoomScale
????一個(gè)浮點(diǎn)數(shù)匀借,表示能放最大的倍數(shù)
minimumZoomScale
????一個(gè)浮點(diǎn)數(shù),表示能縮最小的倍數(shù)
pagingEnabled
????當(dāng)值是 YES 會(huì)自動(dòng)滾動(dòng)到 subview 的邊界平窘。默認(rèn)是NO
scrollEnabled
????決定是否可以滾動(dòng)
delaysContentTouches
????是個(gè)布爾值吓肋,當(dāng)值是 YES 的時(shí)候,用戶觸碰開始瑰艘,scroll view要延遲一會(huì)是鬼,看看是否用戶有意圖滾動(dòng)。假如滾動(dòng)了紫新,那么捕捉 touch-down 事件均蜜,否則就不捕捉。假如值是NO芒率,當(dāng)用戶觸碰兆龙, scroll view 會(huì)立即觸發(fā)touchesShouldBegin:withEvent:inContentView:,默認(rèn)是YES
canCancelContentTouches
????當(dāng)值是 YES 的時(shí)候敲董,用戶觸碰后紫皇,然后在一定時(shí)間內(nèi)沒有移動(dòng),scrollView 發(fā)送 tracking events腋寨,然后用戶移動(dòng)手指足夠長(zhǎng)度觸發(fā)滾動(dòng)事件聪铺,這個(gè)時(shí)候,scrollView 發(fā)送了 touchesCancelled:withEvent: 到 subview萄窜,然后 scroView 開始滾動(dòng)铃剔。假如值是 NO撒桨,scrollView 發(fā)送 tracking events 后,就算用戶移動(dòng)手指键兜,scrollView 也不會(huì)滾動(dòng)凤类。
contentSize
????里面內(nèi)容的大小,也就是可以滾動(dòng)的大小普气,默認(rèn)是0谜疤,沒有滾動(dòng)效果。
showsHorizontalScrollIndicator
????滾動(dòng)時(shí)是否顯示水平滾動(dòng)條
showsVerticalScrollIndicator
????滾動(dòng)時(shí)是否顯示垂直滾動(dòng)條
bounces
????默認(rèn)是 yes现诀,就是滾動(dòng)超過邊界會(huì)反彈有反彈回來的效果夷磕。假如是 NO,那么滾動(dòng)到達(dá)邊界會(huì)立刻停止仔沿。
bouncesZoom
????和 bounces 類似,區(qū)別在于:這個(gè)效果反映在縮放上面坐桩,假如縮放超過最大縮放,那么會(huì)反彈效果封锉;假如是 NO绵跷,則到達(dá)最大或者最小的時(shí)候立即停止。
directionalLockEnabled
????默認(rèn)是 NO成福,可以在垂直和水平方向同時(shí)運(yùn)動(dòng)碾局。當(dāng)值是 YES 時(shí),假如一開始是垂直或者是水平運(yùn)動(dòng)闷叉,那么接下來會(huì)鎖定另外一個(gè)方向的滾動(dòng)。 假如一開始是對(duì)角方向滾動(dòng)脊阴,則不會(huì)禁止某個(gè)方向
indicatorStyle
????滾動(dòng)條的樣式握侧,基本只是設(shè)置顏色『倨冢總共3個(gè)顏色:默認(rèn)品擎、黑、白
scrollIndicatorInsets
????設(shè)置滾動(dòng)條的位置
2.2 具體使用范例
使用一個(gè)ScrollView
// 創(chuàng)建一個(gè)UIScrollView
CGRectframe = CGRectMake( 0, 0, 200, 200);
scrollView = [[UIScrollView alloc] initWithFrame: frame];
// 添加子視圖(框架可以超過scroll view的邊界)
frame = CGRectMake( 0, 0, 500, 500);
myImageView = [[UIImageView alloc] initWithFrame: frame];
[scrollView addSubview: myImageView];
// 設(shè)置內(nèi)容尺寸
scrollView.contentSize = CGSize(500,500);
2.3 擴(kuò)展ScrollView的行為
????????應(yīng)用程序通常需要知道有關(guān)的滾圖的事件:
????scrolloffset改變的時(shí)候
????拖動(dòng)開始和結(jié)束
? ? 減速的開始和結(jié)束
2.3.1 通過子類化擴(kuò)展ScrollView的行為
????????創(chuàng)建一個(gè)子類
????????重寫一些功能并改變行為
????????關(guān)于這種方式的爭(zhēng)議
? ? ? ? 應(yīng)用程序的邏輯和行為變成了視圖本身的一部分备徐,就像萄传,你可能有一些定制的滾軸邏輯,蜜猾,在那你只在意一個(gè)視圖控制秀菱,但你想在不同地方重復(fù)使用你的滾軸視圖,如果你必須為每個(gè)都子類化蹭睡,你最后會(huì)有很多不同的滾軸視圖子類以及在視圖中的特定應(yīng)用邏輯衍菱。
? ? ? ? 編寫很多子類是很沉悶的事情,你最后會(huì)有很多無法重復(fù)使用的單獨(dú)視圖肩豁,而MVC的視圖部分的一個(gè)重點(diǎn)是視圖是可以在不同的控制器和不同的模式之中重復(fù)使用的脊串,如果我們把所有邏輯都放在視圖中辫呻,它減少了可復(fù)用性。
? ? ? ?你的代碼變得很牢固地配對(duì)在一起琼锋,它實(shí)際上變成了超類的一部分放闺,你無法從UIScrollView中析取它,之后用其它東西代替缕坎,如果它在你控制器中且為控制器的一部分怖侦,在之后更容易改變它工作的方式和重新安排你應(yīng)用程序的一些部分。
2.3.2 通過委托來擴(kuò)展ScrollView的行為(常用的)
????????委托是一個(gè)單獨(dú)的對(duì)象念赶,協(xié)議础钠,定義了委托會(huì)實(shí)現(xiàn)的一系列功能的Objective-C協(xié)議,它創(chuàng)建了一系列很清晰的撤銷點(diǎn)叉谜,在那里你能定制行為和外觀旗吁。它在這些對(duì)象之間保持了松散的配對(duì),視圖本身與視圖控制器或任何其它的控制器對(duì)象停局,委托不是滾軸視圖的直接子類很钓,它比起牢固配對(duì)的子類更加的松散。
2.4 開發(fā)技巧
2.4.1 計(jì)算當(dāng)前頁(yè)面數(shù)
- (void) scrollViewDidEndDecelerating: (UIScrollView*)scrollView {
????// 得到每頁(yè)寬度
??? CGFloat pageWidth = CGRectGetWidth(self.paggingScrollView.frame);
??? // 根據(jù)當(dāng)前的x坐標(biāo)和頁(yè)寬度計(jì)算出當(dāng)前頁(yè)數(shù)
??? //此屬性變化會(huì)調(diào)用Set方法
??? self.currentPage = floor((scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
}
- (void) setCurrentPage: (NSInteger)currentPage {
??? if (_currentPage==currentPage)
??????? return;
??? _lastPage = _currentPage;
??? _currentPage= currentPage;
??? self.paggingNavbar.currentPage= currentPage;
??? [self setupScrollToTop];
??? [self callBackChangedPage];
}
2.4.2? 指定Cell大小與間距
2.4.3 支持點(diǎn)擊狀態(tài)欄回到頁(yè)面頂部
????????scrollsToTop是UIScrollView的一個(gè)屬性董栽,主要用于點(diǎn)擊設(shè)備的狀態(tài)欄時(shí)码倦,是scrollsToTop == YES的控件滾動(dòng)返回至頂部。
????????每一個(gè)默認(rèn)的UIScrollView的實(shí)例锭碳,他的scrollsToTop屬性默認(rèn)為YES袁稽,所以要實(shí)現(xiàn)某一UIScrollView的實(shí)例點(diǎn)擊設(shè)備狀態(tài)欄返回頂部,則需要關(guān)閉其他的UIScrollView的實(shí)例的scrollsToTop屬性為NO擒抛。很好理解:若多個(gè)scrollView響應(yīng)返回頂部的事件推汽,系統(tǒng)就不知道到底要將那個(gè)scrollView返回頂部了,因此也就不做任何操作了歧沪。
3 參考資料
Scroll View Programming Guide for iOS筆記
http://blog.sina.com.cn/s/blog_67419c420100phyf.html
第二歹撒、UIScrollView的使用大全
http://blog.csdn.net/ch_soft/article/details/6947695
[置頂] UIScrollView用法
http://blog.csdn.net/mengtnt/article/details/6723245
UIScrollView原理詳解
http://blog.csdn.net/likendsl/article/details/7592867
TwitterPaggingViewer——類似Twitter,將滑動(dòng)視圖的UIPageControl(就是記錄當(dāng)前頁(yè)面的一串小點(diǎn))放到導(dǎo)航欄
http://code4app.com/ios/TwitterPaggingViewer/53a7ed4a933bf0794c8b48f9
UICollectionViewLayout
http://blog.csdn.net/majiakun1/article/details/17204921
ios開發(fā)——解決UICollectionView的cell間距與設(shè)置不符問題
http://www.bkjia.com/IOSjc/917782.html
IOS中scrollsToTop問題小結(jié)
http://blog.csdn.net/enuola/article/details/32331933
ios重寫Cell后tabelView不能響應(yīng)點(diǎn)擊狀態(tài)欄回到到頂部
http://www.cocoachina.com/bbs/read.php?tid-248386.html
深入理解iOS開發(fā)中的UIScrollView
http://mobile.51cto.com/hot-443341.htm
IOS學(xué)習(xí)筆記——iOS組件之UIScrollView詳解
https://segmentfault.com/a/1190000002412930