如何快速的开发一个完整的iOS直播app(礼物篇)
搭建礼物列表
- 使用modal,设置modal样式为custom,就能做到从小往上显示礼物列表,并且能看见前面的直播界面
- 礼物模型设计
- 一开始创建3个礼物模型,保存到数组,传入给礼物View展示,本来礼物数据应该从服务器获取,这里没做了。
- 到时候拿到礼物View就能拿到对应按钮,传给服务器就好了.
礼物模型设计
礼物模型
- 用户模型(userID,userName),用于标志哪个用户发送,这里为方便测试,保证UserID一样
- 礼物ID(giftID),用于标志当前礼物
- 礼物名称(giftName)
- 礼物总数(giftCount),用于记录礼物连发数,总共发了多少礼物
- 发送礼物的房间Key(roomKey),用于知道是发送个哪个房间
@interface XMGGiftItem : NSObject
// 礼物ID
@property (nonatomic, assign) NSInteger giftId;
// 用户模型:记录哪个用户发送
@property (nonatomic, strong) XMGUserItem *user;
// 礼物名称
@property (nonatomic, strong) XMGUserItem *giftName;
// 礼物个数,用来记录礼物的连击数
@property (nonatomic, assign) NSInteger giftCount;
// 发送哪个房间
@property (nonatomic, strong) NSString *roomKey;
+ (instancetype)giftWithGiftId:(NSInteger)giftId userId:(NSInteger)userId giftCount:(NSInteger)giftCount roomKey:(NSString *)roomKey;
@end
+ (instancetype)giftWithGiftId:(NSInteger)giftId giftCount:(NSInteger)giftCount roomKey:(NSString *)roomKey giftName:(NSString *)giftName
{
XMGGiftItem *item = [[self alloc] init];
item.giftId = giftId;
item.user = [[XMGUserItem alloc] init];
// ID一样,模拟只有一个用户
item.user.id = 1;
item.user.userName = @"用户1";
item.giftCount = giftCount;
item.roomKey = roomKey;
item.giftName = giftName;
return item;
}
监听礼物点击
点击礼物的时候,发送礼物
- 这里使用了websocket搭建的后台服务器,进行礼物发送
// 发送礼物
SocketIOClient *socket = [SocketIOClient shareSocketIOClient];
XMGGiftItem *gift = [XMGGiftItem giftWithGiftId:sender.tag userId:socket.user.id giftCount:1 roomKey:socket.roomKey];
[socket emit:@"gift" with:@[gift.mj_keyValues]];
三、礼物界面监听礼物发送
// 监听礼物
SocketIOClient *socket = [SocketIOClient shareSocketIOClient];
[socket on:@"gift" callback:^(NSArray * _Nonnull data, SocketAckEmitter * _Nonnull ask) {
NSLog(@"接收到礼物%@",data);
XMGGiftItem *item = [XMGGiftItem mj_objectWithKeyValues:data[0]];
// 显示礼物动画
[self setupGiftAnim:item];
}];
四、设置礼物动画
显示礼物业务逻辑
- 1.并不是每次接收到礼物,都需要创建对应礼物动画View,一次最多显示2个礼物View,当执行完一个礼物,就判断是否还有未执行的礼物,继续执行.
- 2.需要搞个礼物队列(
数组
)保存所有需要执行的礼物模型,并不是只保存未执行的礼物模型.- 2.1
什么是需要执行的礼物模型
?每一个需要执行的礼物模型都对应一个礼物View - 2.2 如果只保存未执行的礼物,不记录之前的执行礼物,没法判断下一个礼物是否是连发礼物,因为拿不到之前的做判断。
- 2.3 什么是连发礼物,同一个用户,连续发送相同的礼物。
- 2.4 因此每接收一个新的礼物,需要与之前的礼物对比,是否是同一个人发送的相同礼物。
- 2.1
@property (nonatomic, strong) NSMutableArray *giftQueue; - (NSMutableArray *)giftQueue
{
if (_giftQueue == nil) {
_giftQueue = [NSMutableArray array];
}
return _giftQueue;
}
```
* 3.判断是否是连发礼物
* 3.1 遍历礼物队列中所有礼物,判断当前接收的礼物与之前礼物是否有相同的UserID和相同的礼物ID。
* 3.2 如果有相同的UserID和相同的礼物ID,就表示是连发礼物,,把礼物模型的礼物总数+1.
* 3.3 不需要把连发礼物添加到礼物队列中,因为只要是连发礼物就表示之前已经有相同的礼物,会和之前礼物共用同一个礼物View,不需要创建新的礼物View.
* 3.4 因此只要是连发礼物,就直接return,不做操作.
```
pragma mark - 判断当前接收礼物是否属于连发礼物
(BOOL)isComboGift:(XMGGiftItem *)gift
{
XMGGiftItem *comboGift = nil;for (XMGGiftItem *giftItem in self.giftQueue) {
// 如果是连发礼物就记录下来
if (giftItem.giftId == gift.giftId && giftItem.userId == gift.userId) {
comboGift = giftItem;
}
}
if (comboGift) { // 连发礼物有值
// 礼物模型的礼物总数+1
comboGift.giftCount += 1;
return YES;
}return NO;
}
* 4.如果不是连发礼物,直接把接收到的礼物添加到礼物队列
* 5.搞个数组记录当前显示的动画View
* 5.1 最多显示两个礼物动画View,记录当前正在做动画的View
* 5.2 如果超过2个显示的View,就先不创建礼物View,直接retun@property (nonatomic, strong) NSMutableArray *giftAnimViews;
- (NSMutableArray *)giftAnimViews
{
if (_giftAnimViews == nil) {
_giftAnimViews = [NSMutableArray array];
}
return _giftAnimViews;
}
* 6.过滤掉以上2个条件之后,`处理礼物动画`
* 6.1 创建礼物View
* 6.2 设置礼物View的frame
* 6.2.1 分为上下两部分,先显示到底部,在显示顶部
* 6.2.2 怎么才知道当前礼物View显示在哪部分,搞个位置数组,每次从数组中取出一个位置,取完,就移除,这样下次就不会显示重复的地方了。
* 6.3 添加礼物View到控制器的View中
* 6.4 做礼物平移动画
* 6.5 礼物平移动画做完,开始做连击动画// 处理礼物动画
- (NSMutableArray *)giftAnimViews
(void)handleGiftAnim:(XMGGiftItem *)gift
{
// 1.创建礼物动画的View
XMGGiftAnimView *giftView = [XMGGiftAnimView giftAnimView];CGFloat h = self.view.bounds.size.height * 0.5;
CGFloat w = self.view.bounds.size.width;// 取出礼物位置
id position = self.positions.lastObject;// 从数组移除位置
[self.positions removeObject:position];CGFloat y = [position floatValue] * h;
// 2.设置礼物View的frame
giftView.frame = CGRectMake(0, y, w, h);// 3.传递礼物模型
giftView.gift = gift;// 记录当前位置
giftView.tag = [position floatValue];// 添加礼物View
[self.view addSubview:giftView];// 添加记录礼物View数组
[self.giftAnimViews addObject:giftView];__weak typeof(self) weakSelf = self;
giftView.dismissView = ^(XMGGiftAnimView *giftView){
[weakSelf dismissView:giftView];
};// 设置动画
giftView.transform = CGAffineTransformMakeTranslation(-w, 0);
[UIView animateWithDuration:.25 delay:0 usingSpringWithDamping:0.6 initialSpringVelocity:1 options:UIViewAnimationOptionCurveLinear animations:^{
giftView.transform = CGAffineTransformIdentity;
} completion:^(BOOL finished) {
// 开始连击动画
[giftView startComboAnim];
}];
}
* 7.礼物连击动画
* 7.1 封装到礼物View中,礼物需要拿到礼物连击Label做事情
* 7.2 每隔一段时间,需要修改连击数,搞个定时器,每隔0.3秒做事情
* 7.3 连击动画,也需要控制在0.3秒刚好做完,就能直接做下一次动画。
* 7.4 搞个属性记录当前连击数,没执行一次连击就++,当当前连击数大于礼物总数的时候,表示连击动画执行完毕,需要销毁定时器,销毁当前礼物View
* 7.5 `注意点`:当当前连击数大于礼物总数的时候,不能马上确定连击动画执行完毕,因为电脑执行速度大于用户点击速度,有可能用户在点的时候,没有电脑执行快,电脑执行完直接把礼物View移除了,就看不到连击效果了。
* 7.6 因此需要延迟销毁定时器,而且只要有新的连击数了,需要取消销毁定时器,要不然可能连击数还没显示完,定时器就销毁了- (void)startComboAnim{
if (_timer == nil) {
_timer = [NSTimer scheduledTimerWithTimeInterval:0.3 target:self selector:@selector(combo) userInfo:nil repeats:YES];
_curComboCount = 1;
}
}
// 连击
- (void)startComboAnim{
(void)combo
{
// 当前连发数,已经显示完毕
if (_curComboCount > _gift.giftCount) { // 停止定时器
// 停止定时器
[self performSelector:@selector(cancel) withObject:nil afterDelay:1];
} else {// 修改label显示
_comboView.text = [NSString stringWithFormat:@"x%ld",_curComboCount]; [UIView animateWithDuration:0.15 animations:^{
_comboView.transform = CGAffineTransformMakeScale(3, 3);
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.15 animations:^{
_comboView.transform = CGAffineTransformIdentity;
}];
}]; // 取消定时器销毁
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(cancel) object:nil]; _curComboCount++;
}
}
* 8.连击动画做完,
* 8.1 需要停止定时器
* 8.2 需要移除礼物动画的View
* 8.3 把礼物动画的View和礼物都移除数组,需要回到之前控制器,用Block
* `注意点`:cancel方法可能会调用多次,定时器没有销毁,就会一直调用cancel方法,但是只需要执行一次,需要搞个属性记录下.
* 原因:因为要在1秒之后才会调用cancle,那在这一秒内,肯定又会调用定时器方法,而且这时候当前连击数已经大于礼物总数,就会在1秒内多少执行cancle方法,导致cancle在1秒内调用多次. ```
- (void)cancel
{
if (_isCancel == NO) { _isCancel = YES; [_timer invalidate];
_timer = nil; if (_dismissView) {
_dismissView(self);
}
}
}
``` * 9.连击动画结束后执行的DismissBlock.
* 9.1 做礼物View移除动画,往上移动,透明度为0
* 9.2 把礼物模型从队列移除
* 9.3 把礼物View从显示的礼物View数组移除
* 9.4 移除当前View
* 9.5 恢复位置到位置数组
* 9.3.1 怎么知道恢复哪个位置?可以用礼物View的tag记录当前礼物View的位置
* 9.3.2 如果当前tag为0,需要插入第0个位置,其他情况使用addObject.
* 9.6 当一个礼物做完动画,查看队列中是否还有未执行的礼物。 ```
- (void)dismissView:(XMGGiftAnimView *)giftView
{ [UIView animateWithDuration:0.25 animations:^{ giftView.alpha = 0;
giftView.transform = CGAffineTransformMakeTranslation(0, -20); } completion:^(BOOL finished) { // 移除当前礼物
[self.giftQueue removeObject:giftView.gift]; // 移除当前动画的View
[giftView removeFromSuperview]; // 移除礼物动画View数组
[self.giftAnimViews removeObject:giftView]; // 恢复当前位置
if (giftView.tag == 0) {
// 插入第0个位置
[self.positions insertObject:@(0) atIndex:0];
} else {
[self.positions addObject:@(giftView.tag)];
} // 判断队列中是否还有未处理的礼物
XMGGiftItem *item = [self fetchNoHandleGiftItemOfQueue]; // 处理礼物动画
if (item) {
[self handleGiftAnim:item];
} }]; }
```
* 10.执行完一个礼物,判断礼物队列是否还有未执行的礼物
* 10.1 遍历礼物队列中所有礼物,查看是否有未执行的礼物
* 10.2 取出的礼物,有可能是当前正在执行的礼物,需要排除掉
* 10.2.1 遍历当前正在执行的礼物View,查看取出的礼物是否和它的礼物相同,相同表示当前礼物在执行
* 10.3 获取到未执行的礼物,直接处理礼物
```
// 搜索礼物队列中未执行的礼物
- (XMGGiftItem *)fetchNoHandleGiftItemOfQueue
{ // 取出队列中的礼物
for (XMGGiftItem *item in self.giftQueue) {
// 当前礼物模型有可能在执行
if (![self isExcutingGift:item]) return item;
} return nil;
} // 判断当前礼物是否正在执行
- (BOOL)isExcutingGift:(XMGGiftItem *)gift
{
// 判断当前模型是否已经在执行,执行就不需要在做动画
for (XMGGiftAnimView *giftView in self.giftAnimViews) { if (giftView.gift == gift) return YES;
} return NO;
} ```
礼物整体业务逻辑
- (void)setupGiftAnim:(XMGGiftItem *)gift
{
// 1.判断当前接收的礼物是否属于连发礼物 属于直接return,不需要在重新创建新的动画View
if ([self isComboGift:gift]) return; // 2.添加到礼物队列
[self.giftQueue addObject:gift]; // 3.判断当前显示多少个礼物动画View
if (self.giftAnimViews.count == 2) return; // 4.处理礼物动画
[self handleGiftAnim:gift];
}
如何快速的开发一个完整的iOS直播app(礼物篇)的更多相关文章
- 如何快速的开发一个完整的iOS直播app(美颜篇)
前言 在看这篇之前,如果您还不了解直播原理,请查看这篇文章如何快速的开发一个完整的iOS直播app(原理篇) 开发一款直播app,美颜功能是很重要的,如果没有美颜功能,可能分分钟钟掉粉千万,本篇主要讲 ...
- 如何快速的开发一个完整的iOS直播app(原理篇)
目录 [如何快速的开发一个完整的iOS直播app](原理篇) [如何快速的开发一个完整的iOS直播app](播放篇) [如何快速的开发一个完整的iOS直播app](采集篇) 前言 大半年没写博客了,但 ...
- 【如何快速的开发一个完整的iOS直播app】(美颜篇)
原文转自:袁峥Seemygo 感谢分享.自我学习 前言 在看这篇之前,如果您还不了解直播原理,请查看这篇文章如何快速的开发一个完整的iOS直播app(原理篇) 开发一款直播app,美颜功能是很重 ...
- 【如何快速的开发一个完整的iOS直播app】(采集篇)
原文转自:袁峥Seemygo 感谢分享.自我学习 前言 在看这篇之前,如果您还不了解直播原理,请查看这篇文章如何快速的开发一个完整的iOS直播app(原理篇) 开发一款直播app,首先需要采集主 ...
- 【如何快速的开发一个完整的iOS直播app】(播放篇)
原文转自:袁峥Seemygo 感谢分享.自我学习 前言 在看这篇之前,如果您还不了解直播原理,请查看上篇文章如何快速的开发一个完整的iOS直播app(原理篇) 开发一款直播app,集成ijkpl ...
- 【如何快速的开发一个完整的iOS直播app】(推流篇)
前言 在看这篇之前,如果您还不了解直播原理,请查看这篇文章如何快速的开发一个完整的iOS直播app(原理篇) 开发一款直播app,肯定需要流媒体服务器,本篇主要讲解直播中流媒体服务器搭建,并且讲解了如 ...
- 【如何快速的开发一个完整的 iOS 直播 app】(美颜篇)
来源:袁峥Seemygo 链接:http://www.jianshu.com/p/4646894245ba 前言 在看这篇之前,如果您还不了解直播原理,请查看这篇文章如何快速的开发一个完整的iOS直播 ...
- 【如何快速的开发一个完整的iOS直播app】(原理篇)
原文转自:袁峥Seemygo 感谢分享.自我学习 目录 [如何快速的开发一个完整的iOS直播app](原理篇) [如何快速的开发一个完整的iOS直播app](播放篇) [如何快速的开发一个完整的 ...
- 【如何快速的开发一个简单的iOS直播app】(代码篇)
开篇([如何快速的开发一个完整的iOS直播app](原理篇)) 好久没写简书,因为好奇的我跑去学习直播了,今天就分享一下我的感慨. 目前为止直播还是比较热点的技术的,简书,git上有几篇阅读量和含金量 ...
- 抖音短视频爆火的背后到底是什么——如何快速的开发一个完整的直播app
前言 今年移动直播行业的兴起,诞生了一大批网红,甚至明星也开始直播了,因此不得不跟上时代的步伐,由于第一次接触的原因,因此花了很多时间了解直播,今天我来教你从零开始搭建一个完整的直播app,希望能帮助 ...
随机推荐
- Sealos 基础教程:Sealos Devbox 的架构原理解析
今天这篇文章咱们来聊一聊 Sealos Devbox 到底是怎么设计的,据说隔壁老奶奶最喜欢看这种有技术深度的文章了. Devbox 返璞归真,把开发者的开发精力放到开发中去,真正做到了摈弃复杂的 C ...
- Matlab矩阵运算的硬件资源分析
在用Matlab创建矩阵并计算矩阵乘法运算时,要注意计算机的可用内存大小(空间资源)和CPU性能(影响所用计算时间). 例如我们做以下测试:n为qubit的数目,那么一个矩阵Matrix_A=rand ...
- DDCA —— 缓存(Cache):缓存体系结构、缓存操作
1. 存储器层次(The Memory Hierarchy) 1.1 现代系统中的存储器 其中包括L1.L2.L3和DRAM 1.2 存储器的局限 理想存储器的需求如下: 零延迟 容量无限 零成本 带 ...
- Codeforces 909 A-F
CF909 题解 题目链接 A B C D E F 难度:红 黄 绿 蓝 绿 紫 题解 A 题目翻译:给定两个字符串,求字典序最小的"两字符串非空前缀拼接形成的字符串". 算法标签 ...
- 低功耗4G模组Air780E快速入门:使用文件系统存储温湿度数据
伙伴们,今天我们来学习合宙低功耗4G模组Air780E快速入门之使用文件系统存储温湿度数据. 一.编写脚本 1.1 准备资料 780E开发板购买链接 780E开发板设计资料 LuatOS-Air7 ...
- Air201资产定位模组LuatOS:录音&播放录音功能的操作方法
一直有小伙伴们问,迷你小巧的合宙Air201虽然有很多优点,超低功耗.精准定位,那么它是否支持录音.播放录音功能? 那必须能!高集成化设计的Air201自带了ES8311音频解码芯片(Audio ...
- 微信小程序原生AI运动(动作)检测识别解决方案
前几年受疫情影响,人员流动受限,反而让"AI运动"概念风靡一时.空前火爆.目前已经在AI运动锻炼.体育教学.线上运动主题活动等场景中,成功得到了应用,并获得了广大互联网用户的认可. ...
- apisix lua插件开发
1. 怎么定义ngx自定义变量 ngx.var.custom_var nginx_config: # config for render the template to generate nginx. ...
- golang之协程+chan通道
[管道] 分为 有缓冲和无缓冲两种 无缓冲的与有缓冲channel有着重大差别,那就是一个是同步的 一个是非同步的. 比如: c1:=make(chan int) 无缓冲 c2:=make(chan ...
- getent使用小结
转载请注明出处: getent 是一个用于访问系统数据库的命令,通常用于获取与网络有关的信息,比如用户.组.主机名.服务等.这个命令是 Linux 和 Unix 系统中非常有用的工具,可以用来查询多种 ...