【问题标题】:UIScrollView's origin changes after popping back to the UIViewController弹回 UIViewController 后 UIScrollView 的原点发生变化
【发布时间】:2023-03-15 22:53:01
【问题描述】:

我有一个UIViewController 子类作为情节提要中的一个场景,其中包含一个包含各种子视图的UIScrollView。其中一个子视图是UIButton,它进入另一个场景UIViewController 子类。当我从视图返回时(将UIViewController 从导航控制器堆栈中弹出),我发现滚动视图的原点发生了某种变化,尽管contentsizecontentoffset 似乎是正确的。

同样有趣的是,该应用程序有一个标签栏,当我离开并返回该视图时,滚动视图被正确设置为偏移量为 (0, 0)。

这个过程基本上不涉及任何代码,因为它几乎都在情节提要中。由于我对使用情节提要相当陌生,因此我认为我做错了什么,尽管我不知道是什么。关于那可能是什么的任何想法?也许是规模问题或限制?

【问题讨论】:

  • 我在展示 VC 时遇到了与 UICollectionView 类似的问题,但仍未解决
  • 您是否尝试在 viewWillAppear 中设置原点?那是控制器从弹出窗口返回的地方。它不会在第二次(或第三次......)加载时看到 viewDidLoad。
  • 好吧,我在viewWillAppear 中尝试了以下操作:self.scrollView.frame = CGRectMake(self.scrollView.frame.origin.x, self.scrollView.frame.origin.y - y, self.scrollView.frame.size.width, self.scrollView.frame.size.height);(其中 y 是新的“原点”高度),但似乎没有效果。还有其他方法可以设置原点吗?
  • 您是否在情节提要中使用自动布局?如果是这样,设置滚动视图的框架将不起作用。您需要设置相关的约束。在您的情况下,也许尝试将滚动视图的顶部固定到其包含视图。
  • 斯蒂芬,谢谢你的回复。正如你所说,我尝试固定滚动视图,但是滚动视图已经有很多“顶部空间”约束必须是自动生成的(超过 100 个!)。我想我必须删除它们或至少降低它们的优先级,这似乎相当痛苦。最后,我使用了stackoverflow.com/questions/12580434/… 中答案的变体。

标签: ios cocoa-touch uiscrollview storyboard


【解决方案1】:

万一上述方法都没有帮助,我遇到了这个问题,而对我来说问题是我有以下代码..

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    tableView.reloadData()
}

注释掉这个 reloadData() 行为我解决了这个问题。我不需要这条线所以不知道为什么我什至添加它!

希望此解决方案对其他人有所帮助。

【讨论】:

    【解决方案2】:

    否则关闭该 xib 的 AutoLayout 选项。

    【讨论】:

      【解决方案3】:

      我结合了 SO 上随处发布的不同解决方案,并提出了这个子类:

      // Keeps track of the recent content offset to be able to restore the
      // scroll position when a modal viewcontroller is dismissed
      class ScrollViewWithPersistentScrollPosition: UIScrollView {
      
          // The recent content offset for restoration.
          private var recentContentOffset: CGPoint = CGPoint(x: 0, y: 0)
      
          override func willMove(toWindow newWindow: UIWindow?) {
              if newWindow != nil {
                  // save the scroll offset.
                  self.recentContentOffset = self.contentOffset
              }
              super.willMove(toWindow: newWindow)
          }
      
          override func didMoveToWindow() {
              if self.window != nil {
                  // restore the offset.
                  DispatchQueue.main.async(execute: {
                      // restore it
                      self.contentOffset = self.recentContentOffset
                  })
              }
              super.didMoveToWindow()
          }
       }
      

      在 iOS 11.2 / Xcode 9.2 上测试。

      【讨论】:

        【解决方案4】:

        以上方法都不适合我,我设法制作了自己的自定义推/拉动画,它就像一个魅力。

        首先,添加这个实现Push场景的类

        class PushAnimator: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate {
        
        func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return 0.5
        }
        
        func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        
            // get reference to our fromView, toView and the container view that we should perform the transition in
            let container = transitionContext.containerView
            let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from)!
            let toView = transitionContext.view(forKey: UITransitionContextViewKey.to)!
        
            // start the toView to the right of the screen
            var frame = toView.frame
            frame.origin.x = container.frame.width
            toView.frame = frame
        
            // add the both views to our view controller
            container.addSubview(toView)
            container.addSubview(fromView)
        
            // get the duration of the animation
            let duration = self.transitionDuration(using: transitionContext)
        
            // perform the animation!
            UIView.animate(withDuration: duration, animations: {
        
                var frame = fromView.frame
                frame.origin.x = -container.frame.width
                fromView.frame = frame
        
                toView.frame = container.bounds
        
            }, completion: { _ in
                // tell our transitionContext object that we've finished animating
                transitionContext.completeTransition(true)
        
            })
        }
        }
        

        然后添加这个实现pop场景的类

        import Foundation
        import UIKit
        
        class PopAnimator: NSObject, UIViewControllerAnimatedTransitioning, UIViewControllerTransitioningDelegate {
        
        func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
            return 0.5
        }
        func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
            // get reference to our fromView, toView and the container view that we should perform the transition in
            let container = transitionContext.containerView
            let fromView = transitionContext.view(forKey: UITransitionContextViewKey.from)!
            let toView = transitionContext.view(forKey: UITransitionContextViewKey.to)!
        
            // set up from 2D transforms that we'll use in the animation
            let offScreenRight = CGAffineTransform(translationX: container.frame.width, y: 0)
        
            // start the toView to the right of the screen
            var frame = toView.frame
            frame.origin.x = -container.frame.width
            toView.frame = frame
        
            // add the both views to our view controller
            container.addSubview(toView)
            container.addSubview(fromView)
        
            // get the duration of the animation
            let duration = self.transitionDuration(using: transitionContext)
        
            // perform the animation!
            UIView.animate(withDuration: duration, animations: {
        
                fromView.transform = offScreenRight
                toView.frame = container.bounds
        
            }, completion: { _ in
                // tell our transitionContext object that we've finished animating
                transitionContext.completeTransition(true)
        
            })
        }
        }
        

        然后将此行添加到您的 viewDidLoad 方法以更改默认 NavigationController 委托

        self.navigationController?.delegate = self
        

        看看魔法:)

        【讨论】:

          【解决方案5】:

          在 iOS 11 中,我遇到了类似的问题,当我在弹出视图控制器后返回时,前一个视图控制器中的 tableview 用于自动调整其内容插入,导致 tableview 内容突然从顶部跳转。以下解决方案在 iOS 11 中对我有用。

          在 Storyboard 中,选择 tableview 并转到 Attributes inspector 并取消选中 Row Height 和 Estimate 字段的“Automatic”。还将内容插图从“自动”更改为“从不”。

          []

          【讨论】:

            【解决方案6】:

            您是否尝试过检查“在不透明条下延伸边缘”选项?它可以在情节提要内的控制器属性检查器中找到。

            它使用 XCode 9 iOS 11 为我解决了问题。

            【讨论】:

              【解决方案7】:

              我正在使用 collectionView 并且遇到了类似的问题。对于 iOS 11:在尺寸检查器中,有“内容插入”。将其设置为“从不”。这为我解决了问题。我希望这可以帮助别人。

              目标 C:

              if (@available(iOS 11, *)) {
                 [UIScrollView appearance].contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
              }
              

              斯威夫特:

              if #available(iOS 11, *) {
              UIScrollView.appearance().contentInsetAdjustmentBehavior = .never
              }
              

              【讨论】:

              • 唯一有帮助的。谢谢!
              • 没问题。很高兴我能帮上忙!
              • 这应该是使用 iOS 11 时公认的答案
              • 在这里尝试了几个答案之后,这个答案奏效了。 Xcode 9,iOS 11。谢谢!
              • @JosephFrancis 你使用这个有没有崩溃?
              【解决方案8】:

              在尝试了很多事情之后:

              • 将 automaticallyAdjustsScrollViewInsets 设置为 false 以查看问题是否出在导航栏上。
              • 使用固定的单元格高度...
              • 正在缓存单元格高度...
              • 使用表视图偏移量保存属性,将其缓存并将其设置在 viewWillAppear...

              我意识到问题出在estimatedRowHeight 值上,该值设置为 50 像素,而实际上应该是 ~150 像素。更新值修复了问题,现在表格视图保持相同的偏移量。

              【讨论】:

                【解决方案9】:
                - (void)viewDidDisappear:(BOOL)animated {
                    [super viewDidDisappear:animated];
                
                    if ([[[UIDevice currentDevice] systemVersion] floatValue] < 7) {
                        _isRecoredforios6 = YES;
                        _recordedOffsetforios6 = _scrollView.contentOffset;
                        _recordedSizeforios6 = _scrollView.contentSize;
                        _scrollView.contentOffset = CGPointZero;
                    }
                
                }
                
                - (void)viewDidLayoutSubviews {
                    [super viewDidLayoutSubviews];
                
                    if (_isRecoredforios6) {
                        _isRecoredforios6 = NO;
                        _scrollView.contentSize = _recordedSizeforios6;
                        _scrollView.contentOffset = _recordedOffsetforios6;
                    }
                }
                

                我已经修复了这个 ios6 错误,可以使用这些代码来解决。 我解决了 Scrollview 中发生的错误。 首先感谢我的朋友们!

                【讨论】:

                  【解决方案10】:

                  在 iOS 7/8/9 中,简单的 self.automaticallyAdjustsScrollViewInsets = NO; 解决了我的问题。

                  【讨论】:

                  • 简单的解决方案!我刚刚通过搜索找到了这个并解决了我的问题。
                  • 优秀。我在标签栏的标签之间切换时使用过。
                  • 附注:假设您有一个 TabBarController,其中包含一个 UIViewController,该 UIViewController 包含一个 ContainerController,这应该添加到您的 TabBarController。
                  • 对我来说将其设置为 self.automaticallyAdjustsScrollViewInsets = YES;解决了我在 iOS8 中的问题
                  • 如果 Cocoa 更体面,甚至不应该发生的另一个问题
                  【解决方案11】:

                  最近遇到了这个bug,可以用这些代码解决:

                  // fix ios 6 bug begin
                  - (void)viewDidDisappear:(BOOL)animated
                  {
                      [super viewDidDisappear:animated];
                  
                      if ([[[UIDevice currentDevice] systemVersion] floatValue] < 7) {
                          isRecoredforios6 = YES;
                          recordedOffsetforios6 = self.tableView.contentOffset;
                          recordedSizeforios6 = self.tableView.contentSize;
                      }
                  }
                  
                  - (void)viewDidLayoutSubviews
                  {
                      [super viewDidLayoutSubviews];
                      if (isRecoredforios6) {
                          isRecoredforios6 = NO;
                          self.tableView.contentSize = recordedSizeforios6;
                          self.tableView.contentOffset = recordedOffsetforios6;
                      }
                  }
                  // fix ios 6 bug end
                  

                  感谢彼得·雅各布斯!

                  【讨论】:

                    【解决方案12】:

                    按照当前答案继续问题:

                    第二次尝试打开显示的视图控制器,但没有离开显示的视图控制器,问题仍然存在。这就是为什么我要发布导致我的解决方案的确切步骤

                    1. 所以,我在 Storyboard 中重置 collectionView 的约束,确保它们被固定到 presentingViewController 的主视图。

                    2. 添加self.view.translatesAutoresizingMaskIntoConstraints = YES; 在呈现视图控制器的 viewDidLoad 内。

                    3. 并且在模态呈现的视图控制器出现之前,私下存储集合视图的 contentOffset:

                      (void)viewWillDisappear:(BOOL)animated
                      {
                          [super viewWillDisappear:animated];
                      
                          self.contentOffset = self.collectionView.contentOffset;
                          self.collectionView.contentOffset = CGPointZero;
                      }
                      
                      - (void)viewDidLayoutSubviews {
                           [super viewDidLayoutSubviews];
                           self.collectionView.contentOffset = self.contentOffset;
                      }
                      

                    【讨论】:

                    • 嗨,viewDidLayoutSubviews 没有调用,当我没有提供带有 overcurrentcontext 的视图控制器时
                    • @kemdo 如果您向下滚动到此stackoverflow.com/questions/11236367/… 上的较新回复,它可能会有所帮助。
                    【解决方案13】:

                    我遇到了类似的问题,在关闭 viewController 后,tableView 的 contentOffset 更改为 (0, -64)。

                    我的解决方案有点奇怪,我尝试了所有其他答案但没有成功,唯一解决我的问题是在 .xib 的控件树中切换 tableView 位置

                    它是父视图中的第一个控件,如下所示:

                    我在 ImageView 之后移动了 tableView 并且它起作用了:

                    似乎将表格视图放在第一个位置会导致问题,将表格视图移动到另一个位置可以解决问题。

                    P.D.我没有使用 autoLayout 也没有故事板

                    希望这可以帮助某人!

                    【讨论】:

                    • 这对我来说有故事板和自动布局...我只是在框架 {0,0,0,0} 的所有其他内容下放置了一些透明的虚拟视图,它就成功了!
                    【解决方案14】:

                    我最近发布了一个关于类似问题但在不同背景下的问题: Frame doesn't reflect auto layout constraints after dismissing modal view controller

                    在我的例子中,滚动视图内的自定义容器视图的原点被移位,而不是滚动视图本身的原点。关于自动布局,自定义容器视图被固定到滚动视图的四个侧面。

                    容器视图是通过编程而不是在 IB 中创建和配置的。虽然容器视图的约束没有改变,但容器视图的原点在关闭模态视图控制器后发生了位移。约束和框架之间的这种脱节是无法解释的。

                    更值得注意的是,在我删除并重新添加所有相关约束后,原点位移问题仍然存在。

                    我想出了一个类似的解决方案:保存当前内容偏移的值,将内容偏移设置为“零”,让系统交换您的视图,然后恢复内容偏移(即内容偏移舞蹈)。

                    但是,就我而言,我必须采取额外的步骤来解决问题。我的滚动视图是一个分页滚动视图,它从 tilePages 方法(在旧的 WWDC 视频中演示)获取其子视图。更改滚动视图的内容偏移量会通过 UIScrollViewDelegate 的 scrollViewDidScroll: 方法触发 tilePages 方法。在进行内容偏移舞蹈时,我必须设置一个标志来关闭 tilePages,否则应用程序会崩溃。

                    希望当我升级到 iOS 7 时,我可以删除内容偏移舞蹈的代码。

                    【讨论】:

                      【解决方案15】:

                      不幸的是,Peter 和 MacMark 的建议对我不起作用(Xcode 5 w/ auto-layout)。解决方案是转到情节提要,选择视图控制器,然后Reset to Suggested Constraints in View Controller

                      【讨论】:

                      • 那么建议的限制是什么?
                      • @pe60t0 忽略这个答案,它解决了这个问题而不是修复它。亚历克斯的回答是正确的。
                      【解决方案16】:

                      在你弹回的视图控制器的 viewWillAppear 中试试这个:

                       self.scrollView.contentOffset = CGPointMake(0, 0);
                      

                      编辑:同时添加 Peter 的代码时,您可以获得最佳结果:

                      - (void)viewWillAppear:(BOOL)animated {
                          [super viewWillAppear:YES];
                          self.scrollView.contentOffset = CGPointMake(0, 0);
                      }
                      

                      - (void)viewWillDisappear:(BOOL)animated {  
                          self.recentContentOffset = self.scrollView.contentOffset;
                          [super viewWillDisappear:animated];
                      }
                      

                      - (void)viewDidLayoutSubviews {
                          [super viewDidLayoutSubviews];
                          self.scrollView.contentOffset = CGPointMake(0, self.recentContentOffset.y);
                      }
                      

                      您返回到原始滚动位置,没有视觉副作用,并且滚动视图本身在返回后正确定位在其父视图中(这是一个问题)。

                      【讨论】:

                      • 我已经把 viewDidLayoutSubviews 的代码放在 viewWillAppear 中,因为 viewDidLayoutSubviews 在弹出后不会被调用。不过谢谢你的回答!
                      • 非常感谢。解决了我的问题。
                      • Ian Wilkinson 通过子类化 UIScrollView 解决了这个问题,我认为这很棒stackoverflow.com/a/19370881/263003
                      【解决方案17】:

                      实际上,我把那行代码放在viewDidDisappear中,为了在视图重新出现时记住偏移量,我在它之前添加了这一行

                       self.contentOffset = self.scrollView.contentOffset;
                      

                      还有

                       - (void)viewDidLayoutSubviews { 
                             self.scrollView.contentOffset = self.contentOffset; 
                       }
                      

                      【讨论】:

                        猜你喜欢
                        • 2010-11-14
                        • 2016-10-06
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2013-09-12
                        • 2017-09-10
                        • 1970-01-01
                        • 2012-11-23
                        相关资源
                        最近更新 更多