在UIScrollView中侧边放置UIViewControllers

3
我有一个主视图控制器,其中包含一个UIScrollView的子视图和另外两个UIViewControllers(每个都有UIView和按钮的nibs)。使用addSubView方法将UIViewControllers的UIView添加到UIScrollView中。我想把这两个UIViewControllers并排放置。
我有以下代码,但似乎不起作用。
[self scrollView].pagingEnabled = YES;
[self scrollView].contentSize = CGSizeMake(768 * 2, 1024);
[[self scrollView] setDelegate:self];

CGRect frame1;
frame1.origin.x = 0;
frame1.origin.y = 0;
frame1.size = CGSizeMake(768, 1024);

// Model one
oneView = [[OneViewController alloc] initWithNibName:@"OneViewController" bundle:nil];
//oneView.view.frame = frame1;
[self.scrollView addSubview:oneView.view];
oneView.view.frame = frame1;

// Model two

CGRect frame2;
frame2.origin.x = 768;
frame2.origin.y = 0;
frame2.size = CGSizeMake(768, 1024);

twoView = [[TwoViewController alloc] initWithNibName:@"TwoViewController" bundle:nil];
//twoView.view.frame = frame2;
[self.scrollView addSubview:twoView.view];
twoView.view.frame = frame2;

当我设置它们的frame.origin.x时,为什么UIScrollView中的oneView和twoView在同一位置?twoView似乎在oneView上面,而它应该是并排的。你有任何想法我做错了什么吗?谢谢!更新:这是我在github上的完整项目代码:http://bit.ly/PLe1Le

1
仅仅说“frame.origin.x = 768;”会有什么问题? - Mick MacCallum
请注意,您没有将Xcode项目文件提交到代码库中。 - Carl Veazey
谢谢。我认为发生的情况类似于torrey.lyons在下面的回答。如果内容视图的高度达到或超过1024像素,就会发生这种情况;如果您不让它们变得那么高,就不会发生。我不知道为什么 - 它似乎实际上与内容大小上的任何东西都没有关联。虽然我还不能告诉你为什么会发生这种情况,但如果您认为这足以获得悬赏,我可以给您一个修复问题的代码答案。 - Carl Veazey
是的,请。我已经准备好将赏金授予代码修复。也许当其他专家加入并提供更详细的解释时,我会将他们的答案作为被接受的答案。 :) - Scott
抱歉,SO没有通知我有新回复!好吧,我想我终于弄清楚原因了。让我再测试几个东西,等我确认后再给你答案。 - Carl Veazey
显示剩余5条评论
4个回答

3
这种情况发生的原因是任何一个作为UIViewControllerview属性值的UIView实例有时会收到一个消息,要求将其视图调整为其父视图的大小。
具体来说,当视图控制器的view添加到窗口中时,将调用未经记录的方法viewDidMoveToWindow:shouldAppearOrDisappear:,此处在应用程序启动时发生。此时,如果视图控制器的view实例的大小与主窗口的大小相同或更大,则它将被调整为其父视图的大小。
原始代码将从.xib加载的两个视图的大小设置为768 * 1024,这是窗口的大小。这发生在调用窗口上的-makeKeyAndVisible之前,导致触发调整大小。我在应用程序委托中使用了KVO来观察框架的调整大小;以下是调整大小时的堆栈跟踪:
* 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

我们可以通过实例化一个视图并将其同时添加到滚动视图中,从而验证这是因为内容视图是UIViewController的视图实例。 我在ViewControllerviewDidLoad实现末尾添加了以下代码:
CGRect frame3 = frame2;
frame3.origin.x = 768.0f * 2.0f;
UIView *thirdView = [[UIView alloc] initWithFrame:frame3];
[thirdView setBackgroundColor:[UIColor redColor]];
[self.scrollView addSubview:thirdView];

同时,将分配内容大小的代码行更改为:
[self scrollView].contentSize = CGSizeMake(768 * 3, 1024);

这个视图在makeKeyAndVisible期间没有设置它的框架,而且位置正确。

为了解决这个问题,我建议在窗口调用-makeKeyAndVisible后设置视图层次结构,可能在viewWillAppear中(不确定是否会有相同的问题)或通过以其他方式构造代码来延迟滚动视图的布局。或者,在稍后的时间重新设置框架,作为明确撤销此错误的一种方法。

希望这有助于解释发生了什么,并且我提供的证据是清晰和可靠的;似乎您已经针对您特定情况进行了修复,但希望这个答案作为当UIViewController的视图在滚动视图内部布局时出现框架行为异常的通用解决方案。


谢谢你的解释,卡尔!赏金将在1小时后颁发(系统限制)。 :) - Scott
非常感谢你,Eddy。我真的很感激。如果还有其他需要帮忙的地方或者需要更进一步的解释,请告诉我。谢谢! - Carl Veazey

0
我猜想您的问题是您的两个视图在垂直方向上太大,超出了滚动视图内容大小的范围。您的滚动视图内容只有(768 * 2)x 900。您将两个子视图都调整为768 x 1024。由于它们无论如何都不合适,因此滚动视图可能只是将它们放在原点。请将第二行更改为:
[self scrollView].contentSize = CGSizeMake(768 * 2, 1024);

我将UIViewController的height编辑为900,以查看模拟器中是否有重叠或其他问题。但是没有发现任何问题,所以我将其恢复为1024。我已经更新了上面的代码,并且还添加了一个指向我的源代码的GitHub链接。 - Scott

0
从这篇文章中借鉴了一些代码:UIScrollView adding UIViewController as a sub view? with UIPageControl,我认为通过使用self.scrollView.frame.size.width而不是使用固定值768,我已经解决了它。
[self scrollView].pagingEnabled = YES;
[self scrollView].contentSize = CGSizeMake(self.scrollView.frame.size.width * 2, self.scrollView.frame.size.height);
[[self scrollView] setDelegate:self];

CGRect frame1;
frame1.origin.x = self.scrollView.frame.size.width * 0;
frame1.origin.y = 0;
frame1.size = self.scrollView.frame.size;

// Model one
oneView = [[OneViewController alloc] initWithNibName:@"OneViewController" bundle:nil];
[self.scrollView addSubview:oneView.view];
oneView.view.frame = frame1;

// Model two
CGRect frame2;
frame2.origin.x = self.scrollView.frame.size.width * 1;
frame2.origin.y = 0;
frame2.size = self.scrollView.frame.size;

twoView = [[TwoViewController alloc] initWithNibName:@"TwoViewController" bundle:nil];
[self.scrollView addSubview:twoView.view];
twoView.view.frame = frame2;

另外,我已经更新了我的代码库,这样你就可以整体测试项目了。这是链接:http://bit.ly/PLe1Le :)


0
你试过先将子视图添加到滚动视图中,然后再设置它们的位置吗?我在另一种情况下发现,在将信息添加到视图并显示在滚动视图中之前,文本字段会变成空白。视图有一个奇怪的特性,即在显示时可能会返回到默认值。

我已经尝试在将UIViewControllers添加到UIScrollView之前和之后设置它们的位置。请参见我的更新,其中包含github链接。 :) - Scott

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接