Created
May 26, 2025 14:18
-
-
Save longseespace/8cf3a728635f6194ed024c653ce9ea38 to your computer and use it in GitHub Desktop.
Simple render FPS counter for SwiftUI (NOT GPU FPS)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
import Combine | |
class FPSCounter: ObservableObject { | |
@Published var fps: Double = 0.0 | |
private var timer: Timer? | |
private var lastUpdateTime: Date = Date() | |
private var frameCount: Int = 0 | |
// How often to update the published FPS value (e.g., every 0.5 seconds) | |
private let fpsUpdateInterval: TimeInterval = 0.5 | |
// How frequently the internal timer should try to fire (e.g., aiming for 120Hz) | |
private let internalTimerInterval: TimeInterval = 1.0 / 120.0 | |
init() {} | |
func start() { | |
guard timer == nil else { return } | |
lastUpdateTime = Date() | |
frameCount = 0 | |
// This timer will fire frequently to count "frames" | |
timer = Timer.scheduledTimer(withTimeInterval: internalTimerInterval, repeats: true) { [weak self] _ in | |
self?.tick() | |
} | |
// Ensure the timer runs on high-priority run loops if needed, e.g., for UI updates | |
// RunLoop.current.add(timer!, forMode: .common) | |
} | |
private func tick() { | |
frameCount += 1 | |
let now = Date() | |
let elapsedTime = now.timeIntervalSince(lastUpdateTime) | |
if elapsedTime >= fpsUpdateInterval { | |
DispatchQueue.main.async { | |
self.fps = Double(self.frameCount) / elapsedTime | |
self.frameCount = 0 | |
self.lastUpdateTime = now | |
} | |
} | |
} | |
func stop() { | |
timer?.invalidate() | |
timer = nil | |
// Optionally reset FPS to 0 when stopped | |
// DispatchQueue.main.async { | |
// self.fps = 0.0 | |
// } | |
} | |
deinit { | |
stop() | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
struct FPSView: View { | |
@StateObject private var fpsCounter = FPSCounter() | |
var body: some View { | |
Text("FPS: \(fpsCounter.fps, specifier: "%.1f")") | |
.font(.caption) | |
.foregroundColor(.white) | |
.padding(4) | |
.background(Color.black.opacity(0.5)) | |
.clipShape(RoundedRectangle(cornerRadius: 5)) | |
.onAppear { | |
fpsCounter.start() | |
} | |
.onDisappear { | |
fpsCounter.stop() | |
} | |
} | |
} | |
struct FPSView_Previews: PreviewProvider { | |
static var previews: some View { | |
FPSView() | |
.padding() | |
.previewLayout(.sizeThatFits) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment