How can we programmatically change the brightness of the iPhone screen? - iphone

How can I change the brightness of the screen programmatically using iPhone SDK?

[[UIScreen mainScreen] setBrightness: yourvalue];
Requires iOS 5.0 or later. yourvalue is a float between 0.0 and 1.0.

UPDATE: For Swift 3
UIScreen.main.brightness = YourBrightnessValue
Here's the swift answer to perform this
UIScreen.mainScreen().brightness = YourBrightnessValue
YourBrightnessValue is a float between 0.0 and 1.0

I had some problems with changing the screen brightness in viewDidLoad/viewWillDisappear so I created a singleton class to handle all the action. This is how I do it:
import Foundation
import UIKit
final class ScreenBrightnessHelper {
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
}
}

Related

Fade out AVAudioEngine AVAudioPlayerNode

I´m trying to fade out an mp3 file using AVAudioEngine and AVAudioPlayerNode. I cannot figure it out. Can anybody please help in Swift?
I tried some things already. Everything works quite good but not as smooth as I would like it to be.
var fadeOutPlayer: Float = playerVolume
var fadeOutQuinte: Float = playerQuinteVolume
while fadeOutPlayer > 0.0 {
player.volume = fadeOutPlayer
playerQuinte.volume = fadeOutQuinte
fadeOutPlayer -= 0.001
fadeOutQuinte -= 0.001
usleep(100)
}
I also tried a timer (which doesn't do a fade):
if !isTimerFadeOutRunning {
player.volume = 0
playerQuinte.volume = 0
counter = 0
timerFadeOut = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(runTimerFadeOut), userInfo: nil, repeats: true)
isTimerFadeOutRunning = true
}
}
#objc func runTimerFadeOut() {
counter += 0.1
if counter == 0.2 {
player.pause()
playerQuinte.pause()
}
}
I've attached an AVAudioUnitEQ to my flow before mainMixerNode and then controlled the globalGain. The following code shows the idea:
private func manageFadeOut(volume: Double, fadeOutSecs: Double) {
let timeSecs = 0.001
DispatchQueue.main.asyncAfter(deadline: .now() + timeSecs) {
if !self.isFadingOut { return }
for fx in self.fadeOutEffects { fx.globalGain = Float(-96 * (1.0 - volume)) }
if volume <= 0 {
for player in self.players { player.stop() }
self.isFadingOut = false
return
}
let fade = timeSecs / fadeOutSecs
self.manageFadeOut(volume: volume - fade, fadeOutSecs: fadeOutSecs)
}
}

Using UIPercentDrivenInteractiveTransition with CABasicAnimation has weird glitch

I'm implementing custom transition using CABasicAnimation and UIView.animate both. Also need to implement a custom interactive transition using UIPercentDrivenInteractiveTransition which exactly copies the behavior of the native iOS swipe back. Animation without a back swipe gesture (when I'm pushing and popping by the back arrow) works fine and smoothly. Moreover, swipe back also works smoothly, except when the gesture velocity is more than 900
Gesture Recognition function:
#objc func handleBackGesture(_ gesture: UIScreenEdgePanGestureRecognizer) {
guard animationTransition != nil else { return }
switch gesture.state {
case .began:
interactionController = TransparentNavigationControllerTransitionInteractor(duration: anumationDuration)
popViewController(animated: true)
case .changed:
guard let view = gesture.view?.superview else { return }
let translation = gesture.translation(in: view)
var percentage = translation.x / view.bounds.size.width
percentage = min(1.0, max(0.0, percentage))
shouldCompleteTransition = percentage > 0.5
interactionController?.update(percentage)
case .cancelled, .failed, .possible:
if let interactionController = self.interactionController {
isInteractiveStarted = false
interactionController.cancel()
}
case .ended:
interactionController?.completionSpeed = 0.999
let greaterThanMaxVelocity = gesture.velocity(in: view).x > 800
let canFinish = shouldCompleteTransition || greaterThanMaxVelocity
canFinish ? interactionController?.finish() : interactionController?.cancel()
interactionController = nil
#unknown default: assertionFailure()
}
}
UIPercentDrivenInteractiveTransition class. Here I'm synchronizing layer animation.
final class TransparentNavigationControllerTransitionInteractor: UIPercentDrivenInteractiveTransition {
// MARK: - Private Properties
private var context: UIViewControllerContextTransitioning?
private var pausedTime: CFTimeInterval = 0
private let animationDuration: TimeInterval
// MARK: - Initialization
init(duration: TimeInterval) {
self.animationDuration = duration * 0.4 // I dk why but layer duration should be less
super.init()
}
// MARK: - Public Methods
override func startInteractiveTransition(_ transitionContext: UIViewControllerContextTransitioning) {
super.startInteractiveTransition(transitionContext)
context = transitionContext
pausedTime = transitionContext.containerView.layer.convertTime(CACurrentMediaTime(), from: nil)
transitionContext.containerView.layer.speed = 0
transitionContext.containerView.layer.timeOffset = pausedTime
}
override func finish() {
restart(isFinishing: true)
super.finish()
}
override func cancel() {
restart(isFinishing: false)
super.cancel()
}
override func update(_ percentComplete: CGFloat) {
super.update(percentComplete)
guard let transitionContext = context else { return }
let progress = CGFloat(animationDuration) * percentComplete
transitionContext.containerView.layer.timeOffset = pausedTime + Double(progress)
}
// MARK: - Private Methods
private func restart(isFinishing: Bool) {
guard let transitionLayer = context?.containerView.layer else { return }
transitionLayer.beginTime = transitionLayer.convertTime(CACurrentMediaTime(), from: nil)
transitionLayer.speed = isFinishing ? 1 : -1
}
}
And here is my Dismissal animation function in UIViewControllerAnimatedTransitioning class
private func runDismissAnimationFrom(
_ fromView: UIView,
to toView: UIView,
in transitionContext: UIViewControllerContextTransitioning) {
guard let toViewController = transitionContext.viewController(forKey: .to) else { return }
toView.frame = toView.frame.offsetBy(dx: -fromView.frame.width / 3, dy: 0)
let toViewFinalFrame = transitionContext.finalFrame(for: toViewController)
let fromViewFinalFrame = fromView.frame.offsetBy(dx: fromView.frame.width, dy: 0)
// Create mask to hide bottom view with sliding
let slidingMask = CAShapeLayer()
let initialMaskPath = UIBezierPath(rect: CGRect(
x: fromView.frame.width / 3,
y: 0,
width: 0,
height: toView.frame.height)
)
let finalMaskPath = UIBezierPath(rect: toViewFinalFrame)
slidingMask.path = initialMaskPath.cgPath
toView.layer.mask = slidingMask
toView.alpha = 0
let slidingAnimation = CABasicAnimation(keyPath: "path")
slidingAnimation.fromValue = initialMaskPath.cgPath
slidingAnimation.toValue = finalMaskPath.cgPath
slidingAnimation.timingFunction = .init(name: .linear)
slidingMask.path = finalMaskPath.cgPath
slidingMask.add(slidingAnimation, forKey: slidingAnimation.keyPath)
UIView.animate(
withDuration: duration,
delay: 0,
options: animationOptions,
animations: {
fromView.frame = fromViewFinalFrame
toView.frame = toViewFinalFrame
toView.alpha = 1
},
completion: { _ in
toView.layer.mask = nil
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
})
}
I note that glitch occurs only when a swipe has a grand velocity.
Here a video with the result of smooth animation at normal speed and not smooth at high speed - https://youtu.be/1d-kTPlhNvE
UPD:
I've already tried to use UIViewPropertyAnimator combine with
interruptibleAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating
But the result is another type of glitching.
I've solved the issue, just change a part of restart function:
transitionLayer.beginTime =
transitionLayer.convertTime(CACurrentMediaTime(), from: nil) - transitionLayer.timeOffset
transitionLayer.speed = 1
I don't really understand why, but looks like timeOffset subtraction works!

How to modify view.alphaValue at runtime and take effect

I modified the alphaValue at runtime but has not any effect , what is the problem. how to modify the alphaValue at runntime ?
I want to adjust the alphaValue of view at runtime
Xcode 10.2.1 Swift 4.2
//
// ViewController.swift
// macosappalphaValuetest
//
// Created by miller on 2019/7/25.
// Copyright © 2019 miller. All rights reserved.
//
import Cocoa
class ViewController: NSViewController {
#IBAction func btnclick1(_ sender: Any) {
self.view.alphaValue = 0.8
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
override func viewWillAppear() {
super.viewWillAppear()
view.window?.isOpaque = false
view.window?.backgroundColor = NSColor(red: 0.5, green: 0.5, blue: 0.5, alpha: 0.5)
self.view.alphaValue = 0.4
}
}
In NSViewController's viewWillAppear method your view's window is probably nil. You can check it by printing print(view.window). So you need to get the correct instance of your window. See below method:
func validWindow() -> NSWindow? {
var window = NSApplication.shared.mainWindow
if (window == nil) {
window = NSApplication.shared.windows.first
}
return window
}
And just write below code to make your window transparent:
guard let window = self.validWindow() else { return }
window.alphaValue = 0.5
It works well for me.
Output:
Increasing and Decreasing alpha value recursively:
func decreaseAlpha(_ window: NSWindow) {
let alpha = window.alphaValue - 0.1
if alpha < 0.0 {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
self.increaseAlpha(window)
}
return
}
window.alphaValue = alpha
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
self.decreaseAlpha(window)
}
}
func increaseAlpha(_ window: NSWindow) {
let alpha = window.alphaValue + 0.1
if alpha > 1.0 {
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
self.decreaseAlpha(window)
}
return
}
window.alphaValue = alpha
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
self.increaseAlpha(window)
}
}
And write this in viewDidLoad: method:
guard let window = self.validWindow() else { return }
decreaseAlpha(window)
Output:
alphaValue is the Objective-C version of alpha. Applying alpha on the viewController view will make it slightly transparent and possible show the viewController that is beneath it if there is one. If there are no
view controllers beneath it, it will lean towards a darker background as the default background is black.

Swift - Animate Image sequence

I have a list of images that I want to animate in Swift. I have tried to find the best possible way to do that - but I still have some problems.
The way I animate my image-list right now is by:
var animatedImage = UIImageView()
var velkomstImgList = [UIImage]()
override func viewDidAppear(_ animated: Bool) {
animatedImage.frame = CGRect(x: 0, y: 0, width: self.scrollView.frame.width, height: 200)
animatedImage.contentMode = .scaleAspectFit
scrollView.addSubview(animatedImage)
animateVelkomst()
}
func animateVelkomst() {
for i in 0...150 {
velkomstImgList.append(UIImage(named:"Velkomst_\(i).png")!)
if i == 150 {
self.animatedImage.animationImages = self.velkomstImgList
self.animatedImage.animationDuration = 5.0
self.animatedImage.startAnimating()
}
}
}
It works and the animation appears as expected. But the for loop in the beginning takes pretty long time and I dont think this is the right way to show the animation. Any suggestions on how I should animate the image sequence?
loading is slow because you are attempt to load 150 images same time. you can use timer and load image when you need it.
let imageCount = 150
var frameIndex = 0
var timer: Timer?
func startAnimation() {
frameIndex = 0
let deltaTime = 5.0 / Double(imageCount)
timer = Timer(timeInterval: deltaTime, target: self, selector: #selector(updateFrame), userInfo: nil, repeats: true)
}
func stopAnimation() {
timer?.invalidate()
timer = nil
}
#objc func updateFrame(_ timer: Timer) {
self.animatedImage.image = UIImage(named:"Velkomst_\(frameIndex).png")
frameIndex += 1
if frameIndex > imageCount {
frameIndex = 0
}
}

Swift 3: class doesn't function correctly

So I've got a class named FlashyPaddleEffect. I also mention it in another class, GameScene, but it doesn't apply any effect that it should. The paddle should be flashing blue and white colors, but it simply stays white. The paddle also should lose its physics body when it is blue.
If you need any other information, don't hesitate to ask.
NOTE: There might be problems with the code I gave (because of indents, it's quite tricky to make code by indenting 4 spaces every line, sorry).
import SpriteKit
import GameplayKit
class FlashyPaddleEffect {
var node = SKSpriteNode()
var ballNode = SKSpriteNode()
var updateTimer: Timer? = nil
var timer: Timer? = nil
#objc func changeNodeColor() {
switch node.color {
case SKColor.blue: node.color = SKColor.white
case SKColor.white: node.color = SKColor.blue
default: _ = 1 + 2
}
}
#objc func update() //I used the objc prefix to silence the warning the selectors of the timers produced. {
let previousPhysicsBody = node.physicsBody
if node.color == SKColor.blue {
node.physicsBody = nil
}
node.physicsBody = previousPhysicsBody
}
func make(applyEffectTo: SKSpriteNode, ball: SKSpriteNode) {
node = applyEffectTo
ballNode = ball
timer = Timer.scheduledTimer(timeInterval: 0.6, target: self, selector: #selector(changeNodeColor), userInfo: nil, repeats: true)
updateTimer = Timer.scheduledTimer(timeInterval: 0.3, target: self, selector: #selector(update), userInfo: nil, repeats: true)
_ = node
_ = timer
}
}
class GameScene: SKScene {
var ball = SKSpriteNode()
var player = SKSpriteNode()
var enemy = SKSpriteNode()
var scores = [0, 0]
var initScores = [0, 0]
var scoreLabels: [SKLabelNode]? = nil
let playLabel = SKLabelNode()
let timeLabel = SKLabelNode()
let timeUpLabel = SKLabelNode()
var secondsLeft: Int = 180
var initialTime: Int? = nil
var timer = Timer()
var amountOfPauseMenuCloses = 0
var resultsLabel = SKLabelNode()
let flashEffect = FlashyPaddleEffect() //I define a variable to mention the class easier later on.
override func didMove(to view: SKView) {
//Initialize nodes of the scene editor.
ball = self.childNode(withName: "Ball") as! SKSpriteNode
player = self.childNode(withName: "PPaddle") as! SKSpriteNode
enemy = self.childNode(withName: "EPaddle") as! SKSpriteNode
//Set styles for the Play Notification Label (referred to as PNL later)
playLabel.position = CGPoint(x: 0, y: 0)
playLabel.text = "Tap anywhere to play."
playLabel.name = "playLabel"
//Set styles for the Timer Label (referred to as Timer later)
timeLabel.position = CGPoint(x: 90, y: 0)
timeLabel.text = String(secondsLeft)
//Doing manipulations connected with scores here.
scores = [0, 0]
initScores = scores
scoreLabels = [self.childNode(withName: "PScoreLabel") as! SKLabelNode, self.childNode(withName: "EScoreLabel") as! SKLabelNode]
//Create a border for our ball to bounce off.
let border = SKPhysicsBody(edgeLoopFrom: self.frame)
border.friction = 0
border.restitution = 1
border.linearDamping = 0
border.angularDamping = 0
self.physicsBody = border
//To avoid the ball's damping.
ball.physicsBody?.linearDamping = 0
ball.physicsBody?.angularDamping = 0
showPause() //Show the (pause) menu at the beginning
//Set a variable to refer to as a time standard later.
initialTime = secondsLeft
//The game timer.
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(reduceSecondFromTimer), userInfo: nil, repeats: true)
flashEffect.make(applyEffectTo: player, ball: ball) //This is where I reference the class in another class.
}
//A function to show the (pause) menu.
func showPause(hideNodes: Bool = true) {
self.addChild(playLabel)
if hideNodes == true {
ball.removeFromParent()
player.removeFromParent()
enemy.removeFromParent()
scoreLabels?[0].removeFromParent()
scoreLabels?[1].removeFromParent()
}
}
override func update(_ currentTime: TimeInterval) {
var alreadyChangedTextLabel = false
for i in scoreLabels! {
if i.name == "PScoreLabel" {i.text = String(scores[0])}
if i.name == "EScoreLabel" {i.text = String(scores[1])}
}
if ball.position.y <= player.position.y {scores[1] += 1}
if ball.position.y >= enemy.position.y {scores[0] += 1}
if secondsLeft == 0 {
showPause(hideNodes: false)
if alreadyChangedTextLabel == false {
timeUpLabel.text = "TIME UP! \(whoIsWinning(scores: scores)) won!"
alreadyChangedTextLabel = true
}
timeUpLabel.name = "timeUpLabel"
timeUpLabel.position = CGPoint(x: 0, y: 180)
if !(self.children.contains(timeUpLabel)) {
self.addChild(timeUpLabel)
}
timeLabel.removeFromParent()
}
if self.children.contains(playLabel) {
secondsLeft = initialTime!
}
timeLabel.text = String(secondsLeft)
let alignWithBall = SKAction.move(to: CGPoint(x: ball.position.x, y: enemy.position.y), duration: 0.8)
enemy.run(alignWithBall)
}
func initGame() {
//Initializing process for every game.
scores = initScores
ball.position = CGPoint(x: 0, y: 0)
if amountOfPauseMenuCloses == 1 {ball.physicsBody?.applyImpulse(CGVector(dx: 15, dy: 15))}
secondsLeft = initialTime!
timeLabel.text = String(secondsLeft)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for childNode in self.children {
if childNode.name == "playLabel" {
if self.children.contains(playLabel) {
playLabel.removeFromParent()
}
if !(self.children.contains(player)) {
self.addChild(player)
}
if !(self.children.contains(enemy)) {
self.addChild(enemy)
}
if !(self.children.contains(ball)) {
self.addChild(ball)
}
if !(self.children.contains((scoreLabels?[0])!)) {
self.addChild((scoreLabels?[0])!)
}
if !(self.children.contains((scoreLabels?[1])!)) {
self.addChild((scoreLabels?[1])!)
}
if !(self.children.contains(timeLabel)) {
self.addChild(timeLabel)
}
if self.children.contains(timeUpLabel) {
timeUpLabel.removeFromParent()
}
amountOfPauseMenuCloses += 1
initGame()
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches {
if self.nodes(at: t.location(in: self)).contains(player) {player.position.x = t.location(in: self).x}
}
}
func reduceSecondFromTimer() {
secondsLeft -= 1
}
func whoIsWinning(scores: Array<Int>) -> String {
var r: String? = nil
if scores[0] >= scores[1] {
r = "You"
}
if scores[1] >= scores[0] {
r = "Enemy"
}
return r!
}
}
Thanks a lot for answers.
P.S It's my first question ever so don't judge me strictly.
1) Do not use NSTimer use SKAction. I can see the way you are doing it you stack timer after timer, this is bad.
2) Do not have your temp variable global (node in this case), it makes code hard to read
3) Do not remove your physics body, simply remove the category.
func make(applyEffectTo: SKSpriteNode, ball: SKSpriteNode) {
ballNode = ball
let blue = SKAction.colorize(with SKColor.blue, colorBlendFactor: 1.0, duration sec: 0)
let white = SKAction.colorize(with SKColor.white, colorBlendFactor: 1.0, duration sec: 0)
let wait = SKAction.wait(for:0.6)
let turnOnPhysics = SKAction.run({applyEffectTo.physicsBody?.categoryBitmask = #######})
let turnOffPhysics = SKAction.run({applyEffectTo.physicsBody?.categoryBitmask = 0})
let seq = [blue, turnOffPhysics,wait,white,turnOnPhysics,wait]
let repeat = SKAction.repeatForever(seq)
applyEffectTo.run(repeat, withKey:"flashing")
}
Note: I have no idea what your categoryBitmask is, you need to fill it in