I have a view in wich I present two different uiwebviews (loaded from local html). Everytime the user taps a button located in the navigation bar, I switch from one webview to another with a page curl animation (curl up and curl down). Everything seems to work fine but sometimes the app freezes when the user tap that button. In most cases the app freezes when I tap the button repeteadly until the app freezes, then I pause the debug and I get this:
self.navigationItem.titleView.userInteractionEnabled = NO; //Disable button interaction until animation is finished
if (self.isVisibleFrontView)
{
self.isVisibleFrontView = NO;
[UIView transitionWithView:self.view
duration:0.5
options:UIViewAnimationOptionTransitionCurlUp | UIViewAnimationOptionCurveEaseInOut
animations:^{
[[[self.view subviews] objectAtIndex:0] removeFromSuperview];
[self.view addSubview:self.patientViewReverse];
}
completion:^(BOOL finished){
self.navigationItem.titleView.userInteractionEnabled = YES;
}];
}
else
{
self.isVisibleFrontView = YES;
[UIView transitionWithView:self.view
duration:0.5
options:UIViewAnimationOptionTransitionCurlDown | UIViewAnimationOptionCurveEaseInOut
animations:^{
[[[self.view subviews] objectAtIndex:0] removeFromSuperview];
[self.view addSubview:self.patientView];
}
completion:^(BOOL finished){
self.navigationItem.titleView.userInteractionEnabled = YES;
}];
}
[self showNavigationBarButtons]; //change button image
}
I think webView is acquiring lock when removeFromSuperview but in some cases never release it, resulting in a main thread freeze. What do you think? What is the best approach to achieve this without freezing the app?
Thanks in advance guys.
Finally I've fixed it.
Now all I do is setHidden:YES the webview I don't want to show and setHidden:NO the other webview in the animation block instead of removing and adding as subviews the webviews each time. This is more efficient and the webview doesn't use the webthreadlock.
Related
Starting in iOS 11, UIViewController's transitionFromViewController:toViewController:duration:options:animations:completion: method appears to not call its completion block anymore.
Sample code snippet below:
[self addChildViewController:toVC];
[fromVC willMoveToParentViewController:nil];
[self transitionFromViewController:fromVC
toViewController:toVC
duration:0.4
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{}
completion:^(BOOL finished) {
NSLog(#"Completion called"); // this completion is never executed
}];
This is causing me all kinds of issues in getting my views to transition and animate correctly. Has anyone else run into this behavior, and/or discovered a workaround?
So it turns out that I wasn't explicitly adding toVC.view as a subview to self.view after adding toVC as a child view controller to self. Odd that this behaves differently in iOS 11 vs. previous versions, but this did the trick:
[self addChildViewController:toVC];
[self.view addSubview:toVC.view]; // This line is what is needed
[fromVC willMoveToParentViewController:nil];
[self transitionFromViewController:fromVC
toViewController:toVC
duration:0.4
options:UIViewAnimationOptionTransitionCrossDissolve
animations:^{}
completion:^(BOOL finished) {
NSLog(#"Completion called");
}];
I am using the following code to override the perform method in a custom segue to achieve sliding effect when moving between view controllers.
- (void)perform
{
MasterController *sourceController = (MasterController *)self.sourceViewController;
MasterController *destinationController = (MasterController *)self.destinationViewController;
CGRect frame = sourceController.view.frame;
[sourceController.view addSubview:destinationController.view];
[destinationController.view setFrame:CGRectOffset(frame, frame.size.width, 0)];
[UIView animateWithDuration:0.5 animations:^{
[sourceController.view setFrame:CGRectOffset(frame, -frame.size.width, 0)];
} completion:^(BOOL finished) {
[sourceController presentViewController:destinationController animated:NO completion:nil];
[destinationController.view removeFromSuperview];
}];
}
Similar to this code exists all over the internet. the problem is that "sometimes" after the animation finish the screen flashes/blinks/flickers then gets back normal.
removing [destinationController.view removeFromSuperview]; line of code seems to solve the problem. but, that doesn't look right! right?
Any ideas how to solve this?
Yes, simply remove [destinationController.view removeFromSuperview];. It will be done for you. At the end of the segue, destinationController.view will have a new superview and will be removed from sourceController.view.
I'm trying to create an unwind segue with the page curl animation. I've followed a tutorial I found only to have the destination view controller call segueForUnwindingToViewController to return an instance of my custom segue class. However, at best it just instantly changes to the appropriate view without a delay or any animation.
Any help would be appreciated.
The contents of perform:
[UIView animateWithDuration:0.5
delay:0.0
options:UIViewAnimationOptionTransitionCurlUp
animations:^{}
completion:^(BOOL finished){
[destinationViewController.view removeFromSuperview];
[sourceViewController dismissViewControllerAnimated:NO completion:NULL];
}];
I just tried my making a custom transition between view controllers. It basically spins the next one into view, and it works. Except that the source view controller flickers briefly back into visibility right as the animation completes, just as the destination view controller achieves its final position.
I'm also getting a warning about Unbalanced calls to begin/end appearance transitions which I'm still working on fixing-- I don't know if they're related.
Does anyone see anything here that jumps out as not quite right that would cause a flicker?
I then just assigned a button to do a custom segue via storyboard editor.
-(void)perform
{
UIViewController *source = self.sourceViewController;
UIViewController *destination = self.destinationViewController;
[source.view addSubview:destination.view];
destination.view.transform = CGAffineTransformMakeRotation(M_PI / 2);
[UIView animateWithDuration:0.5 delay:0.0 options:UIViewAnimationCurveEaseInOut animations:^{
destination.view.transform = CGAffineTransformMakeRotation(0);
}completion:^(BOOL finished){
[destination.view removeFromSuperview];
[source presentViewController:destination animated:NO completion:NULL];
}];
}
Take out the removeFromSuperview.
I am trying to override removeFromSuperview for a custom view i created. The view is intended to contain a UIActivityIndicatorView and i managed to animate its appearance successfully overriding the initWithFrame method. Now i would like to override also its removeFromSuperview adding in it a fade out animation so that from the view controller i can simply call [self.loadingView removeFromSuperview]; Unfortunately the loadingView is removed without the animation, probably because of the [super removeFromSuperview]; i need to call when overriding. Here is the code i'm using, is there a way i can fix it?
-(void)removeFromSuperview
{
[super removeFromSuperview];
[UIView animateWithDuration:0.5
animations:^{self.alpha = 0.0;}
completion:^(BOOL finished){[self removeFromSuperview];}];
}
Try putting the [super removeFromSuperview] in the completion block for the animation:
-(void)removeFromSuperview
{
[UIView animateWithDuration:0.5
animations:^{self.alpha = 0.0;}
completion:^(BOOL finished){[super removeFromSuperview];}];
}