15

Summary

  1. Add a childVC to a parentVC
  2. Snapshot childVC.view
  3. It bugs.

Why?

Situation

I've just encountered a strange behavior and I would like to know if it is normal or if it is a bug.

I have a view controller childVC which is a child of parentVC. When creating the parent/child relationship, my code is

    [parentVC addChildViewController:childVC] ;
    [parentVC.view addSubview:childVC.view] ;
    [childVC didMoveToParentViewController: parentVC] ;

Few lines further, I want to create a snapshot of childVC.view. My code is

UIView * view = childVC.view ;

UIGraphicsBeginImageContextWithOptions(view.contentSize, NO, 0);
{
    [...]

    [view drawViewHierarchyInRect:view.bounds
               afterScreenUpdates:YES];

    image = UIGraphicsGetImageFromCurrentImageContext();

    [...]
}
UIGraphicsEndImageContext();

Bug

Then I have the error:

* Terminating app due to uncaught exception 'UIViewControllerHierarchyInconsistency', reason: 'child view controller: should have parent view controller:(null) but actual parent is:' * First throw call stack: ( 0 CoreFoundation 0x0260b466 __exceptionPreprocess + 182 1 libobjc.A.dylib
0x02290a97 objc_exception_throw + 44 2 CoreFoundation
0x0260b38d +[NSException raise:format:] + 141 3 UIKit
0x01136710 -[UIView(Hierarchy) _associatedViewControllerForwardsAppearanceCallbacks:performHierarchyCheck:isRoot:] + 352 4 UIKit 0x01136b13 -[UIView(Hierarchy) _willMoveToWindow:withAncestorView:] + 285 5 UIKit 0x0114330a -[UIView(Internal) _addSubview:positioned:relativeTo:] + 511 6 UIKit 0x01136252 -[UIView(Hierarchy) addSubview:] + 56 7 UIKit
0x0114ab0e +[_UIReplicantView _pendingSnapshotOfTarget:snapshotBlock:] + 584 8 UIKit 0x011312fe -[UIView drawViewHierarchyInRect:afterScreenUpdates:] + 287

Question

Why is it so? Can I solve this issue?

More details

Actually, childVC.view is a UIScrollView and the code for snapshotting is

UIScrollView * scrollView = (UIScrollView *)childVC.view;

UIGraphicsBeginImageContextWithOptions(scrollView.contentSize, NO, 0);
{
    CGPoint savedContentOffset = scrollView.contentOffset;
    CGRect savedFrame = scrollView.frame;

    scrollView.contentOffset = CGPointZero;
    scrollView.frame = CGRectMake(0,
                            0,
                            scrollView.contentSize.width,
                            scrollView.contentSize.height);

    [scrollView drawViewHierarchyInRect:scrollView.bounds
               afterScreenUpdates:YES];

    image = UIGraphicsGetImageFromCurrentImageContext();

    scrollView.contentOffset = savedContentOffset;
    scrollView.frame = savedFrame;
}
UIGraphicsEndImageContext();

Maybe that makes a big difference.

Colas
  • 3,473
  • 4
  • 29
  • 68
  • 2
    That's 3 years old, but it's happening to me today in Swift 4. Have you solved your issue since then? – agirault Jul 12 '18 at 21:33
  • 2
    Nope, sorry. Good luck. – Colas Jul 13 '18 at 15:17
  • this is the weirdest bug! it works exactly once for me & crashes on subsequent snapshots – batman Nov 12 '18 at 09:16
  • I had exactly the same crash but with different circumstances. This happens when you snapshot a view without a window (e.g. `view.window == nil`) – rabbitinspace Jan 18 '19 at 06:54
  • 1
    I've run into this as well. My solution is to remove the child from the parent, snapshot, then add it back to the parent. I guess child viewControllers are like my kids - no snapshots please! – Brian M Feb 16 '19 at 16:56
  • I had the same problem and solve it. To debug that print scrollView.window, if it's nil, somewhere in your hierarchy a view is not added to his supposed superview. In my case the topViewController of a UINavigationController. – Mathieu Hausherr Mar 04 '19 at 17:37

3 Answers3

3

I ran into this same issue today, when implementing a custom transition using UIViewControllerAnimatedTransitioning. The problem was that when I tried to snapshot the child view controller, its parent was not part of a view hierarchy yet.

The solution is to make sure that the parent's view has been added to a view hierarchy before you try to snapshot the child. In my case, I just needed to add the "to" view controller to the transitionContext.containerView first.

From your code snippet, I can't tell when you are trying to take the snapshot. But if it is before viewDidAppear(), it probably won't work.

Nate Petersen
  • 888
  • 8
  • 11
  • You just save my life! Had an issue inside a custom transition as well, I just didn't get any error message or crash, only a messed up layout. Adding to the container first solved it. Thanks a lot! – Micky Sep 28 '21 at 14:27
0

I encountered this crash when using view.snapshotView(afterScreenUpdates: true) to capture child view controller's view before removing it from parent.

For me, passing false to afterScreenUpdates solved the issue.

kubrick G
  • 856
  • 6
  • 10
-2

Maybe your parent view controller is getting deallocated and hence the (null) in the exception message?

Remover
  • 1,616
  • 1
  • 17
  • 27