Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

UINavigationController hidesBarOnSwipe memory leak issue

I've got a problem with hidesBarOnSwipe property of UINavigationController.

Overview :

Link to project file

I have one controller named FirstViewController which is root view of UINavigationController. Everything is in Main.storyboard. FirstViewController contains UIButton action. Inside that action I instantiate a SecondViewController and push it on a navigation stack.

- (IBAction)button:(id)sender {
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
    UIViewController *vc = [storyboard instantiateViewControllerWithIdentifier:@"SecondViewController"];

    [self.navigationController pushViewController:vc animated:YES];
}

Inside SecondViewController there is only an hidesBarsOnSwipe property set to YES on viewDidLoad :

- (void)viewDidLoad {
    [super viewDidLoad];
    self.navigationController.hidesBarsOnSwipe = YES;
}

and dealloc gets NSLogged :

- (void)dealloc {
    NSLog(@"Dealloc");
}

Problem :

When we swipe up to hide navigationBar, dealloc is never get called. Instruments shows a SecondViewController memory leak here.

When we are on SecondViewController and we just press back button - everything is fine. Dealloc gets called.

There is definitly some kind of retain cycle but i have no idea why and how to avoid this kind of situation.

like image 506
polech Avatar asked Oct 29 '22 22:10

polech


1 Answers

Some updates and temporary solution :

There is another method to perform navigationBar hiding. What worked for me is to use :

[self.navigationController setNavigationBarHidden:hidden animated:YES];

To achieve good results add a property in your class to keep track status of navigationBar animation :

@property (assign, nonatomic) BOOL statusBarAnimationInProgress;

Implement UIScrollViewDelegate like this :

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    CGFloat yVelocity = [scrollView.panGestureRecognizer velocityInView:scrollView].y;
    if (yVelocity > 0 && !self.statusBarAnimationInProgress) {
        [self setNavigationBarHidden:NO];
    } else if (yVelocity < 0 && !self.statusBarAnimationInProgress) {
        [self setNavigationBarHidden:YES];
    }
}

Set navigation bar hidden should look like :

- (void)setNavigationBarHidden:(BOOL)hidden {
    [CATransaction begin];
    self.statusBarAnimationInProgress = YES;
    [CATransaction setCompletionBlock:^{
        self.statusBarAnimationInProgress = NO;
    }];
    [self.navigationController setNavigationBarHidden:hidden animated:YES];
    [CATransaction commit];
}

I use CATransaction to check if animation of navigation bar is completed. Any way that works. Not so easy solution but at least there is no leak :)

like image 130
polech Avatar answered Nov 15 '22 05:11

polech