問題描述
我使用的是 iOS 6、分頁 UIScrollView 和純自動布局.
I'm using iOS 6, a paging UIScrollView, and pure auto layout.
總結:我創建了一個滾動內容頁面的視圖控制器.一些視圖是在情節提要中創建和配置的,另一些是通過編程方式創建和配置的.這是視圖層次結構:
Summary: I created a view controller that scrolls pages of content. Some of the views are created and configured in storyboard, others programmatically. Here's the view hierarchy:
- Main view (storyboard)
- UIScrollView (storyboard)
- content view (programmatically)
- subviews representing pages of content (programmatically)
滾動視圖的約束在 IB 中配置.以下是我在代碼中為內容視圖配置約束的方式:
The constraints for the scroll view are configured in IB. Here's how I configured the constraints for the content view in code:
- (void)viewDidLoad
{
// ABPageScrollerContentView is a subclass of UIView; it overrides intrinsicContentSize; the size is calculated without referencing the scroll view's dimensions
self.contentView = [[ABPageScrollerContentView alloc] init];
self.contentView.translatesAutoresizingMaskIntoConstraints = NO;
[self.pageScrollerView addSubview:self.contentView];
// configure constraints between scroll view and content view...
UIView *contentView = self.contentView;
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(contentView);
[self.pageScrollerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[contentView]|" options:0 metrics:0 views:viewsDictionary]];
[self.pageScrollerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[contentView]|" options:0 metrics:0 views:viewsDictionary]];
// the content view's subviews are added/removed in the tilePages method (not shown); tilePages is called later in the view controller lifecycle...
}
如果用戶點擊一個編輯按鈕,另一個視圖控制器會在情節提要中使用 segue 以模態方式呈現.視圖控制器關閉后,系統似乎莫名其妙地修改了內容視圖的框架,即使約束沒有改變.
If the user taps an edit button, another view controller is presented modally using a segue in the storyboard. After the view controller is dismissed, the system appears to inexplicably modify the frame of the content view even though the constraints are unchanged.
我在以下委托方法中關閉呈現的視圖控制器:
I dismiss the presented view controller in the following delegate method:
- (void)didExitEditPageViewVC:(id)controller
{
// update currently displayed page view from data model...
// logged content view frame = (0, 0; 1020, 460)
[self dismissViewControllerAnimated:YES completion:^{
// logged content view frame = (-170, 0; 1020, 460)
}];
}
我不明白框架原點的 x 分量如何從 0 變為 -170.解除視圖控制器之前和之后的約束是相同的.
I don't understand how the x component of the frame's origin changed from 0 to -170. The constraints are identical before and after dismissing the view controller.
這是調用dismissViewControllerAnimated:completion:方法之前的框架和約束:
Here is the frame and the constraints right before calling the dismissViewControllerAnimated:completion: method:
(lldb) po self.contentView
$0 = 0x1ede2b40 <AEBPageScrollerContentView: 0x1ede2b40; frame = (0 0; 1020 460); layer = <CALayer: 0x1edd6f00>>
(lldb) po self.pageScrollerView.constraints
$1 = 0x1ed076c0 <__NSArrayM 0x1ed076c0>(
<NSLayoutConstraint:0x1ede2980 H:|-(0)-[AEBPageScrollerContentView:0x1ede2b40] (Names: '|':UIScrollView:0x1edd3410 )>,
<NSLayoutConstraint:0x1eded480 H:[AEBPageScrollerContentView:0x1ede2b40]-(0)-| (Names: '|':UIScrollView:0x1edd3410 )>,
<NSLayoutConstraint:0x1edecbc0 V:|-(0)-[AEBPageScrollerContentView:0x1ede2b40] (Names: '|':UIScrollView:0x1edd3410 )>,
<NSLayoutConstraint:0x1ede1040 V:[AEBPageScrollerContentView:0x1ede2b40]-(0)-| (Names: '|':UIScrollView:0x1edd3410 )>
)
這是呈現視圖控制器重新出現后的框架和約束:
Here is the frame and the constraints after the presenting view controller re-appears:
contentView = <AEBPageScrollerContentView: 0x1ede2b40; frame = (-170 0; 1020 460); layer = <CALayer: 0x1edd6f00>>
self.pageScrollerView.constraints =
(
"<NSLayoutConstraint:0x1ede2980 H:|-(0)-[AEBPageScrollerContentView:0x1ede2b40] (Names: '|':UIScrollView:0x1edd3410 )>",
"<NSLayoutConstraint:0x1eded480 H:[AEBPageScrollerContentView:0x1ede2b40]-(0)-| (Names: '|':UIScrollView:0x1edd3410 )>",
"<NSLayoutConstraint:0x1edecbc0 V:|-(0)-[AEBPageScrollerContentView:0x1ede2b40] (Names: '|':UIScrollView:0x1edd3410 )>",
"<NSLayoutConstraint:0x1ede1040 V:[AEBPageScrollerContentView:0x1ede2b40]-(0)-| (Names: '|':UIScrollView:0x1edd3410 )>"
)
為什么內容視圖的框架發生了意外變化?為什么它不符合約束的規定?
Why did the content view's frame change unexpectedly? And why doesn't it match what is dictated by the constraints?
對 hasAmbiguousLayout 的延遲調用令人驚訝地返回 false.不拋出異常.滾動視圖甚至滾動,盡管內容視圖部分在屏幕外.
A delayed call to hasAmbiguousLayout returns false surprisingly. No exceptions are thrown. The scroll view even scrolls, albeit the content view is partly off-screen.
沒有我在哪里明確設置滾動視圖的內容大小;我把它留給系統.內容視圖具有固有大小(內容視圖的大小似乎很好;問題在于內容視圖的來源).
No where do I explicitly set the scroll view's content size; I leave that to the system. The content view has an intrinsic size (the content view's size appears to be fine; it's the content view's origin that is the problem).
滾動視圖的內容偏移在關閉視圖控制器之前和之后是相同的.但是,內容視圖原點的 x 分量的位移與內容偏移量成正比.內容偏移量越大,模式視圖控制器關閉后內容視圖原點的 x 分量就越負.并且,在零"的內容偏移量處,x 分量為零.因此,如果在查看內容的第一頁時呈現模態視圖控制器(當內容偏移量為零"時),則在關閉視圖控制器時內容視圖的框架是正確的.內容偏移為零的情況是內容視圖的框架正確反映其約束的唯一情況.
The scroll view's content offset is the same before and after dismissing the view controller. However, the displacement of the x component of the content view's origin is proportional to the content offset. The greater the content offset, the more negative the x component of the content view's origin is after the modal view controller is dismissed. And, at a content offset of "zero", the x component is zero. So if the modal view controller is presented while viewing the first page of content (when the content offset is "zero"), the content view's frame is correct upon dismissal of the view controller. The content-offset-of-zero case is the only circumstance in which the content view's frame correctly reflects its constraints.
我嘗試在不同的地方插入對 layoutIfNeeded 的調用,但沒有結果.
I have tried inserting calls to layoutIfNeeded in various places with no results.
有什么建議嗎?
推薦答案
我創建了一個 UIScrollView 子類來解決這個問題(順便說一句,iOS7 中已修復):
I created a UIScrollView subclass that works around this issue (which BTW is fixed in iOS7):
@interface ConstraintsSafeScrollView : UIScrollView
@end
@implementation ConstraintsSafeScrollView {
CGPoint _savedContentOffset;
UIEdgeInsets _savedContentInset;
}
- (void)willMoveToWindow:(UIWindow *)newWindow {
if (newWindow) {
// Reset the scrollview to the top.
[super setContentOffset:CGPointMake(-_savedContentInset.left, -_savedContentInset.top)];
}
[super willMoveToWindow:newWindow];
}
// Overridden to store the latest value.
- (void)setContentOffset:(CGPoint)contentOffset {
_savedContentOffset = contentOffset;
[super setContentOffset:contentOffset];
}
// Overridden to store the latest value.
- (void)setContentInset:(UIEdgeInsets)contentInset {
_savedContentInset = contentInset;
[super setContentInset:contentInset];
}
- (void)didMoveToWindow {
if (self.window) {
// Restore offset and insets to their previous values.
self.contentOffset = _savedContentOffset;
self.contentInset = _savedContentInset;
}
[super didMoveToWindow];
}
@end
這篇關于關閉模式視圖控制器后,框架不反映自動布局約束的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!