How to set screen brightness with fade animations?
Asked Answered
D

7

8

Is it possible to animate the screen brightness change on iOS 5.1+? I am using [UIScreen mainScreen] setBrightness:(float)] but I think that the abrupt change is ugly.

Dahle answered 5/4, 2013 at 18:27 Comment(0)
R
6

I don't know if this is "animatable" in some other way, but you could do it yourself. For instance the following example code was hooked up to "Full Bright" and "Half Bright" buttons in the UI. It uses performSelector...afterDelay to change the brightness by 1% every 10ms till the target brightness is reached. You would pick an appropriate change rate based on some experimenting. Actually the refresh rate is, I think, 60 hz so there is probably no point in doing a change at an interval smaller than 1/60th of a second (My example rate was chosen to have nice math). Although you might want to do this on a non-UI thread, it doesn't block the UI.

- (IBAction)fullBright:(id)sender {
    CGFloat brightness = [UIScreen mainScreen].brightness;
    if (brightness < 1) {
        [UIScreen mainScreen].brightness += 0.01;
        [self performSelector:@selector(fullBright:) withObject:nil afterDelay:.01];
    }
}

- (IBAction)halfBright:(id)sender {
    CGFloat brightness = [UIScreen mainScreen].brightness;
    if (brightness > 0.5) {
        [UIScreen mainScreen].brightness -= 0.01;
        [self performSelector:@selector(halfBright:) withObject:nil afterDelay:.01];
    }
}
Rolandrolanda answered 5/4, 2013 at 19:20 Comment(0)
C
12

Swift 5

import UIKit

extension UIScreen {
    
    public func setBrightness(to value: CGFloat, duration: TimeInterval = 0.3, ticksPerSecond: Double = 120) {
        let startingBrightness = UIScreen.main.brightness
        let delta = value - startingBrightness
        let totalTicks = Int(ticksPerSecond * duration)
        let changePerTick = delta / CGFloat(totalTicks)
        let delayBetweenTicks = 1 / ticksPerSecond
        
        let time = DispatchTime.now()
        
        for i in 1...totalTicks {
            DispatchQueue.main.asyncAfter(deadline: time + delayBetweenTicks * Double(i)) {
                UIScreen.main.brightness = max(min(startingBrightness + (changePerTick * CGFloat(i)),1),0)
            }
        }
        
    }
}
Celesta answered 15/5, 2019 at 16:20 Comment(0)
M
11

I ran into issues with the accepted answer when attempting to animate to another value with a previous animation in progress. This solution cancels an in-progress animation and animates to the new value:

extension UIScreen {

    func setBrightness(_ value: CGFloat, animated: Bool) {
        if animated {
            _brightnessQueue.cancelAllOperations()
            let step: CGFloat = 0.04 * ((value > brightness) ? 1 : -1)
            _brightnessQueue.add(operations: stride(from: brightness, through: value, by: step).map({ [weak self] value -> Operation in
                let blockOperation = BlockOperation()
                unowned let _unownedOperation = blockOperation
                blockOperation.addExecutionBlock({
                    if !_unownedOperation.isCancelled {
                        Thread.sleep(forTimeInterval: 1 / 60.0)
                        self?.brightness = value
                    }
                })
                return blockOperation
            }))
        } else {
            brightness = value
        }
    }

}

private let _brightnessQueue: OperationQueue = {
    let queue = OperationQueue()
    queue.maxConcurrentOperationCount = 1
    return queue
}()
Magnify answered 3/6, 2016 at 23:2 Comment(1)
You're going to run into issues updating the brightness on a thread other than main due to main thread checker.. Consider wrapping self?.brightness = value inside of DispatchQueue.main.async - iOS13 Beta 5Rowenarowland
R
6

I don't know if this is "animatable" in some other way, but you could do it yourself. For instance the following example code was hooked up to "Full Bright" and "Half Bright" buttons in the UI. It uses performSelector...afterDelay to change the brightness by 1% every 10ms till the target brightness is reached. You would pick an appropriate change rate based on some experimenting. Actually the refresh rate is, I think, 60 hz so there is probably no point in doing a change at an interval smaller than 1/60th of a second (My example rate was chosen to have nice math). Although you might want to do this on a non-UI thread, it doesn't block the UI.

- (IBAction)fullBright:(id)sender {
    CGFloat brightness = [UIScreen mainScreen].brightness;
    if (brightness < 1) {
        [UIScreen mainScreen].brightness += 0.01;
        [self performSelector:@selector(fullBright:) withObject:nil afterDelay:.01];
    }
}

- (IBAction)halfBright:(id)sender {
    CGFloat brightness = [UIScreen mainScreen].brightness;
    if (brightness > 0.5) {
        [UIScreen mainScreen].brightness -= 0.01;
        [self performSelector:@selector(halfBright:) withObject:nil afterDelay:.01];
    }
}
Rolandrolanda answered 5/4, 2013 at 19:20 Comment(0)
G
1

A Swift extension:

  extension UIScreen {

    private static let step: CGFloat = 0.1

    static func animateBrightness(to value: CGFloat) {
        guard fabs(UIScreen.main.brightness - value) > step else { return }

        let delta = UIScreen.main.brightness > value ? -step : step

        DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) {
            UIScreen.main.brightness += delta
            animateBrightness(to: value)
        }
    }
}
Glucoside answered 6/3, 2018 at 18:47 Comment(0)
T
0

Based on Charlie Price's great answer, here's a method for "animating" a change in screen brightness to any value desired.

- (void)graduallyAdjustBrightnessToValue:(CGFloat)endValue
{
    CGFloat startValue = [[UIScreen mainScreen] brightness];

    CGFloat fadeInterval = 0.01;
    double delayInSeconds = 0.01;
    if (endValue < startValue)
        fadeInterval = -fadeInterval;

    CGFloat brightness = startValue;
    while (fabsf(brightness-endValue)>0) {

        brightness += fadeInterval;

        if (fabsf(brightness-endValue) < fabsf(fadeInterval))
            brightness = endValue;

        dispatch_time_t dispatchTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
        dispatch_after(dispatchTime, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [[UIScreen mainScreen] setBrightness:brightness];
        });
    }
}
Tremolo answered 12/10, 2014 at 3:16 Comment(0)
P
0

Or you can use NSTimer instead of while loops and performSelector.

finalValue - is value you want to achieve.

Timer fires 30 times with duration 0.02 second for each (you can choose something different but smoothly) and changes brightness value.

weak var timer: NSTimer?
var count = 1
let maxCount = 30
let interval = 0.02


timer = NSTimer
            .scheduledTimerWithTimeInterval(interval,
                                            target: self,
                                            selector: #selector(changeBrightness),
                                            userInfo: nil,
                                            repeats: true)
func changeBrightness()
{
    guard count < maxCount else { return }

    let currentValue = UIScreen.mainScreen().brightness 
    let restCount = maxCount - count
    let diff = (finalValue - currentValue) / CGFloat(restCount)
    let newValue = currentValue + diff
    UIScreen.mainScreen().brightness = newValue

    count += 1
}
Philemol answered 15/6, 2016 at 15:59 Comment(0)
M
0

You can use this helper if you need to change brightness of your specific ViewController

import Foundation
import UIKit

final class ScreenBrightness {

    private var timer: Timer?
    private var brightness: CGFloat?
    private var isBrighteningScreen = false
    private var isDarkeningScreen = false

    private init() { }

    static let shared = ScreenBrightnessHelper()

    //Увеличение яркости экрана до максимального уровня
    func brightenDisplay() {
        resetTimer()
        isBrighteningScreen = true
        if #available(iOS 10.0, *), timer == nil {
            brightness = UIScreen.main.brightness
            timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { (timer) in
                UIScreen.main.brightness = UIScreen.main.brightness + 0.01
                if UIScreen.main.brightness > 0.99 || !self.isBrighteningScreen {
                    self.resetTimer()
                }
            }
        }
        timer?.fire()
    }

    //Затемнение экрана до предыдущего уровня
    func darkenDisplay() {
        resetTimer()
        isDarkeningScreen = true
        guard let brightness = brightness else {
            return
        }
        if #available(iOS 10.0, *), timer == nil {
            timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { (timer) in
                UIScreen.main.brightness = UIScreen.main.brightness - 0.01

                if UIScreen.main.brightness <= brightness || !self.isDarkeningScreen {
                    self.resetTimer()
                    self.brightness = nil
                }
            }
            timer?.fire()
        }
    }

    private func resetTimer() {
        timer?.invalidate()
        timer = nil
        isBrighteningScreen = false
        isDarkeningScreen = false
    }
}

Call ScreenBrightness.shared.brightenDisplay() in viewWillAppear and if you wanna change it back call method ScreenBrightness.shared.darkenDisplay() that will change brightness back

Mccullers answered 17/11, 2020 at 11:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.