viewDidMoveToWindow:shouldAppearOrDisappear:
ref: http://www.thebirdietoldme.com/userActions/thread/Question.aspx?id=12103677
The reason why this started happening is that any UIView
instance that is the value of a UIViewController
's view
property will sometimes get a message to resize its view to that of its superviews.
Specifically, the undocumented method viewDidMoveToWindow:shouldAppearOrDisappear:
will be called when a view controller's view
is added to a window, as is happening at application launch in this instance. At this time, if the view controller's view
instance is the size of the main window or larger, it will be resized to the size of its superview.
The original code took the two views that were loaded from the the .xibs and sized them to 768 * 1024, which is the size of the window. This happens before -makeKeyAndVisible
is called on the window, which ends up triggering the resize. I used KVO in the app delegate to watch the frames get resized; here is a stack trace at the time of resize:
* thread #1: tid = 0x1f03, 0x000030f9 scroller`-[ViewController observeValueForKeyPath:ofObject:change:context:] + 105 at ViewController.m:71, stop reason = breakpoint 1.1
frame #0: 0x000030f9 scroller`-[ViewController observeValueForKeyPath:ofObject:change:context:] + 105 at ViewController.m:71
frame #1: 0x0093ad91 Foundation`NSKeyValueNotifyObserver + 345
frame #2: 0x0093a895 Foundation`NSKeyValueDidChange + 438
frame #3: 0x0092033e Foundation`-[NSObject(NSKeyValueObserverNotification) didChangeValueForKey:] + 131
frame #4: 0x009cdcb4 Foundation`_NSSetRectValueAndNotify + 187
frame #5: 0x000e1786 UIKit`-[UIViewController viewDidMoveToWindow:shouldAppearOrDisappear:] + 657
frame #6: 0x0005310c UIKit`-[UIView(Internal) _didMoveFromWindow:toWindow:] + 1040
frame #7: 0x00052edb UIKit`-[UIView(Internal) _didMoveFromWindow:toWindow:] + 479
frame #8: 0x0005d5ab UIKit`-[UIScrollView _didMoveFromWindow:toWindow:] + 65
frame #9: 0x00052edb UIKit`-[UIView(Internal) _didMoveFromWindow:toWindow:] + 479
frame #10: 0x0004f692 UIKit`-[UIView(Hierarchy) _postMovedFromSuperview:] + 158
frame #11: 0x0005446f UIKit`-[UIView(Internal) _addSubview:positioned:relativeTo:] + 1633
frame #12: 0x0004e14b UIKit`-[UIView(Hierarchy) addSubview:] + 56
frame #13: 0x0003d550 UIKit`-[UIWindow addRootViewControllerViewIfPossible] + 380
frame #14: 0x0003d670 UIKit`-[UIWindow _setHidden:forced:] + 280
frame #15: 0x0003d836 UIKit`-[UIWindow _orderFrontWithoutMakingKey] + 49
frame #16: 0x0004472a UIKit`-[UIWindow makeKeyAndVisible] + 35
frame #17: 0x00002575 scroller`-[AppDelegate application:didFinishLaunchingWithOptions:] + 661 at AppDelegate.m:21
frame #18: 0x00015386 UIKit`-[UIApplication _callInitializationDelegatesForURL:payload:suspended:] + 1292
frame #19: 0x00016274 UIKit`-[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:] + 524
frame #20: 0x00025183 UIKit`-[UIApplication handleEvent:withNewEvent:] + 1027
frame #21: 0x00025c38 UIKit`-[UIApplication sendEvent:] + 68
frame #22: 0x00019634 UIKit`_UIApplicationHandleEvent + 8196
frame #23: 0x0139eef5 GraphicsServices`PurpleEventCallback + 1274
frame #24: 0x01488195 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ + 53
frame #25: 0x013ecff2 CoreFoundation`__CFRunLoopDoSource1 + 146
frame #26: 0x013eb8da CoreFoundation`__CFRunLoopRun + 2218
frame #27: 0x013ead84 CoreFoundation`CFRunLoopRunSpecific + 212
frame #28: 0x013eac9b CoreFoundation`CFRunLoopRunInMode + 123
frame #29: 0x00015c65 UIKit`-[UIApplication _run] + 576
frame #30: 0x00017626 UIKit`UIApplicationMain + 1163
frame #31: 0x000022ad scroller`main + 141 at main.m:16
We can verify that this happens because the content views are a UIViewController
's view instance by instantiating a view and adding it the scroll view at the same time. I added the following code to the end of ViewController
's viewDidLoad
implementation:
CGRect frame3 = frame2;
frame3.origin.x = 768.0f * 2.0f;
UIView *thirdView = [[UIView alloc] initWithFrame:frame3];
[thirdView setBackgroundColor:[UIColor redColor]];
[self.scrollView addSubview:thirdView];
As well as changing the line assigning content size to:
[self scrollView].contentSize = CGSizeMake(768 * 3, 1024);
This view does not get its frame set during makeKeyAndVisible
and is in the correct place.
To work around the issue, I'd recommend setting up the view hierarchy after the window has -makeKeyAndVisible
called, possibly in a viewWillAppear (Not sure if that will have the same issue) or by otherwise structuring your code to delay the laying out of the scroll view. Alternatively, re-set the frames at a later time as an explicit undoing of this bug.
I hope this helps explain what was happening and that the evidence I provide is clear and credible; it seems you already have a fix for your particular case but hopefully this answer stands as a general solution to frame misbehavior when UIViewController
's have their views laid out inside scroll views.