視差化滾動(dòng)cell

首先欣賞下demo的效果圖

大家可以到我的github下載代碼,自己跑一下效果會(huì)更好喲(__)

parallax.gif

我們追求的視差效果

  • 當(dāng)我們的cell滾動(dòng)到屏幕中間的時(shí)候,所展示的圖片也應(yīng)該剛好處于中心
  • 當(dāng)一個(gè)cell從底部滾動(dòng)到頂部的過(guò)程,cell中的圖片位置應(yīng)該從偏高滾動(dòng)偏低(偏高、偏低是針對(duì)圖片自己來(lái)說(shuō)的)

如何去實(shí)現(xiàn)

既然我們要隨著tableView的滾動(dòng)而更新cell中圖片的位置,必然會(huì)使用到tableView的contentOffset,然后為了性能著想,我們更新圖片位置的cell不應(yīng)該是全部,而應(yīng)該是當(dāng)前屏幕中的cell,所以又想到了tableView的visibleCells屬性

Just do it!!!

為了方便移植使用,我創(chuàng)建了基類(lèi)cell ParallaxCell,然后新建一個(gè)cell繼承于它就可以使用視差化子視圖的特性了;

首先看一下ParallaxCell.h文件

//初始化cell的時(shí)候記得先用注冊(cè)喲?。?!且僅針對(duì)cell的高度是一樣的cell
@interface ParallaxCell : UITableViewCell

/**
 *  重置視差化狀態(tài),因?yàn)閏ell的重用機(jī)制,重用之后要刷新?tīng)顟B(tài)
 */
- (void)resetParallaxState;

/**
 *  設(shè)置可視差化的子視圖,以及centerY上偏移的值和centerY下偏移的值
 *
 *  @param view   允許被可視差化的視圖
 *  @param minNum centerY上偏移的值
 *  @param maxNum centerY下偏移的值
 */
- (void)parallaxWithView:(UIView *)view offsetUp:(CGFloat)offsetUp offsetDown:(CGFloat)offsetDown;

/**
 *  通過(guò)傳入scrollview就更可以根據(jù)滾動(dòng)的情況,更新之前需要視差化的視圖的位置
 *
 *  @param scrollView scrollView
 */
- (void)updateViewFrameWithScrollView:(UIScrollView *)scrollView;

再看一下ParallaxCell.m文件

static NSString *const kParallaxView = @"kParallaxView";
static NSString *const kParallaxOriginalCenterY =  @"kParallaxOriginalCenterY";
static NSString *const kParallaxOffsetUp = @"kParallaxOffsetUp";
static NSString *const kParallaxOffsetDown = @"kParallaxOffsetDown";

@interface ParallaxCell ()

@property (nonatomic, strong) NSMutableArray *originalCenterYArray;//用于記錄視差化視圖的原始中心Y值
@property (nonatomic, strong) NSMutableArray *parallaxViewArray;//記錄視差化的視圖
@property (nonatomic, assign) BOOL hasInited;//為了應(yīng)付重用,用于只記錄alloc出來(lái)的,重用出來(lái)的需要重置狀態(tài)

@end

@implementation ParallaxCell

- (void)resetParallaxState
{
self.parallaxViewArray = [NSMutableArray array];
}

- (void)parallaxWithView:(UIView *)view offsetUp:(CGFloat)offsetUp offsetDown:(CGFloat)offsetDown
{
if (!self.hasInited) {
    if (!self.originalCenterYArray) {
        self.originalCenterYArray = [NSMutableArray array];
    }
    [self.originalCenterYArray addObject:@(view.center.y)];
}

NSDictionary *dict = @{kParallaxView : view,
                       kParallaxOriginalCenterY : self.originalCenterYArray[self.parallaxViewArray.count],
                       kParallaxOffsetUp : @(offsetUp),
                       kParallaxOffsetDown : @(offsetDown)};
[self.parallaxViewArray addObject:dict];
}

- (void)updateViewFrameWithScrollView:(UIScrollView *)scrollView
{
self.hasInited = YES;
//(cell的origin.y 加上 一個(gè)cell的高度 減去 當(dāng)前滾動(dòng)的偏移Y值 )除以 (屏幕的高度 加上 一個(gè)cell的高度)這個(gè)下面有畫(huà)圖解釋
CGFloat percent = (self.frame.origin.y + self.frame.size.height - scrollView.contentOffset.y)/([UIScreen mainScreen].bounds.size.height+self.frame.size.height);
[self updateViewFrameWithPercent:percent];
}

- (void)updateViewFrameWithPercent:(CGFloat)percent
{
for (NSInteger index = 0; index < self.parallaxViewArray.count; index ++) {
    NSDictionary *dataDict = self.parallaxViewArray[index];
    UIView *view = dataDict[kParallaxView];
    CGFloat originalCenterY = [dataDict[kParallaxOriginalCenterY] floatValue];
    CGFloat offsetUp = [dataDict[kParallaxOffsetUp] floatValue];
    CGFloat offsetDown = [dataDict[kParallaxOffsetDown] floatValue];
    view.center = CGPointMake(view.center.x, [self interpolateFrom:originalCenterY - offsetUp to:originalCenterY + offsetDown percent:percent]);
}
}

/**
 *  插值計(jì)算(線性),設(shè)置一個(gè)值的起始值與結(jié)束值,然后根據(jù)傳入的百分比返回當(dāng)前對(duì)應(yīng)的值
 *
 *  @param from    起始值
 *  @param to      結(jié)束值
 *  @param percent 百分比
 *
 *  @return 當(dāng)前值
 */
- (CGFloat)interpolateFrom:(CGFloat)from to:(CGFloat)to percent:(CGFloat)percent
{
if (percent > 1) {
    return to;
}
if (percent < 0) {
    return from;
}
return (to - from)*percent + from;
}

@end

最需要注意的地方:

  • 就是cell 的重用機(jī)制,當(dāng)cell從重用隊(duì)列取出cell的時(shí)候,這個(gè)時(shí)候它的狀態(tài)還是之前cell的狀態(tài),比如我們之前對(duì)它圖片的位置進(jìn)行的修改也保留了下來(lái),如果我們重用之后不進(jìn)行重置,就會(huì)以之前的狀態(tài)進(jìn)行處理,這當(dāng)然不是我們想要的;
  • 然后又因?yàn)槲沂褂?code>- (void)parallaxWithView:(UIView *)view offsetUp:(CGFloat)offsetUp offsetDown:為需要視差化的控件,逐一添加,而不是一次性添加所有需要視差化的控件,所以?xún)?nèi)部不好判斷時(shí)機(jī)去重置cell的狀態(tài),所以我就添加了一個(gè)- (void)resetParallaxState方法去重置;
  • 然后就是percent的計(jì)算原理,如下圖所示:
QQ20151214-1.png

cell的位置相對(duì)于視差化范圍height的percent ,和cell中視差化視圖的位置相對(duì)于移動(dòng)范圍的percent是相同的。比如說(shuō)最上面即將離開(kāi)屏幕cell在視差化范圍的percent接近于0,那么cell中的圖片也就處于可移動(dòng)范圍中最偏下的位置;同理,最下面即將進(jìn)入屏幕cell在視差化范圍percent接近于1,那么cell中圖片也就處于可移動(dòng)范圍中最偏上的位置。

自定義cell的使用

自定義一個(gè)cell繼承于ParallaxCell,并且把需要可視差化的控件暴露在.h文件,這里代碼需要注意布局,如果用masonry代碼約束,或者xib約束,可能出現(xiàn)異常;然后就是cell的真實(shí)高度和寬度獲取,在初始化的時(shí)候是獲取不到的,所以布局的時(shí)候使用[UIScreen mainScreen].bounds.size.width來(lái)獲取寬度,高度就類(lèi)方法+ (CGFloat)getHeight來(lái)獲?。黄鋵?shí)我想說(shuō)的是,如果你用ParallaxCell來(lái)實(shí)現(xiàn)視差化,如果出現(xiàn)異常,麻煩看看自定義cell的布局;

ViewController中的使用

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomScrollCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CustomScrollCell" forIndexPath:indexPath];
[cell resetParallaxState];
cell.headerImageView.image = [UIImage imageNamed:[self.imageNameArray objectAtIndex:indexPath.row]];
[cell parallaxWithView:cell.headerImageView offsetUp:50 offsetDown:50];
[cell parallaxWithView:cell.nameLabel offsetUp:10 offsetDown:10];
[cell updateViewFrameWithScrollView:tableView];
return cell;
}

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
for (CustomScrollCell *cell in self.tableView.visibleCells) {
    [cell updateViewFrameWithScrollView:scrollView];
}
}

注意調(diào)用方法順序:

  • 第一要先調(diào)用[cell resetParallaxState];
  • 第二調(diào)用[cell parallaxWithView:cell.headerImageView offsetUp:50 offsetDown:50];
  • 第三調(diào)用[cell updateViewFrameWithScrollView:tableView];
  • cellForRowscrollViewDidScroll方法里面都要調(diào)用[cell updateViewFrameWithScrollView:scrollView];

寫(xiě)在最后

自己參考過(guò)兩個(gè)demo,代碼質(zhì)量與實(shí)現(xiàn)方法有高低之分;而且別人的代碼總有取巧的方法,如果你不能完全明白作者的意圖,可能會(huì)思考一段時(shí)間才會(huì)想通;所以你覺(jué)得不錯(cuò)的,多花點(diǎn)時(shí)間耐心的看下去總會(huì)懂的;
第一個(gè)參考DiceTableViewCell;這個(gè)有點(diǎn)low,不過(guò)至少實(shí)現(xiàn)了一些基本效果但是問(wèn)題很多,可以喵一眼;
第二個(gè)參考MJParallaxCollectionView;我的思路大體和它相同,其中有一些取巧的地方需要自己甄別一下;但是我覺(jué)得它沒(méi)有進(jìn)行封裝,所以進(jìn)行了大量?jī)?yōu)化;
最最后,如果描述中出現(xiàn)錯(cuò)誤,抑或?qū)崿F(xiàn)思路有問(wèn)題,麻煩各位大神多多指正?。。?/p>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫(kù)、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 15,148評(píng)論 4 61
  • 《六項(xiàng)精進(jìn)》打卡第43天 姓名:攸攸 公司:悅美家居旗下北美之家 《六項(xiàng)精進(jìn)》: 226 期 蘇州 感謝一組學(xué)員 ...
    攸攸_b854閱讀 256評(píng)論 0 0
  • 那年去敦煌,騎了半天的駱駝過(guò)了鳴沙山,傍晚,看見(jiàn)那些勞累一天的駱駝,被圍在柵欄里,用很短的繩子套住,只有跪地吃草,...
    枚雨閱讀 384評(píng)論 0 5
  • 我總覺(jué)得愧疚了些。 今天翻出touch,連了網(wǎng),突然看見(jiàn)touch里留存的大量有關(guān)于他的東西。我至今無(wú)法心平氣和地...
    旖旎麥格里波克閱讀 201評(píng)論 0 0
  • 貓咪一改變環(huán)境總是顯得有些膽小,尤其是不僅環(huán)境變了連主人也變了的時(shí)候,這不一不留神就扎床底下不出來(lái)了。還好系了...
    舉步踏輕塵閱讀 235評(píng)論 0 1

友情鏈接更多精彩內(nèi)容