iOS 实现类似抖音滚动效果

效果图

思路

整体上我们使用tableView实现,为了预留内容的缓冲,我们将tableView 的contentinset设置为上面一个屏幕的高度,下面一个屏幕的高度,左右为0,这样保证我们滚动过去的时候

都是准备好的内容

然后就是滑动效果的实现了,主要就是我们在scrollViewWillEndDragging方法中获取到停止拖动(手指离开)时候的速度。 在scrollViewDidEndDragging 方法中

通过translationInView方法判断当前滑动的方向,

然后刚才获取到的速度就派上用场了,当我们手指离开时候的速度大于0.4的时候,我们切换页面的临界偏移量就是8 ,否则临界偏移量就是60, 同时,通过

CGPoint translatedPoint = scrollView.panGestureRecognizer translationInView:scrollView;判断translatedPoint.y我们可以

判断滚动的方向,判断出方向之后,

使用UIView animateWithDuration动画快速翻页

代码

复制代码
//
//  ViewController.m
//  LBDouyinScroll
//
//  Created by mac on 2024/6/26.
//

#import "ViewController.h"
#import "DouyinScrollTableViewCell.h"

#define kScreenWidth  [UIScreen mainScreen].bounds.size.width
#define kScreenHeight [UIScreen mainScreen].bounds.size.height

@interface ViewController ()
@property (nonatomic, strong) UITableView *tableView;

@property (nonatomic, assign) NSInteger currentIndex;

@property (nonatomic, assign) CGFloat velocity;

@property (nonatomic, strong) NSMutableArray *colorArray;

@end

@implementation ViewController

- (BOOL)prefersStatusBarHidden
{
    return YES;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self.view addSubview:self.tableView];
    self.colorArray = [NSMutableArray array];
    for (int i = 0; i < 10; i ++) {
        int r = arc4random() % 255;
        int g = arc4random() % 255;
        int b = arc4random() % 255;
        CGFloat rr = r / 255.0;
        CGFloat rg = g / 255.0;
        CGFloat rb = b / 255.0;
        UIColor *color = [[UIColor alloc]initWithRed:rr green:rg blue:rb alpha:1];
        [self.colorArray addObject:color];
    }
    [self.tableView reloadData];
    // Do any additional setup after loading the view.
}

#pragma mark - UITableViewDelegate, UITableViewDataSource

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    DouyinScrollTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([DouyinScrollTableViewCell class])];
    [cell updateWithColor:self.colorArray[indexPath.row]];
    //    cell.textLabel.text = [NSString stringWithFormat:@"%ld",indexPath.row];
    //    cell.backgroundColor = self.colorArray[indexPath.row];
    //    if (!cell.contentView.backgroundColor) {
    //        cell.contentView.backgroundColor = self.colorArray[indexPath.row];
    //    }
    //    return cell;
    return cell;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return 10;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return kScreenHeight;
}

#pragma mark - scrolllVIewDelegate

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    
}

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
    self.velocity = velocity.y;
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
    dispatch_async(dispatch_get_main_queue(), ^{
        CGPoint translatedPoint = [scrollView.panGestureRecognizer translationInView:scrollView];
        //UITableView禁止响应其他滑动手势
        scrollView.panGestureRecognizer.enabled = NO;
        CGFloat translateCheck = 60;
        if (fabs(self.velocity) > 0.4) {
            translateCheck = 8;
        }
        if(translatedPoint.y < -translateCheck && self.currentIndex < 10) {
            self.currentIndex ++;   //向下滑动索引递增
        }
        if(translatedPoint.y > translateCheck && self.currentIndex > 0) {
            self.currentIndex --;   //向上滑动索引递减
        }
        
        if (self.currentIndex == 10) {
        } else {
            
            [UIView animateWithDuration:0.15
                                  delay:0.0
                                options:UIViewAnimationOptionCurveEaseOut animations:^{
                //UITableView滑动到指定cell
                [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:self.currentIndex inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
            } completion:^(BOOL finished) {
                //UITableView可以响应其他滑动手势
                scrollView.panGestureRecognizer.enabled = YES;
            }];
        }
        
    });
}

#pragma - private

- (void)animationToIndex:(NSInteger)index
{
    [UIView animateWithDuration:0.2 delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
        self.tableView.contentOffset = CGPointMake(0, kScreenHeight * index);
    } completion:^(BOOL finished) {
        
    }];
}

#pragma mark - lazy load

- (UITableView *)tableView
{
    if (!_tableView) {
        _tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, - kScreenHeight, CGRectGetWidth(self.view.bounds), kScreenHeight * 3) style:UITableViewStylePlain];
        [_tableView registerClass:[DouyinScrollTableViewCell class] forCellReuseIdentifier:NSStringFromClass([DouyinScrollTableViewCell class])];
        _tableView.rowHeight = kScreenHeight;
        _tableView.contentInset = UIEdgeInsetsMake(kScreenHeight , 0, kScreenHeight, 0);
        _tableView.estimatedRowHeight = kScreenHeight;
        _tableView.delegate = self;
        _tableView.dataSource = self;
        _tableView.backgroundColor = [UIColor redColor];
        _tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
        _tableView.separatorInset = UIEdgeInsetsZero;
        _tableView.decelerationRate = UIScrollViewDecelerationRateFast;
    }
    return _tableView;
}


@end

其中最关键的就是下面的

复制代码
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
    self.velocity = velocity.y;
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
    dispatch_async(dispatch_get_main_queue(), ^{
        CGPoint translatedPoint = [scrollView.panGestureRecognizer translationInView:scrollView];
        //UITableView禁止响应其他滑动手势
        scrollView.panGestureRecognizer.enabled = NO;
        CGFloat translateCheck = 60;
        if (fabs(self.velocity) > 0.4) {
            translateCheck = 8;
        }
        if(translatedPoint.y < -translateCheck && self.currentIndex < 10) {
            self.currentIndex ++;   //向下滑动索引递增
        }
        if(translatedPoint.y > translateCheck && self.currentIndex > 0) {
            self.currentIndex --;   //向上滑动索引递减
        }
        
        if (self.currentIndex == 10) {
        } else {
            
            [UIView animateWithDuration:0.15
                                  delay:0.0
                                options:UIViewAnimationOptionCurveEaseOut animations:^{
                //UITableView滑动到指定cell
                [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:self.currentIndex inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO];
            } completion:^(BOOL finished) {
                //UITableView可以响应其他滑动手势
                scrollView.panGestureRecognizer.enabled = YES;
            }];
        }
        
    });
}

demo: link

相关推荐
Privasa-隐私实验室7 小时前
跨端技术选型|Flutter搭建隐私加密App,安卓与iOS双端差异适配实战
android·笔记·安全·flutter·ios·隐私安全·aes-256
sakiko_1 天前
OC基础语法(与Swift对比)-1
开发语言·ios·objective-c·swift
未来猫咪花1 天前
Everything is ViewModel:让状态管理回到对象世界
android·flutter·ios
Crazy_MT1 天前
不越狱,iPhone 为什么也能安装任意 IPA?聊聊 iLoader、SideStore 和 LiveContainer 的原理
ios·开源
JoyCong19981 天前
从iPhone Ultra到ToDesk:折叠大屏时代的效率革命,硬件只是开始
大数据·运维·ios·智能手机·iphone·远程工作·远程操作
2501_916007471 天前
Flutter与游戏App加固避坑指南:如何为混合开发与高交互应用选对安全方案?
安全·flutter·游戏·ios·小程序·uni-app·iphone
2501_916008891 天前
Flutter 开发 iOS,项目打包成 IPA,命令与免 Xcode两种方法
ide·vscode·ios·objective-c·个人开发·swift·敏捷流程
SameX2 天前
iOS 独立开发者转鸿蒙:我把足迹地图 App 迁移到 ArkTS 的全过程
ios
sakiko_3 天前
Swift学习笔记42-SwiftUI的属性包装器(讲解+面试)
笔记·学习·ios·swiftui·swift