Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resizing an UIImageView via UIViewPropertyAnimator makes loaded image disappear and placeholder appear #2209

Open
3 tasks done
m4p opened this issue Feb 16, 2024 · 3 comments

Comments

@m4p
Copy link

m4p commented Feb 16, 2024

Check List

Thanks for considering to open an issue. Before you submit your issue, please confirm these boxes are checked.

Issue Description

What

Resizing an UIImageView via UIViewPropertyAnimator makes loaded image disappear and placeholder appear. Only happens when image is not cached. I'm attaching sample code and a screen recording.

Simulator Screen Recording - iPad (10th generation) - 2024-02-16 at 18 54 07

Reproduce

//
//  ViewController.swift
//  KingFisherResizing
//
//  Created by Martin Pittenauer on 16.02.24.
//

import UIKit
import Kingfisher

class ViewController: UIViewController {

    private lazy var imageView: UIImageView = {
        let imageView = UIImageView()
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.contentMode = .scaleAspectFill
        imageView.clipsToBounds = true
        return imageView
    }()

    lazy var widthConstraint: NSLayoutConstraint = { imageView.widthAnchor.constraint(equalToConstant: 200)
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        view.addSubview(imageView)
        NSLayoutConstraint.activate([
            imageView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            imageView.topAnchor.constraint(equalTo: view.topAnchor),
            imageView.heightAnchor.constraint(equalTo: imageView.widthAnchor, multiplier: 9/16),
            widthConstraint
        ])

        let imageUrl = URL(string: "https://res.cloudinary.com/dg-stage/image/fetch/w_2642/https://dg-stage-production-pictures.s3.eu-west-1.amazonaws.com/be9e66cd3f8fc57d008b5969ad87ce04c385bbbe.jpg?_a=AEABoAB0")!
        let placeholder = UIImage(named: "placeholder")
        
        imageView.kf.setImage(with: imageUrl, placeholder: placeholder, options: [.transition(.fade(0.4)), .forceRefresh])
        
        startTimer()
    }

    var fraction: Double = 0.0
    var change: Double = 0.02
    var timer: Timer?
    var animator: UIViewPropertyAnimator?

    func startTimer() {
        widthConstraint.constant = 900
        animator = UIViewPropertyAnimator(duration: 1.0, curve: .easeInOut) { [weak self] in
            self?.view.layoutIfNeeded()
        }
        animator?
            .pauseAnimation()


        timer = Timer.scheduledTimer(withTimeInterval: 0.1, repeats: true) { [weak self] _ in
            guard let self else { return }
            if self.fraction <= 0 {
                change = 0.02
            }
            if self.fraction >= 1 {
                change = -0.02
            }
            
            self.fraction = self.fraction + change
            animator?.fractionComplete = self.fraction
        }
    }
}
@m4p
Copy link
Author

m4p commented Feb 16, 2024

This is caused by the UIView.transition used by Kingfisher in makeTransition() still being part of the UIImageView and being triggered in reverse. It needs to be removed after it is finished. Unfortunately it seems UIViewPropertyAnimator captures it and prevents it from being removed automatically.

A hacky workaround that seems to work is:

        imageView.kf.setImage(with: imageUrl, placeholder: placeholder, options: [.transition(.fade(0.4)), .forceRefresh, .waitForCache]) { _ in
            if let keys = self.layer.animationKeys(), keys.contains("contents") {
                self.imageView.layer.removeAnimation(forKey: "contents")
            }
        }

@onevcat
Copy link
Owner

onevcat commented Feb 18, 2024

Ummm...

I used the sample code to try to reproduce the issue, but was unsuccessful. There may be other factors causing the problem...

Simulator.Screen.Recording.-.iPhone.8.-.2024-02-18.at.16.18.49.mp4

@m4p
Copy link
Author

m4p commented Feb 18, 2024

Here's the complete reproduction Xcode project:
samplecode.zip
I'm using Xcode 15.2 and the iPad (10th gen) simulator.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants