【如何快速的開發一個完整的iOS直播app】(禮物篇)
搭建禮物列表
使用modal,設定modal樣式為custom,就能做到從小往上顯示禮物列表,並且能看見前面的直播介面
禮物模型設計
一開始建立3個禮物模型,儲存到陣列,傳入給禮物View展示,本來禮物資料應該從伺服器獲取,這裡沒做了。
到時候拿到禮物View就能拿到對應按鈕,傳給伺服器就好了.
禮物模型設計
禮物模型
使用者模型(userID,userName),用於標誌哪個使用者傳送,這裡為方便測試,保證UserID一樣
禮物ID(giftID),用於標誌當前禮物
禮物名稱(giftName)
禮物總數(giftCount),用於記錄禮物連發數,總共發了多少禮物
傳送禮物的房間Key(roomKey),用於知道是傳送個哪個房間
@interfaceXMGGiftItem:NSObject
// 禮物ID
@property(nonatomic,assign)NSInteger giftId;
// 使用者模型:記錄哪個使用者傳送
@property(nonatomic,strong) XMGUserItem *user;// 禮物名稱
@property(nonatomic,strong) XMGUserItem *giftName;// 禮物個數,用來記錄禮物的連擊數
@property(nonatomic,assign)NSIntegergiftCount;// 傳送哪個房間@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 = [[selfalloc] init];
item.giftId = giftId;
item.user = [[XMGUserItem alloc] init];// ID一樣,模擬只有一個使用者item.user.id =1;
item.user.userName [email protected]"使用者1";
item.giftCount = giftCount;
item.roomKey = roomKey;
item.giftName = giftName;returnitem;
}
監聽禮物點選
點選禮物的時候,傳送禮物
這裡使用了websocket搭建的後臺伺服器,進行禮物傳送
// 傳送禮物
SocketIOClient *socket = [SocketIOClient shareSocketIOClient];
XMGGiftItem *gift = [XMGGiftItemgiftWithGiftId:sender.taguserId:socket.user.idgiftCount:1roomKey:socket.roomKey]; [socketemit:@"gift"with:@[gift.mj_keyValues]];
三、禮物介面監聽禮物傳送
// 監聽禮物
SocketIOClient *socket = [SocketIOClient shareSocketIOClient];
[socketon:@"gift"callback:^(NSArray * _Nonnull data, SocketAckEmitter * _Nonnull ask) {
NSLog(@"接收到禮物%@",data);
XMGGiftItem *item = [XMGGiftItemmj_objectWithKeyValues:data[0]];
// 顯示禮物動畫
[selfsetupGiftAnim:item];
}];
四、設定禮物動畫
顯示禮物業務邏輯
1.並不是每次接收到禮物,都需要建立對應禮物動畫View,一次最多顯示2個禮物View,當執行完一個禮物,就判斷是否還有未執行的禮物,繼續執行.
2.需要搞個禮物佇列(陣列)儲存所有需要執行的禮物模型,並不是只儲存未執行的禮物模型.
2.1什麼是需要執行的禮物模型?每一個需要執行的禮物模型都對應一個禮物View
2.2 如果只儲存未執行的禮物,不記錄之前的執行禮物,沒法判斷下一個禮物是否是連發禮物,因為拿不到之前的做判斷。
2.3 什麼是連發禮物,同一個使用者,連續傳送相同的禮物。
2.4 因此每接收一個新的禮物,需要與之前的禮物對比,是否是同一個人傳送的相同禮物。
@property(nonatomic,strong)NSMutableArray*giftQueue;
- (NSMutableArray*)giftQueue{
if(_giftQueue ==nil) {
_giftQueue = [NSMutableArrayarray];
}
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
{
XMGGiftItemcomboGift = nil;
for (XMGGiftItem *giftItem in self.giftQueue) {
// 如果是連發禮物就記錄下來if(giftItem.giftId== gift.giftId && giftItem.userId== gift.userId) {comboGift= giftItem; }
}
if (comboGift) { // 連發禮物有值
// 禮物模型的禮物總數+1comboGift.giftCount +=1;returnYES;
}
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 禮物平移動畫做完,開始做連擊動畫
```
// 處理禮物動畫
(void)handleGiftAnim:(XMGGiftItem)gift
{
// 1.建立禮物動畫的View
XMGGiftAnimViewgiftView = [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)combo
{
// 當前連發數,已經顯示完畢
if (_curComboCount > _gift.giftCount) { // 停止定時器
// 停止定時器[selfperformSelector:@selector(cancel)withObject:nilafterDelay:1];
} else {
// 修改label顯示_comboView.text = [NSStringstringWithFormat:@"x%ld",_curComboCount]; [UIViewanimateWithDuration:0.15animations:^{ _comboView.transform = CGAffineTransformMakeScale(3,3); }completion:^(BOOL finished) { [UIViewanimateWithDuration:0.15animations:^{ _comboView.transform = CGAffineTransformIdentity; }]; }];// 取消定時器銷燬[NSObjectcancelPreviousPerformRequestsWithTarget:selfselector:@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 {
[UIViewanimateWithDuration:0.25animations:^{
giftView.alpha =0;
giftView.transform =CGAffineTransformMakeTranslation(0,-20);
} completion:^(BOOLfinished) {
// 移除當前禮物
[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 = [selffetchNoHandleGiftItemOfQueue];
// 處理禮物動畫
if(item) {
[selfhandleGiftAnim:item];
}
}];
} ```
*10.執行完一個禮物,判斷禮物佇列是否還有未執行的禮物 *10.1遍歷禮物佇列中所有禮物,檢視是否有未執行的禮物 *10.2取出的禮物,有可能是當前正在執行的禮物,需要排除掉 *10.2.1遍歷當前正在執行的禮物View,檢視取出的禮物是否和它的禮物相同,相同表示當前禮物在執行 *10.3獲取到未執行的禮物,直接處理禮物 ```// 搜尋禮物佇列中未執行的禮物
- (XMGGiftItem *)fetchNoHandleGiftItemOfQueue
{
// 取出佇列中的禮物
for(
XMGGiftItem *iteminself.giftQueue)
{
// 當前禮物模型有可能在執行if(![selfisExcutingGift:item])returnitem;
}
returnnil;
}// 判斷當前禮物是否正在執行
- (BOOL)isExcutingGift:(XMGGiftItem *)gift
{
// 判斷當前模型是否已經在執行,執行就不需要在做動畫
for(XMGGiftAnimView *giftViewinself.giftAnimViews) {
if(giftView.gift == gift)returnYES;
}
returnNO;
} ```
禮物整體業務邏輯
- (void)setupGiftAnim:(XMGGiftItem *)gift {
//1.判斷當前接收的禮物是否屬於連發禮物 屬於直接return,不需要在重新建立新的動畫Viewif([selfisComboGift:gift])return;
//2.新增到禮物佇列
[self.giftQueueaddObject:gift];
//3.判斷當前顯示多少個禮物動畫
Viewif(self.giftAnimViews.count ==2)
return;
//4.處理禮物動畫
[selfhandleGiftAnim:gift];
}