UIScrollView在返回到UIViewController后其起始位置发生了变化

58

我在故事板中有一个UIViewController子类作为场景,其中包含一个UIScrollView,其中包含各种子视图。 其中一个子视图是一个UIButton,它连接到另一个场景的UIViewController子类。 当我从视图返回时(弹出导航控制器堆栈中的UIViewController),我发现滚动视图的原点已经改变,尽管contentsizecontentoffset看起来正确。

有趣的是,应用程序具有选项卡栏,当我切换到该视图并返回时,滚动视图会正确设置,并且偏移为(0,0)。

这个过程基本上没有涉及任何代码,因为它基本上都在故事板中。由于我对使用Storyboard相当新,所以我认为我做错了什么,但我不知道是什么。 任何想法可能是什么?也许是尺寸问题或约束问题?


我在使用UICollectionView以模态方式呈现VC时遇到了类似的问题,但仍未解决。 - Andrea
你尝试在viewWillAppear中设置原点了吗?那是控制器从弹出返回的地方。第二次(或第三次...)加载时,它不会再看到viewDidLoad。 - timquinn
好的,我在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是新的“origin”高度。但是它似乎没有任何效果。有没有其他方法可以设置原点? - Peter Jacobs
你的故事板中使用了自动布局吗?如果是,那么设置scrollview的frame将不起作用。你需要设置相应的约束条件。在你的情况下,也许尝试将scrollview的顶部与其包含视图固定在一起。 - Steph Sharp
Steph,感谢您的回复。我按照您说的将scrollview固定了,但是已经有太多的“顶部空间”约束条件被自动生成了(超过100个!)。我想我必须删除它们或者至少降低它们的优先级,这似乎相当痛苦。最终,我使用了https://dev59.com/h2cs5IYBdhLWcg3wym4h中答案的变体。 - Peter Jacobs
17个回答

0

很遗憾,Peter和MacMark的建议对我没有用(使用带自动布局的Xcode 5)。解决方案是进入Storyboard,选择视图控制器,并在其中重置视图控制器中的建议约束

enter image description here


那么,建议的限制条件是什么? - Petar
@pe60t0 请忽略此答案,它只是绕过了问题而不是解决它。Alex的答案是正确的。 - Kyle Clegg

0

最近我发了一个关于类似问题的问题,但是上下文不同:Frame doesn't reflect auto layout constraints after dismissing modal view controller">Frame doesn't reflect auto layout constraints after dismissing modal view controller

在我的情况中,滚动视图内自定义容器视图的起点被移位,而不是滚动视图本身的起点。就自动布局而言,自定义容器视图被固定在滚动视图的四个边缘。

容器视图是以编程方式创建和配置的,而不是在 IB 中。尽管容器视图的约束未更改,但在关闭模态视图控制器后,容器视图的起点被移位。约束和框架之间的这种脱节是无法解释的。

更为显著的是,即使我删除并重新添加所有相关约束,起点偏移问题仍然存在。

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

在我的情况下,我必须采取额外的步骤来解决这个问题。我的滚动视图是一个分页滚动视图,它从tilePages方法获取其子视图(在旧的WWDC视频中演示)。更改滚动视图的内容偏移通过UIScrollViewDelegate的scrollViewDidScroll:方法触发tilePages方法。我不得不设置一个标志来关闭tilePages,同时进行内容偏移操作,否则应用程序会崩溃。
希望当我升级到iOS 7时,可以删除内容偏移操作的代码。

0

你尝试过检查不透明栏下的扩展边缘选项吗?它可以在你的控制器属性检查器中找到,位于故事板内。

对我来说,在使用XCode 9 iOS 11时,这个方法很有效。

enter image description here


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 上测试通过。


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!


0

如果以上方法都没有帮助到你,我也遇到了这个问题,我的问题是因为我有以下代码...

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

将这个reloadData()行注释掉,问题就解决了。我不需要这行代码,所以不确定为什么会添加它!

希望这个解决方案能帮助其他人。


-9

否则,请关闭该xib的AutoLayout选项。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接