You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
582 lines
22 KiB
582 lines
22 KiB
4 years ago
|
//
|
||
|
// OCBarrageContentView.m
|
||
|
// TestApp
|
||
|
//
|
||
|
// Created by QMTV on 2017/8/22.
|
||
|
// Copyright © 2017年 LFC. All rights reserved.
|
||
|
//
|
||
|
|
||
|
#define kNextAvailableTimeKey(identifier, index) [NSString stringWithFormat:@"%@_%d", identifier, index]
|
||
|
|
||
|
#import "OCBarrageRenderView.h"
|
||
|
#import "OCBarrageTrackInfo.h"
|
||
|
|
||
|
@implementation OCBarrageRenderView
|
||
|
|
||
|
- (void)dealloc {
|
||
|
NSLog(@"%s", __func__);
|
||
|
}
|
||
|
|
||
|
- (instancetype)init {
|
||
|
self = [super init];
|
||
|
if (self) {
|
||
|
_animatingCellsLock = dispatch_semaphore_create(1);
|
||
|
_idleCellsLock = dispatch_semaphore_create(1);
|
||
|
_trackInfoLock = dispatch_semaphore_create(1);
|
||
|
_lowPositionView = [[UIView alloc] init];
|
||
|
[self addSubview:_lowPositionView];
|
||
|
_middlePositionView = [[UIView alloc] init];
|
||
|
[self addSubview:_middlePositionView];
|
||
|
_highPositionView = [[UIView alloc] init];
|
||
|
[self addSubview:_highPositionView];
|
||
|
_veryHighPositionView = [[UIView alloc] init];
|
||
|
[self addSubview:_veryHighPositionView];
|
||
|
self.layer.masksToBounds = YES;
|
||
|
_trackNextAvailableTime = [NSMutableDictionary dictionary];
|
||
|
}
|
||
|
|
||
|
return self;
|
||
|
}
|
||
|
|
||
|
- (nullable OCBarrageCell *)dequeueReusableCellWithClass:(Class)barrageCellClass {
|
||
|
OCBarrageCell *barrageCell = nil;
|
||
|
|
||
|
dispatch_semaphore_wait(_idleCellsLock, DISPATCH_TIME_FOREVER);
|
||
|
for (OCBarrageCell *cell in self.idleCells) {
|
||
|
if ([NSStringFromClass([cell class]) isEqualToString:NSStringFromClass(barrageCellClass)]) {
|
||
|
barrageCell = cell;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (barrageCell) {
|
||
|
[self.idleCells removeObject:barrageCell];
|
||
|
barrageCell.idleTime = 0.0;
|
||
|
} else {
|
||
|
barrageCell = [self newCellWithClass:barrageCellClass];
|
||
|
}
|
||
|
dispatch_semaphore_signal(_idleCellsLock);
|
||
|
if (![barrageCell isKindOfClass:[OCBarrageCell class]]) {
|
||
|
return nil;
|
||
|
}
|
||
|
|
||
|
return barrageCell;
|
||
|
}
|
||
|
|
||
|
- (OCBarrageCell *)newCellWithClass:(Class)barrageCellClass {
|
||
|
OCBarrageCell *barrageCell = [[barrageCellClass alloc] init];
|
||
|
if (![barrageCell isKindOfClass:[OCBarrageCell class]]) {
|
||
|
return nil;
|
||
|
}
|
||
|
|
||
|
return barrageCell;
|
||
|
}
|
||
|
|
||
|
- (void)start {
|
||
|
switch (self.renderStatus) {
|
||
|
case OCBarrageRenderStarted: {
|
||
|
return;
|
||
|
}
|
||
|
break;
|
||
|
case OCBarrageRenderPaused: {
|
||
|
[self resume];
|
||
|
return;
|
||
|
}
|
||
|
break;
|
||
|
default: {
|
||
|
_renderStatus = OCBarrageRenderStarted;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- (void)pause {
|
||
|
switch (self.renderStatus) {
|
||
|
case OCBarrageRenderStarted: {
|
||
|
_renderStatus = OCBarrageRenderPaused;
|
||
|
}
|
||
|
break;
|
||
|
case OCBarrageRenderPaused: {
|
||
|
return;
|
||
|
}
|
||
|
break;
|
||
|
default: {
|
||
|
return;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
dispatch_semaphore_wait(_animatingCellsLock, DISPATCH_TIME_FOREVER);
|
||
|
NSEnumerator *enumerator = [self.animatingCells reverseObjectEnumerator];
|
||
|
OCBarrageCell *cell = nil;
|
||
|
while (cell = [enumerator nextObject]){
|
||
|
CFTimeInterval pausedTime = [cell.layer convertTime:CACurrentMediaTime() fromLayer:nil];
|
||
|
cell.layer.speed = 0.0;
|
||
|
cell.layer.timeOffset = pausedTime;
|
||
|
}
|
||
|
dispatch_semaphore_signal(_animatingCellsLock);
|
||
|
}
|
||
|
|
||
|
- (void)resume {
|
||
|
switch (self.renderStatus) {
|
||
|
case OCBarrageRenderStarted: {
|
||
|
return;
|
||
|
}
|
||
|
break;
|
||
|
case OCBarrageRenderPaused: {
|
||
|
_renderStatus = OCBarrageRenderStarted;
|
||
|
}
|
||
|
break;
|
||
|
default: {
|
||
|
return;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
dispatch_semaphore_wait(_animatingCellsLock, DISPATCH_TIME_FOREVER);
|
||
|
NSEnumerator *enumerator = [self.animatingCells reverseObjectEnumerator];
|
||
|
OCBarrageCell *cell = nil;
|
||
|
while (cell = [enumerator nextObject]){
|
||
|
CFTimeInterval pausedTime = cell.layer.timeOffset;
|
||
|
cell.layer.speed = 1.0;
|
||
|
cell.layer.timeOffset = 0.0;
|
||
|
cell.layer.beginTime = 0.0;
|
||
|
CFTimeInterval timeSincePause = [cell.layer convertTime:CACurrentMediaTime() fromLayer:nil] - pausedTime;
|
||
|
cell.layer.beginTime = timeSincePause;
|
||
|
}
|
||
|
dispatch_semaphore_signal(_animatingCellsLock);
|
||
|
}
|
||
|
|
||
|
- (void)stop {
|
||
|
switch (self.renderStatus) {
|
||
|
case OCBarrageRenderStarted: {
|
||
|
_renderStatus = OCBarrageRenderStoped;
|
||
|
}
|
||
|
break;
|
||
|
case OCBarrageRenderPaused: {
|
||
|
_renderStatus = OCBarrageRenderStoped;
|
||
|
}
|
||
|
break;
|
||
|
default: {
|
||
|
return;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (_autoClear) {
|
||
|
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(clearIdleCells) object:nil];
|
||
|
}
|
||
|
|
||
|
dispatch_semaphore_wait(_animatingCellsLock, DISPATCH_TIME_FOREVER);
|
||
|
NSEnumerator *animatingEnumerator = [self.animatingCells reverseObjectEnumerator];
|
||
|
OCBarrageCell *animatingCell = nil;
|
||
|
while (animatingCell = [animatingEnumerator nextObject]){
|
||
|
CFTimeInterval pausedTime = [animatingCell.layer convertTime:CACurrentMediaTime() fromLayer:nil];
|
||
|
animatingCell.layer.speed = 0.0;
|
||
|
animatingCell.layer.timeOffset = pausedTime;
|
||
|
[animatingCell.layer removeAllAnimations];
|
||
|
[animatingCell removeFromSuperview];
|
||
|
}
|
||
|
[self.animatingCells removeAllObjects];
|
||
|
dispatch_semaphore_signal(_animatingCellsLock);
|
||
|
|
||
|
dispatch_semaphore_wait(_idleCellsLock, DISPATCH_TIME_FOREVER);
|
||
|
[self.idleCells removeAllObjects];
|
||
|
dispatch_semaphore_signal(_idleCellsLock);
|
||
|
|
||
|
dispatch_semaphore_wait(_trackInfoLock, DISPATCH_TIME_FOREVER);
|
||
|
[_trackNextAvailableTime removeAllObjects];
|
||
|
dispatch_semaphore_signal(_trackInfoLock);
|
||
|
}
|
||
|
|
||
|
- (void)fireBarrageCell:(OCBarrageCell *)barrageCell {
|
||
|
switch (self.renderStatus) {
|
||
|
case OCBarrageRenderStarted: {
|
||
|
|
||
|
}
|
||
|
break;
|
||
|
case OCBarrageRenderPaused: {
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
return;
|
||
|
break;
|
||
|
}
|
||
|
if (!barrageCell) {
|
||
|
return;
|
||
|
}
|
||
|
if (![barrageCell isKindOfClass:[OCBarrageCell class]]) {
|
||
|
return;
|
||
|
}
|
||
|
[barrageCell clearContents];
|
||
|
[barrageCell updateSubviewsData];
|
||
|
[barrageCell layoutContentSubviews];
|
||
|
[barrageCell convertContentToImage];
|
||
|
[barrageCell sizeToFit];
|
||
|
[barrageCell removeSubViewsAndSublayers];
|
||
|
[barrageCell addBorderAttributes];
|
||
|
|
||
|
dispatch_semaphore_wait(_animatingCellsLock, DISPATCH_TIME_FOREVER);
|
||
|
_lastestCell = [self.animatingCells lastObject];
|
||
|
[self.animatingCells addObject:barrageCell];
|
||
|
barrageCell.idle = NO;
|
||
|
dispatch_semaphore_signal(_animatingCellsLock);
|
||
|
|
||
|
[self addBarrageCell:barrageCell WithPositionPriority:barrageCell.barrageDescriptor.positionPriority];
|
||
|
CGRect cellFrame = [self calculationBarrageCellFrame:barrageCell];
|
||
|
barrageCell.frame = cellFrame;
|
||
|
[barrageCell addBarrageAnimationWithDelegate:self];
|
||
|
[self recordTrackInfoWithBarrageCell:barrageCell];
|
||
|
|
||
|
_lastestCell = barrageCell;
|
||
|
}
|
||
|
|
||
|
- (void)addBarrageCell:(OCBarrageCell *)barrageCell WithPositionPriority:(OCBarragePositionPriority)positionPriority {
|
||
|
switch (positionPriority) {
|
||
|
case OCBarragePositionMiddle: {
|
||
|
[self insertSubview:barrageCell aboveSubview:_middlePositionView];
|
||
|
}
|
||
|
break;
|
||
|
case OCBarragePositionHigh: {
|
||
|
[self insertSubview:barrageCell belowSubview:_highPositionView];
|
||
|
}
|
||
|
break;
|
||
|
case OCBarragePositionVeryHigh: {
|
||
|
[self insertSubview:barrageCell belowSubview:_veryHighPositionView];
|
||
|
}
|
||
|
break;
|
||
|
default: {
|
||
|
[self insertSubview:barrageCell belowSubview:_lowPositionView];
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- (CGRect)calculationBarrageCellFrame:(OCBarrageCell *)barrageCell {
|
||
|
CGRect cellFrame = barrageCell.bounds;
|
||
|
cellFrame.origin.x = CGRectGetMaxX(self.frame);
|
||
|
|
||
|
if (![[NSValue valueWithRange:barrageCell.barrageDescriptor.renderRange] isEqualToValue:[NSValue valueWithRange:NSMakeRange(0, 0)]]) {
|
||
|
CGFloat cellHeight = CGRectGetHeight(barrageCell.bounds);
|
||
|
CGFloat minOriginY = barrageCell.barrageDescriptor.renderRange.location;
|
||
|
CGFloat maxOriginY = barrageCell.barrageDescriptor.renderRange.length;
|
||
|
if (maxOriginY > CGRectGetHeight(self.bounds)) {
|
||
|
maxOriginY = CGRectGetHeight(self.bounds);
|
||
|
}
|
||
|
if (minOriginY < 0) {
|
||
|
minOriginY = 0;
|
||
|
}
|
||
|
CGFloat renderHeight = maxOriginY - minOriginY;
|
||
|
if (renderHeight < 0) {
|
||
|
renderHeight = cellHeight;
|
||
|
}
|
||
|
|
||
|
int trackCount = floorf(renderHeight/cellHeight);
|
||
|
int trackIndex = arc4random_uniform(trackCount);//用户改变行高(比如弹幕文字大小不会引起显示bug, 因为虽然是同一个类, 但是trackCount变小了, 所以不会出现trackIndex*cellHeight超出屏幕边界的情况)
|
||
|
|
||
|
dispatch_semaphore_wait(_trackInfoLock, DISPATCH_TIME_FOREVER);
|
||
|
OCBarrageTrackInfo *trackInfo = [_trackNextAvailableTime objectForKey:kNextAvailableTimeKey(NSStringFromClass([barrageCell class]), trackIndex)];
|
||
|
if (trackInfo && trackInfo.nextAvailableTime > CACurrentMediaTime()) {//当前行暂不可用
|
||
|
|
||
|
NSMutableArray *availableTrackInfos = [NSMutableArray array];
|
||
|
for (OCBarrageTrackInfo *info in _trackNextAvailableTime.allValues) {
|
||
|
if (CACurrentMediaTime() > info.nextAvailableTime && [info.trackIdentifier containsString:NSStringFromClass([barrageCell class])]) {//只在同类弹幕中判断是否有可用的轨道
|
||
|
[availableTrackInfos addObject:info];
|
||
|
}
|
||
|
}
|
||
|
if (availableTrackInfos.count > 0) {
|
||
|
OCBarrageTrackInfo *randomInfo = [availableTrackInfos objectAtIndex:arc4random_uniform((int)availableTrackInfos.count)];
|
||
|
trackIndex = randomInfo.trackIndex;
|
||
|
} else {
|
||
|
if (_trackNextAvailableTime.count < trackCount) {//刚开始不是每一条轨道都跑过弹幕, 还有空轨道
|
||
|
NSMutableArray *numberArray = [NSMutableArray array];
|
||
|
for (int index = 0; index < trackCount; index++) {
|
||
|
OCBarrageTrackInfo *emptyTrackInfo = [_trackNextAvailableTime objectForKey:kNextAvailableTimeKey(NSStringFromClass([barrageCell class]), index)];
|
||
|
if (!emptyTrackInfo) {
|
||
|
[numberArray addObject:[NSNumber numberWithInt:index]];
|
||
|
}
|
||
|
}
|
||
|
if (numberArray.count > 0) {
|
||
|
trackIndex = [[numberArray objectAtIndex:arc4random_uniform((int)numberArray.count)] intValue];
|
||
|
}
|
||
|
}
|
||
|
//真的是没有可用的轨道了
|
||
|
}
|
||
|
}
|
||
|
dispatch_semaphore_signal(_trackInfoLock);
|
||
|
|
||
|
barrageCell.trackIndex = trackIndex;
|
||
|
cellFrame.origin.y = trackIndex*cellHeight+minOriginY;
|
||
|
} else {
|
||
|
switch (self.renderPositionStyle) {
|
||
|
case OCBarrageRenderPositionRandom: {
|
||
|
CGFloat maxY = CGRectGetHeight(self.bounds) - CGRectGetHeight(cellFrame);
|
||
|
int originY = floorl(maxY);
|
||
|
cellFrame.origin.y = arc4random_uniform(originY);
|
||
|
}
|
||
|
break;
|
||
|
case OCBarrageRenderPositionIncrease: {
|
||
|
if (_lastestCell) {
|
||
|
CGRect lastestFrame = _lastestCell.frame;
|
||
|
cellFrame.origin.y = CGRectGetMaxY(lastestFrame);
|
||
|
} else {
|
||
|
cellFrame.origin.y = 0.0;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
default: {
|
||
|
CGFloat renderViewHeight = CGRectGetHeight(self.bounds);
|
||
|
CGFloat cellHeight = CGRectGetHeight(barrageCell.bounds);
|
||
|
int trackCount = floorf(renderViewHeight/cellHeight);
|
||
|
int trackIndex = arc4random_uniform(trackCount);//用户改变行高(比如弹幕文字大小不会引起显示bug, 因为虽然是同一个类, 但是trackCount变小了, 所以不会出现trackIndex*cellHeight超出屏幕边界的情况)
|
||
|
|
||
|
dispatch_semaphore_wait(_trackInfoLock, DISPATCH_TIME_FOREVER);
|
||
|
OCBarrageTrackInfo *trackInfo = [_trackNextAvailableTime objectForKey:kNextAvailableTimeKey(NSStringFromClass([barrageCell class]), trackIndex)];
|
||
|
if (trackInfo && trackInfo.nextAvailableTime > CACurrentMediaTime()) {//当前行暂不可用
|
||
|
NSMutableArray *availableTrackInfos = [NSMutableArray array];
|
||
|
for (OCBarrageTrackInfo *info in _trackNextAvailableTime.allValues) {
|
||
|
if (CACurrentMediaTime() > info.nextAvailableTime && [info.trackIdentifier containsString:NSStringFromClass([barrageCell class])]) {//只在同类弹幕中判断是否有可用的轨道
|
||
|
[availableTrackInfos addObject:info];
|
||
|
}
|
||
|
}
|
||
|
if (availableTrackInfos.count > 0) {
|
||
|
OCBarrageTrackInfo *randomInfo = [availableTrackInfos objectAtIndex:arc4random_uniform((int)availableTrackInfos.count)];
|
||
|
trackIndex = randomInfo.trackIndex;
|
||
|
} else {
|
||
|
if (_trackNextAvailableTime.count < trackCount) {//刚开始不是每一条轨道都跑过弹幕, 还有空轨道
|
||
|
NSMutableArray *numberArray = [NSMutableArray array];
|
||
|
for (int index = 0; index < trackCount; index++) {
|
||
|
OCBarrageTrackInfo *emptyTrackInfo = [_trackNextAvailableTime objectForKey:kNextAvailableTimeKey(NSStringFromClass([barrageCell class]), index)];
|
||
|
if (!emptyTrackInfo) {
|
||
|
[numberArray addObject:[NSNumber numberWithInt:index]];
|
||
|
}
|
||
|
}
|
||
|
if (numberArray.count > 0) {
|
||
|
trackIndex = [[numberArray objectAtIndex:arc4random_uniform((int)numberArray.count)] intValue];
|
||
|
}
|
||
|
}
|
||
|
//真的是没有可用的轨道了
|
||
|
}
|
||
|
}
|
||
|
dispatch_semaphore_signal(_trackInfoLock);
|
||
|
|
||
|
barrageCell.trackIndex = trackIndex;
|
||
|
cellFrame.origin.y = trackIndex*cellHeight;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (CGRectGetMaxY(cellFrame) > CGRectGetHeight(self.bounds)) {
|
||
|
cellFrame.origin.y = 0.0; //超过底部, 回到顶部
|
||
|
} else if (cellFrame.origin.y < 0) {
|
||
|
cellFrame.origin.y = 0.0;
|
||
|
}
|
||
|
|
||
|
return cellFrame;
|
||
|
}
|
||
|
|
||
|
- (void)clearIdleCells {
|
||
|
dispatch_semaphore_wait(_idleCellsLock, DISPATCH_TIME_FOREVER);
|
||
|
NSTimeInterval timeInterval = [[NSDate date] timeIntervalSince1970];
|
||
|
NSEnumerator *enumerator = [self.idleCells reverseObjectEnumerator];
|
||
|
OCBarrageCell *cell;
|
||
|
while (cell = [enumerator nextObject]){
|
||
|
CGFloat time = timeInterval - cell.idleTime;
|
||
|
if (time > 5.0 && cell.idleTime > 0) {
|
||
|
[self.idleCells removeObject:cell];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (self.idleCells.count == 0) {
|
||
|
_autoClear = NO;
|
||
|
} else {
|
||
|
[self performSelector:@selector(clearIdleCells) withObject:nil afterDelay:5.0];
|
||
|
}
|
||
|
dispatch_semaphore_signal(_idleCellsLock);
|
||
|
}
|
||
|
|
||
|
- (void)recordTrackInfoWithBarrageCell:(OCBarrageCell *)barrageCell {
|
||
|
NSString *nextAvalibleTimeKey = kNextAvailableTimeKey(NSStringFromClass([barrageCell class]), barrageCell.trackIndex);
|
||
|
CFTimeInterval duration = barrageCell.barrageAnimation.duration;
|
||
|
NSValue *fromValue = nil;
|
||
|
NSValue *toValue = nil;
|
||
|
if ([barrageCell.barrageAnimation isKindOfClass:[CABasicAnimation class]]) {
|
||
|
fromValue = [(CABasicAnimation *)barrageCell.barrageAnimation fromValue];
|
||
|
toValue = [(CABasicAnimation *)barrageCell.barrageAnimation toValue];
|
||
|
} else if ([barrageCell.barrageAnimation isKindOfClass:[CAKeyframeAnimation class]]) {
|
||
|
fromValue = [[(CAKeyframeAnimation *)barrageCell.barrageAnimation values] firstObject];
|
||
|
toValue = [[(CAKeyframeAnimation *)barrageCell.barrageAnimation values] lastObject];
|
||
|
} else {
|
||
|
|
||
|
}
|
||
|
const char *fromeValueType = [fromValue objCType];
|
||
|
const char *toValueType = [toValue objCType];
|
||
|
if (!fromeValueType || !toValueType) {
|
||
|
return;
|
||
|
}
|
||
|
NSString *fromeValueTypeString = [NSString stringWithCString:fromeValueType encoding:NSUTF8StringEncoding];
|
||
|
NSString *toValueTypeString = [NSString stringWithCString:toValueType encoding:NSUTF8StringEncoding];
|
||
|
if (![fromeValueTypeString isEqualToString:toValueTypeString]) {
|
||
|
return;
|
||
|
}
|
||
|
if ([fromeValueTypeString containsString:@"CGPoint"]) {
|
||
|
CGPoint fromPoint = [fromValue CGPointValue];
|
||
|
CGPoint toPoint = [toValue CGPointValue];
|
||
|
|
||
|
dispatch_semaphore_wait(_trackInfoLock, DISPATCH_TIME_FOREVER);
|
||
|
OCBarrageTrackInfo *trackInfo = [_trackNextAvailableTime objectForKey:nextAvalibleTimeKey];
|
||
|
if (!trackInfo) {
|
||
|
trackInfo = [[OCBarrageTrackInfo alloc] init];
|
||
|
trackInfo.trackIdentifier = nextAvalibleTimeKey;
|
||
|
trackInfo.trackIndex = barrageCell.trackIndex;
|
||
|
}
|
||
|
trackInfo.barrageCount++;
|
||
|
|
||
|
trackInfo.nextAvailableTime = CGRectGetWidth(barrageCell.bounds);
|
||
|
CGFloat distanceX = fabs(toPoint.x - fromPoint.x);
|
||
|
CGFloat distanceY = fabs(toPoint.y - fromPoint.y);
|
||
|
CGFloat distance = MAX(distanceX, distanceY);
|
||
|
CGFloat speed = distance/duration;
|
||
|
if (distanceX == distance) {
|
||
|
CFTimeInterval time = CGRectGetWidth(barrageCell.bounds)/speed;
|
||
|
trackInfo.nextAvailableTime = CACurrentMediaTime() + time + 0.1;//多加一点时间
|
||
|
[_trackNextAvailableTime setValue:trackInfo forKey:nextAvalibleTimeKey];
|
||
|
} else if (distanceY == distance) {
|
||
|
// CFTimeInterval time = CGRectGetHeight(barrageCell.bounds)/speed;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
}
|
||
|
dispatch_semaphore_signal(_trackInfoLock);
|
||
|
return;
|
||
|
} else if ([fromeValueTypeString containsString:@"CGVector"]) {
|
||
|
|
||
|
return;
|
||
|
} else if ([fromeValueTypeString containsString:@"CGSize"]) {
|
||
|
|
||
|
return;
|
||
|
} else if ([fromeValueTypeString containsString:@"CGRect"]) {
|
||
|
|
||
|
return;
|
||
|
} else if ([fromeValueTypeString containsString:@"CGAffineTransform"]) {
|
||
|
|
||
|
return;
|
||
|
} else if ([fromeValueTypeString containsString:@"UIEdgeInsets"]) {
|
||
|
|
||
|
return;
|
||
|
} else if ([fromeValueTypeString containsString:@"UIOffset"]) {
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
#pragma mark ----- CAAnimationDelegate
|
||
|
|
||
|
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
|
||
|
!self.animationStopBlock ?: self.animationStopBlock();
|
||
|
if (!flag) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (self.renderStatus == OCBarrageRenderStoped) {
|
||
|
return;
|
||
|
}
|
||
|
OCBarrageCell *animationedCell = nil;
|
||
|
dispatch_semaphore_wait(_animatingCellsLock, DISPATCH_TIME_FOREVER);
|
||
|
for (OCBarrageCell *cell in self.animatingCells) {
|
||
|
CAAnimation *barrageAnimation = [cell barrageAnimation];
|
||
|
if (barrageAnimation == anim) {
|
||
|
animationedCell = cell;
|
||
|
[self.animatingCells removeObject:cell];
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
dispatch_semaphore_signal(_animatingCellsLock);
|
||
|
|
||
|
if (!animationedCell) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
dispatch_semaphore_wait(_trackInfoLock, DISPATCH_TIME_FOREVER);
|
||
|
OCBarrageTrackInfo *trackInfo = [_trackNextAvailableTime objectForKey:kNextAvailableTimeKey(NSStringFromClass([animationedCell class]), animationedCell.trackIndex)];
|
||
|
if (trackInfo) {
|
||
|
trackInfo.barrageCount--;
|
||
|
}
|
||
|
dispatch_semaphore_signal(_trackInfoLock);
|
||
|
|
||
|
[animationedCell removeFromSuperview];
|
||
|
[animationedCell prepareForReuse];
|
||
|
|
||
|
dispatch_semaphore_wait(_idleCellsLock, DISPATCH_TIME_FOREVER);
|
||
|
animationedCell.idleTime = [[NSDate date] timeIntervalSince1970];
|
||
|
[self.idleCells addObject:animationedCell];
|
||
|
dispatch_semaphore_signal(_idleCellsLock);
|
||
|
|
||
|
if (!_autoClear) {
|
||
|
[self performSelector:@selector(clearIdleCells) withObject:nil afterDelay:5.0];
|
||
|
_autoClear = YES;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
|
||
|
if (event.type == UIEventTypeTouches) {
|
||
|
UITouch *touch = [touches.allObjects firstObject];
|
||
|
CGPoint touchPoint = [touch locationInView:self];
|
||
|
[self trigerActionWithPoint:touchPoint];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
- (BOOL)trigerActionWithPoint:(CGPoint)touchPoint
|
||
|
{
|
||
|
dispatch_semaphore_wait(_animatingCellsLock, DISPATCH_TIME_FOREVER);
|
||
|
|
||
|
BOOL anyTriger = NO;
|
||
|
NSEnumerator *enumerator = [self.animatingCells reverseObjectEnumerator];
|
||
|
OCBarrageCell *cell = nil;
|
||
|
while (cell = [enumerator nextObject]){
|
||
|
if ([cell.layer.presentationLayer hitTest:touchPoint]) {
|
||
|
if (cell.barrageDescriptor.touchAction) {
|
||
|
cell.barrageDescriptor.touchAction(cell.barrageDescriptor);
|
||
|
anyTriger = YES;
|
||
|
}
|
||
|
if (cell.barrageDescriptor.cellTouchedAction) {
|
||
|
cell.barrageDescriptor.cellTouchedAction(cell.barrageDescriptor, cell);
|
||
|
anyTriger = YES;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
dispatch_semaphore_signal(_animatingCellsLock);
|
||
|
|
||
|
return anyTriger;
|
||
|
}
|
||
|
|
||
|
#pragma mark ----- getter
|
||
|
- (NSMutableArray<OCBarrageCell *> *)animatingCells {
|
||
|
if (!_animatingCells) {
|
||
|
_animatingCells = [[NSMutableArray alloc] init];
|
||
|
}
|
||
|
|
||
|
return _animatingCells;
|
||
|
}
|
||
|
|
||
|
- (NSMutableArray<OCBarrageCell *> *)idleCells {
|
||
|
if (!_idleCells) {
|
||
|
_idleCells = [[NSMutableArray alloc] init];
|
||
|
}
|
||
|
|
||
|
return _idleCells;
|
||
|
}
|
||
|
|
||
|
- (OCBarrageRenderStatus)renderStatus {
|
||
|
return _renderStatus;
|
||
|
}
|
||
|
|
||
|
@end
|
||
|
|
||
|
|