在UITableView或UIScrollView中添加吸附位置

10

在UITableView或UIScrollView中是否可以添加一个“对齐位置”的功能?我的意思不是当我按下按钮或调用某个方法时自动滚动到一个位置,而是当我在特定点,比如0, 30附近滚动我的滚动视图或表格视图时,它会自动对齐并停留在那里。所以如果我的滚动视图或表格视图滚动,在用户在0, 250, 35之间释放时,它将自动“捕捉”并滚动到那里。我可以想象在UIScrollView的scrollViewDidEndDragging:WillDecelerate:scrollViewWillBeginDecelerating:方法中放入一个if语句来测试位置是否落在该区域内,但我不确定如何在UITableView中实现这一点。任何指导都将不胜感激。


你其实已经走在正确的路上了,兄弟。你能把你尝试过的代码粘贴一下吗? - Pavan
@Pavan,请查看我的回答。 - Milo
我已经看到了你的帖子Milo,并且也回复了你的帖子。如果我给你发布一个 Xcode 项目,让你知道如何正确地做动态滚动视图,以便你不需要不断更改滚动视图代理方法,这是否有用呢? 这将是一个更清晰的解决方案,伙计:) 让我知道吧 - Pavan
1
如果您为滚动视图打开“分页”,它将自动执行此操作。 - John Gibb
8个回答

26

就像Pavan所说的,你应该使用scrollViewWillEndDragging: withVelocity: targetContentOffset:这个方法。它适用于表格视图和滚动视图。如果你正在使用表格视图或垂直滚动的滚动视图,下面的代码应该适用于你。44.0是示例代码中表格单元格的高度,所以你需要将该值调整为你的单元格的高度。如果用于水平滚动的滚动视图,请将y换成x,并将44.0改为滚动视图中各个部分的宽度。

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
    // Determine which table cell the scrolling will stop on.
    CGFloat cellHeight = 44.0f;
    NSInteger cellIndex = floor(targetContentOffset->y / cellHeight);

    // Round to the next cell if the scrolling will stop over halfway to the next cell.
    if ((targetContentOffset->y - (floor(targetContentOffset->y / cellHeight) * cellHeight)) > cellHeight) {
        cellIndex++;
    }

    // Adjust stopping point to exact beginning of cell.
    targetContentOffset->y = cellIndex * cellHeight;
}

兄弟,谢谢。我确实试着告诉他几次。但我认为他坚持自己的方法。 - Pavan

6

我建议您使用scrollViewWillEndDragging: withVelocity: targetContentOffset:方法,这个方法正是为您的目的而设计的,可以将目标内容偏移设置为您想要的位置。

我还建议您查看SO上已经发布的重复问题。

请查看这些帖子


4

如果你真的必须手动操作,这里提供了一个 Swift3 版本。但是,强烈建议为 UITableView 开启分页功能,这样已经为你处理好了。

let cellHeight = 139
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    targetContentOffset.pointee.y = round(targetContentOffset.pointee.y / cellHeight) * cellHeight
}

// Or simply
self.tableView.isPagingEnabled = true

启用分页似乎会降低速度。 - Will

3
在Swift 2.0中,当表格具有内容插入并且简化处理时,ninefifteen的精彩答案如下:
func scrollViewWillEndDragging(scrollView:            UIScrollView,
                               withVelocity velocity: CGPoint,
                               targetContentOffset:   UnsafeMutablePointer<CGPoint>)
{
    let cellHeight = 44
    let y          = targetContentOffset.memory.y + scrollView.contentInset.top + cellHeight / 2
    var cellIndex  = floor(y / cellHeight)

    targetContentOffset.memory.y = cellIndex * cellHeight - scrollView.contentInset.top;
}

只需添加cellHeight / 2,就不再需要ninefifteen的if语句来递增索引。

2

这是Swift 2.0的等效代码

func scrollViewWillEndDragging(scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    let cellWidth = CGRectGetWidth(frame) / 7   // 7 days
    var index = round(targetContentOffset.memory.x / cellWidth)
    targetContentOffset.memory.x = index * cellWidth
}

只要使用round而不是floor,就完全不需要进行这种复杂的四舍五入处理。


2
这段代码可以让你在单元格高度可变(或未知)的情况下对齐到单元格,如果你将滚动到行的下半部分,它会自动对齐到下一行,使得对齐更加“自然”。 原始 Swift 4.2 代码:(为了方便起见,这是我开发和测试的实际代码)
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    guard var scrollingToIP = table.indexPathForRow(at: CGPoint(x: 0, y: targetContentOffset.pointee.y)) else {
        return
    }
    var scrollingToRect = table.rectForRow(at: scrollingToIP)
    let roundingRow = Int(((targetContentOffset.pointee.y - scrollingToRect.origin.y) / scrollingToRect.size.height).rounded())
    scrollingToIP.row += roundingRow
    scrollingToRect = table.rectForRow(at: scrollingToIP)
    targetContentOffset.pointee.y = scrollingToRect.origin.y
}

(翻译)Objective-C 代码:(因为这个问题标记了 objective-c

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
    NSIndexPath *scrollingToIP = [self.tableView indexPathForRowAtPoint:CGPointMake(0, targetContentOffset->y)];
    if (scrollingToIP == nil)
        return;
    CGRect scrollingToRect = [table rectForRowAtIndexPath:scrollingToIP];
    NSInteger roundingRow = (NSInteger)(round(targetContentOffset->y - scrollingToRect.origin.y) / scrollingToRect.size.height));
    scrollingToIP.row += roundingRow;
    scrollingToRect = [table rectForRowAtIndexPath:scrollingToIP];
    targetContentOffset->y = scrollingToRect.origin.y;
}

完美!只需进行最少的更改即可作为插件使用。非常感谢。 - Dave Y

1

我相信如果你使用委托方法:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    NSLog(@"%f",scrollView.contentOffset.y);
    if (scrollView.contentOffset.y > 350 && scrollView.contentOffset.y < 370)
    {
        NSLog(@"setContentOffset");
        [scrollView setContentOffset:CGPointMake(0, 350) animated:YES];
    }
}

请玩弄它,直到您获得所需的行为。也许计算下一个tableViewCell/UIView的顶部边缘,并在最近的减速处停止。

之所以这样做是因为:如果 (scrollView.contentOffset.y > 350 && scrollView.contentOffset.y < 370),是因为滚动视图在快速速度时会跳跃地调用scrollViewDidScroll,因此我使用了if语句来判断是否在两者之间。

您还可以通过以下方式知道速度正在减慢:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    NSLog(@"%f",scrollView.contentOffset.y);
    int scrollSpeed = abs(scrollView.contentOffset.y - previousScrollViewYOffset);
    previousTableViewYOffset = scrollView.contentOffset.y;
    if (scrollView.contentOffset.y > 350 && scrollView.contentOffset.y < 370 && scrollSpeed < minSpeedToStop)
    {
        NSLog(@"setContentOffset");
        [scrollView setContentOffset:CGPointMake(0, 350) animated:YES];
    }
}

0

已更新至Swift 5

作为额外的奖励,它适用于即将着陆的单元格的任何高度

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
    guard let indexPath = self.tableView.indexPathForRow(at: CGPoint(x: self.tableView.frame.midX, y: targetContentOffset.pointee.y)) else {
        return
    }
    var cellHeight = tableView(self.tableView, heightForRowAt: indexPath)
    let cellIndex = floor(targetContentOffset.pointee.y / cellHeight)
    
    if targetContentOffset.pointee.y - (floor(targetContentOffset.pointee.y / cellHeight) * cellHeight) > cellHeight {
        cellHeight += 1
    }
    targetContentOffset.pointee.y = cellIndex * cellHeight
}
    

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