Search code examples
iosobjective-cgifcgimage

Create a gif with UIImages


I am referring to this post . I am trying to make a gif file with the images created by screenshot. I am using a timer to create the snapshot of the screen,so that I get required amount of frames that could be used to create a gif. I take snaphots every 0.1 seconds (I will later end this timer after 3 seconds).

Here is my code to take snapshots of my UIView:

-(void)recordScreen{

   self.timer= [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(takeSnapShot) userInfo:nil repeats:YES];

}

-(void)takeSnapShot{

    //capture the screenshot of the uiimageview and save it in camera roll
    UIGraphicsBeginImageContext(self.drawView.frame.size);
    [self.drawView.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

}

and the post I am referring to, shows a helper function to create gif. I am not sure how should I pass my images to the helper function. Here is what I tried:

I tried to modify this part :

 static NSUInteger kFrameCount = 10;
 for (NSUInteger i = 0; i < kFrameCount; i++) {
        @autoreleasepool {
            UIImage *image = [self takeSnaphot];
            CGImageDestinationAddImage(destination, image.CGImage, (__bridge CFDictionaryRef)frameProperties);
        }
    }

This creates the gif with 10 frames of my UIView.

Now....

What I am trying to do is:

I am drawing a simple drawing with finger on my UIView using UIBeizerPath and I am taking snapshots in parallel to my drawing ,so that I will have around 50-100 of PNG files. I am trying to pass all these images to the makeGifMethod.

WorkFlow:

  1. Start the App.
  2. Tap on a button which starts the timer, and take pics for every 0.1 sec
  3. Draw with finger (so all the drawing will be captured evry 0.1 secs)
  4. After each snapshot, I call makeanimatedgif method,so that the snapshot taken will be added to the previous frame in the gif file
  5. Stop everything

Issue:

-Case 1:

  1. Tap on button (which does create gif immediatley)
  2. Start drawing
  3. Stop
  4. Check gif (a gif with 10 frames is created with white background,since I drew nothing when I hit button)

If I call my snapShot method in a loop, I get the last 10 frames of my drawing,but not everything.

for (NSUInteger i = 0; i < 10; i++) {
        @autoreleasepool {
            UIImage *image = [self takeSnapShot];
            CGImageDestinationAddImage(destination, image.CGImage, (__bridge CFDictionaryRef)frameProperties);
        }
    }

-Case 2:

  1. Hit button (start timer, take snapshot, call makeGifMethod in parallel to it)
  2. Draw somthing
  3. Stop
  4. Check gif (empty gif is created without any frames,I beleieve calling makeGifMethod every 0.1 seconds didnt work as expected)

Here is the code for case 2:

  -(void)recordScreen{

   self.timer= [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(takeSnapShot) userInfo:nil repeats:YES];

}

 -(void)takeSnapShot{

    //capture the screenshot of the uiimageview and save it in camera roll
    UIGraphicsBeginImageContext(self.drawView.frame.size);
    [self.drawView.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [self makeAnimatedGif:(UIImage *)viewImage];

    });

} 


-(void) makeAnimatedGif:(UIImage *)image{

    NSDictionary *fileProperties = @{
                                     (__bridge id)kCGImagePropertyGIFDictionary: @{
                                             (__bridge id)kCGImagePropertyGIFLoopCount: @0, // 0 means loop forever
                                             }
                                     };

    NSDictionary *frameProperties = @{
                                      (__bridge id)kCGImagePropertyGIFDictionary: @{
                                              (__bridge id)kCGImagePropertyGIFDelayTime: @0.02f, // a float (not double!) in seconds, rounded to centiseconds in the GIF data
                                              }
                                      };

    documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:nil];
    fileURL = [documentsDirectoryURL URLByAppendingPathComponent:@"animated.gif"];

    CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef)fileURL, kUTTypeGIF, 10, NULL);
    CGImageDestinationSetProperties(destination, (__bridge CFDictionaryRef)fileProperties);

            CGImageDestinationAddImage(destination, image.CGImage, (__bridge CFDictionaryRef)frameProperties);

    if (!CGImageDestinationFinalize(destination)) {
        NSLog(@"failed to finalize image destination");
    }
    CFRelease(destination);

    NSLog(@"url=%@", fileURL);
}

Can someone please suggest me how to pass the captured images to the above method to make a gif?


Solution

  • After few work arounds, I choose to store all the captured images into an array, and use that array to pass the images to gifMethod. And it worked so cool!!!

    I have stored all the images into array:

    -(void)takeSnapShot{
    
    //capture the screenshot of the uiimageview and save it in camera roll
    UIGraphicsBeginImageContext(self.drawView.frame.size);
    [self.drawView.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    //store all the images into array
    [imgArray adDObject:viewImage]; 
    
    } 
    

    NOTE: make sure you resize the image before you store them into array else you might end up with memory warning followed by app crash if this is used for a longer period.

    and later used the same array :

    -(void)makeAnimatedGif {
        NSUInteger kFrameCount = imgArray.count;
    
        NSDictionary *fileProperties = @{
                                         (__bridge id)kCGImagePropertyGIFDictionary: @{
                                                 (__bridge id)kCGImagePropertyGIFLoopCount: @0, // 0 means loop forever
                                                 }
                                         };
    
        NSDictionary *frameProperties = @{
                                          (__bridge id)kCGImagePropertyGIFDictionary: @{
                                                  (__bridge id)kCGImagePropertyGIFDelayTime: @0.08f, // a float (not double!) in seconds, rounded to centiseconds in the GIF data
                                                  }
                                          };
    
        NSURL *documentsDirectoryURL = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:nil];
        NSURL *fileURL = [documentsDirectoryURL URLByAppendingPathComponent:@"animated.gif"];
    
        CGImageDestinationRef destination = CGImageDestinationCreateWithURL((__bridge CFURLRef)fileURL, kUTTypeGIF, kFrameCount, NULL);
        CGImageDestinationSetProperties(destination, (__bridge CFDictionaryRef)fileProperties);
    
        for (NSUInteger i = 0; i < kFrameCount; i++) {
            @autoreleasepool {
                UIImage *image =[imgArray objectAtIndex:i];  //Here is the change
                CGImageDestinationAddImage(destination, image.CGImage, (__bridge CFDictionaryRef)frameProperties);
            }
        }
    
        if (!CGImageDestinationFinalize(destination)) {
            NSLog(@"failed to finalize image destination");
        }
        CFRelease(destination);
    
        NSLog(@"url=%@", fileURL);
    
      }