I'm developing a camera app that has a video function. I want the button that the user presses to begin recording, which it does, and stop recording. This works properly. But, I want to raise the alpha of the recordButton to 1.0 when the button is pressed. This works properly, but when it is pressed again to stop recording the alpha remains at 1.0. The if...else statement that stops recording should trigger [self.recordButton setAlpha:0.50], but for some reason nothing happens, aside from recording stopping properly. Any clarification would be greatly appreciated.
- (IBAction)toggleMovieRecording:(id)sender
{
[[self recordButton] setEnabled:NO];
dispatch_async([self sessionQueue], ^{
if (![[self movieFileOutput] isRecording])
{
[self setLockInterfaceRotation:YES];
[self.recordButton setAlpha:1.0];
if ([[UIDevice currentDevice] isMultitaskingSupported])
{
// Setup background task. This is needed because the captureOutput:didFinishRecordingToOutputFileAtURL: callback is not received until the app returns to the foreground unless you request background execution time. This also ensures that there will be time to write the file to the assets library when the app is backgrounded. To conclude this background execution, -endBackgroundTask is called in -recorder:recordingDidFinishToOutputFileURL:error: after the recorded file has been saved.
[self setBackgroundRecordingID:[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil]];
}
// Update the orientation on the movie file output video connection before starting recording.
[[[self movieFileOutput] connectionWithMediaType:AVMediaTypeVideo] setVideoOrientation:[[(AVCaptureVideoPreviewLayer *)[[self previewView] layer] connection] videoOrientation]];
// Turn OFF flash for video recording
[AAPLCameraViewController setFlashMode:AVCaptureFlashModeOff forDevice:[self videoDevice]];
// Start recording to a temporary file.
NSString *outputFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[#"movie" stringByAppendingPathExtension:#"mov"]];
[[self movieFileOutput] startRecordingToOutputFileURL:[NSURL fileURLWithPath:outputFilePath] recordingDelegate:self];
}
else
{
[[self movieFileOutput] stopRecording];
[self.recordButton setAlpha:0.50];
}
});
}
You have to make sure that anything related to UI have to be done on the main queue. Check this answer
iPhone - Grand Central Dispatch main thread
To make sure add this block around setting alpha.
dispatch_async(dispatch_get_main_queue(), ^{
[self.recordButton setAlpha:0.50];
});
Let me know if that worked.
Related
I have this bit of code that I use to switch the root view controller with a nice little animation, it had been working for months... but then randomly stopped working.
UIView snapshot = [self.window snapshotViewAfterScreenUpdates:YES];
[viewController.view addSubview:snapshot];
self.window.rootViewController = viewController;
NSLog(#"check point 1");
[UIView animateWithDuration:0.3 animations:^{
NSLog(#"check point 2");
snapshot.layer.opacity = 0;
NSLog(#"check point 3");
snapshot.layer.transform = CATransform3DMakeScale(1.5, 1.5, 1.5);
NSLog(#"check point 4");
} completion:^(BOOL finished) {
NSLog(#"check point 5");
[snapshot removeFromSuperview];
NSLog(#"check point 6");
}];
I put in these checkpoints, everything through checkpoint 4 triggers.. but 5 and 6 never trigger. So weird to me because even if it failed, the completion block should still trigger.
On the new root view controller that loads, the user's permission to gather his or her location is requested. So maybe when that pop up shows up, it wrecks this transition? It didn't used to though.
The completion block will not call if there is nothing to animate or the transition part already done by some another part of your source code and the code snippet runs on different thread(besides UI thread). Therefore just to illustrate, completion block of below code will never trigger,
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(#"check point 1");
[UIView animateWithDuration:10.3 animations:^{
NSLog(#"check point 4");
} completion:^(BOOL finished) {
NSLog(#"check point 5");
[snapshot removeFromSuperview];
NSLog(#"check point 6");
}];
});
To solve this, Enclose your code within this code,
dispatch_async(dispatch_get_main_queue(), ^{
//Your code, This runs on main thread
});
I'm using Apple's AVPlayerDemo in order to playback video files (the videos are .mp4 coming from my Amazon S3 server).
The player is a subview of a custom table view cell, and the user can scroll the table to switch between videos (an players). There is no simultaneous playback, the player is deallocated on the 'didEndDisplayingCell' method.
The problem is that there is a deadlock after several scrolls (the number of scrolls before the deadlock varies, but eventually it always happens).
the deadlock is on this line (running on the main thread):
[self setPlayer:[AVPlayer playerWithPlayerItem:self.playerItem]];
It seems that playerWithPlayerItem is on mutexwait, waiting for a background thread.
Logging shows that the dealloc of the former AVPlayerDemoPlaybackViewController is called before the deadlock. here is the dealloc code:
if (self.player) {
[self.player removeObserver:self forKeyPath:#"currentItem"];
[self.player.currentItem removeObserver:self forKeyPath:#"status"];
[self.player pause];
[self.playbackView.layer removeFromSuperlayer];
[self.playbackView removeFromSuperview];
self.player = nil;
}
The view controller's player property is nil before it is assigned by the dead-locked line.
The dead-locked line is called from this block:
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:mURL options:nil];
NSArray *requestedKeys = #[#"playable"];
/* Tells the asset to load the values of any of the specified keys that are not already loaded. */
[asset loadValuesAsynchronouslyForKeys:requestedKeys completionHandler:
^{
dispatch_async( dispatch_get_main_queue(),
^{
[self prepareToPlayAsset:asset withKeys:requestedKeys];
});
}];
Here is the threads state at the time of the deadlock:
Please advise - thanks!!
I am using AVCaptureMovieFileOutput to record videos and I want to add a UIProgressView to represent how much time there is left before the video stops recording.
I set a max duration of 15 seconds:
CMTime maxDuration = CMTimeMakeWithSeconds(15, 50);
[[self movieFileOutput] setMaxRecordedDuration:maxDuration];
I can't seem to find if AVCaptureMovieFileOutput has a callback for when the video is recording or for when recording begins. My question is, how can I get updates on the progress of the recording? Or if this isn't something that is available, how can I tell when recording begins in order to start a timer?
Here is how I was able to add a UIProgressView
recording is a property of AVCaptureFileOutput which is extended by AVCaptureMovieFileOutput
I have a variable movieFileOutput of type AVCaptureMovieFileOutput that I am using to capture data to a QuickTime movie.
#property (nonatomic) AVCaptureMovieFileOutput *movieFileOutput;
I added an observer to the recording property to detect a change in recording.
[self addObserver:self
forKeyPath:#"movieFileOutput.recording"
options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew)
context:RecordingContext];
Then in the callback method:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
I created a while loop to be executed in the background, then I made sure to dispatch updates to the view on the main thread like this:
dispatch_async([self sessionQueue], ^{ // Background task started
// While the movie is recording, update the progress bar
while ([[self movieFileOutput] isRecording]) {
double duration = CMTimeGetSeconds([[self movieFileOutput] recordedDuration]);
double time = CMTimeGetSeconds([[self movieFileOutput] maxRecordedDuration]);
CGFloat progress = (CGFloat) (duration / time);
dispatch_async(dispatch_get_main_queue(), ^{ // Here I dispatch to main queue and update the progress view.
[self.progressView setProgress:progress animated:YES];
});
}
});
I am developing an iphone app with phonegap and jquery mobile. When the app is closed and enters the background mode, I am trying to blur the screen so that when the app is resumed, sensitive data on the screen will be blurred out.
I am trying to do this in the on pause event, but it looks like IOS is taking a screenshot of the app before the pause event, thus my blur code does not get captured in the screen shot that IOS shows when resuming the app.
Does anyone have any ideas on how to get this to work?
The pause event is triggered by UIApplicationDidEnterBackgroundNotification, which means that apps already in the background the screen shot has been taken. Is there event before this that I can hook into?
This will be even more important in ios7 when the screen shot is shown when you double click the home button. In ios6 it is only shown for a split second while the app resumes and is loaded.
Thanks!
Code that I have tried in pause and resume phonegap listeners.
// listen for events
document.addEventListener("resume", onResume, false);
document.addEventListener("pause", onPause, false);
// show passcode if enabled, maybe even re-fresh app to start new session and clean up memory issues?
function onResume() {
// unblur page
var filterVal = 'blur(0px)';
$('.ui-page').delay(1000).css('webkitFilter', filterVal);
}
function onPause() {
var filterVal = 'blur(10px)';
$('.ui-page').css('webkitFilter', filterVal);
}
The phonegap plugin cordova-plugin-privacyscreen does the trick - it replaces the view with the splash image prior to backgrounding and clears it afterwards.
https://www.npmjs.com/package/cordova-plugin-privacyscreen
I am facing the same problem here. If your app is enabled multitasking. You can change the native code in appdelegate by adding the current code
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSLog(#"Application Did Enter Background");
self.viewController.webView.hidden = YES;
NSString *splashImage;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
splashImage = #"Default-Portrait~ipad.png";
}
else {
splashImage = #"Default~iphone.png";
}
UIImageView *splash = [[UIImageView alloc]initWithFrame:[self.window frame]];
[splash setImage:[UIImage imageNamed:splashImage]];
[self.window addSubview:splash];
}
- (void) applicationDidBecomeActive:(UIApplication *)application {
NSLog(#"Application Did Become Active");
if([[self.window subviews] count]>1) {
[NSThread sleepForTimeInterval:0.3];
[[[self.window subviews] lastObject] removeFromSuperview];
}
self.viewController.webView.hidden = NO;
}
- (void)applicationWillResignActive:(UIApplication *)application {
NSLog(#"Application Did Resign Active");
self.viewController.webView.hidden = YES;
NSString *splashImage;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
splashImage = #"Default-Portrait~ipad.png";
}
else {
splashImage = #"Default~iphone.png";
}
UIImageView *splash = [[UIImageView alloc]initWithFrame:[self.window frame]];
[splash setImage:[UIImage imageNamed:splashImage]];
[self.window addSubview:splash];
}
However, make sure your app is support Multitasking. Otherwise this won't work due to the applicationDidEnterBackground is NOT called when the multitasking feature is turned off. And the function(applicationWillTerminate) which should be called and it is NOT called neither.
I have an iPhone app (5.1 SDK) which plays audio in the background. Many times when there is an incoming call, the incoming call is displayed but the user cannot slide the slider to answer it and the call is missed.
I am using - (void)beginInterruption to pause all audio when an incoming call arrives but it doesn't seem to stop this issue from occurring.
Has anyone ever encountered this before?
In case you wanna mark this as the answer since it was indeed your problem I'll repost my comment.
Is your beginInterruption code being called? If so, is the music actually stopping? Are you streaming audio over the network or local files? What player are you using?
- (void) beginInterruption {
if (playing) {
playing = NO;
interruptedWhilePlaying = YES;
[self updateUserInterface];
}
}
NSError *activationError = nil;
- (void) endInterruption {
if (interruptedWhilePlaying) {
BOOL success = [[AVAudioSession sharedInstance] setActive: YES error: &activationError];
if (!success) { /* handle the error in activationError */ }
[player play];
playing = YES;
interruptedWhilePlaying = NO;
[self updateUserInterface];
}
}