Skip to content

Instantly share code, notes, and snippets.

@steipete
Last active August 29, 2015 13:57
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save steipete/9723421 to your computer and use it in GitHub Desktop.
Save steipete/9723421 to your computer and use it in GitHub Desktop.
[PSPDFHUDView updatePageLabelFrameAnimated:] resizes a label that is inside PSPDFHUDView by calling sizeToFit. This triggers layoutSubviews on PSPDFHUDView. That's something I would like to prevent. What could I do here? Followup: https://gist.github.com/steipete/9725247
(lldb) bt
* thread #1: tid = 0x6e9f, 0x0036d774 PSPDFCatalog`-[PSPDFDebugLayer setNeedsLayout](self=0x1656a190, _cmd=0x302e001d) + 44 at PSPDFDebugHelper.m:456, queue = 'com.apple.main-thread', stop reason = breakpoint 14.1
frame #0: 0x0036d774 PSPDFCatalog`-[PSPDFDebugLayer setNeedsLayout](self=0x1656a190, _cmd=0x302e001d) + 44 at PSPDFDebugHelper.m:456
frame #1: 0x2f9763e8 QuartzCore`CA::Layer::property_did_change(CA::Transaction*, unsigned int) + 1188
frame #2: 0x2f975f1c QuartzCore`CA::Layer::end_change(CA::Transaction*, unsigned int, objc_object*) + 64
frame #3: 0x2f976a54 QuartzCore`CA::Layer::set_bounds(CA::Rect const&, bool) + 540
frame #4: 0x2f976776 QuartzCore`-[CALayer setBounds:] + 110
frame #5: 0x2f97789c QuartzCore`-[CALayer setFrame:] + 636
frame #6: 0x2fcf6336 UIKit`-[UIView(Geometry) setFrame:] + 254
frame #7: 0x2fd075f4 UIKit`-[UIView(Geometry) sizeToFit] + 304
* frame #8: 0x00461980 PSPDFCatalog`-[PSPDFHUDView updatePageLabelFrameAnimated:](self=0x165afce0, _cmd=0x008297de, animated='\x01') + 684 at PSPDFHUDView.m:245
frame #9: 0x004615ea PSPDFCatalog`-[PSPDFHUDView updatePageLabelAnimated:](self=0x165afce0, _cmd=0x008298b0, animated='\x01') + 1354 at PSPDFHUDView.m:228
frame #10: 0x0046285c PSPDFCatalog`-[PSPDFHUDView configurationChangedNotification:](self=0x165afce0, _cmd=0x0082977a, notification=0x17a903e0) + 264 at PSPDFHUDView.m:284
frame #11: 0x2d536e70 CoreFoundation`__CFNOTIFICATIONCENTER_IS_CALLING_OUT_TO_AN_OBSERVER__ + 12
frame #12: 0x2d4aaab0 CoreFoundation`_CFXNotificationPost + 1720
frame #13: 0x2de90ec4 Foundation`-[NSNotificationCenter postNotificationName:object:userInfo:] + 72
frame #14: 0x003acf92 PSPDFCatalog`-[PSPDFViewController setPageInternal:](self=0x16bcfc00, _cmd=0x0082213d, page=91) + 674 at PSPDFViewController.m:1059
frame #15: 0x00386400 PSPDFCatalog`-[PSPDFPageScrollViewController tilePagesForced:](self=0x167edba0, _cmd=0x00821f79, forceUpdate='\0') + 4908 at PSPDFPageScrollViewController.m:320
frame #16: 0x00388654 PSPDFCatalog`-[PSPDFPageScrollViewController scrollViewDidScroll:](self=0x167edba0, _cmd=0x302e1207, scrollView=0x167ee1a0) + 236 at PSPDFPageScrollViewController.m:489
frame #17: 0x2ff9b8bc UIKit`-[UIScrollView(UIScrollViewInternal) _notifyDidScroll] + 64
frame #18: 0x2fd1818c UIKit`-[UIScrollView setContentOffset:] + 604
frame #19: 0x2fda2ba4 UIKit`-[UIScrollView(UIScrollViewInternal) _setContentOffset:animated:animationCurve:animationAdjustsForContentOffsetDelta:] + 412
frame #20: 0x2fda2a02 UIKit`-[UIScrollView(UIScrollViewInternal) _setContentOffset:animated:animationCurve:] + 38
frame #21: 0x2fda29d6 UIKit`-[UIScrollView setContentOffset:animated:] + 30
frame #22: 0x00384b44 PSPDFCatalog`-[PSPDFPageScrollViewController setPage:animated:](self=0x167edba0, _cmd=0x00809ecc, page=91, animated='\0') + 748 at PSPDFPageScrollViewController.m:178
frame #23: 0x003ad576 PSPDFCatalog`-[PSPDFViewController setPage:animated:](self=0x16bcfc00, _cmd=0x00809ecc, page=91, animated='\0') + 1058 at PSPDFViewController.m:1094
frame #24: 0x003cc366 PSPDFCatalog`-[PSPDFViewController scrobbleBar:didSelectPage:](self=0x16bcfc00, _cmd=0x00829941, scrobbleBar=0x165dd930, page=91) + 246 at PSPDFViewController.m:3635
frame #25: 0x002b9870 PSPDFCatalog`-[PSPDFScrobbleBar processTouch:](self=0x165dd930, _cmd=0x00819807, touch=0x16527b40) + 1116 at PSPDFScrobbleBar.m:256
frame #26: 0x002b80d0 PSPDFCatalog`-[PSPDFScrobbleBar touchesMoved:withEvent:](self=0x165dd930, _cmd=0x302eddf2, touches=0x179ae100, event=0x16562fc0) + 140 at PSPDFScrobbleBar.m:102
frame #27: 0x2fe7fea2 UIKit`forwardTouchMethod + 234
frame #28: 0x2fd2d376 UIKit`-[UIWindow _sendTouchesForEvent:] + 354
frame #29: 0x2fd28450 UIKit`-[UIWindow sendEvent:] + 832
frame #30: 0x2fcfdd78 UIKit`-[UIApplication sendEvent:] + 196
frame #31: 0x2fcfc568 UIKit`_UIApplicationHandleEventQueue + 7116
frame #32: 0x2d53ff1e CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 14
frame #33: 0x2d53f3e6 CoreFoundation`__CFRunLoopDoSources0 + 206
frame #34: 0x2d53dbd6 CoreFoundation`__CFRunLoopRun + 630
frame #35: 0x2d4a8470 CoreFoundation`CFRunLoopRunSpecific + 524
frame #36: 0x2d4a8252 CoreFoundation`CFRunLoopRunInMode + 106
frame #37: 0x321af2ea GraphicsServices`GSEventRunModal + 138
frame #38: 0x2fd5d844 UIKit`UIApplicationMain + 1136
frame #39: 0x0012132a PSPDFCatalog`main(argc=1, argv=0x27d48cfc) + 134 at main.m:16
What I ended up using:
// Define a custom layer class:
+ (Class)layerClass {
return PSPDFSetNeedsLayoutSupresserLayer.class;
}
// Add block that executes frame changes, using the generic layer store:
static NSString *const PSPDFSupressLayoutKey = @"supressSetNeedsLayout";
- (void)performWithoutTriggeringSetNeedsLayout:(dispatch_block_t)block {
[self.layer setValue:@YES forKey:PSPDFSupressLayoutKey];
block();
[self.layer setValue:@NO forKey:PSPDFSupressLayoutKey];
}
@implementation PSPDFSetNeedsLayoutSupresserLayer
// Checks the layer store to supress certain layoutSubview triggers that we don't want for performance reasons.
- (void)setNeedsLayout {
if (![[self valueForKey:PSPDFSupressLayoutKey] boolValue]) {
[super setNeedsLayout];
}
}
@end
@steipete
Copy link
Author

I can think of lots of workarounds, like defining my own block to ignore the following subview request - but since that is triggered async it's a bit harder. I could also override the layer to work against the async (since dirty flagging is sync) but I'd much rather understand the magic of CA::Layer::property_did_change and block this at the source.

@toco
Copy link

toco commented Mar 23, 2014

In my simple test, calling sizeToFit on a label which is a subview of parentView did not result layoutSubviews being called on parentView, only setNeedsLayout is called on parentView.layer like in your example.

Are sure layoutSubviews is triggered? Whats the backtrace?

@steipete
Copy link
Author

The above IS the backtrace. setNeedsLayout on the layer is invoked - this triggers layoutSubviews at the end of the runloop. It doesn't happen always, that's why I'm so confused.

@steipete
Copy link
Author

So apparently changing the size of a frame does trigger the parent's layoutSubviews UNLESS the frame is set within the parent's layoutSubviews method (that would otherwise lead to it being invoked in each runloop until the frame doesn't change anymore)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment