1. 程式人生 > >OCiOS動效設計:UITableView 實現滾動視差效果

OCiOS動效設計:UITableView 實現滾動視差效果

前言

最近在使用‘悅跑圈’這個App,其‘跑鞋展廳’功能的滾動視差效果深深地吸引了我,在網上搜羅了大量的資料,如下,我將仿照該效果簡單介紹實現方法。

效果演示

這裡寫圖片描述

細節實現

OffsetImageCell.m

- (void)cellOffset {
    // 1、獲取cell在螢幕中的rect
    CGRect  centerToWindow = [self convertRect:self.bounds toView:self.window];
    // 2、獲取cell中心點y軸座標
    CGFloat centerY        = CGRectGetMidY(centerToWindow);
    // 3、獲取cell父檢視的中心點
CGPoint windowCenter = self.superview.center; // 4、獲取距離差 CGFloat cellOffsetY = centerY - windowCenter.y; // 5、距離差 / 2倍父檢視高度 CGFloat offsetDig = cellOffsetY / self.superview.frame.size.height * 2; CGFloat offset = -offsetDig * (kScreenHeight * 0.5 - kCellHeight) / 2; CGAffineTransform transY = CGAffineTransformMakeTranslation(0
, offset); self.pictureView.transform = transY; }

viewController.m

- (void)tableView:(UITableView *)tableView willDisplayCell:(OffsetImageCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    // 1、cell 出現時的效果
    // 防止載入時滑動卡頓
    CATransform3D rotation;//3D旋轉

    rotation = CATransform3DMakeTranslation(0
,50 ,20); // rotation = CATransform3DMakeRotation( M_PI_4 , 0.0, 0.7, 0.4); // 逆時針旋轉 rotation = CATransform3DScale(rotation, 0.9, .9, 1); rotation.m34 = 1.0/ -600; cell.layer.shadowColor = [[UIColor blackColor]CGColor]; cell.layer.shadowOffset = CGSizeMake(10, 10); cell.alpha = 0; cell.layer.transform = rotation; [UIView beginAnimations:@"rotation" context:NULL]; //旋轉時間 [UIView setAnimationDuration:0.6]; cell.layer.transform = CATransform3DIdentity; cell.alpha = 1; cell.layer.shadowOffset = CGSizeMake(0, 0); [UIView commitAnimations]; // 2、滾動視差效果實現 [cell cellOffset]; } - (void)tableView:(UITableView *)tableView didEndDisplayingCell:(OffsetImageCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { [cell cancelAnimation]; }

完整實現

Tips

1、圖片素材及描述資訊可自行設定;

2、修改狀態列樣式可參考這篇文章

steps 1:自定義 cell

#import <UIKit/UIKit.h>

@interface OffsetImageCell : UITableViewCell

@property (nonatomic, strong) UIImageView *pictureView;  /**< 圖片檢視 */

@property (nonatomic, strong) UILabel *titleLabel;  /**< 標題文字 */

- (void)cellOffset;  /**< 偏移單元格 */
- (void)cancelAnimation;  /**< 取消動畫 */

@end
#import "OffsetImageCell.h"

// 螢幕高度
#define  kScreenHeight  [UIScreen mainScreen].bounds.size.height
// 螢幕寬度
#define  kScreenWidth   [UIScreen mainScreen].bounds.size.width
// cell高度
#define kCellHeight 250

@implementation OffsetImageCell

#pragma mark - Initialize
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {

        // 1、配置cell屬性
        self.selectionStyle = UITableViewCellSelectionStyleNone;
        self.clipsToBounds = YES;

        // 2、檢視載入
        [self.contentView addSubview:self.pictureView];
        [self.contentView addSubview:self.titleLabel];

        // 3、自定義分割線
        UIView *line = [[UIView alloc] initWithFrame:CGRectMake(0, kCellHeight - 1, kScreenWidth, 1)];
        line.backgroundColor = [UIColor lightGrayColor];
        [self.contentView addSubview:line];
    }
    return self;
}

#pragma mark - Handle offset
- (void)cancelAnimation {

    [self.pictureView.layer removeAllAnimations];

}

- (void)cellOffset {
    // 1、獲取cell在螢幕中的rect
    CGRect  centerToWindow = [self convertRect:self.bounds toView:self.window];
    // 2、獲取cell中心點y軸座標
    CGFloat centerY        = CGRectGetMidY(centerToWindow);
    // 3、獲取cell父檢視的中心點
    CGPoint windowCenter   = self.superview.center;
    // 4、獲取距離差
    CGFloat cellOffsetY = centerY - windowCenter.y;
    // 5、距離差 / 2倍父檢視高度
    CGFloat offsetDig =  cellOffsetY / self.superview.frame.size.height * 2;
    // 6、計算偏移 kScreenHeight * 0.5 為圖片檢視的高度
    CGFloat offset    =  -offsetDig * (kScreenHeight * 0.5 - kCellHeight) / 2;

    CGAffineTransform transY   = CGAffineTransformMakeTranslation(0, offset);
    self.pictureView.transform = transY;
}

#pragma mark - Getters
- (UIImageView *)pictureView {
    if (!_pictureView) {
        _pictureView = [[UIImageView alloc]initWithFrame:CGRectMake(0, - (kScreenHeight * 0.5 - kCellHeight) / 2, kScreenWidth, kScreenHeight * 0.5)];
    }
    return _pictureView;
}


- (UILabel *)titleLabel {
    if (!_titleLabel) {
        _titleLabel = [[UILabel alloc] init];
        _titleLabel.bounds = CGRectMake(0, 0, kScreenWidth, 45);
        _titleLabel.center = CGPointMake(kScreenWidth * 0.5, kCellHeight * 0.5);
        _titleLabel.font = [UIFont boldSystemFontOfSize:25];
        _titleLabel.textColor = [UIColor whiteColor];
        _titleLabel.textAlignment = NSTextAlignmentCenter;
    }
    return _titleLabel;
}

@end

steps 2:ViewController 程式碼實現


#import "ViewController.h"
#import "OffsetImageCell.h"

@interface ViewController () <UITableViewDataSource, UITableViewDelegate>
{
    NSArray *_dataSource;  /**< 資料來源 */
}
@property (nonatomic, strong) UITableView *tableView;  /**< 表格檢視 */

- (void)initializeDataSource; /**< 初始化資料來源 */
- (void)initializeUserInterface; /**< 初始化使用者介面 */

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self initializeDataSource];
    [self initializeUserInterface];
}

#pragma mark - Initialize
- (void)initializeDataSource {
    // 初始化資料來源;
    // 圖片素材可自己準備;
    _dataSource = @[@{@"imgName":@"0.jpg", @"description":@"柯尼塞格"},
                    @{@"imgName":@"1.jpg", @"description":@"布加迪威龍"},
                    @{@"imgName":@"2.jpg", @"description":@"阿斯頓馬丁"},
                    @{@"imgName":@"3.jpg", @"description":@"法拉利"},
                    @{@"imgName":@"4.jpg", @"description":@"邁凱倫"},
                    @{@"imgName":@"5.jpg", @"description":@"蘭博基尼"},
                    @{@"imgName":@"6.jpg", @"description":@"賓士"},
                    @{@"imgName":@"7.jpg", @"description":@"奧迪"},
                    @{@"imgName":@"8.jpg", @"description":@"勞斯萊斯"},
                    @{@"imgName":@"9.jpg", @"description":@"瑪莎拉蒂"}];
}

- (void)initializeUserInterface {
    // 1、關閉系統自動偏移
    self.automaticallyAdjustsScrollViewInsets = NO;

    // 2、載入表格檢視
    [self.view addSubview:self.tableView];
}

#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return _dataSource.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    OffsetImageCell *cell  = [tableView dequeueReusableCellWithIdentifier:@"123" forIndexPath:indexPath];
    cell.pictureView.image = [UIImage imageNamed:_dataSource[indexPath.row][@"imgName"]];
    cell.titleLabel.text   = _dataSource[indexPath.row][@"description"];
    return cell;
}

#pragma mark - UITableViewDelegate

// 將要顯示cell時呼叫
- (void)tableView:(UITableView *)tableView willDisplayCell:(OffsetImageCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
    // 1、cell 出現時的效果
    // 防止載入時滑動卡頓
    CATransform3D rotation;//3D旋轉

    rotation = CATransform3DMakeTranslation(0 ,50 ,20);
    // rotation = CATransform3DMakeRotation( M_PI_4 , 0.0, 0.7, 0.4);
    // 逆時針旋轉

    rotation = CATransform3DScale(rotation, 0.9, .9, 1);

    rotation.m34 = 1.0/ -600;

    cell.layer.shadowColor = [[UIColor blackColor]CGColor];
    cell.layer.shadowOffset = CGSizeMake(10, 10);
    cell.alpha = 0;

    cell.layer.transform = rotation;

    [UIView beginAnimations:@"rotation" context:NULL];
    //旋轉時間
    [UIView setAnimationDuration:0.6];
    cell.layer.transform = CATransform3DIdentity;
    cell.alpha = 1;
    cell.layer.shadowOffset = CGSizeMake(0, 0);
    [UIView commitAnimations];

    // 2、滾動視差效果實現
    [cell cellOffset];
}

// cell顯示完時呼叫
- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(OffsetImageCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
     [cell cancelAnimation];
}

// 設定cell高度
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
    return 250;
}

#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    // 獲取可以見到的 cell,讓圖片在cell座標改變的時候偏移
    NSArray<OffsetImageCell *> *array = [self.tableView visibleCells];
    [array enumerateObjectsUsingBlock:^(OffsetImageCell * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        [obj cellOffset];

    }];

}

#pragma mark - Getters
- (UITableView *)tableView {
    if (!_tableView) {
        _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, CGRectGetHeight(self.view.bounds)) style:UITableViewStylePlain];
        _tableView.dataSource = self;
        _tableView.delegate = self;
        [_tableView registerClass:[OffsetImageCell class] forCellReuseIdentifier:@"123"];
    }
    return _tableView;
}

@end

原始碼地址