Last active
November 30, 2022 18:28
-
-
Save JosephDuffy/be906b3c6a0c036cf8fa922cf5726f14 to your computer and use it in GitHub Desktop.
Animates changing the brightness of a `UIScreen`
This file contains 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 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) | |
} | |
} |
This file contains 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 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 | |
} | |
} | |
} | |
} |
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
@JosephDuffy thanks so much for this!