UIView AnimateWithDuration Never Reaches Completion Block - ios

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

Related

iOS 11: Is it possible to block screen recording?

I have an app which plays video, and I don't want people to use the new iOS-11 feature to record these videos and make them public. That feature is described here.
I could not find any documentation regarding an option for my app to prevent users from recording it.
Can anybody please guide me to anything related to this?
Thank you!
I am publishing here the official response from Apple Developer Technical Support (DTS):
While there is no way to prevent screen recording, as part of iOS 11, there are new APIs on UIScreen that applications can use to know when the screen is being captured:
UIScreen.isCaptured Instance Property
UIScreenCapturedDidChange Notification Type Property
The contents of a screen can be recorded, mirrored, sent over AirPlay, or otherwise cloned to another destination. UIKit sends the UIScreenCapturedDidChange notification when the capture status of the screen changes.
The object of the notification is the UIScreen object whose isCaptured property changed. There is no userInfo dictionary. Your application can then handle this change and prevent your application content from being captured in whatever way is appropriate for your use.
HTH!
The feature is available on and above iOS11. Better keep it inside didFinishLaunchingWithOptions
Objective-C syntax
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if (#available(iOS 11.0, *)) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(screenCaptureChanged) name:UIScreenCapturedDidChangeNotification object:nil];
}
return YES;
}
-(void)screenCaptureChanged{
if (#available(iOS 11.0, *)) {
BOOL isCaptured = [[UIScreen mainScreen] isCaptured];// will keep on checking for screen recorder if it is runnuning or not.
if(isCaptured){
UIView *colourView = [[UIView alloc]initWithFrame:self.window.frame];
colourView.backgroundColor = [UIColor blackColor];
colourView.tag = 1234;
colourView.alpha = 0;
[self.window makeKeyAndVisible];
[self.window addSubview:colourView];
// fade in the view
[UIView animateWithDuration:0.5 animations:^{
colourView.alpha = 1;
}];
}else{
// grab a reference to our coloured view
UIView *colourView = [self.window viewWithTag:1234];
// fade away colour view from main view
[UIView animateWithDuration:0.5 animations:^{
colourView.alpha = 0;
} completion:^(BOOL finished) {
// remove when finished fading
[colourView removeFromSuperview];
}];
}
} else {
// Fallback on earlier versions
// grab a reference to our coloured view
UIView *colourView = [self.window viewWithTag:1234];
if(colourView!=nil){
// fade away colour view from main view
[UIView animateWithDuration:0.5 animations:^{
colourView.alpha = 0;
} completion:^(BOOL finished) {
// remove when finished fading
[colourView removeFromSuperview];
}];
}
}
}

Animation for imageView

I have an image and i did an animation with :
- (void) spinWithOptions: (UIViewAnimationOptions) options {
// this spin completes 360 degrees every 2 seconds
//Spin rotate image
[UIView animateWithDuration: 5.5f
delay: 0.0f
options: options
animations: ^{
self.rotateImage.transform = CGAffineTransformRotate(self.rotateImage.transform, M_PI / 2);
}
completion: ^(BOOL finished) {
if (finished) {
if (animating) {
// if flag still set, keep spinning with constant speed
[self spinWithOptions: UIViewAnimationOptionCurveLinear];
} else if (options != UIViewAnimationOptionCurveEaseOut) {
// one last spin, with deceleration
[self spinWithOptions: UIViewAnimationOptionCurveEaseOut];
}
}
}];
}
and in my viewDidLoad
- (void)viewDidLoad {
[super viewDidLoad];
[self spinWithOptions: UIViewAnimationOptionCurveEaseIn];
}
and it's ok on i luanch the app , but if i get into another screen and then back t the first screen , the animation is stoped . Any ideea how can i modify this ?
Because viewDidLoad is called once and the code inside it are compiled once(tell/suggest me if I'm wrong here). So to start your animation when you come back to the view you need to call your animation code inside viewWillAppear method. Hence, each time your view appears the code inside viewWillAppear will get compiled again.
Snippet:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self spinWithOptions: UIViewAnimationOptionCurveEaseIn];
}
viewDidLoad: is called only once, i.e. when the view is loaded into the memory.
you need to either implement the method inside viewWillAppear: or viewDidAppear: callback methods.

setAlpha not triggering after touchUpInside event occurs

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.

CATransaction Completionblock triggers immediately [duplicate]

This question already has answers here:
CATransaction completion being called immediately
(4 answers)
Closed 8 years ago.
I'm trying to wait for an animation to finish before starting another task. I looked at different methods but using CATransactions seems to be the most used method to do this.
Somehow, my CATransaction Completionblock triggers immediately after the animation starts, not after it finishes.
Here's my code:
[CATransaction begin];
[CATransaction setCompletionBlock: ^{
NSLog(#"Animation ends");
}];
NSLog(#"Animation begins");
[tableView setEditing:NO animated:YES];
[CATransaction commit];
When looking at the console I get this:
2014-03-17 15:44:12.995 BarTap[89934:70b] Animation begins
2014-03-17 15:44:12.997 BarTap[89934:70b] Animation ends
So appearently the Completionblock starts 0.002 seconds after the animation begins, but the animation definitely takes longer than that.
Could anyone help me? Thanks!
Animation completion triggers immediately because there are no tasks for animation in your code.
CAAnimation effects to CALayer properties and it finishes immediately if there no changes in animatable properties.
Try this:
[UIView animateWithDuration:timeInterval animations:^{
[tableView setEditing:NO animated:NO];
} completion:^(BOOL finished) {
// Perform tasks after animation completion here
}];

ios fade out splash screen (iphone 5 friendly)

I'm wanting to spoof the feel of the main splash screen fading out whenever applicationDidBecomeActive is called, but it's not working. What am I doing wrong?
- (void)applicationDidBecomeActive:(UIApplication *)application
{
if(IS_IPHONE_5)
splash = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Default-568h.png"]];
else
splash = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"Default.png"]];
[self.window.rootViewController.view addSubview:splash];
[UIView animateWithDuration:0.5
animations:^{
splash.alpha = 0;
}
completion:^(BOOL finished) {
[splash removeFromSuperview];
}];
}
Then you need to define the following somewhere. I use the project .pch but you can use your header if you want.
#define IS_IPHONE_5 ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )568 ) < DBL_EPSILON )
I find, from ios6 you get a nice transition doing this
-(BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[UIView animateWithDuration:0.2
delay:0
options: UIViewAnimationCurveEaseIn
animations:^{
self.window.viewForBaselineLayout.alpha = 0; // and at this alpha
}
completion:^(BOOL finished){
}];
return YES;
}
then immediately at the start of
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[UIView animateWithDuration:0.5
delay:0
options: UIViewAnimationCurveEaseOut
animations:^{
self.window.viewForBaselineLayout.alpha = 1; // and at this alpha
}
completion:^(BOOL finished){
}];
It gives a cross fadeish effect from the loading screen to the now loaded app screen.
If that is really your code, you probably have a typo in the image name. (If not, let us know what "not working" means.)
Also, the splash screen doesn't normally come up every applicationDidBecomeActive:. didFinishLaunchingWithOptions: is the time you know that you have been launched and the splash screen had been shown on your behalf.
Try adding it directly to your window instead of the rootViewController.view.
[self.window addSubview:splash];
You may also need to rotate the image using view.transform to align with the startup image.
Your code looks about right; I do this in several apps.
However, you want to do this as part of applicationDidFinishLaunching:options: and not in applicationDidBecomeActive:. It only makes sense to fade the splash screen when it is shown, which is only when the app is launched and not already running. When your app becomes active, it may have been in the background -- i.e. already launched -- so fading the splash screen in this case doesn't make sense.
Or, did you want your splash screen to appear ALWAYS when it becomes active, even if it is resumed from the background from a suspended state?

Resources