UIScrollView視差滑動(dòng)輪播圖(二) 優(yōu)化

iOS仿格瓦拉 輪播圖 Parallax Rolling Banner
作者:yuan

前言

上一篇中灰蛙,我們實(shí)現(xiàn)了一個(gè)視差滾動(dòng)的輪播圖,但是它是由通過(guò)設(shè)置UIScrollViewpageEnable的屬性來(lái)實(shí)現(xiàn)的挚歧。 然而你會(huì)發(fā)現(xiàn)他并沒(méi)有像格瓦拉的滾動(dòng)視圖那樣絲滑,減速動(dòng)畫(huà)阻尼巨大
格瓦拉的滾動(dòng)視圖:

優(yōu)化思路

既然系統(tǒng)提供的分頁(yè)方式不能滿(mǎn)足我們吁峻,那么我們?nèi)绾巫约簩?shí)現(xiàn)一個(gè)paging的效果呢滑负?答案就在Apple的官方文檔里面,在查閱UIScrollView的代理后用含,我們可以發(fā)現(xiàn)里面有這樣一個(gè)代理方法:

//scrollView    The scroll-view object where the user ended the touch..
//velocity  The velocity of the scroll view (in points) at the moment the touch was released.
//targetContentOffset   The expected offset when the scrolling action decelerates to a stop.
//Your application can change the value of the targetContentOffset parameter to adjust where the scrollview finishes its scrolling animation.

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
                     withVelocity:(CGPoint)velocity
              targetContentOffset:(inout CGPoint *)targetContentOffset

在這個(gè)方法中有一個(gè)targetContentOffset的參數(shù)矮慕,文檔里指出,我們可以通過(guò)修改這個(gè)參數(shù)啄骇,來(lái)改變scrollView在滑動(dòng)動(dòng)畫(huà)結(jié)束時(shí)的位置痴鳄。 Bingo, 這就是我們想要的缸夹!通過(guò)修改targetContentOffset直接修改目標(biāo)offset為整數(shù)頁(yè)位置

實(shí)現(xiàn)

我們所需要的paging:

  • 設(shè)置滑動(dòng)阻尼痪寻,當(dāng)滑動(dòng)沒(méi)有達(dá)到滑動(dòng)阻尼位置時(shí)螺句,標(biāo)記這次滑動(dòng)為cancel,并修改targetContentOffset.x為起始位置 self.bounds.size.width
  • 當(dāng)已經(jīng)超過(guò)滑動(dòng)阻尼時(shí)橡类, 標(biāo)記這次滑動(dòng)為complete,并通過(guò)滑動(dòng)方向計(jì)算相應(yīng)的targetContentOffset.x結(jié)束位置為 0或者 self.bounds.size.width * 2

核心代碼:

- (CGFloat)targetOffsetForMoveX:(CGFloat)moveX velocity:(CGFloat)velocity{
    BOOL complete = fabs(moveX) >= self.bounds.size.width * 0.3 ||
                    (fabs(velocity) > 0 && fabs(moveX) >= self.bounds.size.width * 0.1 )? YES : NO;
    BOOL leftDirection = moveX > 0 ? YES : NO;
    if (complete) {
        if (leftDirection) {
            return self.bounds.size.width * 2;
        }
        return 0; //right Direction
    }else {
        return self.bounds.size.width;//cancel
    }
}

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
    if (self.pagingEnabled)return;
    CGFloat moveX = scrollView.contentOffset.x - self.bounds.size.width;
    CGFloat targetX = [self targetOffsetForMoveX:moveX velocity:velocity.x];
    if (targetX == self.bounds.size.width) {//cancel
        targetContentOffset->x = scrollView.contentOffset.x;
        [self setContentOffset:CGPointMake(targetX, targetContentOffset->y) animated:YES];
    } else {//complete
        targetContentOffset->x = targetX;
    }
}

注意在這里我們做了一些優(yōu)化壹蔓,當(dāng)滑動(dòng)被判斷為cancel時(shí),我們并沒(méi)有直接把其實(shí)位置self.bounds.size.width賦值給targetContentOffset->x猫态。那是因?yàn)橛度兀?jīng)常我們的滑動(dòng)的時(shí)候會(huì)伴隨加速度velocity.x。當(dāng)targetContentOffset->xscrollViewcontentOffset.x非常相近亲雪,又伴隨加速度velocity.x時(shí)勇凭,cancel的滑動(dòng)動(dòng)畫(huà)的持續(xù)時(shí)間就會(huì)非常短。這帶來(lái)的滑動(dòng)體驗(yàn)非常不好义辕。

在這里虾标,我們放棄修改targetContentOffset->x為起始值,轉(zhuǎn)而直接把他賦值為當(dāng)前的contentOffset.x灌砖,這樣滑動(dòng)手勢(shì)所帶來(lái)的velocity.x就不會(huì)影響到cancel的滑動(dòng)動(dòng)畫(huà);同時(shí)我們?cè)偈褂孟到y(tǒng)自帶的方法來(lái)完成這個(gè)cancel的結(jié)束動(dòng)畫(huà)

[self setContentOffset:CGPointMake(targetX, targetContentOffset->y) animated:YES] 

RunLoop 與 targetContentOffset

如果你注意觀(guān)察璧函,你會(huì)發(fā)現(xiàn):當(dāng)我們修改scrollViewtargetContentOffset時(shí),scrollView會(huì)有一次準(zhǔn)確的contentOffset等于targetContentOffset的回調(diào)-scrollViewDidScroll:基显。我們暫時(shí)把這個(gè)回調(diào)叫作準(zhǔn)確回調(diào)

由于RunLoop的原故蘸吓,在代理方法-(void)scrollViewDidScroll:中對(duì)于scrollViewcontentOffset監(jiān)聽(tīng)非常不精確。所以我們很可能拿到和我們所期望值不一樣的contentOffset

如果我們?cè)谧罱咏?code>準(zhǔn)確回調(diào)的回調(diào)里修改了scrollViewcontentOffset撩幽,ie: 設(shè)置contentOffset.xself.bounds.size.width库继,同時(shí)緊接著在準(zhǔn)確回掉里又把contentOffset.x設(shè)置為其它值。 那么-(void)scrollViewDidScroll:里面對(duì)于視圖位置的修改可能就會(huì)出現(xiàn)錯(cuò)誤窜醉。因此我們需要加一些處理:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    CGFloat moveX = scrollView.contentOffset.x - self.bounds.size.width;
    if (fabs(self.lastMoveX)>= self.bounds.size.width) {
        //prevent method scrollViewWillEndDragging: withVelocity: targetContentOffset: reload the image postion, 
        //so we reset scrollview to init postion and return,
        [self resetSubViews];
        self.lastMoveX = 0;
        return;
    }
    
    [self adjustSubViews:moveX];
    
    if (fabs(moveX) >= self.bounds.size.width) {
        [self completedHandler];
    }
    self.lastMoveX = moveX;
}

到這里宪萄,我們使用targetContentOffset來(lái)實(shí)現(xiàn)Paging的效果就結(jié)束了。 我們來(lái)看一下效果對(duì)比:

<center>targetContentOffset</center>




<center>pageEnable</center>

GIF可能不是很明顯榨惰,如果你下載了項(xiàng)目拜英,運(yùn)行起來(lái)。你將會(huì)看到非常明顯的差異

總結(jié)

當(dāng)你同時(shí)在-(void)scrollViewDidScroll:- (void)scrollViewWillEndDragging:withVelocity:targetContentOffset:里面對(duì)contentOffset做修改時(shí)你需要格外小心琅催。因?yàn)镽unLoop的原故,contentOffset檢測(cè)是十分不精確的.在你修改contentOffset時(shí)居凶,-(void)scrollViewDidScroll:會(huì)被反復(fù)調(diào)起。

使用pageEnable實(shí)現(xiàn)分頁(yè)恢暖,你將會(huì)失去:

  • 如絲般的順滑
  • 只能以frame size為單位翻頁(yè)排监,減速阻尼巨大,減速過(guò)程不超過(guò)一頁(yè)
  • 不能自由控制動(dòng)畫(huà)complete與cancel的的滑動(dòng)比例

使用targetContentOffset實(shí)現(xiàn)Paging杰捂,減速過(guò)程流暢。并且可以實(shí)現(xiàn)一屏多頁(yè)的效果棋蚌。大家可以自行發(fā)揮嫁佳。

想體驗(yàn)如絲般順滑請(qǐng)戳這里

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末挨队,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蒿往,更是在濱河造成了極大的恐慌盛垦,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瓤漏,死亡現(xiàn)場(chǎng)離奇詭異腾夯,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)蔬充,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)蝶俱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人饥漫,你說(shuō)我怎么就攤上這事榨呆。” “怎么了庸队?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵积蜻,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我彻消,道長(zhǎng)竿拆,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任宾尚,我火速辦了婚禮如输,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘央勒。我一直安慰自己不见,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布崔步。 她就那樣靜靜地躺著稳吮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪井濒。 梳的紋絲不亂的頭發(fā)上灶似,一...
    開(kāi)封第一講書(shū)人閱讀 49,760評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音瑞你,去河邊找鬼酪惭。 笑死,一個(gè)胖子當(dāng)著我的面吹牛者甲,可吹牛的內(nèi)容都是我干的春感。 我是一名探鬼主播,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼鲫懒!你這毒婦竟也來(lái)了嫩实?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤窥岩,失蹤者是張志新(化名)和其女友劉穎甲献,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體颂翼,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡晃洒,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了朦乏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片球及。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖集歇,靈堂內(nèi)的尸體忽然破棺而出桶略,到底是詐尸還是另有隱情,我是刑警寧澤诲宇,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布际歼,位于F島的核電站,受9級(jí)特大地震影響姑蓝,放射性物質(zhì)發(fā)生泄漏鹅心。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一纺荧、第九天 我趴在偏房一處隱蔽的房頂上張望旭愧。 院中可真熱鬧,春花似錦宙暇、人聲如沸输枯。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)桃熄。三九已至,卻和暖如春型奥,著一層夾襖步出監(jiān)牢的瞬間瞳收,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工厢汹, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留螟深,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓烫葬,卻偏偏與公主長(zhǎng)得像界弧,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

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