仿封面視頻詳情視頻播放大小跟隨列表滑動(dòng)變化

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

/// 視圖協(xié)議
@protocol JYVideoNewsContentViewActionProtocol <NSObject>

/// 頭部滑動(dòng)事件UIPanGestureRecognizer的SEL
/// @param pan 滑動(dòng)手勢(shì)
- (void)moveHeaderViewAction:(UIPanGestureRecognizer *)pan;
@end



/// 頭部隨底部ScrollView滾動(dòng)變化的視圖
@interface JYVideoNewsContentView : UIView<JYVideoNewsContentViewActionProtocol>


/// 頭部容器視圖
@property(nonatomic,strong,readonly) UIView*         headerContainerView;

/// 頭部視圖滑動(dòng)手勢(shì)是否可用(默認(rèn)NO)
@property(nonatomic,assign) BOOL                    headerContainViewPanEnable;
/// 初始化
/// @param headerMaxHeight 頭部容器最大高度
/// @param headerMinHeight 頭部容器最小高度
/// @param scrollView 底部可滾動(dòng)視圖(UIScrollview及子類)
/// @param scrollDelegate 底部可滾動(dòng)視圖代理
- (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 - 添加頭部滑動(dòng)事件
- (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]){
        //添加快速滑動(dòng)定位到兩端極限功能
        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 - 處理滾動(dòng)視圖代理方法巍举,該視圖響應(yīng)部分代理方法,代理響應(yīng)所有可響應(yīng)代理回調(diào)
- (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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末妖滔,一起剝皮案震驚了整個(gè)濱河市盆昙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌嗜侮,老刑警劉巖港令,帶你破解...
    沈念sama閱讀 211,194評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異锈颗,居然都是意外死亡顷霹,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門击吱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來淋淀,“玉大人,你說我怎么就攤上這事覆醇《浞祝” “怎么了?”我有些...
    開封第一講書人閱讀 156,780評(píng)論 0 346
  • 文/不壞的土叔 我叫張陵永脓,是天一觀的道長(zhǎng)袍辞。 經(jīng)常有香客問我,道長(zhǎng)常摧,這世上最難降的妖魔是什么搅吁? 我笑而不...
    開封第一講書人閱讀 56,388評(píng)論 1 283
  • 正文 為了忘掉前任威创,我火速辦了婚禮,結(jié)果婚禮上谎懦,老公的妹妹穿的比我還像新娘肚豺。我一直安慰自己,他們只是感情好界拦,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評(píng)論 5 384
  • 文/花漫 我一把揭開白布详炬。 她就那樣靜靜地躺著,像睡著了一般寞奸。 火紅的嫁衣襯著肌膚如雪呛谜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,764評(píng)論 1 290
  • 那天枪萄,我揣著相機(jī)與錄音隐岛,去河邊找鬼。 笑死瓷翻,一個(gè)胖子當(dāng)著我的面吹牛聚凹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播齐帚,決...
    沈念sama閱讀 38,907評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼妒牙,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了对妄?” 一聲冷哼從身側(cè)響起湘今,我...
    開封第一講書人閱讀 37,679評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎剪菱,沒想到半個(gè)月后摩瞎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,122評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡孝常,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評(píng)論 2 325
  • 正文 我和宋清朗相戀三年旗们,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片构灸。...
    茶點(diǎn)故事閱讀 38,605評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡上渴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出喜颁,到底是詐尸還是另有隱情稠氮,我是刑警寧澤,帶...
    沈念sama閱讀 34,270評(píng)論 4 329
  • 正文 年R本政府宣布洛巢,位于F島的核電站括袒,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏稿茉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望漓库。 院中可真熱鬧恃慧,春花似錦、人聲如沸渺蒿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽茂装。三九已至怠蹂,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間少态,已是汗流浹背城侧。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留彼妻,地道東北人嫌佑。 一個(gè)月前我還...
    沈念sama閱讀 46,297評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像侨歉,于是被迫代替她去往敵國(guó)和親屋摇。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評(píng)論 2 348

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