preface

  • Simple call reminder animation effect, live gift display

. The Demo address

API & Property

@property(nonatomic,assign)NSInteger maxCount; /// Automatically disappear time, default 5s @property(nonatomic,assign)CGFloat vanishTime; // whether to allow repetition, default NO @property(nonatomic,assign)BOOL repetition; /// NO @property(nonatomic,assign)BOOL tapVanish; // create singleton + (instanceType)kj_shareInstance; // Add call message, - (void)kj_addCallNotify:(void(^)(KJCallNotifyInfo *info))block RepetitionCondition:(bool(^_Nullable)(KJCallNotifyInfo *info))condition; - (void)kj_tapBlock:(void(^)(KJCallNotifyInfo *info))block;Copy the code

A brief introduction

KJCallNotifyInfo Data model

Declare a data model that accepts the outside world. For now, I just need the image address, name, and unique ID.

@interface KJCallNotifyInfo : NSObject
@property(nonatomic,strong)NSString *imageUrl;
@property(nonatomic,strong)NSString *name;
@property(nonatomic,strong)NSString *userid;
@end
Copy the code

The singleton pattern

Because this thing needs to be there all the time and at the top, I use singletons

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; }Copy the code

Of course, you can not use this singleton, as follows

__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",@" painful faith ",@"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]; }];Copy the code

Gesture penetration processing

This does not affect subsequent interactions

- (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;
}
Copy the code

KJCallView

Call style UI handling

@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_createButtonWithImageName:@"button_like_norm"]; self.button.frame = CGRectMake(self.width-18-8, 0, 18, 18); self.button.centerY = self.height/2; [self addSubview:self.button]; self.label = [UILabel kj_createLabelWithText:name]; [self addSubview:self.label]; Self.label. textColor = [UIColor colorWithRed:51/255.0 green:51/255.0 blue:51/255.0 alpha:1.0]; self.label.textColor = [UIColor colorWithRed:51/255.0 green:51/255.0 blue:51/255.0 alpha:1.0]; self.label.font = [UIFont fontWithName:@"PingFang SC" size:14]; 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; } - (void)kj_invalidateTimer{ [_timer invalidate]; _timer = nil; } @endCopy the code

Click on the event

- (void)kj_tapBlock:(void(^)(KJCallNotifyInfo *info))block{
    self.tapblock = block;
}
Copy the code

Click callback, passing the logic to handle the click

Adding an Incoming call message

Introduces the main methods and emphasis, displayCount: the current display UI number attribute temps: data storage model viewTemps: store call display UI data kj_addCallNotify: RepetitionCondition: : Kj_viewIndex :Info: : Creates call UI kj_displayEnd: : Displays end kj_autoVanish: : Kj_vanish: : Vanish: kj_changeIndex:Y: : Modifies call coordinates recursively

A, kj_addCallNotify: RepetitionCondition:

Add the call message, self.repetition controls whether the call is repeated, and the internal condition determines whether the call is repeated according to the 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]; }}}Copy the code

Second, kj_viewIndex: the Info:

Create a UI control and add a timer view.timer to manage automatic disappearance

- (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; }Copy the code

Three, kj_displayEnd:

Display end, clean up the data, turn off the timer, display the number of processing

- (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--; }}Copy the code

Four, kj_autoVanish:

Automatic disappear processing

- (void)kj_autoVanish:(KJCallView*)view{
    [UIView animateWithDuration:.5 animations:^{
        [self kj_changeIndex:0 Y:-kAutoH(48)];
    } completion:^(BOOL finished) {
        [view removeFromSuperview];
    }];
}
Copy the code

Fifth, kj_vanish:

Dot and fork disappear processing, the current click control disappears, the rest in turn fill

- (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]; }]; }Copy the code

Six, kj_changeIndex: Y:

Recursive processing of the complement

- (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]; }Copy the code

Use the sample

- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. UIButton *button = [UIButton kj_createButtonWithFontSize:15 Title: @ "test call" 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," @ "pain beliefs," @ "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]; }]; }Copy the code
Note: some function methods and Demo used in this article are from the tripartite libraryKJExtensionHandler, if necessary friends canpod 'KJExtensionHandler'Introduction to

Call remind small control introduction to this end, there are related to supplement, write an article is not easy, but also please pointLittle starsportal