Periscope和映客直播页面的综合体

原版Periscope效果图
(颜色比较多所以图片比较大,不过效果还不是很好,建议下个Periscope,开个VPN看看)

本demo效果图:


原文地址
代码地址

实现

手势下拉

模态弹出时需要设置下弹出的模式和风格modalPresentationStyle。是为了回头手势下拉漏出上一个控制器的view时是有内容的,而不会是漆黑一片。因为一个控制器切换到另一个控制器时,系统为了节约资源,会将上一个页面暂时从屏幕上移除,需要的时候再加回来。

LYLiveViewController *liveVC = [LYLiveViewController new];
liveVC.modalPresentationStyle = UIModalPresentationCustom;
[self presentViewController:liveVC animated:YES completion:nil];

那么手势是添加在控制器的view上面吗?如果你观察上面gif,你会发现,在视图下拉的过程中,后面有一层黑色的遮住,松手控制器消失的过程黑色是会有渐变成透明的过程的。所有手势应该加在自定义的view上,回头所以的视图都加在这个创建的view上面。

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor colorWithWhite:0 alpha:0.7];
    
    _containView = [UIView new];
    _containView.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1];
    [_containView addSubview:self.looseCloseTipView];
    [self.view addSubview:_containView];
    
    _panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
    _panGesture.delegate = self;
    [_containView addGestureRecognizer:_panGesture];
}

- (void)viewWillLayoutSubviews {
    [super viewWillLayoutSubviews];
    _containView.frame = self.view.bounds;
}

- (void)handlePanGesture:(UIPanGestureRecognizer *)panGesture {
    CGPoint movePoint = [panGesture translationInView:panGesture.view];
    [panGesture setTranslation:CGPointZero inView:panGesture.view];
    
    if (panGesture.state == UIGestureRecognizerStateBegan) {
        
    } else if (panGesture.state == UIGestureRecognizerStateChanged) {
        _containView.y += movePoint.y / 2.5;
        self.view.backgroundColor = [UIColor colorWithWhite:0 alpha:0.7];
        
        if (_containView.y > 64 && _looseCloseTipView.alpha == 0) {
            [UIView animateWithDuration:0.3 animations:^{
                _looseCloseTipView.alpha = 1;
            }];
        } else if (_containView.y < 64 && _looseCloseTipView.alpha == 1) {
            [UIView animateWithDuration:0.3 animations:^{
                _looseCloseTipView.alpha = 0;
            }];
        } else if (_containView.y < 0) {
            self.view.backgroundColor = [UIColor blackColor];
        }
    } else if (panGesture.state == UIGestureRecognizerStateEnded) {
        if (_containView.y < 0) {
            [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
                _containView.y = 0;
            } completion:^(BOOL finished) {
                
            }];
        } else if (_containView.y > 64) {
            [UIView animateWithDuration:0.5 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
                _containView.y = _containView.height;
                self.view.backgroundColor = [UIColor colorWithWhite:0 alpha:0];
            } completion:^(BOOL finished) {
                [self dismissViewControllerAnimated:NO completion:nil];
            }];
        } else if (_containView.y < 64) {
            [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
                _containView.y = 0;
                self.view.backgroundColor = [UIColor colorWithWhite:0 0.7];
            } completion:^(BOOL finished) {
                
            }];
        }
    }
}

- (UIView *)looseCloseTipView {
    if (!_looseCloseTipView) {
        CGFloat screenWidth =[UIScreen mainScreen].bounds.size.width;
        _looseCloseTipView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, screenWidth, 40)];
        _looseCloseTipView.alpha = 0;
        
        CAGradientLayer *gradient = [CAGradientLayer layer];
        gradient.frame = CGRectMake(0, 0, screenWidth, 40);
        gradient.colors = @[(__bridge id)[UIColor colorWithWhite:0 alpha:0.5].CGColor,
                            (__bridge id)[UIColor colorWithWhite:0 alpha:0.2].CGColor,
                            (__bridge id)[UIColor colorWithWhite:0 alpha:0].CGColor];
        [_looseCloseTipView.layer addSublayer:gradient];
        
        UILabel *label = [[UILabel alloc] initWithFrame:_looseCloseTipView.bounds];
        NSMutableAttributedString *attrString = [[NSMutableAttributedString alloc] initWithString:@"松开以关闭"];
        [attrString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:12.0] range:NSMakeRange(0, 3)];
        [attrString addAttribute:NSFontAttributeName value:[UIFont boldSystemFontOfSize:12] range:NSMakeRange(3, 2)];
        [attrString addAttribute:NSForegroundColorAttributeName value:[UIColor whiteColor] range:NSMakeRange(0, attrString.length)];
        label.attributedText = attrString;
        label.textAlignment = 1;
        [_looseCloseTipView addSubview:label];
    }
    return _looseCloseTipView;
}

手势移动多少,视图并没有跟着移动多少,所以我这边将移动的距离除以2.5,_containView.y += movePoint.y / 2.5;
  如果手势是上拉,则漏出来的是全黑色的,所以在_containView.y < 0控制器的view就设置为黑色。

加载动画

这个一条一条的图,其实只是由一个小图片填充而成的。

CGFloat screenWidth =[UIScreen mainScreen].bounds.size.width;
_loadingView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, screenWidth * 2, 221)];
_loadingView.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:@"loading_pattern_video"]];
_loadingView.layer.anchorPoint = CGPointMake(0, 0);
[_containView addSubview:_loadingView];

动画的过程,其实只是图片向做移动的过程而已。不断的重复动画即可。

CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
animation.repeatCount = MAXFLOAT;
animation.duration = 5;
animation.delegate = self;
animation.removedOnCompletion = YES;
animation.fillMode = kCAFillModeForwards;
animation.values = @[[NSValue valueWithCGPoint:CGPointMake(0, 0)],
                     [NSValue valueWithCGPoint:CGPointMake(-screenWidth, 0)]];
[_loadingView.layer addAnimation:animation forKey:@"animation"];
添加scrollView
- (UIScrollView *)scrollView {
    if (!_scrollView) {
        _scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, kScreenHeight)];
        _scrollView.contentSize = CGSizeMake(kScreenWidth * 2, 0);
        _scrollView.pagingEnabled = YES;
        _scrollView.showsHorizontalScrollIndicator = NO;
        _scrollView.contentOffset = CGPointMake(kScreenWidth, 0);
        
        LYLiveDetailViewController *detailVC = [LYLiveDetailViewController new];
        detailVC.view.frame = CGRectMake(0, 0, kScreenWidth, kScreenHeight);
        [self addChildViewController:detailVC];
        [_scrollView addSubview:detailVC.view];
        
        LYLiveChatViewController *_chatVC = [LYLiveChatViewController new];
        _chatVC.view.frame = CGRectMake(kScreenWidth, 0, kScreenWidth, kScreenHeight);
        [self addChildViewController:_chatVC];
        [_scrollView addSubview:_chatVC.view];
    }
    return _scrollView;
}

scrollView上面添加两个控制器LYLiveDetailViewControllerLYLiveChatViewController,将代码分开管理。LYLiveDetailViewController是管理直播间的详情和直播观众的列表,LYLiveChatViewController是聊天室的一些东西,比如送礼物、点赞动画、发送聊天上面的。

实现LYLiveDetailViewController内容

当tableView在滚动的时候,上面的‘English Chap On Hong Kong Radio!’视图会跟着移动,本文是通过tableView的scrollViewDidScroll:代理,实时的改变它的坐标,来实现粘合的感觉。

#define LYLiveDetailTitleViewHeight 60
#define LYLiveDetailTableY 128
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    CGFloat contentOffsetY = scrollView.contentOffset.y;
    _titleView.y = (contentOffsetY < 0 ? -contentOffsetY : 0) + LYLiveDetailTableY - LYLiveDetailTitleViewHeight;
}

此时效果已经基本实现了,如下图:



  但是会遇到一个问题,当tableView已经滚到下面的时候,想滑动contentInset.Top区域想实现让整个控制器都下移,来实现退出直播的效果,却被tableView给接收了,之前的pan手势没有被激活。那么怎么办?那么只要当tableView滚至底部的时候,点击contentInset.Top的区域tableView不要接收touch就好了,所以咱们要自定义tableView,实现hitTest:withEvent:方法来进行判断。代码量很少,但是却实现了需要的效果,代码如下:

@interface LYLiveDetailTableView : UITableView
@end
@implementation LYLiveDetailTableView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    return point.y < 0 ? nil : [super hitTest:point withEvent:event];
}
@end

早一些假数据后得到下面效果:

上面直播观众的视图有悬浮的效果,并不是通过headerSection来实现的,而是重新创建的视图,在scrollViewDidScroll:方法中控制hidden属性。

实现LYLiveChatViewController内容

这个页面的难点不是很多,都是一些交互的细节,也没啥子好说的了。不过可以说下控制送礼物在屏幕上的数量,观察发现映客上面不管多少个人送礼物,始终只有两个礼物在上面显示


  本demo给到的方案是,当收到礼物消息的时候,不是马上做显示到屏幕上的操作,而是先存到一个数组里面。
  再创建一个定时器,定时去判断两个礼物位置是否正在做动画,如果没有,则从刚刚存礼物消息的数组中取出最先存进去的firstObject礼物消息,做动画操作。
  具体代码看工程里LYLiveGiftBarrageLYLiveGiftBarrageView文件。

END

想讲的说的差不多了,如果有不理解或者更好的建议欢迎评论里沟通。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容