See live the children’s shoes will often have to see the full screen rolling barrage, see the barrage of the first impression is how many efficient loading to avoid caton, barrage part contains the avatars, user nickname, the content of the barrage, expression, etc., this paper introduces the implementation of the principle is to the parts drawing as a picture, and then move through the timer barrage pictures, Destroy images when they are out of screen range.

Let’s take a look at the effect


I’ll explain the implementation in detail

  • 1. Obtain the data source of the barrage, because I simulate the generation of the barrage, and the data of the barrage are stored in the PLIST file of the project

emotions
type
text
userName

#pragma mark - Get data source- (void)loadData{// Obtain the plist full path NSString *filePath = [[NSBundle mainBundle] pathForResource:@"barrage.plist"ofType:nil]; / / load the data from the specified path NSArray * array = [NSArray arrayWithContentsOfFile: filePath]; // go through the number groupfor (NSDictionary *dict inArray) {// BAModle *barrageM = [BAModle barrageWithDict:dict]; [self.danMus addObject:barrageM]; }}Copy the code

  • 2. Generate bullet screen pictures according to the model. Generate model by clicking the screen and draw pictures according to the model.
#pragma mark - Touch the screen in response to events- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{// get a random integer NSInteger index = arc4random_uniform((u_int32_t)self.danMus.count); BAModle *danMu = self.danMus[index]; // BAImage *image = [self. DanMuview imageWithBarrage:danMu]; / / adjust the barrage loading area image. X = self. The bounds. Size. The width; image.y = arc4random_uniform(self.danMuview.bounds.size.height - image.size.height); // Add the image to the marquee view [self.danMuview addImage:image]; }Copy the code

The following is the specific drawing process of bullet screen pictures. I will briefly introduce it first. First, the size of the context should be determined before drawing, which is equivalent to the size of the drawing board. It then draws the background image, the user’s nickname, the content and the expression, and finally returns a picture. There are two points to note here: 1. Since the head is a rectangle, if you want to display it as a circle, draw a circle first and set the part beyond the circle to be cropped before drawing the head. If CGContextSaveGState(CTX) is executed in the circle area, a copy of the artboard (context) is stored in the stack. Before drawing the background image, run CGContextRestoreGState(CTX) to replace the current artboard with the previously saved artboard, because the previously saved artboard does not have any requirements for clipping beyond the circle area. Of course, replacing the current artboard will copy the drawing on the current artboard.

#pragma Mark - Draws bullet screen images- (BAImage *)imageWithBarrage:(BAModle *)danMu{// UIFont *font = [UIFont systemFontOfSize:13]; // avatar CGFloat iconH = 30; CGFloat iconW = iconH; // Spacing CGFloat marginX = 5; CGFloat emotionW = 25; CGFloat emotionH = emotionW; / / calculate user name occupy area CGSize nameSize = [danMu. UserName boundingRectWithSize: CGSizeMake (MAXFLOAT, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:font} context:nil].size; / / calculation content area CGSize textSize = [danMu. Text boundingRectWithSize: CGSizeMake (MAXFLOAT, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:font} context:nil].size; // Size of bitmap context CGFloat contentH = iconH; CGFloat contentW = iconW + 4 * marginX + nameSize.width + textSize.width + danMu.emotions.count * emotionH; CGSize contextSize = CGSizeMake(contentW, contentH); UIGraphicsBeginImageContextWithOptions (contextSize, NO, 0.0); / / get the bitmap context CGContextRef CTX = UIGraphicsGetCurrentContext (); // Save the context to the stack CGContextSaveGState(CTX); CGRect iconFrame = CGRectMake(0, 0, iconW, iconH); / / draw the image circle CGContextAddEllipseInRect (CTX, iconFrame); // Clipping CGContextClip(CTX) beyond the circle; UIImage *icon = danmu. type? [UIImage imageNamed:@"headImage_1"]:[UIImage imageNamed:@"headImage_2"]; [icon drawInRect:iconFrame]; // Replace the current context with CGContextRestoreGState(CTX); // 3. Draw the background image CGFloatbgX = iconW + marginX;
    CGFloat bgY = 0;
    CGFloat bgW = contentW - bgX;
    CGFloat bgH = contentH;
    danMu.type ? [[UIColor orangeColor] set]:[[UIColor whiteColor] set];
    [[UIBezierPath bezierPathWithRoundedRect:CGRectMake(bgX, bgY, bgW, bgH) cornerRadius: 20.0] fill]; // 4. Draw the user name CGFloat nameX =bgX + marginX; CGFloat nameY = (contenth-namesize.height) * 0.5; [danMu.userName drawAtPoint:CGPointMake(nameX, nameY) withAttributes:@{NSAttachmentAttributeName:font,NSForegroundColorAttributeName:danMu.type == NO ? [UIColor orangeColor]:[UIColor blackColor]}]; TextX = nameX + namesize.width + marginX; CGFloat textY = nameY; [danMu.text drawAtPoint:CGPointMake(textX, textY) withAttributes:@{NSAttachmentAttributeName:font,NSForegroundColorAttributeName:danMu.type == NO ? [UIColor blackColor]:[UIColor whiteColor]}]; __block CGFloat emotionX = textX + textsize.width; CGFloat emotionY = (contenth-emotionh) * 0.5; [danMu.emotions enumerateObjectsUsingBlock:^(NSString *emotionName, NSUInteger idx, UIImage *emotion = [UIImage imageNamed:emotionName]; [emotion] DrawInRect :CGRectMake(emotionX, emotionY, emotionW, emotionH)]; / / from a bitmap in the context of rendering good pictures UIImage * image = UIGraphicsGetImageFromCurrentImageContext ();return [[BAImage alloc] initWithCGImage:image.CGImage scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp];
}

Copy the code
  • 3.Start drawing timer, the callback method issetNeedsDisplay, then it will be executed- (void)drawRect:(CGRect)rectEvery changeimage.xSince UIImage has no x or y attributes, we write a class extensionBAImage), scrolling out of the screen will be destroyed
#pragma mark - Adds timer
- (void)addTimer{
    if (self.link) {
        return; } / / perform 60 per second callback CADisplayLink * link = [CADisplayLink displayLinkWithTarget: self selector: @ the selector (setNeedsDisplay)]; // Add timer to runLoop [link addToRunLoop:[NSRunLoop currentRunLoop]forMode:NSRunLoopCommonModes];
    self.link = link;
}
#pragma Mark - Draw moves
- (void)drawRect:(CGRect)rect{
    
    for (BAImage *image inself.imageArray) { image.x -= 3; [image drawAtPoint:CGPointMake(image.x, image.y)]; // Determine if the image is off screenif(image.x + image.size.width < 0) { [self.deleteImageArray addObject:image]; }} // Remove bullets that exceed the screenfor (BAImage *image in self.deleteImageArray) {
        [self.imageArray removeObject:image];
    }
    [self.deleteImageArray removeAllObjects];
}
Copy the code

Finally, attach the gitHub address

Thank you and welcome your advice!