a Custom Segue that Simulates a Push Segue turns VC into Zombie

后端 未结 3 2040
粉色の甜心
粉色の甜心 2021-02-10 19:17

[To Make things short and clear]

I\'ve written a custom segue.

-(void)perform {
UIView *preV = ((UIViewController *)self.sourceViewController).view;
UI         


        
相关标签:
3条回答
  • 2021-02-10 19:30

    You need to keep a reference to destinationViewController so that it doesn't get deallocated. Obviously, you cannot do this in your custom segue as this object is freed after the transition has finished. The standard push segue does this by adding the view controller to the viewControllers property of a UITabBarController, for example.

    In your case, you could call

    [sourceViewController addChildViewController:destinationViewController];
    

    to establish a proper connection between the view controllers. Don't forget to call removeFromParentViewController in your reverse segue.

    0 讨论(0)
  • 2021-02-10 19:31

    in iOS, the relationship between a viewController and it's .view is special.

    all of those runtime calls (initWithCoder: , UIStoryboardSegue initWithIdentifier:source:destination: , et al) point to an attempt to access something to do with the viewController behind the scenes, and the one thing you've done is removed it's main .view from where it was expected in its superview.

    by performing a removeFromSuperview on the .view of the sourceViewController, you are inviting havoc.

    if you want a view that is not a push-segue, you can make the segue a Modal segue. this will keep you from having to mess with the navigation-bar, and yet can allow the CocoaTouch runtime to do the work of the segue (and the cleanup afterwards) for you.

    if you really want control to stay in the same viewController, then you could modify your code to perform [preV setHidden:YES] or a [preV setAlpha:0]. it will still be there so it doesn't turn into a zombie, and you can get back to it by reversing whichever of the above two actions you prefer.

    you might even just try removing (or commenting out the call) to [preV removeFromSuperview], and sliding it back in when you return from whatever you're doing in newV.

    Edit

    Another thing to consider is the use of the __block Storage Type on the variable preV, since you are declaring it locally, and since you are leaving scope and the original viewController may be causing it to go away. in the completion block, you could end up with a reference to a variable that's already had its ref-count dropped to 0 and removed by the time you get there. __block is meant to prevent this.

    0 讨论(0)
  • 2021-02-10 19:39

    You're getting a crash just because your new controller not retained after segue execution.

    What you do is this:

    • src and dest controllers are instantiated
    • you perform your animations
    • in completion you remove src view
    • your src controller gets released, but window's rootViewController still pointing to it and your destination view controller not added to window's hierarchy.

    This will work just as intended:

    -(void)perform {
      UIView *preV = ((UIViewController *)self.sourceViewController).view;
      UIView *newV = ((UIViewController *)self.destinationViewController).view;
    
      UIWindow *window = [[[UIApplication sharedApplication] delegate] window];
      newV.center = CGPointMake(preV.center.x + preV.frame.size.width, newV.center.y);
      [window insertSubview:newV aboveSubview:preV];
    
      [UIView animateWithDuration:0.4
                       animations:^{
                           newV.center = CGPointMake(preV.center.x, newV.center.y);
                           preV.center = CGPointMake(0- preV.center.x, newV.center.y);}
                       completion:^(BOOL finished){
                           [preV removeFromSuperview];
                           window.rootViewController = self.destinationViewController;
                       }];
    }
    
    0 讨论(0)
提交回复
热议问题