【问题标题】:stopping a UIScrollView at a specific position在特定位置停止 UIScrollView
【发布时间】:2011-05-24 11:49:12
【问题描述】:

我正在尝试为 iPad 应用程序实现水平滚动视图。滚动视图显示一系列图像。滚动后,当滚动视图停止时,我希望图像沿水平方向沿特定点对齐。为此,我尝试了 2 种方法:

1) 禁用 UIScrollView 的滚动,而是实现一个 PanGestureRecogniser,它会检测“滑动/拖动”运动的平移和速度,并根据所需的预定点设置滚动视图的内容偏移量。但是,这里的问题是,如果用户缓慢拖动滚动视图,滚动视图的运动会非常不稳定,因为检索到的滚动视图的 x 坐标会围绕手指按下的点来回跳跃。例如:当手指沿 60-70 移动时,x 值从 50 变为 70。如何使滚动视图的运动平滑?

2) 为 UIScrollView 启用滚动并检查减速结束,然后设置滚动视图的内容偏移以将其调整到必要的位置。但这里的问题是,滚动视图只有在停止减速并且有明显的延迟之后才会调整到其最终位置,而且这个动画看起来也不太好。有没有一种方法可以检测滚动视图何时即将停止滚动,然后调整其位置而不是等待它结束滚动。从 iv 在某些论坛上读到的内容来看,这是不可能的。

3) 在这种情况下,分页滚动视图将不起作用,因为我不希望滚动图像的整个宽度。

有人对我如何实现上述功能有任何想法吗?

【问题讨论】:

    标签: iphone ipad uiscrollview


    【解决方案1】:

    您实际上仍然可以使用分页滚动视图来执行此操作。您只需将clipsToBounds 设置为NO(以便滚动视图中的内容可以在其框架之外呈现),然后将frame 设置为所需的对齐步骤。

    例如,如果你想在 200 的倍数上对齐滚动视图,你可以这样做:

    scrollView.pagingEnabled = YES;
    scrollView.clipsToBounds = NO;
    
    // horizontally center scroll view with a width of 200 and the parent's height
    scrollView.frame = CGRectMake(
        floor((parentView.bounds.size.width - 200) / 2),
        0,
        200,
        parentView.bounds.size.height
    );
    
    [parentView addSubview:scrollView];
    

    【讨论】:

    • 是的。但在这种情况下,“可滚动/可滑动”区域是否也被限制为 200 的宽度?这意味着用户将无法在渲染图像的整个水平部分上滑动,并且只能在这 200 个单位内滚动/滑动。我希望用户能够滚动滚动视图的整个宽度。
    • 您可以通过子类化UIScrollView 并覆盖hitTest:withEvent: 来规避此问题,以返回YES 以获取您想要滚动的区域内的任何内容(父视图的整个宽度,在此案例)。
    • 好的,我应该试试。但是,分页滚动视图带来了另一个基本问题。如果用户进行“快速滑动”,那么每次只有一个图像(或一组固定的图像)会滚动出来,对吗?它不会像一个免费的滚动视图,每次滚动的图像数量会根据滑动的距离和速度而变化。抱歉,如果看起来我在这里扮演魔鬼的拥护者,但我只是试图让功能正确。
    • @Nathan 从用户体验的角度来看,如果您的滚动视图将沿着某些点对齐,那实际上就是您想要的交互。我认为用户应该能够快速滑动以向左或向右移动一项,因为这是为“对齐”或分页滚动视图定义的标准范例。
    • 但我在这里尝试实现的功能类似于“连线”杂志的 iPad 应用程序。在那里,他们有一个缩略图滚动视图,它实现了我正在谈论的功能。我认为他们没有使用分页滚动视图,并且滚动视图中的缩略图确实在水平方向的特定点对齐。
    【解决方案2】:

    我在verticalscrollview 上遇到了同样的问题,并使用了方法 2。即使这个问题很老,我也没有在这里找到解决方案,觉得回答会很好。

    简短的回答是scrollViewWillEndDragging:withVelocity:targetContentOffset: UIScrollViewDelegateprotocol中的方法。

    当用户完成滚动并且动画开始减速时,它会为您提供targetContentOffset。您可以在此方法中更改targetContentOffset,因此animation 将在您可以的位置结束。你的工作只是计算目标位置。

    这是我创建的UIView 子类。我想创建一个类似UIPickerView 的类,但我希望它停止在特定位置(例如日期选择器)。

    我创建了一个符合UIScrollViewDelegateUIView 子类。在这个UIView 中,我确实添加了一个scrollView。这个scrollView 将包含UILabel 实例。小心,我使用ARC,所以我没有发布任何东西。

    #import <UIKit/UIKit.h>
    
    @protocol MyPickerViewDataSource <NSObject>
    
    -(NSUInteger)numberOfRowsForSender:(id)sender;
    -(NSString *)titleForRowAtIndex:(NSUInteger)rowIndex sender:(id)sender;
    
    @end
    
    @interface MyPickerView : UIView <UIScrollViewDelegate>
    
    @property (weak, nonatomic) id<MyPickerViewDataSource> dataSource;
    
    @end
    

    实施:

    #import "MyPickerView.h"
    
    #define CONTENT_ROW_HEIGHT 30
    
    typedef enum {
        ScrollDirectionUp,
        ScrollDirectionDown
    } ScrollDirection;
    
    @interface MyPickerView() {
    
        int topOffset;
        int bottomOffset;
        int lastContentOffset;
        int targetLabelIndex;
        ScrollDirection scrollDirection;
    }
    
    @implementation PickerView
    
    - (void)initSetup {
    self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(self.frame.origin.x,self.frame.origin.y, self.frame.size.width, self.frame.size.height)];
    self.scrollView.delegate = self;
    self.scrollView.showsHorizontalScrollIndicator = NO;
    self.scrollView.showsVerticalScrollIndicator = NO;
    self.scrollView.userInteractionEnabled = YES;
    [self addSubview:self.scrollView];
    
    targetLabelIndex = 0;
    
    topOffset = (self.scrollView.frame.size.height/2)-CONTENT_ROW_HEIGHT/2;
    bottomOffset = topOffset;
    }
    
    - (id)initWithCoder:(NSCoder *)aDecoder {
        self = [super initWithCoder:aDecoder];
        if(self) {
            [self initSetup];
        }
        return self;
    }
    
    - (id)initWithFrame:(CGRect)frame {
        self = [super initWithFrame:frame];
        if(self) {
            [self initSetup];
        }
        return self;
    }
    
    - (void)layoutSubviews {
    
        [super layoutSubviews];
    
        NSUInteger numberOfRows = [self.dataSource numberOfRowsForSender:self];
    
        self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width, topOffset+numberOfRows*CONTENT_ROW_HEIGHT+bottomOffset);
    
        for (int index = 0; index < numberOfRows; index++)
        {
            UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(self.scrollView.frame.origin.x,
                                                                       topOffset+index*CONTENT_ROW_HEIGHT,
                                                                       self.scrollView.frame.size.width,
                                                                       CONTENT_ROW_HEIGHT)];
    
            label.backgroundColor = [UIColor clearColor];
            label.textColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];
            label.text = [self.dataSource titleForRowAtIndex:index sender:self];
    
            [self.scrollView addSubview:label];
        }
    
        [(UILabel *)[self.scrollView.subviews objectAtIndex:targetLabelIndex] setTextColor:[UIColor blueColor]];
    }
    
    - (void)reloadData {
    
        NSUInteger numberOfRows = [self.dataSource numberOfRowsForSender:self];
    
        self.scrollView.contentSize = CGSizeMake(self.scrollView.frame.size.width, numberOfRows*CONTENT_ROW_HEIGHT);
    
        [self.scrollView setContentOffset:CGPointMake(0.0, 0.0) animated:NO];
    
        [self layoutSubviews];
    }
    
    #pragma mark - UIScrollViewDelegate
    
    - (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    
        if(lastContentOffset>self.scrollView.contentOffset.y) {
    
            scrollDirection = ScrollDirectionUp;
        }
        else {
    
            scrollDirection = ScrollDirectionDown;
        }
    
        lastContentOffset = self.scrollView.contentOffset.y;
    }
    
    - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
    
        targetLabelIndex = (targetContentOffset->y/CONTENT_ROW_HEIGHT)+1;
    
        if(scrollDirection==ScrollDirectionUp && targetLabelIndex>0) {
    
            targetLabelIndex--;
        }
    
        targetContentOffset->y = targetLabelIndex*CONTENT_ROW_HEIGHT;
    
        [(UILabel *)[self.scrollView.subviews objectAtIndex:targetLabelIndex] setTextColor:[UIColor blueColor]];
    }
    
    @end
    

    【讨论】:

    • 感谢您发布此信息;我在使用 UICollectionView 时遇到了同样的问题。我注意到方法文档建议采用这种方法:“您的应用程序可以更改 targetContentOffset 参数的值以调整滚动视图完成其滚动动画的位置。”
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-27
    • 2019-06-10
    • 2021-11-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多