弹出回到UIViewController后,UIScrollView的原点发生了变化

时间:2013-07-01 12:38:39

标签: ios cocoa-touch uiscrollview storyboard

我有一个UIViewController子类作为故事板中的一个场景,其中包含一个包含各种子视图的UIScrollView。其中一个子视图是UIButton,它分割成另一个场景UIViewController子类。当我从视图中返回时(从导航控制器堆栈中弹出UIViewController),我发现滚动视图的原点已经有所改变,尽管contentsizecontentoffset似乎是正确的。 / p>

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

这个过程基本上没有涉及代码,因为它几乎都在故事板中。由于我对使用故事板相当新,我觉得我做错了什么,虽然我不知道是什么。关于那可能是什么的任何想法?也许是调整问题或限制?

17 个答案:

答案 0 :(得分:92)

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

答案 1 :(得分:30)

在弹出的视图控制器的viewWillAppear中尝试此操作:

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

编辑:在添加彼得的代码时,您可以获得最佳效果:

- (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);
}

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

答案 2 :(得分:11)

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

 self.contentOffset = self.scrollView.contentOffset;

以及

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

答案 3 :(得分:5)

我遇到了类似的问题,在解除了一个viewController后,我的tableView中的contentOffset被改为(0,-64)。

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

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

before

我在ImageView之后移动了tableView,它运行起来了:

after

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

Pd积。我没有使用autoLayout和故事板

希望这可以帮助别人!

答案 4 :(得分:5)

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

目标C:

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

夫特:

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

答案 5 :(得分:2)

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

在Storyboard中,选择tableview并转到Attributes检查器,并取消选中Row Height和Estimate字段的“Automatic”。同时将内容插入从“自动”更改为“从不”。

[Table view]

答案 6 :(得分:1)

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

enter image description here

答案 7 :(得分:0)

只要以上方法均无济于事,我就会遇到这个问题,而对我来说,我遇到的是以下代码。

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

注释此reloadData()行对我来说已解决此问题。我不需要此行,所以不确定为什么我还要添加它!

希望该解决方案可以帮助其他人。

答案 8 :(得分:0)

我在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上测试。

答案 9 :(得分:0)

以上都没有为我工作,我设法做了我自己的自定义推/拉动画,它就像一个魅力。

首先,添加实现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

看到魔术:)

答案 10 :(得分:0)

您是否尝试过检查不透明条下的延伸边缘选项?它可以在故事板内的控制器属性检查器中找到。

使用XCode 9 iOS 11为我做了诀窍。

enter image description here

答案 11 :(得分:0)

尝试了很多事情之后:

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

我意识到问题是关于estimatedRowHeight值,设置为50px,应该是~150px。更新值修复了问题,现在表视图保持相同的偏移量。

答案 12 :(得分:0)

- (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中发生的错误。 感谢所有朋友!

答案 13 :(得分:0)

最近,我遇到过这个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

感谢Peter Jacobs!

答案 14 :(得分:0)

继续出现当前答案的问题:

第二次尝试打开呈现的视图控制器,而没有离开呈现视图控制器,问题仍然存在。这就是我发布导致我的解决方案的确切步骤的原因。

  1. 因此,我在故事板中重置集合视图的约束,确保它们固定在呈现ViewController的主视图上。

  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;
    }
    

答案 15 :(得分:0)

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

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

以编程方式而不是IB中创建和配置容器视图。尽管容器视图的约束未更改,但在解除模态视图控制器后,容器视图的原点将被替换。约束和框架之间的这种脱节是莫名其妙的。

更令人瞩目的是,在删除并重新添加所有相关约束之后,原始位移问题仍然存在。

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

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

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

答案 16 :(得分:-8)

否则关闭该xib的AutoLayout选项。