【问题标题】:UIScrollView wrong offset with Auto LayoutUIScrollView 错误的偏移与自动布局
【发布时间】:2013-02-27 00:56:10
【问题描述】:

我有一个相当简单的视图配置:

一个UIViewController,有一个孩子UIScrollView 和一个UIImageView 在这个UIScrollView。 我将UIImageView 设置为足以突破可见区域的高度(即高于1024pt),并将UIImageViewBottom space to superview 约束设置为固定的正值(例如20 )。

整个设置按预期工作,图像在其父级中很好地滚动。 除非当视图滚动时(滚动到视图底部效果更明显),然后消失,然后再次出现(您切换到另一个视图并返回)滚动值恢复,但内容滚动视图被移动到其父视图的外部顶部。

这个不好解释,我试着画一下:

如果您想测试/查看源代码(或情节提要,我没有编辑任何一行代码)。我在我的github上放了一个小demo:https://github.com/guillaume-algis/iOSAutoLayoutScrollView

我确实阅读了iOS 6 changelog 和关于这个特定主题的解释,并认为这是第二个选项(纯自动布局)的正确实现,但在这种情况下,为什么UIScrollView 的行为如此不规律?我错过了什么吗?

编辑:这与#12580434 uiscrollview-autolayout-issue 完全相同。答案只是解决方法,因为有人找到了解决此问题的正确方法,还是这是 iOS 错误?

编辑 2:我找到了另一种解决方法,它将滚动位置保持在用户离开它的相同状态(这是对 12580434 接受的答案的改进):

@interface GAViewController ()

@property CGPoint tempContentOffset;

@end


@implementation GAViewController

-(void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    self.tempContentOffset = self.mainScrollView.contentOffset;
    self.scrollView.contentOffset = CGPointZero;
}

-(void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    self.scrollView.contentOffset = self.tempContentOffset;
}

这样基本保存viewWillAppear中的偏移量,重置为原点,然后恢复viewDidAppear中的值。这两个调用之间似乎出现了问题,但我找不到它的起源。

【问题讨论】:

  • 感谢您的第二次编辑,这似乎是我能找到的最好的工作。 Mark Kryzhanouski 的解决方案在我的设置中不起作用。 EDIT 2 的唯一问题是,在向后导航时,您可以看到滚动视图偏移量恢复到其原始位置。
  • 就我而言,我能够通过将所有约束添加到顶层视图而不是滚动视图来解决这个问题。
  • 对这个问题的精彩解释。我现在遇到了它,我不敢相信它还没有被修复。
  • 您真的应该将您的 EDIT 2 写入答案,以便我们对其进行投票。
  • @Pang 支持这个问题 ;)。我没有将其添加为答案,因为它只是一种解决方法。并且 afaik 它已在 iOS 7 中修复。

标签: ios uiscrollview autolayout


【解决方案1】:

是的,在纯自动布局环境中 UIScrollView 发生了一些奇怪的事情。第二十次重读iOS SDK 6.0 release notes发现:

请注意,您可以通过在视图和滚动视图的子树之外的视图(例如滚动视图的超级视图)之间创建约束,使滚动视图的子视图看起来浮动(不滚动)在其他滚动内容之上。

解决方案

将您的子视图连接到外部视图。换句话说,就是嵌入了滚动视图的视图。

由于 IB 不允许我们在 imageView 和滚动视图子树之外的视图之间设置约束,例如滚动视图的超级视图,所以我已经在代码中完成了。

- (void)viewDidLoad {
    [super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
    [self.view removeConstraints:[self.view constraints]];
    [self.scrollView removeConstraints:[self.scrollView constraints]];
    [self.imageView removeConstraints:[self.imageView constraints]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[_scrollView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_scrollView)]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_scrollView]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_scrollView)]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[_imageView(700)]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_imageView)]];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[_imageView(1500)]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_imageView)]];
}

还有 vau!有效!

【讨论】:

  • 哦。有用。但为什么 ?在我看来,这是Apple提供的两种选项的混合......对我来说,你引用的信息解释了如何使用一种“CSS绝对定位”制作视图,它不应该滚动全部。
  • 我也无法解释。似乎这是一种 UIScrollView 错误。希望苹果将来会改变这一点。
  • 哦,很奇怪,您似乎在说“UIImageView 应该始终坚持其父视图的左侧”,并且此约束由控制器的视图持有。您并不是说 UIImageView 应该始终贴在控制器视图的左侧。
  • [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|[_imageView(700)]|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(_imageView)]]; 到底是什么意思? | 这里是指它的父视图(即滚动视图)还是指 self.view?
  • 表示第二个选项(self.view)。它是嵌入滚动视图的视图。
【解决方案2】:

编辑对我不起作用。但这有效:

-(void)viewWillDisappear:(BOOL)animated
{
     [super viewWillDisappear:animated];

     self.tempContentOffset = self.scrollView.contentOffset;
     self.scrollView.contentOffset = CGPointZero;
}

- (void)viewDidLayoutSubviews {
     [super viewDidLayoutSubviews];
     self.scrollView.contentOffset = self.tempContentOffset;
}

【讨论】:

  • 同上。伙计,你知道当你必须连接到-viewDidLayoutSubviews时你会遇到问题......
【解决方案3】:

对我来说,我去了 IB,点击了包含滚动视图的视图控制器。然后我去 Attribute Inspector -> View Controller -> Extend Edges -> 取消选中“Under Top Bars”和“Under Bottom Bars”。

【讨论】:

    【解决方案4】:

    找到了简单的解决方案,放了

    [self setAutomaticallyAdjustsScrollViewInsets:NO];
    

    在您的 ViewControllers viewDidLoad 方法中

    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view from its nib.
    
        [self setAutomaticallyAdjustsScrollViewInsets:NO];
    }
    

    【讨论】:

      【解决方案5】:

      我在 UIViewController 中使用 UIScrollView 时遇到了类似的问题,顶部的额外空间在 Storyboard 中不可见。我的解决方案是取消选中 ViewController 故事板属性上的“调整滚动视图插图”:请参阅答案Extra Space (inset) in Scroll View at run time

      【讨论】:

      • 请仔细阅读问题和答案。这不是同一个问题。这是 iOS 6 中的一个错误,甚至在 iOS 7 发布之前。
      • 他所描述的问题仍然存在于 iOS 8 中,它没有得到修复或再次被重新引入(如果它确实是错误而不是功能,你永远不会知道苹果)。虽然它与问题并没有真正相关,但它帮助我解决了我的问题,所以谢谢你:)
      【解决方案6】:

      添加一个全局属性 contentOffset 并将当前 contentOffset 保存在 viewDidDisappear 中。 一旦你返回方法 viewDidLayoutSubviews 将被调用,你可以设置你的原始 contentOffset。

      - (void)viewDidLayoutSubviews
      {
          [super viewDidLayoutSubviews];
          [self.scrollView setContentOffset:self.contentOffset animated:FALSE];
      }
      
      - (void)viewDidDisappear:(BOOL)animated
      {
          [super viewDidDisappear:animated];
          self.contentOffset = self.scrollView.contentOffset;
          [self.scrollView setContentOffset:CGPointMake(0, 0) animated:FALSE];
      }
      

      【讨论】:

      • 1.这个问题在将近一年前就已经解决了。 2. 这很糟糕,因为viewDidLayoutSubviews 可能会在控制器生命周期的后期再次被调用,这将再次重置 contentOffset(这是不想要的)。 3. 在 Objective-C 中使用NO 而不是FALSE。 4.CGPointMake(0,0) => CGPointZero.
      【解决方案7】:

      viewWillAppear: 期间,dispatch_async 似乎解决了问题

      - (void)viewWillAppear:(BOOL)animated
      {
          [super viewWillAppear:animated];
      
          CGPoint originalContentOffset = self.scrollView.contentOffset;
          self.scrollView.contentOffset = CGPointZero;
      
          dispatch_async(dispatch_get_main_queue(), ^{
              self.scrollView.contentOffset = originalContentOffset;
          });
      }
      

      【讨论】:

      • 为什么这被否决了?它实际上为我修复了该错误,并且它可以在不闪烁的情况下工作,并且无需在代码中重新创建视图约束。
      • 我不知道为什么这被否决了。我遇到了类似的问题,然后需要调用这个 self.contentInset = UIEdgeInsetsZero 然后调用 self.contentInset = insets;再次在主队列中。我记录的所有值都是正确的,但 UI 仍然错误。感谢 Chebur。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-11-27
      • 2021-11-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-07
      • 2012-09-16
      相关资源
      最近更新 更多