0

I have a large image in terms of dimensions that go beyond the device's screen width.

I have loaded that large image into a scrollview on my app.

I have a button which triggers a manual scroll on the scrollview from one end to the other over a 10s period.

I want to convert this animation into a video and my purpose of posting this question is to find out the best / efficient / optimal way to do that.

I found this answer where he Zoul gives some ideas on how to convert an array of images into a video.

So here is what I tried:

I scrolled the scrollview from end to end over a 10s interval and I tried to take a screenshot of the view every 1/60th of a second, thinking I would get a 60 fps video doing this

func didTapPlayButton(_ sender: UIButton) {
      UIView.animate(withDuration: 10.0, delay: 0.0, options: .curveEaseInOut) { [weak self] in
        self?.startTakingScreenshots()
        self?.scrollView.scrollToEnd(animated: false) // Custom UIScrollview extension function

      } completion: { [weak self] (finished) in
        self?.timer.invalidate()
        self?.timer = nil
        self?.generateMovie()
      }
}

func startTakingScreenshots() {
    timer = Timer.scheduledTimer(timeInterval: 1.0/60.0, 
                                 target:self , 
                                 selector: #selector(snapScreenshot), 
                                 userInfo: nil, 
                                 repeats: true)
}
  
  @objc func snapScreenshot() {
    imageArray.append(imageView.takeScreenshot())
}

This is the code I use to take the screenshot which is an extension of UIImageView

extension UIImageView {
  
  func takeScreenshot() -> UIImage {
    
    // Begin context
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, UIScreen.main.scale)
    
    // Draw view in that context
    drawHierarchy(in: self.bounds, afterScreenUpdates: true)
    
    // And finally, get image
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    
    if (image != nil) {
      return image!
    }
    
    return UIImage()
}

I can confirm taking the screenshot works fine but here are the issues I am having:

  1. After running for about 3-4 s, the app crashes and Xcode says it is due to memory pressure. I am guessing it is because holding such a large number of UIImages in an array is not a good idea ?

  2. If I change the frame rate from 1/60s to 1/30s, the app does not crash and I reach the end with 505 frames saved to the array. I am still a bit confused about the math here. If I take 30 screenshots every second for 10 seconds, I was expecting 300 frames or am I getting this wrong ?

  3. Is there a better / efficient / optimal way to convert a UIImage which pans on a scrollview into a video than what I have done above ? I saw some suggestions about screen recording but I do not want to record the whole screen and I am interested in recording the animation for a specific view

  4. Could there be a solution to convert the UIImage directly into a video without needing the scrollview ?

Any ideas and suggestions would be highly appreciated.

Thanks in advance.

Shawn Frank
  • 4,381
  • 2
  • 19
  • 29
  • 1
    You may want to look at this article: https://www.raywenderlich.com/2734-avfoundation-tutorial-adding-overlays-and-animations-to-videos ... in particular the section at the end about Adding Animation. You could (possibly) add an animation shifting the position of your image. – DonMag Mar 18 '21 at 20:00
  • I just gave that a try, and it works well :) – DonMag Mar 18 '21 at 21:09
  • Very cool @DonMag - especially the animation bit. My question would be, since I really only have one thing in my video - an image that pans from left to right - do I create a blank video first and add the image as an animation layer ? – Shawn Frank Mar 19 '21 at 03:39
  • 1
    I've done very little with this stuff, so this comment is based on quick searching and trying a few things.... It *appears* that yes, you'd need a "blank" video as the "base." Either have one in your assets or generate one "on the fly". Using the code from the article I linked to, I edited the "rotating stars" animation layer using a large image and `"position.x"` for the KeyPath. Worked like a charm :) – DonMag Mar 19 '21 at 13:01
  • @DonMag - thank you so much for not only the link, but your thoughts beyond that as I was able to accomplish my task. I would post an answer of what I did but I think you deserve credit for it so if you want to post something, I will mark it as the answer. Thank you once again, it helped me achieve my objective. – Shawn Frank Mar 28 '21 at 06:00
  • Glad to help, although all I did was point you to an article :) I've come across a few other posts with similar questions... if you post your own answer - particularly if you include a (bare minimum example), I expect others would find that very useful. – DonMag Mar 28 '21 at 13:26
  • 1
    @ShawnFrank I think your memory issue is because of the size of the images. I had a similar problem here https://stackoverflow.com/q/63855711/4833705. In the comments, **matt** brought something to my attention which led me to resolving it. His comment `“The images were initially taken with the iPhone full screen camera” Well that’s it right there. You cannot possibly load 20 camera images at once. You say your cell size is 40x40 so your image size needs to be 40x40 (or less)`. He was correct. The array of images isn't your memory issue. – Lance Samaria Mar 18 '22 at 13:06
  • Thanks @LanceSamaria - definitely useful for the future. I can't remember how I solved this last year but I did find some workaround. – Shawn Frank Mar 18 '22 at 15:10

0 Answers0