在解除模态视图控制器后,框架不反映自动布局约束

bil*_*tum 5 uiscrollview ios autolayout

我正在使用iOS 6,分页UIScrollView和纯自动布局.

简介:我创建了一个滚动内容页面的视图控制器.一些视图在故事板中创建和配置,其他视图以编程方式创建和配置.这是视图层次结构:

- Main view (storyboard) 
  - UIScrollView (storyboard)
    - content view (programmatically)
      - subviews representing pages of content (programmatically)
Run Code Online (Sandbox Code Playgroud)

滚动视图的约束在IB中配置.以下是我在代码中为内容视图配置约束的方法:

- (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...
}
Run Code Online (Sandbox Code Playgroud)

如果用户点击编辑按钮,则使用故事板中的segue以模态方式呈现另一个视图控制器.取消视图控制器后,即使约束不变,系统也会莫名其妙地修改内容视图的帧.

我在以下委托方法中忽略了呈现的视图控制器:

- (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)
   }];
}
Run Code Online (Sandbox Code Playgroud)

我不明白帧的原点的x分量是如何从0变为-170的.在解除视图控制器之前和之后,约束是相同的.

这是调用dismissViewControllerAnimated:completion:方法之前的框架和约束:

(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 )>
)
Run Code Online (Sandbox Code Playgroud)

这是呈现视图控制器重新出现后的框架和约束:

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 )>"
)
Run Code Online (Sandbox Code Playgroud)

为什么内容视图的框架意外更改?为什么它不符合约束条件?

对hasAmbiguousLayout的延迟调用令人惊讶地返回false.没有异常被抛出.滚动视图甚至滚动,尽管内容视图部分在屏幕外.

否我在哪里明确设置滚动视图的内容大小; 我把它留给系统.内容视图具有内在大小(内容视图的大小看起来很好;它是内容视图的起源,这是问题).

在关闭视图控制器之前和之后,滚动视图的内容偏移量是相同的.但是,内容视图的原点的x分量的位移与内容偏移成比例.内容偏移越大,内容视图原点的x分量在解除模态视图控制器后就越负.并且,在内容偏移"零"时,x分量为零.因此,如果在查看内容的第一页时呈现模态视图控制器(当内容偏移为"零"时),则在解除视图控制器时内容视图的帧是正确的.内容偏移为零的情况是内容视图的框架正确反映其约束的唯一情况.

我已尝试在各个地方插入对layoutIfNeeded的调用但没有结果.

有什么建议?

Ian*_*son 7

我创建了一个UIScrollView子类来解决这个问题(在iOS7中修复了BTW):

@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
Run Code Online (Sandbox Code Playgroud)