Уменьшите потребление памяти при загрузке GIF-изображений в UIImageView - PullRequest
0 голосов
/ 29 мая 2018

Я хочу показать изображение GIF в UIImageView и с кодом ниже (источник: https://iosdevcenters.blogspot.com/2016/08/load-gif-image-in-swift_22.html, * Я не все коды понимаю), я могу отображать изображения GIF.Однако потребление памяти кажется высоким (проверено на реальном устройстве).Есть ли способ изменить код ниже, чтобы уменьшить потребление памяти?

@IBOutlet weak var imageView: UIImageView!

override func viewDidLoad() {
    super.viewDidLoad()
    let url =  "https://cdn-images-1.medium.com/max/800/1*oDqXedYUMyhWzN48pUjHyw.gif"
    let gifImage = UIImage.gifImageWithURL(url)
    imageView.image = gifImage 
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

fileprivate func < <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
    switch (lhs, rhs) {
    case let (l?, r?):
        return l < r
    case (nil, _?):
        return true
    default:
        return false
    }
}

extension UIImage {
   public class func gifImageWithData(_ data: Data) -> UIImage? {
        guard let source = CGImageSourceCreateWithData(data as CFData, nil) else {
            print("image doesn't exist")
            return nil
        }
            return UIImage.animatedImageWithSource(source)
    }

    public class func gifImageWithURL(_ gifUrl:String) -> UIImage? {
        guard let bundleURL:URL? = URL(string: gifUrl) else {
            return nil
        }
        guard let imageData = try? Data(contentsOf: bundleURL!) else {
            return nil
        }
        return gifImageWithData(imageData)
    }

    public class func gifImageWithName(_ name: String) -> UIImage? {
        guard let bundleURL = Bundle.main
        .url(forResource: name, withExtension: "gif") else {
            return nil
        }
        guard let imageData = try? Data(contentsOf: bundleURL) else {
            return nil
        }
        return gifImageWithData(imageData)
    }

    class func delayForImageAtIndex(_ index: Int, source: CGImageSource!) -> Double {
        var delay = 0.1
        let cfProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil)
        let gifProperties: CFDictionary = unsafeBitCast(
        CFDictionaryGetValue(cfProperties,
                             Unmanaged.passUnretained(kCGImagePropertyGIFDictionary).toOpaque()),
        to: CFDictionary.self)

        var delayObject: AnyObject = unsafeBitCast(
        CFDictionaryGetValue(gifProperties,
                             Unmanaged.passUnretained(kCGImagePropertyGIFUnclampedDelayTime).toOpaque()),
        to: AnyObject.self)
        if delayObject.doubleValue == 0 {
            delayObject = unsafeBitCast(CFDictionaryGetValue(gifProperties,
                                                         Unmanaged.passUnretained(kCGImagePropertyGIFDelayTime).toOpaque()), to: AnyObject.self)
        }

        delay = delayObject as! Double

        if delay < 0.1 {
            delay = 0.1
        }

        return delay
    }

    class func gcdForPair(_ a: Int?, _ b: Int?) -> Int {
        var a = a
        var b = b
        if b == nil || a == nil {
            if b != nil {
                return b!
            } else if a != nil {
                return a!
            } else {
                return 0
            }
        }

        if a < b {
            let c = a
            a = b
            b = c
        }

        var rest: Int
        while true {
            rest = a! % b!

            if rest == 0 {
               return b!
            } else {
                a = b
                b = rest
            }
        }
    }

    class func gcdForArray(_ array: Array<Int>) -> Int {
        if array.isEmpty {
            return 1
        }

        var gcd = array[0]

        for val in array {
            gcd = UIImage.gcdForPair(val, gcd)
        }

       return gcd
    }

    class func animatedImageWithSource(_ source: CGImageSource) -> UIImage? {
        let count = CGImageSourceGetCount(source)
        var images = [CGImage]()
        var delays = [Int]()

        for i in 0..<count {
            if let image = CGImageSourceCreateImageAtIndex(source, i, nil) {
                images.append(image)
            }

            let delaySeconds = UIImage.delayForImageAtIndex(Int(i),
                                                        source: source)
            delays.append(Int(delaySeconds * 1000.0)) // Seconds to ms
        }

        let duration: Int = {
            var sum = 0

            for val: Int in delays {
                sum += val
            }

            return sum
         }()

        let gcd = gcdForArray(delays)
        var frames = [UIImage]()

        var frame: UIImage
        var frameCount: Int
        for i in 0..<count {
            frame = UIImage(cgImage: images[Int(i)])
            frameCount = Int(delays[Int(i)] / gcd)

            for _ in 0..<frameCount {
                frames.append(frame)
            }
        }

        let animation = UIImage.animatedImage(with: frames,
                                          duration: Double(duration) / 1000.0)

        return animation
    }
}

tested as gif

Когда я отрисовываю изображение как обычное png-изображение, его потребление составляет около 10 МБ.tested as non gif

Ответы [ 2 ]

0 голосов
/ 20 марта 2019

Чтобы отобразить GIF-файлы с меньшим потреблением памяти, попробуйте BBWebImage .

BBWebImage определит, сколько кадров изображений будет декодировано и кэшировано в зависимости от текущего использования памяти.Если свободной памяти недостаточно, только часть кадров изображения декодируется и кэшируется.

Для Swift 4:

// BBAnimatedImageView (subclass UIImageView) displays animated image
imageView = BBAnimatedImageView(frame: frame)

// Load and display gif
imageView.bb_setImage(with: url,
                      placeholder: UIImage(named: "placeholder"))
{ (image: UIImage?, data: Data?, error: Error?, cacheType: BBImageCacheType) in
    // Do something when finish loading
}
0 голосов
/ 30 мая 2018

GIF, о котором идет речь, имеет разрешение 480 × 288 и содержит 10 кадров.

Учитывая, что UIImageView хранит кадры в виде 4-байтового RGBA, этот GIF занимает 4 × 10 × 480 × 288 = 5.529 600 байт в оперативной памяти, что превышает 5 мегабайт.

Существует множество способов уменьшить это, но только один из них не создает дополнительной нагрузки на процессор;другие - просто компромиссы между процессором и оперативной памятью.

Метод, о котором я говорю, это создание подклассов UIImageView и загрузка ваших GIF-файлов вручную, сохраняя их внутреннее представление (индексированное изображение + палитра).Это позволило бы сократить использование памяти в четыре раза.

Примечание: даже если GIF-изображения могут храниться как полные изображения для каждого кадра (что имеет место для рассматриваемого GIF-файла), многие этого не делают.Наоборот, большинство кадров может содержать только те пиксели, которые изменились со времени предыдущего.Таким образом, в общем случае внутреннее представление GIF позволяет отображать кадры только в прямом порядке.

Другие способы сохранения ОЗУ включают, например, перечитывание каждого кадра с диска перед его отображением, что, безусловно, не хорошо для срока службы батареи..

...