UIScrollView 上的边界自动更改,带有内容插图 [英] Bounds automatically changes on UIScrollView with content insets

查看:15
本文介绍了UIScrollView 上的边界自动更改,带有内容插图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用 UIScrollView 作为我的分页滚动视图,pagesScrollView.在其中,我放置了专门用于缩放的单个 UIScrollView.在其中的每一个中,我都有一个视图,它是应该可以缩放的页面项目.所有这些都在带有半透明导航栏的 UINavigationController 中.

I'm using a UIScrollView as my paging scroll view, pagesScrollView. Inside that, I put individual UIScrollViews which are used exclusively for zooming. Inside each of those, I have one view which is the page item which should be zoomable. All of that is inside a UINavigationController with a translucent navbar.

我的 pagesScrollViewcontentInset.top = 64bounds.origin.y = -64 (这对我来说似乎很奇怪,但那是系统为我自动设置的内容),这很好用.我的屏幕看起来很棒!

My pagesScrollView has contentInset.top = 64 and bounds.origin.y = -64 (that seems weird to me, but that's what the system is setting automatically for me), and this works just fine. My screen looks great!

但是,在我滚动 pagesScrollView 一点点之后,只要调用 scrollViewWillEndDraggingpagesScrollView 就会开始动画更改bounds.origin.y = -64bounds.origin.y = 0 这会导致我的页面项目被导航栏遮挡.

However, after I scroll the pagesScrollView even a tiny bit, as soon as scrollViewWillEndDragging is called, the pagesScrollView begins an animated change from bounds.origin.y = -64 to bounds.origin.y = 0 which causes my page items to be obscured by the navbar.

左边是加载时的样子,右边是我拖动几个像素然后松开后的样子,它在导航栏下向上滑动(因为 bounds.origin.y 去了到 0).

On the left is what it looks like when it loads, on the right is what it looks like after I drag just a few pixels and then let go, it slides up under the navbar (because the bounds.origin.y goes to 0).

问题是我没有任何代码可以改变边界,并且我在各种滚动委托方法中没有任何代码可以做任何事情.我添加了一堆滚动委托方法,并且刚刚添加了 NSLog(),这样我就可以知道更改发生的时间/地点,但它并没有在我的代码中的任何地方发生.

The problem is that I don't have any code that is altering the bounds and I don't have any code in the various scroll delegate methods that do anything. I've added a bunch of scroll delegate methods and just added NSLog()s so I can figure out when/where the change is happening, but it's not happening anywhere in my code.

所以,我不知道我可以向您展示什么代码来帮助您帮助我.

So, I don't know what code I can show you to help you help me.

我从头开始构建了一个新项目以删除所有其他变量.我将一个裸 UIViewController 放入 UINavigationController.我将一个 UIScrollView 放入我的视图中,整个视图的大小.以下代码是整个项目.

I built a new project from scratch to remove all other variables.. I put a bare UIViewController into a UINavigationController. I put a UIScrollView into my View the entire size of the view. The following code is the entire project.

事实证明,问题(如下所述)仅在 UIScrollView 上启用 PAGING 后才会出现!什么?:)

It turns out the issue (described below) only appears once PAGING IS ENABLED on the UIScrollView! Wtf? :)

这里是一个下载基本项目的链接,其中只有几行代码来演示该问题.只需单击滚动视图,您就会看到它随着边界的变化而向上移动.http://inadaydevelopment.com/stackoverflow/WeirdScrollViews.zip

Here is a link to download a basic project with only a few lines of code which demonstrates the problem. Just click in the scrollview and you'll see it shift up as the bounds change. http://inadaydevelopment.com/stackoverflow/WeirdScrollViews.zip

如何在滚动视图上启用分页,而不会在滚动和移动导航栏下的所有内容时出现边界问题?

How can I have paging enabled on my scrollview without the bounds freaking out during scrolling and shifting everything under the nav bar?

可以将导航栏设置为不透明并避免问题,但理想的情况是具有标准的 iOS7 行为,以便在内容视图缩放后,然后允许内容位于导航栏下方并应通过半透明正常.

It's possible to set the navbar to opaque and the problem is avoided, but the ideal is to have standard iOS7 behavior so that after the content view is zoomed, THEN the content is allowed to be under the navbar and should show through the translucency normally.

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

    NSArray *colors = @[
                        [UIColor blueColor],
                        [UIColor orangeColor],
                        [UIColor magentaColor],
                        ];

    NSArray *zoomerColors = @[
                        [UIColor greenColor],
                        [UIColor yellowColor],
                        [UIColor purpleColor],
                        ];


    self.scroller.pagingEnabled = YES;

    [self.scroller setContentSize:CGSizeMake(self.scroller.frame.size.width*colors.count, self.scroller.frame.size.height)];

    CGRect subviewFrame = CGRectMake(0, 0, 160, 240);
    for (int index=0; index < colors.count; index++) {
        UIColor *color = [colors objectAtIndex:index];
        UIColor *zoomerColor = [zoomerColors objectAtIndex:index];

        UIView *subview = [[UIView alloc] initWithFrame:subviewFrame];
        subview.backgroundColor = color;

        CGRect zoomerFrame = CGRectMake(index*self.scroller.frame.size.width, 0, self.scroller.frame.size.width, self.scroller.frame.size.height);

        UIScrollView *zoomer = [[UIScrollView alloc] initWithFrame:zoomerFrame];
        [zoomer addSubview:subview];
        zoomer.backgroundColor = zoomerColor;

        [self.scroller addSubview:zoomer];

    }
}

推荐答案

这是一个 iOS 错误.我创建了 UIScrollView 的以下子类,以记录 y 随着时间的推移发生了什么以及谁在推动它:

It's an iOS bug. I created the following subclass of UIScrollView to get a log of what happens to y over time and who was pushing it:

@implementation CSScrollView

- (void)setContentOffset:(CGPoint)contentOffset
{
    NSLog(@"%0.0f %@", contentOffset.y, [NSThread callStackSymbols]);
    NSLog(@"[%@]", self.layer.animationKeys);
    [super setContentOffset:contentOffset];
}

@end

(并更改了故事板中的视图类)

(and changed the view class in the storyboard)

当您松开手指时,名为 UIScrollView _smoothScrollDisplayLink: 的方法开始将滚动视图动画化到其最终位置.根据第二个日志,不涉及 CAAnimation,滚动视图使用自己的显示链接进行自己的转换.该自定义代码似乎犯了从 y = 不管y = 0 的动画错误,没有考虑到内容偏移.

When you release your finger, a method called UIScrollView _smoothScrollDisplayLink: starts animating the scroll view to its final position. As per the second log, there's no CAAnimation involved, the scroll view uses its own display link to do its own transition. That custom code appears to make the mistake of animating from y = whatever to y = 0, failing to take the content offset into account.

作为概念验证黑客,我将代码更改为:

As a proof-of-concept hack I changed the code to:

@implementation CSScrollView

- (void)setContentOffset:(CGPoint)contentOffset
{
    contentOffset.y = -64.0f;
    [super setContentOffset:contentOffset];
}

@end

不出所料,问题就消失了.

And, unsurprisingly, the problem went away.

您可能不想对 -64.0f 进行硬编码,但我的结论是:

You probably don't want to hard code the -64.0f but I'd conclude:

  • 这是一个 iOS 错误;
  • 通过 UIScrollView 的子类和 - setContentOffset: 的适当自定义实现拒绝无意义的值来解决此问题.
  • it's an iOS bug;
  • work around it by rejecting nonsensical values via a subclass of UIScrollView with a suitable custom implementation of - setContentOffset:.

一种合理的通用方法可能是检查 self.panGestureRecognizerstate - 这将允许您区分用户负责的滚动和其他不负责的滚动依赖于任何未记录的 API 或复杂的委托事件捕获.然后,如有必要,从当前值中删除正确的 contentOffset.y 而不是硬编码.

A sensible generic means might be to check the state of self.panGestureRecognizer — that'll allow you to differentiate between scrolls the user is responsible for and other scrolls without relying on any undocumented API or complicated capturing of delegate events. Then if necessary crib the correct contentOffset.y from the current value rather than hardcoding it.

这篇关于UIScrollView 上的边界自动更改,带有内容插图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆