time delay on focus action - ios

I've got a Collection View and have arranged it in such a way that there is a single column on one side of the screen and content that is replaced based on what collection item is in focus.
I'd like to be able to on swap the content out if focus on an item has been held for more then .5 seconds.
Here is what I currently have, and it swaps the data out instantly.
if self.focused {
self.label.alpha = 1
self.priceLabel.alpha = 1
if self.representedDataItem?.imageUrl! == "https://s3-us-west-2.amazonaws.com/random/image.png" ||
self.representedDataItem?.imageUrl! == "" {
self.backgroundImage.image = UIImage(named: "titleImage")
}
else {
ImageCache.sharedLoader.imageForUrl((self.representedDataItem?.imageUrl!)!, completionHandler:{(image: UIImage?, url: String) in
self.backgroundImage.image = image!
})
}
}
else {
self.label.alpha = 0.2
self.priceLabel.alpha = 0.2
}

Don't know the self.focused how to determine, assume you already took care of it. Therefore, you could use GCD to delay the execution while focused.
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.5 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
// code to be executed after 0.5 sec
}

if self.focused {
self.label.alpha = 1
self.priceLabel.alpha = 1
}
else {
self.label.alpha = 0.2
self.priceLabel.alpha = 0.2
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.5 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
// code to be executed after 0.5 sec
if self.focused {
if self.representedDataItem?.imageUrl! == "https://s3-us-west-2.amazonaws.com/random/image.png" ||
self.representedDataItem?.imageUrl! == "" {
self.backgroundImage.image = UIImage(named: "titleImage")
}
else {
ImageCache.sharedLoader.imageForUrl((self.representedDataItem?.imageUrl!)!, completionHandler:{(image: UIImage?, url: String) in
self.backgroundImage.image = image!
})
}
}
}

Related

Coreplot Animation of a sequence of plots

I have a number of plots, which depict (x,y) data at different time intervals, and wish to plot them in a sequence one after the other(like a gif file). My approach was generate all the plots, use a Timer.scheduledTimer and initially hide all plots, unhiding current plot and hiding previous plot at each fired schedule. The time between each plot hiding/unhiding shows a blank graph for more time than the plots are shown. Each plot has 32x32 data points. How can I speed this up, so I never see a blank graph? Another approach was to fade out one plot, whilst introducing the next, but I see the same effect.
#objc func tapAnimationButton(_ sender: Any) {
isAnimating = !isAnimating
plotSpacesUserInteraction = !plotSpacesUserInteraction
if let _animationButton = animationButton {
_animationButton.isSelected = isAnimating
previousAnimatedPlot = nil
if isAnimating {
animationCounter = 0
for i in 0..<plotDetails.count {
// fieldsplots[i].isHidden = true
fieldsplots[i].opacity = 0.0
}
animationTimer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(animateGraph(_:)), userInfo: nil, repeats: true)
if let _animationTimer = animationTimer {
animateGraph(_animationTimer)
}
}
else {
animationTimer?.invalidate()
animationTimer = nil
}
}
}
#objc func animateGraph(_ timer: Timer) {
if animationSeconds > 120.0 {
timer.invalidate()
self.animationTimer = nil
animationSeconds = 0.0
animationCounter = 0
previousAnimatedPlot = nil
}
else {
if let currentAnimatedPlot = self.graph.plot(at: animationCounter) {
// previousAnimatedPlot?.isHidden = true
// currentAnimatedPlot.isHidden = false
previousAnimatedPlot?.opacity = 1.0
let fadeOutAnimation = CABasicAnimation(keyPath: "opacity")
fadeOutAnimation.duration = 0.1
fadeOutAnimation.isRemovedOnCompletion = false
fadeOutAnimation.fillMode = CAMediaTimingFillMode.forwards
fadeOutAnimation.toValue = Float(0.0)
previousAnimatedPlot?.add(fadeOutAnimation, forKey: "animateOpacity")
currentAnimatedPlot.opacity = 0.0
let fadeInAnimation = CABasicAnimation(keyPath: "opacity")
fadeInAnimation.duration = 0.1
fadeInAnimation.isRemovedOnCompletion = false
fadeInAnimation.fillMode = CAMediaTimingFillMode.forwards
fadeInAnimation.toValue = Float(1.0)
currentAnimatedPlot.add(fadeInAnimation, forKey: "animateOpacity")
previousAnimatedPlot = currentAnimatedPlot
}
animationSeconds += 0.5
animationCounter += 1;
if animationCounter >= plotDetails.count {
animationCounter = 0
}
}
}
The fade in/out method actually works...not sure how I missed that.
As an add-on to answer, in order to produce a gif image one needs to hide/unhide the plots in sequence
for currentPlot in self.graph.allPlots() {
currentPlot.isHidden = true
}
var images: [UIImage] = []
var previousPlot: CPTPlot?
for currentPlot in self.graph.allPlots() {
if let _previousPlot = previousPlot {
_previousPlot.isHidden = true
}
currentPlot.isHidden = false
if let image = graph.imageOfLayer() {
images.append(image)
}
previousPlot = currentPlot
}
for currentPlot in self.graph.allPlots() {
currentPlot.isHidden = false
}
if images.count > 2 {
TwoDPlot_Utilities.GIFExport(with: images, plot: thisplot, plotIndex: plotIndex, frameDelay: 0.5)
}

How to detect apple watch position using accelerometer and Gravity in swift?

I have creating an application for apple watch. The Logic is, when the user rise their hand and tap a button from app. At that time I will fetch the accelerometer values. And whenever user rise their hand and meet the captured position, I have to send message to iPhone.
For me am getting the values correctly But, It will always give the values based on accelerometer. Which means user doesn't rise the hand but accelerometer values matched. So values will send to mobile.
func startUpadateAccelerometer() {
self.motionManager.accelerometerUpdateInterval = 1.0 / 10.0
self.motionManager.startAccelerometerUpdates(to: OperationQueue()) { (accelerometerData, error) -> Void in
guard accelerometerData != nil else
{
print("There was an error: \(String(describing: error))")
return
}
DispatchQueue.main.async {
if(self.can_reset){
let differenceX : Bool = self.validateButtom(currentValue: accelerometerData!.acceleration.x, inititalValue: self.gravityReference.x)
let differenceY : Bool = self.validateButtom(currentValue: accelerometerData!.acceleration.y, inititalValue: self.gravityReference.y)
if(differenceX && differenceY && self.gravityOffsetDifference(currentValue: accelerometerData!.acceleration.x, referenceValue: self.gravityReference.x ) && self.gravityOffsetDifference(currentValue: accelerometerData!.acceleration.y, referenceValue: self.gravityReference.y)){
WKInterfaceDevice().play(.success)
// self.addLog(_logStr: EventsTypes.Achievements1.rawValue)
self.logString += String(format: "X: %0.3f Y: %0.3f Z: %0.3f \n", accelerometerData!.acceleration.x,accelerometerData!.acceleration.y,accelerometerData!.acceleration.z)
self.m_XYZValueLbl.setText(self.logString)
self.is_RechedZeroPos = true
self.session?.sendMessage(["msg" : "\(self.logString)"], replyHandler: nil) { (error) in
NSLog("%#", "Error sending message: \(error)")
}
} else {
if(self.checkAchievements2_3(deviceMotionData: accelerometerData!.acceleration) == true) {
if self.is_RechedZeroPos == true {
self.addLog(_logStr: EventsTypes.Achievements2.rawValue)
self.is_RechedZeroPos = false
} else {
self.addLog(_logStr: EventsTypes.Achievements3.rawValue)
}
}
}
} else {
self.gravityReference = accelerometerData!.acceleration
//self.logString = String(format: "Reference Acceleration %0.3f %0.3f %0.3f \n", self.gravityReference.x,self.gravityReference.y,self.gravityReference.z)
self.can_reset = true
}
}
}
}
func validateButtom(currentValue : Double , inititalValue : Double) -> Bool {
if( currentValue == 0 && inititalValue == 0) {
return true
} else if( currentValue < 0 && inititalValue < 0) {
return true
} else if( currentValue > 0 && inititalValue > 0) {
return true
} else {
return false
}
}
func gravityOffsetDifference(currentValue : Double , referenceValue: Double) -> Bool {
var difference : Double!
if (fabs(currentValue) <= fabs(referenceValue)) {
difference = fabs(referenceValue) - fabs(currentValue)
} else {
difference = fabs(currentValue) - fabs(referenceValue)
}
if (difference <= gravityOffset ) {
return true
} else {
return false
}
}
Please guide me to get the values only when the user captured the position.
Accelerometers measure changes in velocity along the x, y, and z axes
Fetching accelerometer data
let motion = CMMotionManager()
func startAccelerometers() {
// Make sure the accelerometer hardware is available.
if self.motion.isAccelerometerAvailable {
self.motion.accelerometerUpdateInterval = 1.0 / 60.0 // 60 Hz
self.motion.startAccelerometerUpdates()
// Configure a timer to fetch the data.
self.timer = Timer(fire: Date(), interval: (1.0/60.0),
repeats: true, block: { (timer) in
// Get the accelerometer data.
if let data = self.motion.accelerometerData {
let x = data.acceleration.x
let y = data.acceleration.y
let z = data.acceleration.z
// Use the accelerometer data in your app.
}
})
// Add the timer to the current run loop.
RunLoop.current.add(self.timer!, forMode: .defaultRunLoopMode)
}
}
Important
If your app relies on the presence of accelerometer hardware, configure the UIRequiredDeviceCapabilities key of its Info.plist file with the accelerometer value. For more information about the meaning of this key, see Information Property List Key Reference.

UIImageView wait for animation complete

I'm trying to execute 2 different animations, after the first complete, using isAnimating.
but, I see only the first animation...
if anims[0] == 1{
startAnimation(image : #imageLiteral(resourceName: "first"))
}
if anims[1] == 2{
while myView.isAnimating {
RunLoop.current.run(until: Date(timeIntervalSinceNow: 0.05))
}
}
startAnimation(image : #imageLiteral(resourceName: "second") , time : Int)
spriteSheet returns UIImage array after cropping..
func startAnimation(image : UIImage , time : Int){
myView.animationImages = image.spriteSheet(cols: 19, rows: 1)
myView.animationDuration = 1
myView.animationRepeatCount = time
myView.startAnimating()
}
You can always chain animations like
UIView.animate(withDuration: 1, animations: {
//do your animation here
}) { (state) in
UIView.animate(withDuration: 1, animations: {
//do your second animation
})
}
If you are using CABasicAnimations then you can use beginTime and duration to chain them up :)
var totalDuration = 0
let baseicAnim1 = CABasicAnimation()
baseicAnim1.beginTime = CACurrentMediaTime()
totalDuration += 10
baseicAnim1.duration = CFTimeInterval(totalDuration)
let basicAnim2 = CABasicAnimation()
basicAnim2.beginTime = CACurrentMediaTime() + CFTimeInterval(totalDuration)
totalDuration += 10
basicAnim2.duration = CFTimeInterval(totalDuration)
EDIT
Using while loop to keep checking if animation has completed its execution or not is never a suggested approach
EDIT :
Try this,
func startAnimation(image : UIImage , time : Int,completionBlock : (()->())?){
let animationDuration = 1
myView.animationImages = image.spriteSheet(cols: 19, rows: 1)
myView.animationDuration = animationDuration
myView.animationRepeatCount = time
myView.startAnimating()
DispatchQueue.main.asyncAfter(deadline: .now() + animationDuration, execute: {
if let block = completionBlock {
block()
}
})
}
Now your startAnimation function takes completion block as its parameter and executes the completion block after animationDuration. So you can get to know when animation ends :)
to chain simply call
self.startAnimation(image: #imageLiteral(resourceName: "first"), time: 1) {
self.startAnimation(image: #imageLiteral(resourceName: "second"), time: 1, completionBlock: nil)
}
Hope it helps

Swift animate with duration not taking duration into account

The frame is getting updated in 0.2 seconds or so, rather than the desired amount (0.5). When I try 5 seconds it still goes the same speed of around 0.2 seconds.
What am I doing wrong?
Here is my entire method:
#IBAction func photoFullscreenButtonReleased(sender: AnyObject) {
photoFullscreen = !photoFullscreen
if photoFullscreen {
contentHeightConstraint.constant = self.view.frame.height
animateUpdateConstraints()
UIView.animateWithDuration(5) {
self.previewLayer!.frame = self.cameraView.bounds
}
}
else {
contentHeightConstraint.constant = contentHeight
animateUpdateConstraints()
UIHelper.delay(0.25, closure: { () -> () in
UIView.animateWithDuration(0.5) {
self.previewLayer!.frame = self.cameraView.bounds
}
})
}
}
Potential function that could be causing problems?
func animateUpdateConstraints() {
UIView.animateWithDuration(0.5) {
self.view.layoutIfNeeded()
}
}

How to set screen brightness with fade animations?

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

Resources