仿封面视频详情视频播放大小跟随列表滑动变化

2022-04-13  本文已影响0人  小米咸鱼
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

/// 视图协议
@protocol JYVideoNewsContentViewActionProtocol <NSObject>

/// 头部滑动事件UIPanGestureRecognizer的SEL
/// @param pan 滑动手势
- (void)moveHeaderViewAction:(UIPanGestureRecognizer *)pan;
@end



/// 头部随底部ScrollView滚动变化的视图
@interface JYVideoNewsContentView : UIView<JYVideoNewsContentViewActionProtocol>


/// 头部容器视图
@property(nonatomic,strong,readonly) UIView*         headerContainerView;

/// 头部视图滑动手势是否可用(默认NO)
@property(nonatomic,assign) BOOL                    headerContainViewPanEnable;
/// 初始化
/// @param headerMaxHeight 头部容器最大高度
/// @param headerMinHeight 头部容器最小高度
/// @param scrollView 底部可滚动视图(UIScrollview及子类)
/// @param scrollDelegate 底部可滚动视图代理
- (instancetype)initWithHeaderMaxHeight:(CGFloat)headerMaxHeight headerMinHeight:(CGFloat)headerMinHeight followScrollView:(UIScrollView *)scrollView scrollDelegate:(id<UIScrollViewDelegate>)scrollDelegate;
@end
#import "JYVideoNewsContentView.h"
#import <Masonry.h>
@interface JYVideoNewsContentView()<UIScrollViewDelegate>

@property(nonatomic,strong,readwrite) UIView*         headerContainerView;
@property(nonatomic,assign) CGFloat                  headerMaxHeight;
@property(nonatomic,assign) CGFloat                  headerMinHeight;
@property(nonatomic,strong) UIScrollView*            followScrollView;
@property(nonatomic,assign) id<UIScrollViewDelegate>  scrollViewDelegate;
@property(nonatomic,strong) UIPanGestureRecognizer*   headerPanGest;
@end

@implementation JYVideoNewsContentView


- (instancetype)initWithHeaderMaxHeight:(CGFloat)headerMaxHeight headerMinHeight:(CGFloat)headerMinHeight followScrollView:(UIScrollView *)scrollView scrollDelegate:(id<UIScrollViewDelegate>)scrollDelegate{
    self = [self init];
    if (self) {
        self.headerMaxHeight = headerMaxHeight;
        self.headerMinHeight = headerMinHeight;
        self.followScrollView = scrollView;
        self.scrollViewDelegate = scrollDelegate;
        self.followScrollView.delegate = self;
        [self setupUI];
    }
    return self;
}

- (void)setupUI{
    [self addSubview:self.followScrollView];
    [self addSubview:self.headerContainerView];
    
    [self.headerContainerView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.top.width.mas_equalTo(self);
        make.height.mas_equalTo(self.headerMaxHeight);
    }];
    
    [self.followScrollView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.left.bottom.mas_equalTo(0);
        make.width.mas_equalTo(self);
        make.top.mas_equalTo(0);
    }];
    self.followScrollView.contentInset = UIEdgeInsetsMake(self.headerMaxHeight, 0, 0, 0);
    [self.followScrollView setContentOffset:CGPointMake(0, - self.headerMaxHeight) animated:NO];
}

#pragma mark - 添加头部滑动事件
- (void)moveHeaderViewAction:(UIPanGestureRecognizer *)pan{
    static CGFloat beginOffsetY = 0;
    static BOOL canMove = NO;
    switch (pan.state) {
        case UIGestureRecognizerStateBegan:
            beginOffsetY = self.followScrollView.contentOffset.y;
            canMove = beginOffsetY == - CGRectGetHeight(self.headerContainerView.bounds);
            break;
        case UIGestureRecognizerStateEnded:
            canMove = NO;
        default:
            break;
    }
    
    CGPoint move = [pan translationInView:pan.view];
    CGFloat offsetY = beginOffsetY - move.y;
    if (offsetY >  - self.headerMinHeight) {
        offsetY = - self.headerMinHeight;
    }
    if (offsetY < - self.headerMaxHeight) {
        offsetY = - self.headerMaxHeight;
    }
    if (canMove){
        [self.followScrollView setContentOffset:CGPointMake(0, offsetY) animated:NO];
    }
}

#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    if ([scrollView isEqual:self.followScrollView]) {
        CGFloat height = - scrollView.contentOffset.y;
        if (height > self.headerMaxHeight){
            height = self.headerMaxHeight;
        }
        if (height < self.headerMinHeight && height != 0) {
            height = self.headerMinHeight;
        }
        self.followScrollView.bounces = height <= self.headerMinHeight;
        if (height < self.headerMinHeight || height > self.headerMaxHeight) return;
        [self.headerContainerView mas_updateConstraints:^(MASConstraintMaker *make) {
            make.height.mas_equalTo(height);
        }];
    }
    if ([self.scrollViewDelegate respondsToSelector:@selector(scrollViewDidScroll:)]){
        [self.scrollViewDelegate scrollViewDidScroll:scrollView];
    }
}

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{
    if ([scrollView isEqual:self.followScrollView]){
        //添加快速滑动定位到两端极限功能
        if (velocity.y >= 1 && (CGRectGetHeight(self.headerContainerView.bounds) != self.headerMinHeight)) {
            targetContentOffset->x = 0;
            targetContentOffset->y = -self.headerMinHeight;
        }else if (velocity.y <= -1 && (CGRectGetHeight(self.headerContainerView.bounds) != self.headerMaxHeight)){
            targetContentOffset->x = 0;
            targetContentOffset->y = -self.headerMaxHeight;
            self.followScrollView.bounces = NO;
        }
    }
    if ([self.scrollViewDelegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]){
        [self.scrollViewDelegate scrollViewWillEndDragging:scrollView withVelocity:velocity targetContentOffset:targetContentOffset];
    }
}

#pragma mark - 处理滚动视图代理方法,该视图响应部分代理方法,代理响应所有可响应代理回调
- (BOOL)respondsToSelector:(SEL)aSelector{
    return [super respondsToSelector:aSelector] || [self.scrollViewDelegate respondsToSelector:aSelector];
}

- (id)forwardingTargetForSelector:(SEL)aSelector{
    if ([super respondsToSelector:aSelector]) {
        return self;
    }else{
        return self.scrollViewDelegate;
    }
}

#pragma mark - setter
- (void)setHeaderContainViewPanEnable:(BOOL)headerContainViewPanEnable{
    if (_headerContainViewPanEnable == headerContainViewPanEnable) return;
    if (_headerContainViewPanEnable && !headerContainViewPanEnable) [self.headerContainerView removeGestureRecognizer:self.headerPanGest];
    _headerContainViewPanEnable = headerContainViewPanEnable;
    if (_headerContainViewPanEnable) [self.headerContainerView addGestureRecognizer:self.headerPanGest];
}

#pragma mark - getter
- (UIView *)headerContainerView{
    if (!_headerContainerView) {
        _headerContainerView = [UIView new];
        _headerContainerView.backgroundColor = [UIColor yellowColor];
        }
    return _headerContainerView;
}

- (UIPanGestureRecognizer *)headerPanGest{
    if (!_headerPanGest) {
        _headerPanGest = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(moveHeaderViewAction:)];
    }
    return _headerPanGest;
}
@end

使用方式:

#import "JYVideoNewsViewController.h"
#import <Masonry.h>
#import "JYVideoNewsContentView.h"
#import <ZFPlayer/ZFPlayer.h>
#import <ZFPlayer/ZFPlayerControlView.h>
#import <ZFPlayer/ZFAVPlayerManager.h>


@interface JYVideoNewsViewController ()<UIScrollViewDelegate,UITableViewDelegate,UITableViewDataSource>

@property(nonatomic,strong) UITableView*              tableView;
@property(nonatomic,strong) JYVideoNewsContentView*  contentView;
@property(nonatomic,strong) ZFPlayerController*       player;
@property(nonatomic,strong) ZFPlayerControlView*      controlView;
@end

@implementation JYVideoNewsViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self.view addSubview:self.contentView];
    [self.contentView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.mas_equalTo(self.view);
    }];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        self.player.assetURL = [NSURL URLWithString:@"http://jdyapp.obs.cn-north-1.myhuaweicloud.com/trans/2022/04/08/8a137058-47a1-4dfe-b485-bfe50c1638c5.mp4"];
    });
}

#pragma mark -
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return 20;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"celll"];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"celll"];
    }
    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    
}

- (JYVideoNewsContentView *)contentView{
    if (!_contentView) {
        _contentView = [[JYVideoNewsContentView alloc] initWithHeaderMaxHeight:560 headerMinHeight:200 followScrollView:self.tableView scrollDelegate:self];
    }
    return _contentView;
}

- (UITableView *)tableView{
    if (!_tableView) {
        _tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
        if (@available(iOS 11.0, *)) {
            _tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
        }
        _tableView.dataSource = self;
    }
    return _tableView;
}

- (ZFPlayerController *)player{
    if (!_player) {
        ZFAVPlayerManager* manager = [[ZFAVPlayerManager alloc] init];
        _player = [[ZFPlayerController alloc] initWithPlayerManager:manager containerView:self.contentView.headerContainerView];
        _player.controlView = self.controlView;
    }
    return _player;
}

- (ZFPlayerControlView *)controlView{
    if (!_controlView) {
        _controlView = [ZFPlayerControlView new];
        UIPanGestureRecognizer* gest = [[UIPanGestureRecognizer alloc] initWithTarget:self.contentView action:@selector(moveHeaderViewAction:)];
        [_controlView addGestureRecognizer:gest];
    }
    return _controlView;
}

@end
上一篇下一篇

猜你喜欢

热点阅读