Skip to content

Instantly share code, notes, and snippets.

@JosephDuffy
Last active November 30, 2022 18:28
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save JosephDuffy/be906b3c6a0c036cf8fa922cf5726f14 to your computer and use it in GitHub Desktop.
Save JosephDuffy/be906b3c6a0c036cf8fa922cf5726f14 to your computer and use it in GitHub Desktop.
Animates changing the brightness of a `UIScreen`
import UIKit
final class ExampleViewController: UViewController {
private var usersBrightness = UIScreen.main.brightness
private var willEnterForegroundWasCalled = false
private var viewWillDisappearWasCalled = false
override func viewDidLoad() {
super.viewDidLoad()
usersBrightness = UIScreen.main.brightness
NotificationCenter.default.addObserver(self, selector: #selector(applicationWillEnterForeground), name: .UIApplicationWillEnterForeground, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(applicationDidBecomeActive), name: .UIApplicationDidBecomeActive, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(applicationWillResignActive), name: .UIApplicationWillResignActive, object: nil)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
usersBrightness = UIScreen.main.brightness
UIScreen.main.animateBrightness(to: 1, duration: 0.6)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
usersBrightness = UIScreen.main.brightness
UIScreen.main.animateBrightness(to: 1, duration: 0.6)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
viewWillDisappearWasCalled = true
UIScreen.main.animateBrightness(to: usersBrightness, duration: 0.3)
}
@objc private func applicationWillResignActive() {
UIScreen.main.animateBrightness(to: usersBrightness, duration: 0.3)
}
@objc private func applicationWillEnterForeground() {
willEnterForegroundWasCalled = true
usersBrightness = UIScreen.main.brightness
UIScreen.main.animateBrightness(to: 1, duration: 0.6)
}
@objc private func applicationDidBecomeActive() {
// When the app enters the foreground again because the user closed notification
// or control center then `UIApplicationWillEnterForeground` is not called, necessitating
// also listening to `UIApplicationDidBecomeActive`.
// This guard ensures the brightness is not increased when the app is opened from the home
// screen, the multitasker, etc., and also when the view controller is dismissing, which can
// occur if the user closes the view control quickly after closing notification or control center
guard !willEnterForegroundWasCalled, !viewWillDisappearWasCalled else {
willEnterForegroundWasCalled = false
return
}
usersBrightness = UIScreen.main.brightness
UIScreen.main.animateBrightness(to: 1, duration: 0.6)
}
}
import UIKit
extension UIScreen {
public func animateBrightness(to newValue: CGFloat, duration: TimeInterval, ticksPerSecond: Double = 60) {
let startingBrightness = UIScreen.main.brightness
let delta = newValue - startingBrightness
let totalTicks = Int(ticksPerSecond * duration)
let changePerTick = delta / CGFloat(totalTicks)
let delayBetweenTicks = 1 / ticksPerSecond
brightness += changePerTick
DispatchQueue.global(qos: .userInteractive).async {
var previousValue = self.brightness
for _ in 2...totalTicks {
Thread.sleep(forTimeInterval: delayBetweenTicks)
guard previousValue == self.brightness else {
// Value has changed since thread went to sleep 😴
return
}
let nextValue = min(1, self.brightness + changePerTick)
self.brightness = nextValue
// Don't use `nextValue` here as iOS appears to do some rounding, so
// the actual value of `self.brightness` may differ from `nextValue`
previousValue = self.brightness
}
}
}
}
@jasonwiener
Copy link

@JosephDuffy thanks so much for this!

@GabrielTargon
Copy link

GabrielTargon commented Sep 24, 2019

Thanks, but I don't know why it's not working on my project. But the logic for the app in background is perfect.

The method view viewWillAppear is repeated and it's missing the deinit method to remove the NotificationCenterObserver:

deinit {
        NotificationCenter.default.removeObserver(self, name: .UIApplicationWillEnterForeground, object: nil)
        NotificationCenter.default.removeObserver(self, name: .UIApplicationDidBecomeActive, object: nil)
        NotificationCenter.default.removeObserver(self, name: .UIApplicationWillResignActive, object: nil)
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment