前言
- 簡單的來電提醒動畫效果,直播送禮顯示
. Demo地址
API & Property
/// 最大顯示個數,默認3個
@property(nonatomic,assign)NSInteger maxCount;
/// 自動消失時間,默認5s
@property(nonatomic,assign)CGFloat vanishTime;
/// 是否允許重複顯示,默認NO
@property(nonatomic,assign)BOOL repetition;
/// 點擊控件是否消失,默認NO
@property(nonatomic,assign)BOOL tapVanish;
/// 創建單例
+ (instancetype)kj_shareInstance;
/// 添加來電消息,重複條件默認根據 userid 判斷
- (void)kj_addCallNotify:(void(^)(KJCallNotifyInfo *info))block RepetitionCondition:(bool(^_Nullable)(KJCallNotifyInfo *info))condition;
/// 點擊事件
- (void)kj_tapBlock:(void(^)(KJCallNotifyInfo *info))block;
簡單介紹思路
KJCallNotifyInfo數據模型
聲明一個接受外界的數據模型,暫時我只需要圖片地址,名字,唯一id(需要再添加就完事)
@interface KJCallNotifyInfo : NSObject
@property(nonatomic,strong)NSString *imageUrl;
@property(nonatomic,strong)NSString *name;
@property(nonatomic,strong)NSString *userid;
@end
單例模式
由於這玩意需要一直存在並且放在最頂層,所以我採用單例來設計
static KJCallNotifyView *_instance = nil;
+ (instancetype)kj_shareInstance{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (_instance == nil) {
_instance = [[KJCallNotifyView alloc] initWithFrame:CGRectMake(0, 0, kScreenW, kScreenH)];
[kKeyWindow addSubview:_instance];
}
});
return _instance;
}
當然你也可以不使用該單例,則按下面方式
__block KJCallNotifyView *view = [[KJCallNotifyView alloc]initWithFrame:CGRectMake(0, 64, kScreenW, kScreenH-64)];
[self.view addSubview:view];
view.maxCount = 5;
view.vanishTime = 5;
[view kj_tapBlock:^(KJCallNotifyInfo * _Nonnull info) {
}];
__block NSInteger index = 1000;
NSArray *names = @[@"Sone",@"痛苦的信仰",@"X"];
[button kj_addAction:^(UIButton * _Nonnull kButton) {
[view kj_addCallNotify:^(KJCallNotifyInfo * _Nonnull info) {
info.imageUrl = @"xxsf";
info.userid = [NSString stringWithFormat:@"%ld",index];
info.name = names[1];
} RepetitionCondition:nil];
}];
手勢穿透處理
不影響後面的交互效果
- (UIView*)hitTest:(CGPoint)point withEvent:(UIEvent*)event{
NSInteger count = self.subviews.count;
for (int i = 0; i < count; i++){
UIView *childView = self.subviews[count - 1 - I];
CGPoint childPoint = [self convertPoint:point toView:childView];
UIView *view = [childView hitTest:childPoint withEvent:event];
if (view) return view;
}
return nil;
}
KJCallView
來電樣式UI處理
@implementation KJCallView
- (instancetype)kj_initWithFrame:(CGRect)frame Name:(NSString*)name{
if (self==[super initWithFrame:frame]) {
self.centerX = kScreenW/2;
self.backgroundColor = UIColor.whiteColor;
self.cornerRadius = kAutoH(48)/2;
self.shadowColor = [UIColor colorWithRed:0/255.0 green:0/255.0 blue:0/255.0 alpha:0.16];
self.shadowOffset = CGSizeMake(0,3);
self.shadowRadius = 6;
self.shadowOpacity = 1;
CGFloat height = self.height;
self.imageView = [[UIImageView alloc]initWithFrame:CGRectMake(5, 5, height-10, height-10)];
self.imageView.cornerRadius = (height-10)/2;
[self addSubview:self.imageView];
self.button = [UIButton kj_createButton:^(id<KJQuickCreateHandle> _Nonnull handle) {
handle.kj_frame(self.width-18-8, 0, 18, 18).kj_add(self);
handle.kj_imageName(@"xxx").kj_fontSize(14).kj_textColor(UIColor.blackColor);
}];
self.button.centerY = self.height/2;
self.label = [UILabel kj_createLabel:^(id<KJQuickCreateHandle> _Nonnull handle) {
handle.kj_add(self);
handle.kj_text(name).kj_font([UIFont fontWithName:@"PingFang SC" size:14]);
handle.kj_textColor([UIColor colorWithRed:51/255.0 green:51/255.0 blue:51/255.0 alpha:1.0]);
}];
CGFloat width = [self.label kj_calculateWidth];
CGFloat maxw = self.button.x - self.imageView.maxX - 10 - 23 - 10;
if (width>=maxw) width = maxw;
self.label.frame = CGRectMake(self.imageView.maxX+10, 0, width, self.height);
self.tvImageView = [[UIImageView alloc]initWithFrame:CGRectMake(self.label.maxX+2.5, 0, 23, 22)];
self.tvImageView.image = kGetImage(@"wode_nor");
self.tvImageView.centerY = self.height/2;
[self addSubview:self.tvImageView];
}
return self;
}
@end
點擊事件
- (void)kj_tapBlock:(void(^)(KJCallNotifyInfo *info))block{
self.tapblock = block;
}
點擊回調,傳遞出來處理點擊的相關邏輯
添加來電消息
介紹主要方法和核心點,
displayCount
:當前顯示的UI個數屬性
temps
:存儲模型數據
viewTemps
:存儲來電顯示UI數據
kj_addCallNotify:RepetitionCondition:
:添加來電消息
@synchronized (@(self.displayCount))
:互斥鎖,保證正常運行
kj_viewIndex:Info:
:創建來電UI
kj_displayEnd:
:顯示結束
kj_autoVanish:
:自動消失動畫
kj_vanish:
:點叉消失動畫
kj_changeIndex:Y:
:遞歸修改來電座標
一、kj_addCallNotify:RepetitionCondition:
添加來電消息,self.repetition
控制是否重複顯示,內部條件根據userid來判斷是否重複
- (void)kj_addCallNotify:(void(^)(KJCallNotifyInfo *))block RepetitionCondition:(bool(^_Nullable)(KJCallNotifyInfo *))condition{
KJCallNotifyInfo *info = [KJCallNotifyInfo new];
if (block) block(info);
if (self.repetition == NO) {
__block bool skip = false;
if (condition) {
for (KJCallNotifyInfo *_info in self.temps) {
skip = condition(_info);
if (skip) break;
}
}else{
[self.temps enumerateObjectsUsingBlock:^(KJCallNotifyInfo * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
if ([info.userid isEqualToString:obj.userid]) {
skip = true;*stop = YES;
}
}];
}
if (skip) return;
[self.temps addObject:info];
}
@synchronized (@(self.displayCount)) {
self.displayCount++;
KJCallView *view = [self kj_viewIndex:self.displayCount Info:info];
[self.viewTemps addObject:view];
[self addSubview:view];
if (self.displayCount > self.maxCount) {
KJCallView *fristView = self.viewTemps.firstObject;
[self kj_autoVanish:fristView];
[self kj_displayEnd:fristView];
}
}
}
二、kj_viewIndex:Info:
創建UI控件,添加計時器view.timer
來管理自動消失
- (KJCallView*)kj_viewIndex:(NSInteger)index Info:(KJCallNotifyInfo*)info{
CGFloat y = kAutoH(17) + (index-1) * kAutoH(58) + kSTATUSBAR_HEIGHT - 20;
__block KJCallView *view = [[KJCallView alloc]kj_initWithFrame:CGRectMake(0, y, kAutoW(170), kAutoH(48)) Name:info.name];
view.tag = 520 + index - 1;
view.info = info;
view.imageView.image = [UIImage imageNamed:info.imageUrl];
_weakself;
void (^kRemove)(bool tapX) = ^(bool tapX){
if (tapX) {
[weakself kj_vanish:view];
}else{
[weakself kj_autoVanish:view];
}
[weakself kj_displayEnd:view];
};
[view kj_AddTapGestureRecognizerBlock:^(UIView * _Nonnull __view, UIGestureRecognizer * _Nonnull gesture) {
if (weakself.tapblock) {
weakself.tapblock(view.info);
if (weakself.tapVanish) {
[weakself kj_vanish:view];
[weakself kj_displayEnd:view];
}
}
}];
[view.button kj_addAction:^(UIButton * _Nonnull kButton) {
kRemove(true);
}];
view.timer = [NSTimer kj_scheduledNoImmediateTimerWithTimeInterval:self.vanishTime Block:^(NSTimer * _Nonnull timer) {
kRemove(false);
}];
[[NSRunLoop mainRunLoop] addTimer:view.timer forMode:NSRunLoopCommonModes];
[view.timer fire];
return view;
}
三、kj_displayEnd:
顯示結束,清理數據,關閉計時器,顯示個數處理
- (void)kj_displayEnd:(KJCallView*)view{
[view kj_invalidateTimer];
[self.viewTemps removeObject:view];
if (self.repetition == NO) {
[self.temps removeObject:view.info];
}
@synchronized (@(self.displayCount)) {
self.displayCount--;
}
}
四、kj_autoVanish:
自動消失處理
- (void)kj_autoVanish:(KJCallView*)view{
[UIView animateWithDuration:.5 animations:^{
[self kj_changeIndex:0 Y:-kAutoH(48)];
} completion:^(BOOL finished) {
[view removeFromSuperview];
}];
}
五、kj_vanish:
點叉消失處理,當前點擊控件消失,其餘依次補位
- (void)kj_vanish:(KJCallView*)view{
[UIView animateWithDuration:.5 animations:^{
view.hidden = 0;
if (view.tag == 520) {
[self kj_changeIndex:0 Y:-kAutoH(48)];
}else{
[self kj_changeIndex:view.tag-520 Y:view.y];
}
} completion:^(BOOL finished) {
[view removeFromSuperview];
}];
}
六、kj_changeIndex:Y:
遞歸來處理補位
- (void)kj_changeIndex:(NSInteger)index Y:(CGFloat)y{
KJCallView *view = self.viewTemps[index];
view.tag = 520 + index - 1;
CGFloat xy = view.y;view.y = y;
if (index+1<self.viewTemps.count) [self kj_changeIndex:index+1 Y:xy];
}
使用示例
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UIButton *button = [UIButton kj_createButtonWithFontSize:15 Title:@"測試來電" TextColor:UIColor.orangeColor];
button.frame = CGRectMake(0, 0, 100, 50);
button.centerX = kScreenW/2;
button.centerY = kScreenH - 100;
button.borderWidth = 1;
button.borderColor = UIColor.orangeColor;
[self.view addSubview:button];
__block NSInteger index = 520;
NSArray *names = @[@"Sone",@"痛苦的信仰",@"X",@"yang"];
[button kj_addAction:^(UIButton * _Nonnull kButton) {
[[KJCallNotifyView kj_shareInstance] kj_addCallNotify:^(KJCallNotifyInfo * _Nonnull info) {
info.imageUrl = @"xxsf";
info.userid = [NSString stringWithFormat:@"%ld",index++];
info.name = names[index%4];
} RepetitionCondition:^bool(KJCallNotifyInfo * _Nonnull info) {
if ([info.name isEqualToString:names[index%4]]) {
return true;
}
return false;
}];
}];
[KJCallNotifyView kj_shareInstance].maxCount = 5;
[KJCallNotifyView kj_shareInstance].vanishTime = 7;
[KJCallNotifyView kj_shareInstance].repetition = YES;
[[KJCallNotifyView kj_shareInstance] kj_tapBlock:^(KJCallNotifyInfo * _Nonnull info) {
NSLog(@"-----%@",info);
[self.navigationController popViewControllerAnimated:YES];
}];
}