Closed KlemensStrasser closed 2 years ago
I have the same issue. The playback is exactly 2 times slower on iPhone 13 Pro than on older devices. I've also checked if the same issue would occur if I use the Kingfisher library and I can confirm that it does not.
Same Issue here. I was a bit confused as maybe I had set the level of integrity to a low value, but that was not the case. I tested it on my iPhone X and it works as it should.
Ok it seems like I found a workaround by tinkering a bit. I believe the issue to arise from the calculateFrameDelay function in the UIImage+SwiftyGif.swift file. It seemed to have incorrectly taken into account the 120Hz refresh rate of the ProMotion Displays. The correct function code is listed below.
ORIGINAL CODE:
private func calculateFrameDelay(_ delaysArray: [Float], levelOfIntegrity: GifLevelOfIntegrity) {
let levelOfIntegrity = max(0, min(1, levelOfIntegrity))
var delays = delaysArray
var displayRefreshFactors = [Int]()
if #available(iOS 10.3, tvOS 10.3, *) {
// Will be 120 on devices with ProMotion display, 60 otherwise.
displayRefreshFactors.append(UIScreen.main.maximumFramesPerSecond)
}
if let first = displayRefreshFactors.first, first != 60 {
// Append 60 if needed.
displayRefreshFactors.append(60)
}
displayRefreshFactors.append(contentsOf: [30, 20, 15, 12, 10, 6, 5, 4, 3, 2, 1])
// maxFramePerSecond,default is 60
let maxFramePerSecond = displayRefreshFactors[0]
// frame numbers per second
let displayRefreshRates = displayRefreshFactors.map { maxFramePerSecond / $0 }
// time interval per frame
let displayRefreshDelayTime = displayRefreshRates.map { 1 / Float($0) }
// caclulate the time when each frame should be displayed at(start at 0)
for i in delays.indices.dropFirst() {
delays[i] += delays[i - 1]
}
//find the appropriate Factors then BREAK
for (i, delayTime) in displayRefreshDelayTime.enumerated() {
let displayPosition = delays.map { Int($0 / delayTime) }
var frameLoseCount: Float = 0
for j in displayPosition.indices.dropFirst() where displayPosition[j] == displayPosition[j - 1] {
frameLoseCount += 1
}
if displayPosition.first == 0 {
frameLoseCount += 1
}
if frameLoseCount <= Float(displayPosition.count) * (1 - levelOfIntegrity) || i == displayRefreshDelayTime.count - 1 {
imageCount = displayPosition.last
displayRefreshFactor = displayRefreshFactors[i]
displayOrder = []
var oldIndex = 0
var newIndex = 1
let imageCount = self.imageCount ?? 0
while newIndex <= imageCount && oldIndex < displayPosition.count {
if newIndex <= displayPosition[oldIndex] {
displayOrder?.append(oldIndex)
newIndex += 1
} else {
oldIndex += 1
}
}
break
}
}
}
EDITED CODE:
private func calculateFrameDelay(_ delaysArray: [Float], levelOfIntegrity: GifLevelOfIntegrity) {
let levelOfIntegrity = max(0, min(1, levelOfIntegrity))
var delays = delaysArray
var displayRefreshFactors = [Int]()
displayRefreshFactors.append(contentsOf: [60, 30, 20, 15, 12, 10, 6, 5, 4, 3, 2, 1])
// maxFramePerSecond,default is 60
let maxFramePerSecond = displayRefreshFactors[0]
// frame numbers per second
var displayRefreshRates = displayRefreshFactors.map { maxFramePerSecond / $0 }
if #available(iOS 10.3, *) {
// Will be 120 on devices with ProMotion display, 60 otherwise.
let maximumFramesPerSecond = UIScreen.main.maximumFramesPerSecond
if maximumFramesPerSecond == 120 {
displayRefreshRates.append(UIScreen.main.maximumFramesPerSecond)
}
}
// time interval per frame
let displayRefreshDelayTime = displayRefreshRates.map { 1 / Float($0) }
// caclulate the time when each frame should be displayed at(start at 0)
for i in delays.indices.dropFirst() {
delays[i] += delays[i - 1]
}
//find the appropriate Factors then BREAK
for (i, delayTime) in displayRefreshDelayTime.enumerated() {
let displayPosition = delays.map { Int($0 / delayTime) }
var frameLoseCount: Float = 0
for j in displayPosition.indices.dropFirst() where displayPosition[j] == displayPosition[j - 1] {
frameLoseCount += 1
}
if displayPosition.first == 0 {
frameLoseCount += 1
}
if frameLoseCount <= Float(displayPosition.count) * (1 - levelOfIntegrity) || i == displayRefreshDelayTime.count - 1 {
imageCount = displayPosition.last
displayRefreshFactor = displayRefreshFactors[i]
displayOrder = []
var oldIndex = 0
var newIndex = 1
let imageCount = self.imageCount ?? 0
while newIndex <= imageCount && oldIndex < displayPosition.count {
if newIndex <= displayPosition[oldIndex] {
displayOrder?.append(oldIndex)
newIndex += 1
} else {
oldIndex += 1
}
}
break
}
}
}
I believe the issue was a mathematical error which caused the displayRefreshRates to have incorrect values. The 120 value should have been inserted after the displayRefreshFactors mapping onto the displayRefreshRates value.
For those with SPM, I don't think you can edit the code directly, but I think with cocoapods you can.
Hope this issue is fixed globally!
Version: 5.3.2 Dependancy manager: SPM Xcode version: Version 13.2.1 (13C100)
Hi there,
So I recently got a new iPhone 13 Pro, which has the Pro Motion display. But when running my app on it, I notice that the performance of the GIF rendering has drastically worsen compared to my previous phone (iPhone X). The delay between the frames is too big, making the gifs run too slow and thus, feeling sluggish.
Some observations from the iOS Example Application:
Anyone else seeing the issue and any ideas how to solve it? Happy to help digging into it.
Best, Klemens