#import
NS_ASSUME_NONNULL_BEGIN
/// 视图协议
@protocol JYVideoNewsContentViewActionProtocol
/// 头部滑动事件UIPanGestureRecognizer的SEL
/// @param pan 滑动手势
- (void)moveHeaderViewAction:(UIPanGestureRecognizer *)pan;
@end
/// 头部随底部ScrollView滚动变化的视图
@interface JYVideoNewsContentView : UIView
/// 头部容器视图
@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)scrollDelegate;
@end
#import "JYVideoNewsContentView.h"
#import
@interface JYVideoNewsContentView()
@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 scrollViewDelegate;
@property(nonatomic,strong) UIPanGestureRecognizer* headerPanGest;
@end
@implementation JYVideoNewsContentView
- (instancetype)initWithHeaderMaxHeight:(CGFloat)headerMaxHeight headerMinHeight:(CGFloat)headerMinHeight followScrollView:(UIScrollView *)scrollView scrollDelegate:(id)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
#import "JYVideoNewsContentView.h"
#import
#import
#import
@interface JYVideoNewsViewController ()
@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