将UITableView滑动到屏幕上,继续滑动? [英] Swipe UITableView onto screen, keep swiping?

查看:74
本文介绍了将UITableView滑动到屏幕上,继续滑动?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想要一个从屏幕外开始的桌面视图,可以在屏幕上滚动,到达顶部,并继续滚动。我已经在下面看到了所需的互动。



我尝试了两件事,但两件事都没有完全符合我的需要。



我做的第一件事就是将tableview放在scrollview中,并在tableview上检测到平移时移动scrollview。这会阻止tableview中的触摸,即使我能检测到tableview何时触及屏幕顶部,我也不确定如何继续滚动。



我试过的第二件事是将scrollview的内容大小设置为tableview的高度。这让tableview滚动,但我似乎只能在标有List Item 1的初始小矩形中接收触摸。当tableview滚动时,我无法抓住中间并再滚动它。



构建此交互的最佳方式是什么? 修改:地图将此底部视图围绕在左侧,右侧和大部分顶部。当拉出底部视图时,地图左右可见。



1。)



2。)



3。)(这会继续滚动,因为很多项目都是如列表中所示。)

解决方案

我想你想要这样的东西:





或者这个:



< img src =https://i.stack.imgur.com/4994V.gifalt =大清单>



我将表格视图放在我的桌面上地图视图。我设置表视图的 contentInset contentOffset ,如下所示:

   - (void)viewDidLoad {
[super viewDidLoad];
self.tableView.rowHeight = 44;
}

- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
self.tableView.contentInset =(UIEdgeInsets){。top = self.view.bounds.size.height - self.tableView.rowHeight};
self.tableView.contentOffset = CGPointMake(0,-self.tableView.contentInset.top);
}

请注意,虽然默认行高为44,但 tableView.rowHeight 返回-1,除非您明确设置它。 (在故事板中将其设置为44不会改变这一点。)



我使用了 UITableView 的子类我做了两件事:


  1. 我明确设置 self.backgroundColor = [UIColor clearColor] 。我发现在故事板中设置要清除的背景颜色不起作用。


  2. 我覆盖 pointInside:withEvent:

       - (BOOL)pointInside:(CGPoint)指向withEvent:(UIEvent *)event {
    return point.y> = 0&& [super pointInside:指向withEvent:event];
    }


请注意,你不要这里关心 contentInset 。表视图的 contentOffset.y (与 bounds.origin.y 相同)设置为负数暴露其顶级内容插入时的数字。当项目0的顶部位于表格视图的顶部边缘时,它被设置为0,而当项目位于屏幕的下边缘时则不是这样。



您可能想要的另一件事是防止表格停在屏幕的一半。如果用户在屏幕的中间拖动项目0,您希望表格滚动,因此项目0一直在屏幕顶部(如果有足够的项目),并且如果用户将项目0拖到屏幕中间,你希望表滚动,所以只显示项目0。



我通过使我的视图控制器充当表视图的委托并实现此委托方法,继承自 UIScrollViewDelegate

   - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset :( inout CGPoint *)targetContentOffset {
CGFloat yMin = -self.tableView.contentInset.top;
CGFloat yMax = MIN(0,self.tableView.contentSize.height - self.tableView.bounds.size.height);
if(targetContentOffset-> y< yMax){
if(velocity.y< 0){
targetContentOffset-> y = yMin;
} else {
targetContentOffset-> y = yMax;
}
}
}

这种方法是经过精心编写的它适用于表格太短而无法垂直填充屏幕,以及适用于垂直填充屏幕的表格。



我在这里上传了我的测试项目:。


I want a tableview that starts offscreen and can scroll on-screen, reach the top, and keep scrolling. I've made a visual of the desired interaction below.

I've tried two things, and neither work exactly like I need.

The first thing I did was put the tableview in a scrollview, and move the scrollview when panning is detected on the tableview. This blocks touches in the tableview, and even if I could detect when the tableview hit the top of the screen, I'm not sure how I would continue scrolling.

The second thing I tried was to set the content size of the scrollview to the height of the tableview. This lets the tableview scroll, but I only seem to be able to receive touches in the initial small rectangle labeled "List Item 1". As the tableview scrolls, I can't grab the middle and scroll it anymore.

What's the best way to build this interaction? Edit: A map surrounds this bottom view to the left, right, and mostly top. When the bottom view is pulled up, the map is visible to the left and right.

1.)

2.)

3.) (and this keeps scrolling for as many items are as in the list.)

解决方案

I guess you want something like this:

or this:

I laid out my table view over my map view. I set the table view's contentInset and contentOffset like this:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.tableView.rowHeight = 44;
}

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];
    self.tableView.contentInset = (UIEdgeInsets){ .top = self.view.bounds.size.height - self.tableView.rowHeight };
    self.tableView.contentOffset = CGPointMake(0, -self.tableView.contentInset.top);
}

Note that, although the default row height is 44, tableView.rowHeight return -1 unless you explicitly set it. (Setting it to 44 in the storyboard doesn't change this.)

I used a subclass of UITableView in which I did two things:

  1. I explicitly set self.backgroundColor = [UIColor clearColor]. I found that setting the background color to clear in the storyboard didn't work.

  2. I overrode pointInside:withEvent::

    - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
        return point.y >= 0 && [super pointInside:point withEvent:event];
    }
    

Note that you don't care about contentInset here. The table view's contentOffset.y (which is the same as its bounds.origin.y) is set to a negative number when its top content inset is exposed. It's set to 0 when the top of item 0 is at the top edge of the table view, which isn't the case when the item as at the bottom edge of the screen.

Another thing you might want is to prevent the table from stopping half-on the screen. If the user drags item 0 halfway up the screen, you want the table to scroll so item 0 is all the way at the top of the screen (if there are sufficient items), and if the user drags item 0 halfway down the screen, you want the table to scroll so just item 0 is showing.

I did that by making my view controller act as the table view's delegate and implementing this delegate method, inherited from UIScrollViewDelegate:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
    CGFloat yMin = -self.tableView.contentInset.top;
    CGFloat yMax = MIN(0, self.tableView.contentSize.height - self.tableView.bounds.size.height);
    if (targetContentOffset->y < yMax) {
        if (velocity.y < 0) {
            targetContentOffset->y = yMin;
        } else {
            targetContentOffset->y = yMax;
        }
    }
}

That method is carefully written so that it works for tables too short to fill the screen vertically, and for tables that can fill the screen vertically.

I've uploaded my test project here: https://github.com/mayoff/tableView-over-mapview

Update for side-by-side tables

I don't think side-by-side tables is going to be a good user interface. I think it's going to be confusing. But here's how you do it.

The view hierarchy looks like this:

  • Root view
    • MKMapView
    • MyScrollView
      • ScrollContentView
        • MyTableView for first table
        • MyTableView for second table
        • MyTableView for third table
        • etc.

The map view and the scroll view have the same frames. The scroll view handles the sideways scrolling and each table view is independently scrollable vertically.

Since the scroll view should only capture touches that land in one of the table views, it needs a custom hitTest:withEvent: that returns nil for touches outside any of the table views:

@implementation MyScrollView

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    UIView *hitView = [super hitTest:point withEvent:event];
    return hitView == self ? nil : hitView;
}

@end

But this won't actually do the job, because (in my implementation) the scroll view has just one big subview, the ScrollContentView. So we need to do the same thing in ScrollContentView:

@implementation ScrollContentView

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
    UIView *hitView = [super hitTest:point withEvent:event];
    return hitView == self ? nil : hitView;
}

That's sufficient to pass touches down to the map view if they land outside of the tables.

I also use ScrollContentView to lay out the tables and set the scroll view's content size:

- (void)layoutSubviews {
    [super layoutSubviews];

    // Layout of subviews horizontally:
    // [gutter/2][gutter][subview][gutter][subview][gutter][subview][gutter][gutter/2]
    // where 3 * gutter + subview = width of superview

    CGSize superSize = self.superview.bounds.size;
    CGFloat x = kGutterWidth * 3 / 2;
    CGFloat subWidth = superSize.width - kGutterWidth * 3;

    for (UITableView *subview in self.subviews) {
        subview.frame = CGRectMake(x, 0, subWidth, superSize.height);
        x += subWidth + kGutterWidth;

        CGFloat topInset = superSize.height - subview.rowHeight;
        subview.contentInset = (UIEdgeInsets){ .top = topInset };
        subview.contentOffset = CGPointMake(0, -topInset);
    }

    x += kGutterWidth / 2;
    self.frame = CGRectMake(0, 0, x, superSize.height);
    ((UIScrollView *)self.superview).contentSize = self.bounds.size;

    _pageWidth = subWidth + kGutterWidth;
}

I also made my view controller be the scroll view's delegate, and implemented a delegate method to force the scroll view to stop on "page" (table) boundaries:

- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset {
    CGFloat pageWidth = contentView.pageWidth;

    // Force scroll view to stop on a page boundary.
    CGFloat pageNumber = targetContentOffset->x / pageWidth;
    if (velocity.x < 0) {
        pageNumber = floor(pageNumber);
    } else {
        pageNumber = ceil(pageNumber);
    }
    pageNumber = MAX(0, MIN(pageNumber, contentView.subviews.count - 1));
    targetContentOffset->x = pageNumber * pageWidth;
}

The result:

I've updated the git repository with this version.

这篇关于将UITableView滑动到屏幕上,继续滑动?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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