How to merge UIView from "snapshotViewAfterScreenUpdates" and create Video - ios

Is there any way to merge all UIView returned from function "snapshotViewAfterScreenUpdates" and create a video from them?
Currently i am taking screenshot of screen in Timer event using below code
timer = NSTimer.scheduledTimerWithTimeInterval(0.10, target: self, selector: NSSelectorFromString("screenshotTimerEvent"), userInfo: nil, repeats: true)
// Timer event function to capture screenshot and add in Array
func screenshotTimerEvent()
{
self.arrImagesScreenShots.addObject(self.view.snapshotViewAfterScreenUpdates(true))
}
Then after that when the recording is stopped i use below code to show all those screenshots as a video.
timer = NSTimer.scheduledTimerWithTimeInterval(0.10, target: self, selector: NSSelectorFromString("showImages"), userInfo: nil, repeats: true)
// In this function i am adding uiview from start to end so it shows like a video is playing
func showImages()
{
oldView.removeFromSuperview()
if(arrImagesScreenShots.count > count)
{
oldView = arrImagesScreenShots.objectAtIndex(count) as! UIView
self.view.addSubview(oldView)
count += 1
}
else
{
timer.invalidate()
count = 0
}
}
I am not sure if this is the best way to do it but for now its working.
Next thing i want is to merge these UIView into a video and upload to server.
Is there any way?

Related

Scheduled timers in SpriteKit

I have scheduled timers that add sprite nodes to the screen as obstacles
func timers(){
personTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(spawnPerson), userInfo: nil, repeats: true)
bikeTimer = Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(spawnBike), userInfo: nil, repeats: true)
motorcycleTimer = Timer.scheduledTimer(timeInterval: 2.5, target: self, selector: #selector(spawnMotorcycle), userInfo: nil, repeats: true)
}
I added a function to invalidate those timers. so that a bonus "level" can be ran.
func invalidateTimers(){
// Obstacles
personTimer.invalidate()
bikeTimer.invalidate()
motorcycleTimer.invalidate()
}
When the bonus is called
func bonus() {
invalidateTimers()
bonusTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(spawnDiamonds), userInfo: nil, repeats: true)
}
The problem that I'm having is that when the bonus is done running I invalidate the bonusTimer and recall timers(). But when I do all the timers in the function seem to be firing twice. Whats an easy workaround for that since they can't just be paused.
Instead of using timers, consider using SKActions, as they work well with SpriteKit. To start the timer, run:
let wait1 = SKAction.wait(forDuration: 1)
let personTimer = SKAction.repeatForever(SKAction.sequence([wait1, SKAction.run {
spawnPerson() // spawnBike() etc. for each different timer
}]))
self.run(personTimer, withKey: "spawnPerson")
with modified wait values and function calls for each different timer. Then to stop the timer, run:
self.removeAction(forKey: "spawnPerson")
for each action using a different key.
Instead of using timers, you can use update: method of your SKScene subclass. It calls once per frame and has currentTime parameter. So you can easily calculate time intervals you need and trigger corresponding methods.

swift invalidate timer doesn't work

I have this problem for a few days now and I don't get what I am doing wrong.
My application is basically just creating some timers. I need to stop them and create new ones. But at the moment stopping them doesn't work.
self.timer = NSTimer.scheduledTimerWithTimeInterval(timeInterval, target:self, selector: "timerDidEnd:", userInfo: "Notification fired", repeats: false)
That's my timer
func timerDidEnd(timer:NSTimer){
createUnrepeatedAlarmWithUpdateInterval()
}
Because my timer didn't want to stop I am currently using the unrepeated timer and start it myself after it stopped.
func stopAlarm() {
if self.timer != nil {
self.timer!.invalidate()
}
self.timer = nil
self.timer = NSTimer()
}
And that's how I stop my timer.
alarmManager.stopAlarm()
alarmManager.createUnrepeatedAlarmWithUpdateInterval()
I call the stopAlarm() function before creating a new timer.
I really don't know what I am doing wrong so I appreciate every answer :)
class AlarmManager: ViewController{
private var timer : NSTimer?
private var unrepeatedTimer : NSTimer?
private let notificationManager = NotificationManager()
private var current = NSThread()
private let settingsViewController = SettingsViewController()
func createRepeatedAlarmWithUpdateInterval(){
var timeInterval:NSTimeInterval = settingsViewController.getUpdateIntervalSettings()
if timer == nil{
timer = NSTimer.scheduledTimerWithTimeInterval(timeInterval,
target: self,
selector: "repeatedTimerDidEnd:",
userInfo: "Notification fired",
repeats: true)
}
}
func repeatedTimerDidEnd(repeatedTimer:NSTimer){
ConnectionManager.sharedInstance.loadTrainings(settingsViewController.getServerSettings())
createUnrepeatedAlarm(10)
}
func createUnrepeatedAlarm(timeInterval:Double){
unrepeatedTimer = NSTimer.scheduledTimerWithTimeInterval(timeInterval,
target: self,
selector: "unrepeatedTimerDidEnd:",
userInfo: "Notification fired",
repeats: false)
}
func unrepeatedTimerDidEnd(unrepeatedTimer:NSTimer){
notificationManager.createNotification(self, reminderType: NotificationManager.ITEMRATINGREMINDER)
notificationManager.createNotification(self, reminderType: NotificationManager.ITEMREMINDER)
print("UnrepeatedAlarm ended")
}
func stopAlarm(){
print("StopAlarm triggered")
if (timer != nil)
{
print("stoptimer executed")
timer!.invalidate()
timer = nil
}
if (unrepeatedTimer != nil)
{
unrepeatedTimer!.invalidate()
unrepeatedTimer = nil
}
}
}
Thats the whole code of this class. Maybe that helps :D
The usual way to start and stop a timer safely is
var timer : Timer?
func startTimer()
{
if timer == nil {
timer = Timer.scheduledTimer(timeInterval: timeInterval, target: self, selector: #selector(timerFired), userInfo: nil, repeats: true)
}
}
func stopTimer()
{
timer?.invalidate()
timer = nil
}
startTimer() starts the timer only if it's nil and stopTimer() stops it only if it's not nil.
You have only to take care of stopping the timer before creating/starting a new one.
Make sure you're calling invalidate on the same thread as the timer.
From the documentation:
Special Considerations
You must send this message from the thread on which the timer was installed. If you send this message from another thread, the input source associated with the timer may not be removed from its run loop, which could prevent the thread from exiting properly.
https://developer.apple.com/documentation/foundation/nstimer/1415405-invalidate?language=objc
Something that's not really covered by the previous answers is that you should be careful your timer isn't scheduled multiple times.
If you schedule a timer multiple times without first invalidating it, it'll end up scheduled on multiple run loops, and invalidating it then becomes nigh impossible.
For me, it happened when calling my scheduleTimer() function in separate functions in my view controller's life cycle (viewWillAppear, viewDidAppear, ...)
So in short, if you aren't sure (or you cannot guarantee) your Timer is only scheduled once, just always invalidate it first.
I have tried every possible solution found but not able to resolve that at the end I have set repeat "false" while initialising timer like below
self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(viewcontroller.methodname), userInfo: nil, repeats: false)
And need to add above line in my selector method for whatever the condition for which I wanted to repeat the time.
For example:-
My requirement is I want to repeatedly call some method until one condition satisfied. So instead of adding repeats true I set it false as repeat true does not invalidate timer in my case.
I have added below in my viewdidload method
self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(viewcontroller.method), userInfo: nil, repeats: false)
in selector function I added below code
#objc func method{
if condition not matched{
self.timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(viewcontroller.method), userInfo: nil, repeats: false)
}
else{
// once you are here your timer invalidate automatically
}
}
Hope this will solve your problem
For Swift 5 Xcode 12.4 there is example to use timer:
class MyController: UIViewController {
private id: Float;
func setValue(_ value: Float, withAnimation: Bool) {
let step: Float = value / 200
var current: Float = withAnimation ? 0.0 : value
let _ = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: withAnimation) { timer in
DispatchQueue.main.async {
self.id = current
current += step
if current > value || withAnimation == false {
self.id = current
timer.invalidate()
}
}
}
}
}

avplayer finishing song on seekToTime

I'm trying to have avPlayer restart the current song when the skip back button is selected if it's after about 5 seconds into the song, and jump to times according to where the slider is moved to. Here's my code for these functions:
#IBAction func buttonBackTouchDown(sender: AnyObject) {
if (Float(CMTimeGetSeconds((avPlayerItem?.currentTime())!)) > 5) {
//restartSong = true
avPlayer.currentItem!.seekToTime(kCMTimeZero)
//self.loadSongValues()
//restartSongTimer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("setRestartSongToFalse"), userInfo: nil, repeats: false)
}
else if (Float(CMTimeGetSeconds((avPlayerItem?.currentTime())!)) < 5) {
self.previousSong()
}
}
#IBAction func sliderTouchDown(sender: AnyObject) {
self.sliderTimer.invalidate()
avPlayer.pause()
self.overlayOn()
}
#IBAction func sliderTouchUpInside(sender: AnyObject) {
//restartSongTimer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("setRestartSongToFalse"), userInfo: nil, repeats: false)
//NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("startSliderTimer"), userInfo: nil, repeats: false)
self.startLabelTimer()
avPlayer.play()
self.updateNowPlayingInfoCenter()
self.overlayOff()
}
The problem I'm having is that if the song is over 1 minute in, or it's almost finished and I move the slider too far back, it SOMETIMES triggers this observer:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "skipSong", name: AVPlayerItemDidPlayToEndTimeNotification, object: avPlayerItem)
I tried looking in Apple's references and looked to other questions here on StackOverflow, but couldn't find anything.
Maybe I'm not understanding how to properly use CMTime, or something is weird with the files, but this is really weird to me.
Any help is appreciated. Thanks in advance!
I'm experiencing also a lot of Issues with the AVPlayer and particularly the SeekToTime method.
When you do
avPlayer.currentItem!.seekToTime(kCMTimeZero)
avPlayer is Jumping to the 0 Frame and not the First frame as it should. the Frame #0 and the last frame seem to be the Same for the AVPlayer. Try the Following: (like jumping to time 0.001 instead of 0)
let seekTime : CMTime = CMTimeMake(1, 1000)
avPlayer!.seekToTime(seekTime)

Swift: prevent NSTimer starting automatically

I am trying to use the NSTimer to increment the progress bar in my app when recording voice (see the screenshot)
let timedClock = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("Counting:"), userInfo: nil, repeats: true)
internal func Counting(timer: NSTimer!) {
if timeCount == 0 {
//self.timedClock = nil
stopRecording(self) //performs segue to another view controller
} else {
timeCount--;
self.timer.text = "\(timeCount)"
}
print("counting called!")
progressBar.progress += 0.2
}
The progress bar works only for the first time after I compile and run the project. When the recording is finished, the app performs segue to another view controller to play the recorded audio. However, when I go back to the view for recording, the timer/progress bar automatically runs. I suspect the NSTimer object is still alive on the NSRunLoop. So I was wondering how to prevent the NSTimer from automatically running.
Inspired by the answer in this SO thread, I tried the following, but the NSTimer still automatically runs.
let timedClock = NSTimer(timeInterval: 1, target: self, selector: "Counting:", userInfo: nil, repeats: true)
NSRunLoop.currentRunLoop().addTimer(timedClock, forMode: NSRunLoopCommonModes)
This happens because when your controller created it's properties are automatically initialized. According to Apple Docs (and method's name) scheduledTimerWithTimeInterval create and return scheduled timer. So if you only want create your timer and call it by trigger function use it like this:
class MyClass {
var timer: NSTimer?
...
func enableTimer() {
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("Counting:"), userInfo: nil, repeats: true)
}
func disableTimer() {
timer?.invalidate()
timer = nil
}
...
}
Sorry for the quick self-answer, as I just found out that I can use the invalidate() method to prevent the timer from automatically firing:
timedClock.invalidate()
Hope it helps someone in the future!

Swift: NSTimer Alternative

I've made a flappy bird clone, but when trying to implement pause functionality, I've come across issues with my NSTimer, which is spawning the pipes.
var timer = NSTimer.scheduledTimerWithTimeInterval(moveSpeed, target: self, selector: Selector("makePipes"), userInfo: nil, repeats: true)
As NSTimers can't be paused, within the makePipes() function I've implemented a simple if-statement, checking whether the game is paused or not, and if it is, not spawning new pipes. However, the gap between each series of pipes is then inconsistent, due to the timer still firing during the paused state.
Also, when transitioning to the game state from the menu, the timer fires off-time, creating the first two pipes in quick succession.
Is there any alternative to the NSTimer to handle this functionality?
You can stop the timer calling the func invalidate() when your game is paused, and then restart it when your game is un-paused.
Update:
You can add a second timer that fires at the difference from the next fire and the time of pause, the second timer should fire the first timer and then reset the first timer to the initial time.
Steps:
Add lets say a timer that fires every 2 seconds
When game is paused, calculate the time interval from the timer.fireDate and timeOfPause, that should be intervalTillNextTrigger
Add a second afterPauseTimer that triggers at intervallTillNextTrigger and should not repeat
When the afterPauseTimer is called, trigger the first timer with timer.fire(), invalidate timer, because timer.fire() will not interrupt it's regular firing schedule, add timer again with 2 seconds firing interval and invalidate afterPauseTimer.
see code below:
//
// ViewController.swift
// swft ios
//
// Created by Marius Fanu on 30/12/14.
// Copyright (c) 2014 Marius Fanu. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
var timer: NSTimer!
var isPaused = false
var isAfterPause = false
var intervalTillNextTrigger: NSTimeInterval = 0
var afterPauseTimer: NSTimer!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
timer = NSTimer(timeInterval: 2, target: self, selector: Selector("timerTriggerd"), userInfo: nil, repeats: true)
NSRunLoop.mainRunLoop().addTimer(timer, forMode: NSDefaultRunLoopMode)
timer.fire()
}
#IBAction func pauseButtonPressed(sender: UIButton) {
var now = NSDate()
println("now = \(now)")
if isPaused == true {
if isAfterPause {
isAfterPause = false
afterPauseTimer = NSTimer(timeInterval: intervalTillNextTrigger, target: self, selector: Selector("timerAfterIntervalTrigger"), userInfo: nil, repeats: false)
NSRunLoop.mainRunLoop().addTimer(afterPauseTimer, forMode: NSDefaultRunLoopMode)
}
timer = NSTimer(timeInterval: 2, target: self, selector: Selector("timerTriggerd"), userInfo: nil, repeats: true)
NSRunLoop.mainRunLoop().addTimer(timer, forMode: NSDefaultRunLoopMode)
}
else {
isAfterPause = true
intervalTillNextTrigger = timer.fireDate.timeIntervalSinceDate(now)
println("till next trigger \(intervalTillNextTrigger)")
timer.invalidate()
timer = nil
}
isPaused = !isPaused
}
func timerTriggerd() {
NSLog("Triggerd!")
}
func timerAfterIntervalTrigger() {
println("reset timer")
timer.fire()
timer.invalidate()
timer = nil
timer = NSTimer(timeInterval: 2, target: self, selector: Selector("timerTriggerd"), userInfo: nil, repeats: true)
NSRunLoop.mainRunLoop().addTimer(timer, forMode: NSDefaultRunLoopMode)
afterPauseTimer.invalidate()
afterPauseTimer = nil
}
}

Resources