关于UIScrollView嵌套UITableView冲突问题
2014-05-15 20:12
567 查看
scrollview 嵌套tableview 会导致手势冲突这在苹果官方文档已声明。我也做了尝试(本例是tableview作为列加入到scrollview中,如下图),发现用一个scrollview作为控制器的view时,滑动手势无法识别。但做了一次另外的有趣尝试发现,如果将tableview的父视图scrollview作为一个子视图插入到控制器的view中时,竟然可以识别毫无影响!!!在进一步测试发现,scrollview的宽度至少要小于他的父视图2px,才能不导致手势冲突,否则,即比如scrollview的宽度和view的宽度相同,仍然会冲突。分析了半天也没有头绪,看看大家有何看法。另外我还发现更有趣的困惑相信,由于无法用言语表达,在这里不做论述了。大家一起讨论下哈!我想这是很多人关注的问题,刚才还看到有人提问,额。
问题由来:项目需要做类似网易新闻的那种UIScrollView上放多个UITableView的效果,其中UITableView还要有下拉刷新效果。
一开始的思路,也是最直观的思路就是一个UIScrollView上放多个UITableView,然后发现UITableView的滑动和UIScrollView的滑动产生冲突,用户体验不好。主要原因在于UIScrollView的滑动原理。
基础知识看这里:
http://snorlax.sinaapp.com/?p=178 http://www.devdiv.com/forum.php?mod=viewthread&tid=197496
总结这两篇,问题在于如果想让UITableView可以下拉,并且显示下拉刷新组件,那么就不能让UIScrollView滚动(scrollEnabled=NO),如果想左右滑动显示并列的其他UITableView,那么就需要让UIScrollView可以滚动,但是,怎么知道用户想如何操作?所以此路不通。(即使可以滑动,但效果远远达不到产品需求)
与是就有了下面的想法:用一个UITableView作为背景,但这个UITableView仅有一个cell,然后在这个cell上放一个横着的UITableView,然后在这个横着的UITableView上放N个View,这样也达到了“UIScrollView上放多个UITableView的效果”。
上代码:
背景UITableView:
[cpp] view
plaincopyprint?
//实现Table
CGRect scrollRect = CGRectMake(0, 0, 320, 460);
self.tableBgScroll = [[[UITableView alloc] initWithFrame:scrollRect style:UITableViewStylePlain] autorelease];
[self.tableBgScroll setDelegate:self];
[self.tableBgScroll setDataSource:self];
//Table的数据源方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellname = @"cell";
InfoCell *cell = (InfoCell *)[tableView dequeueReusableCellWithIdentifier:cellname];
if (cell == nil)
{
cell = [[[InfoCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellname]autorelease];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 460;
}
InfoCell实现:
[cpp] view
plaincopyprint?
#import <UIKit/UIKit.h>
@interface InfoCell : UITableViewCell<UITableViewDataSource, UITableViewDelegate>
{
UITableView *hortable;
}
@end
@implementation InfoCell
- (void)dealloc
{
[super dealloc];
}
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self)
{
hortable = [[UITableView alloc]initWithFrame:CGRectMake(22, -22, 276, 320) style:UITableViewStylePlain];//由于使用了仿射变换,所以这里的frame显得很诡异,慢慢调吧~
hortable.delegate = self;
hortable.dataSource = self;
hortable.layer.borderColor = [UIColor blackColor].CGColor;
hortable.layer.borderWidth = 1;
hortable.transform = CGAffineTransformMakeRotation(M_PI / 2 *3);
hortable.separatorColor = [UIColor redColor];
hortable.decelerationRate = UIScrollViewDecelerationRateFast;
hortable.showsHorizontalScrollIndicator = NO;
hortable.showsVerticalScrollIndicator = NO;
[self addSubview:hortable];
}
return self;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 5;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = [NSString stringWithFormat:@"cell%d",indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]autorelease];
cell.transform = CGAffineTransformMakeRotation(M_PI/2);
}
//在这里添加你的view,就是那些UITableView,<span style="color:#ff0000">注意,关键在这里:如果添加到cell上的table需要下拉刷新,如果不想滑动时间出现冲突,要保证cell上的UITableView的contentoffset 不等于0和不便宜到最底部,这样下拉刷新才没有问题,例如 当<span style="font-family:Arial,Helvetica,sans-serif">contentoffset.y = 0时候,使其等于1。不然背景的table就会跟着一起滚动,达不到下拉刷新的效果</span></span>
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 320;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(@"点击%d",[indexPath row]);
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
}
@end
这样就解决了开始的问题了!
最上层的TableView如果下拉刷新有问题,需要修复偏移量,可以参考如下代码:
- (void) correctOffSetForDownPull
{
if (self.tableView.contentOffset.y ==0) {
self.tableView.contentOffset =CGPointMake(0,1);
}
if (self.tableView.contentOffset.y == (self.tableView.contentSize.height -self.tableView.frame.size.height))
{
self.tableView.contentOffset =CGPointMake(0,
(self.tableView.contentSize.height -self.tableView.frame.size.height)
-1);
}
}
其中横向UITableView需要pageEnable效果,如下方法可以实现:
-(void)scrollViewWillEndDragging:(UIScrollView*)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inoutCGPoint*)targetContentOffset
{
// Variables
CGPoint offset = (*targetContentOffset);
NSIndexPath* indexPath = [hortableindexPathForRowAtPoint:(*targetContentOffset)]; // Get index path for target row
int numberOfRow = [selftableView:(UITableView *)scrollViewnumberOfRowsInSection:(NSInteger)indexPath.section];
/* Find closest row at *targetContentOffset */
// Row at *targetContentOffset
CGRect rowRect = [hortablerectForRowAtIndexPath:indexPath];
// temporary assign
selectedIndexPath = indexPath;
CGRect targetRect = rowRect;
// Next Row
if (indexPath.row < numberOfRow -1 ){
NSIndexPath *nextPath = [NSIndexPathindexPathForRow: indexPath.row +1 inSection:
indexPath.section];
CGRect nextRowRect = [hortablerectForRowAtIndexPath: nextPath];
// Compare distance
// if next row is closer, set target rect
if (fabs(offset.y -CGRectGetMinY(nextRowRect)) <fabs(offset.y -CGRectGetMinY(rowRect))){
targetRect = nextRowRect;
selectedIndexPath = nextPath;
}
}
/* Centering */
offset = targetRect.origin;
if (self.centering){
offset.y -= (hortable.bounds.size.height *0.5 - targetRect.size.height *0.5);
}
// Assign return value
(*targetContentOffset) = offset;
// Snap speed
// it seems it's better set it slow when the distance of target offset and current offset is small to avoid abrupt jumps
float currentOffset = hortable.contentOffset.y;
float rowH = targetRect.size.height;
static const float thresholdDistanceCoef = 0.25;
if (fabs(currentOffset - (*targetContentOffset).y) > rowH * thresholdDistanceCoef){
hortable.decelerationRate =UIScrollViewDecelerationRateFast;
} else {
hortable.decelerationRate =UIScrollViewDecelerationRateNormal;
}
到现在为止几乎接近完美了~
问题由来:项目需要做类似网易新闻的那种UIScrollView上放多个UITableView的效果,其中UITableView还要有下拉刷新效果。
一开始的思路,也是最直观的思路就是一个UIScrollView上放多个UITableView,然后发现UITableView的滑动和UIScrollView的滑动产生冲突,用户体验不好。主要原因在于UIScrollView的滑动原理。
基础知识看这里:
http://snorlax.sinaapp.com/?p=178 http://www.devdiv.com/forum.php?mod=viewthread&tid=197496
总结这两篇,问题在于如果想让UITableView可以下拉,并且显示下拉刷新组件,那么就不能让UIScrollView滚动(scrollEnabled=NO),如果想左右滑动显示并列的其他UITableView,那么就需要让UIScrollView可以滚动,但是,怎么知道用户想如何操作?所以此路不通。(即使可以滑动,但效果远远达不到产品需求)
与是就有了下面的想法:用一个UITableView作为背景,但这个UITableView仅有一个cell,然后在这个cell上放一个横着的UITableView,然后在这个横着的UITableView上放N个View,这样也达到了“UIScrollView上放多个UITableView的效果”。
上代码:
背景UITableView:
[cpp] view
plaincopyprint?
//实现Table
CGRect scrollRect = CGRectMake(0, 0, 320, 460);
self.tableBgScroll = [[[UITableView alloc] initWithFrame:scrollRect style:UITableViewStylePlain] autorelease];
[self.tableBgScroll setDelegate:self];
[self.tableBgScroll setDataSource:self];
//Table的数据源方法
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellname = @"cell";
InfoCell *cell = (InfoCell *)[tableView dequeueReusableCellWithIdentifier:cellname];
if (cell == nil)
{
cell = [[[InfoCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellname]autorelease];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 460;
}
InfoCell实现:
[cpp] view
plaincopyprint?
#import <UIKit/UIKit.h>
@interface InfoCell : UITableViewCell<UITableViewDataSource, UITableViewDelegate>
{
UITableView *hortable;
}
@end
@implementation InfoCell
- (void)dealloc
{
[super dealloc];
}
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self)
{
hortable = [[UITableView alloc]initWithFrame:CGRectMake(22, -22, 276, 320) style:UITableViewStylePlain];//由于使用了仿射变换,所以这里的frame显得很诡异,慢慢调吧~
hortable.delegate = self;
hortable.dataSource = self;
hortable.layer.borderColor = [UIColor blackColor].CGColor;
hortable.layer.borderWidth = 1;
hortable.transform = CGAffineTransformMakeRotation(M_PI / 2 *3);
hortable.separatorColor = [UIColor redColor];
hortable.decelerationRate = UIScrollViewDecelerationRateFast;
hortable.showsHorizontalScrollIndicator = NO;
hortable.showsVerticalScrollIndicator = NO;
[self addSubview:hortable];
}
return self;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 5;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *CellIdentifier = [NSString stringWithFormat:@"cell%d",indexPath.row];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]autorelease];
cell.transform = CGAffineTransformMakeRotation(M_PI/2);
}
//在这里添加你的view,就是那些UITableView,<span style="color:#ff0000">注意,关键在这里:如果添加到cell上的table需要下拉刷新,如果不想滑动时间出现冲突,要保证cell上的UITableView的contentoffset 不等于0和不便宜到最底部,这样下拉刷新才没有问题,例如 当<span style="font-family:Arial,Helvetica,sans-serif">contentoffset.y = 0时候,使其等于1。不然背景的table就会跟着一起滚动,达不到下拉刷新的效果</span></span>
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 320;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(@"点击%d",[indexPath row]);
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
}
@end
这样就解决了开始的问题了!
最上层的TableView如果下拉刷新有问题,需要修复偏移量,可以参考如下代码:
- (void) correctOffSetForDownPull
{
if (self.tableView.contentOffset.y ==0) {
self.tableView.contentOffset =CGPointMake(0,1);
}
if (self.tableView.contentOffset.y == (self.tableView.contentSize.height -self.tableView.frame.size.height))
{
self.tableView.contentOffset =CGPointMake(0,
(self.tableView.contentSize.height -self.tableView.frame.size.height)
-1);
}
}
其中横向UITableView需要pageEnable效果,如下方法可以实现:
-(void)scrollViewWillEndDragging:(UIScrollView*)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inoutCGPoint*)targetContentOffset
{
// Variables
CGPoint offset = (*targetContentOffset);
NSIndexPath* indexPath = [hortableindexPathForRowAtPoint:(*targetContentOffset)]; // Get index path for target row
int numberOfRow = [selftableView:(UITableView *)scrollViewnumberOfRowsInSection:(NSInteger)indexPath.section];
/* Find closest row at *targetContentOffset */
// Row at *targetContentOffset
CGRect rowRect = [hortablerectForRowAtIndexPath:indexPath];
// temporary assign
selectedIndexPath = indexPath;
CGRect targetRect = rowRect;
// Next Row
if (indexPath.row < numberOfRow -1 ){
NSIndexPath *nextPath = [NSIndexPathindexPathForRow: indexPath.row +1 inSection:
indexPath.section];
CGRect nextRowRect = [hortablerectForRowAtIndexPath: nextPath];
// Compare distance
// if next row is closer, set target rect
if (fabs(offset.y -CGRectGetMinY(nextRowRect)) <fabs(offset.y -CGRectGetMinY(rowRect))){
targetRect = nextRowRect;
selectedIndexPath = nextPath;
}
}
/* Centering */
offset = targetRect.origin;
if (self.centering){
offset.y -= (hortable.bounds.size.height *0.5 - targetRect.size.height *0.5);
}
// Assign return value
(*targetContentOffset) = offset;
// Snap speed
// it seems it's better set it slow when the distance of target offset and current offset is small to avoid abrupt jumps
float currentOffset = hortable.contentOffset.y;
float rowH = targetRect.size.height;
static const float thresholdDistanceCoef = 0.25;
if (fabs(currentOffset - (*targetContentOffset).y) > rowH * thresholdDistanceCoef){
hortable.decelerationRate =UIScrollViewDecelerationRateFast;
} else {
hortable.decelerationRate =UIScrollViewDecelerationRateNormal;
}
到现在为止几乎接近完美了~
相关文章推荐
- 关于viewpager中嵌套的fragment中的listview的横向滑动的滑动冲突的问题。
- [iOS]UIScrollView嵌套UITableView,超出屏幕的cell点击不了问题
- 关于点击UIScrollView跳转的问题,以及怎么在UITableView上展示滚动视图,九宫格,自定义Cell等
- 关于ViewPager被嵌套在ScrollView中不显示以及滑动冲突的问题
- UIScrollView滑动与UITableView左滑删除冲突问题解决方案
- 关于在UIScrollView上添加UITableView出现的问题【原理】
- UIScrollview嵌套UITableview时,点击Statasubar不能滚动到顶部的问题
- IOS开发~UISCrollView与UITableView嵌套使用终极解决方案
- UIScrollView内嵌套UIWebView时,因为无法取得UIWebView的实际高度无法滚动的问题
- 解决viewpager嵌套viewpager和其他view 冲突问题
- 设置UIWebView为自定义UIScrollViewDelegate代理冲突问题
- UITableView与UIScrollView的一些问题(持续更新)
- 关于uitableview禁用滚动后当点击一行后再去滑动时点击的那一行文字颜色变白的问题
- UITableView和UIScrollView共存,滚动条滚动问题
- iOS uiscrollView 嵌套 问题 的解决
- UITableView与UIScrollView的一些问题(持续更新)
- 关于iOS UIScrollView放大的问题
- ios7 中关于导航切换影响UIScrollView的问题
- listview嵌套viewpager 滑动冲突问题
- 关于UIScrollView不能拖动的问题