Как настроить яркость экрана с помощью анимации затухания?

Можно ли анимировать изменение яркости экрана на iOS 5.1+? я использую [UIScreen mainScreen] setBrightness:(float)] но я думаю, что резкие перемены ужасны.

5 ответов

Решение

Я не знаю, является ли это "анимируемым" каким-либо другим способом, но вы могли бы сделать это самостоятельно. Например, следующий пример кода был подключен к кнопкам "Full Bright" и "Half Bright" в пользовательском интерфейсе. Он использует executeSelector...afterDelay, чтобы изменять яркость на 1% каждые 10 мс, пока не будет достигнута целевая яркость. Вы бы выбрали соответствующую скорость изменения, основываясь на некоторых экспериментах. На самом деле частота обновления составляет, я думаю, 60 Гц, так что, вероятно, нет смысла вносить изменения с интервалом, меньшим 1/60 секунды (мой пример частоты был выбран, чтобы иметь хорошую математику). Хотя вы можете захотеть сделать это в потоке без пользовательского интерфейса, он не блокирует пользовательский интерфейс.

- (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];
    }
}

Я столкнулся с проблемами с принятым ответом при попытке анимировать другое значение с предыдущей анимацией. Это решение отменяет текущую анимацию и выполняет анимацию с новым значением:

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
}()

Swift 5

import Foundation

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)
            }
        }

    }
}

Расширение Swift:

  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)
        }
    }
}

Или вы можете использовать NSTimer вместо циклов while и executeSelector.

finalValue - это значение, которого вы хотите достичь.

Таймер срабатывает 30 раз с длительностью 0,02 секунды для каждого (вы можете выбрать что-то другое, но плавно) и изменяет значение яркости.

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
}

Основываясь на отличном ответе Чарли Прайса, вот метод для "анимации" изменения яркости экрана до любого желаемого значения.

- (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];
        });
    }
}

Вы можете использовать этот помощник, если вам нужно изменить яркость вашего конкретного 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
    }
}

Вызовите ScreenBrightness.shared.brightenDisplay() в viewWillAppear, и если вы хотите изменить его, вызовите метод ScreenBrightness.shared.darkenDisplay(), который вернет яркость обратно

Другие вопросы по тегам