kaishin / Gifu

High-performance animated GIF support for iOS in Swift
Other
3.09k stars 279 forks source link

iOS 14 GIFAnimatable UIKit extension doesn't play GIF #167

Closed RemyLivewall closed 4 years ago

RemyLivewall commented 4 years ago

I have updated to the newest version of the library after noticing GIF's were no longer playing on iOS 14, but the issue persists with the implementation I use, which is basically the UIKit extension from: https://github.com/kaishin/Gifu#gifanimatable.

I have also replicated the fix that is implemented in the library itself in the extension, but to no avail.

    override open func display(_ layer: CALayer) {
        if UIImageView.instancesRespond(to: #selector(display(_:))) {
            super.display(layer)
        }
        updateImageIfNeeded()
    }
ivoleko commented 4 years ago

If you are using SPM, try to use master branch as source instead major version. It seems that SPM still uses 3.2.2 as newest.

RemyLivewall commented 4 years ago

If you are using SPM, try to use master branch as source instead major version. It seems that SPM still uses 3.2.2 as newest.

I am using CoocaPods and verified that I am using version 3.3, so unfortunately this doesn't solve the issue for me.

SimeonRumy commented 4 years ago

Currently, I am facing the same issue.

RemyLivewall commented 4 years ago

After looking into the issue a bit more it looks like the CALayerDelegate methods are called on iOS 13 and lower, but won't get called on iOS 14. They should be called after the animator receives a new frame:

extension GIFAnimatable {
  /// Calls setNeedsDisplay on the layer whenever the animator has a new frame. Should *not* be called directly.
  func animatorHasNewFrame() {
    layer.setNeedsDisplay()
  }
}

Adding the updateImageIfNeeded call results in animating GIF's, but is not a decent workaround due to the impact on other users of the GIFAnimatable protocol. This proves the delegate methods don't get called, because they contain the updateImageIfNeeded call. I am not sure how to solve the problem properly, because the animatorHasNewFrame method is an internal extension in GIFAnimatable and is called internally from the Animator as well.

kaishin commented 4 years ago

Thanks for looking into this @RemyLivewall. I need to take a closer look at this. If you find anything please don't hesitate to update the issue.

raffibedo commented 4 years ago

I have the same problema, it doesn't play in iOS 14. In one case, it just show the first frame of the gif. Any idea?

RemyLivewall commented 4 years ago

I did some debugging with the iOS 13 and iOS 14 sims and the stack trace at the animatorHasNewFrame point is the same. GIFAnimatable-iOS13 GIFAnimatable-iOS14

As mentioned before only iOS 13 proceeds to follow the expected path of calling the display(layer) method. I also changed my image views to GIFAnimatable views. As seen in this screenshot on iOS 13 it follows through to both the GIFImageView display(layer) and the UIImageView extension's display(layer).

UIImageViewExtension-iOS13

Unfortunately I don't have animated GIF's working on iOS 14 either which strikes me as odd. This unfortunately means I wasn't able to debug the process further. Even manually calling the display(layer) method for the animatorHasNewFrame method doesn't trigger it.

baiRookie commented 4 years ago

之前一直找不到图片加载不出来的原因,经过一番折腾,发现是UIimageView重写了display方法导致的,最后选择放弃使用此框架,因为UIimageView的继承使项目中其他的图片全部无法展示,最后使用SDWebImage加载,非常完美的解决了这个毛病,

thebugcode commented 4 years ago

As @baiRookie says we Replaced Gifu with SDWebImage as we don't currently have the capacity to investigate all the ins and outs of gif loading. Might get involved in trying to solve the Gifu problem early next week but it's good to have a fallback

Jinxiansen commented 4 years ago

@kaishin How to solve the problem discussed above?

kaishin commented 4 years ago

I should have a fix by the end of the week. Thank you all for the help.

Jinxiansen commented 4 years ago

@kaishin Oh, great! Looking forward to your results! thank you very much!🍺🍺🍺

RemyLivewall commented 4 years ago

I should have a fix by the end of the week. Thank you all for the help.

I found a fix for the problem. Changing the display layer method with the following fixes issues for displaying images and GIF's on my end:

override open func display(_ layer: CALayer) {
        guard let image = image else {
            return
        }
        layer.contentsScale = image.scale
        layer.contents = image.cgImage
        updateImageIfNeeded()
    }

I had issues with normal images no longer showing up either when building my app with Xcode 12. Which got me thinking and checking the original fix as implemented in other libraries. Because we are overriding the implementation of UIImageView in an extension some undefined behavior might be occurring and resulting in the strange behavior we are seeing since iOS 14 and/or Xcode 12.

Because we can't call super when overriding the base UIImageView class we have to implement both the original behavior and add our GIFAnimatable logic to the overriden method.

EDIT: Do you want to send a pull request for the README.md with this change?

KhoiNguyen1004 commented 4 years ago

I should have a fix by the end of the week. Thank you all for the help.

I found a fix for the problem. Changing the display layer method with the following fixes issues for displaying images and GIF's on my end:

override open func display(_ layer: CALayer) {
        guard let image = image else {
            return
        }
        layer.contentsScale = image.scale
        layer.contents = image.cgImage
        updateImageIfNeeded()
    }

I had issues with normal images no longer showing up either when building my app with Xcode 12. Which got me thinking and checking the original fix as implemented in other libraries. Because we are overriding the implementation of UIImageView in an extension some undefined behavior might be occurring and resulting in the strange behavior we are seeing since iOS 14 and/or Xcode 12.

Because we can't call super when overriding the base UIImageView class we have to implement both the original behavior and add our GIFAnimatable logic to the overriden method.

EDIT: Do you want to send a pull request for the README.md with this change?

Hi I don't get what you mean, I am running to display a gift in a collectionViewCell, so I would use alamofire to download to data and use play gift with data with gifu, however you fixed way isn't for the collectionviewcell. Do you have any idea about it ?

RemyLivewall commented 4 years ago

@KhoiNguyen1004 The fix mentioned is only needed if you are using an extension (or category) on UIImageView. Because the original implementation seems to be lost on iOS 14. What kind of view are you using to show the GIF in your UICollectionViewCell?

KhoiNguyen1004 commented 4 years ago

@KhoiNguyen1004 The fix mentioned is only needed if you are using an extension (or category) on UIImageView. Because the original implementation seems to be lost on iOS 14. What kind of view are you using to show the GIF in your UICollectionViewCell?

I am using normal imageview, and make extension as example, I tried to implemented a custom class for gifu but it seems like doesn't work at all.

alanpridestar commented 4 years ago

Hi everyone,

I don't think this issue should be closed. I attempted to follow the suggested solution, but similarly to Khoi, I am unable to play the gif in app.

Any suggestions would be greatly appreciated Thanks

RemyLivewall commented 3 years ago

Are you using the GIFImageView, an extension on UIImage or a different method of trying to play the GIF files? Is it possible for you to share the code you were using when it worked?

DamonHu commented 3 years ago

As @RemyLivewall said, the difference is that after I change the image to activeFrame, it works well. This is the code I modified

override public func display(_ layer: CALayer) {
        guard let image = self.activeFrame else {
            return
        }
        layer.contentsScale = image.scale
        layer.contents = image.cgImage
        updateImageIfNeeded()
 }
viktorvrchlavsky commented 3 years ago

@RemyLivewall @DamonHu Thank you guys for solving this 🙏

dsrees commented 3 years ago

FYI, it appears this was fixed in Gifu 3.3. Updating 3.3 resolved the issue for me.

diegogarciar commented 3 years ago

Hello, I'm still having this issue, I ran the example project and it works, but UIImageViewExtension.swift has no target, so just by setting a target to the file, the extension kicks in and the animation breaks

monalisamarketing commented 3 years ago

With

override public func display(_ layer: CALayer) {
        guard let image = self.activeFrame else {
            return
        }

the gif works well but other images over the app are not displayed neither on testflight nor production but with

override public func display(_ layer: CALayer) {
        guard let image = image else {
            return
        }

the images are displayed but the gif is not displayed at all

wjling commented 2 years ago

Still have the problem that gif won't play. I test it on iOS 15