如何使表格滚动工作:UIControl 内的 UITableView 内的 UIControl
Posted
技术标签:
【中文标题】如何使表格滚动工作:UIControl 内的 UITableView 内的 UIControl【英文标题】:How to make table scrolling work: UIControl within UITableView within UIControl 【发布时间】:2013-12-02 18:39:14 【问题描述】:设置如下:我在表格单元格中有 UIControls,允许从右向左滑动以实现删除功能(如清除)
表格单元格位于 UITableView 中。
TableView 位于另一个 UIControl 中,它允许从左向右或从右向左滑动以更改日期。发生这种情况时,会在主视图的右侧或左侧创建一个新的 TableView,然后从左侧或右侧拉入新的 TableView,直到达到阈值并且动画接管,然后用新的替换旧的。
在某些情况下,所有这些交互实际上都能正常工作。问题是经过几次交互(表格幻灯片似乎有问题)后,滚动表格变得困难/不可能。
这里是 TableViewSlider(包含 TableView 的*** UIControl)的代码。任何想法将不胜感激!
@implementation OSETableViewSlider
- (void) initialize
self.backgroundColor = [UIColor backgroundFlatColor];
self.mainTableView = [self createUITableView];
self.mainTableView.frame = CGRectMake(0,0, self.frame.size.width, self.frame.size.height);
self.mainTableView.hidden = NO;
[self addSubview:self.mainTableView];
self.transitionInProgress = NO;
- (id)initWithFrame:(CGRect)frame
self = [super initWithFrame:frame];
if (self)
[self initialize];
return self;
- (id)initWithCoder:(NSCoder *)aDecoder
self = [super initWithCoder:aDecoder];
if(self)
[self initialize];
return self;
- (UITableView *)createUITableView
UITableView *newTable = [[UITableView alloc] init];
newTable.hidden = YES;
newTable.separatorStyle = UITableViewCellSeparatorStyleNone;
newTable.scrollEnabled = YES;
newTable.bounces = YES;
newTable.dataSource = self;
newTable.delegate = self;
newTable.backgroundColor = [UIColor backgroundFlatColor];
return newTable;
- (void)setFrame:(CGRect)frame
super.frame = frame;
self.mainTableView.frame = CGRectMake(0,0, frame.size.width, frame.size.height);
- (void)transitionToDate:(NSDate *)date fromRight:(BOOL)rightToLeft
[self.viewController beginTransitionToDate:date];
self.transitionTableView = [self createUITableView];
self.transitionTableView.frame = CGRectMake( rightToLeft ? self.frame.size.width : (-1 * self.frame.size.width), 0,
self.frame.size.width, self.frame.size.height );
self.transitionTableView.hidden = NO;
[self addSubview:self.transitionTableView];
self.transitionInProgress = YES;
[UIView animateWithDuration:0.2f animations:^
self.transitionTableView.frame = CGRectMake(0,0, self.frame.size.width, self.frame.size.height);
self.mainTableView.frame = CGRectMake( rightToLeft ? (-1 * self.frame.size.width) : self.frame.size.width, 0,
self.frame.size.width, self.frame.size.height);
completion:^(BOOL completed)
[self.viewController endTransitionDidChange:YES];
[self.mainTableView removeFromSuperview];
self.mainTableView = self.transitionTableView;
self.transitionInProgress = NO;
];
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
return 1;
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
return [self.viewController activityCountForTransition:(tableView!=self.mainTableView)];
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
return [self.viewController cellNumber:indexPath.item forTransition:(tableView!=self.mainTableView)];
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
return [self.viewController cellNumber:indexPath.item forTransition:(tableView!=self.mainTableView)].frame.size.height;
- (void)layoutSubviews
if(!self.transitionInProgress)
[self.mainTableView setFrame:CGRectMake(0,0, self.frame.size.width, self.frame.size.height)];
- (BOOL) beginTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
NSLog(@"begin track");
self.locationInView = [touch locationInView:self.superview];
self.fingerTracking = YES;
self.fingerMoved = NO;
self.transitionTableView = nil;
return YES;
- (CGFloat) calcOffsetForTouch:(UITouch *)touch
CGFloat fingerOffset = [touch locationInView:self.superview].x - self.locationInView.x;
return fingerOffset + self.startingOffset;
- (BOOL) continueTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
self.fingerMoved = YES;
CGFloat offset = [self calcOffsetForTouch:touch];
//NSLog(@"offset is: %f flarb: %f", offset, fabs(offset));
if(offset < 0 && (!self.transitioningLeft || [OSEUtils isNull:self.transitionTableView]))
[self.viewController beginTransitionToDate:[OSEUtils daysOffset:1 fromDate:self.viewController.selectedDate withTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]]];
[self.transitionTableView removeFromSuperview];
self.transitionTableView = [self createUITableView];
self.transitioningLeft = YES;
NSLog(@"going left");
[self addSubview:self.transitionTableView];
[self.transitionTableView reloadData];
if(offset > 0 && (self.transitioningLeft || [OSEUtils isNull:self.transitionTableView]))
[self.viewController beginTransitionToDate:[OSEUtils daysOffset:-1 fromDate:self.viewController.selectedDate withTimeZone:[NSTimeZone timeZoneWithName:@"UTC"]]];
[self.transitionTableView removeFromSuperview];
self.transitionTableView = [self createUITableView];
self.transitioningLeft = NO;
NSLog(@"going right");
[self addSubview:self.transitionTableView];
[self.transitionTableView reloadData];
CGFloat mult = self.transitioningLeft ? 1 : -1;
if(fabs(offset) > (self.frame.size.width / 3.0f))
if(self.transitioningLeft)
offset = -1 * self.frame.size.width;
else
offset = self.frame.size.width;
[UIView animateWithDuration:0.2f animations:^
self.mainTableView.frame = CGRectMake(offset, 0, self.frame.size.width, self.frame.size.height);
self.transitionTableView.frame = CGRectMake(offset + (mult * self.frame.size.width), 0,
self.frame.size.width, self.frame.size.height);
completion:^(BOOL finished)
[self.mainTableView removeFromSuperview];
if(self.transitioningLeft)
[self.viewController.daySelector jumpToNext];
else
[self.viewController.daySelector jumpToPrevious];
self.mainTableView = self.transitionTableView;
self.transitionTableView = nil;
[self.viewController endTransitionDidChange:YES];
];
return NO;
self.mainTableView.frame = CGRectMake(offset, 0, self.frame.size.width, self.frame.size.height);
self.transitionTableView.frame = CGRectMake(offset + (mult * self.frame.size.width), 0, self.frame.size.width, self.frame.size.height);
self.transitionTableView.hidden = NO;
return YES;
- (void) endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event
NSLog(@"end tableslide track");
[UIView animateWithDuration:0.2f animations:^
self.mainTableView.frame = CGRectMake(0,0,self.frame.size.width, self.frame.size.height);
self.transitionTableView.frame = CGRectMake(((self.transitioningLeft ? 1 : -1 ) * self.frame.size.width), 0, self.frame.size.width, self.frame.size.height);
completion:^(BOOL completion)
[self.transitionTableView removeFromSuperview];
self.transitionTableView = nil;
[self.viewController endTransitionDidChange:NO];
];
self.fingerTracking = NO;
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
UIView *tableHitTest = [self.mainTableView hitTest:point withEvent:event];
if([tableHitTest isKindOfClass:[OSECellPanelControl class]])
return tableHitTest;
else
return self;
@end
【问题讨论】:
我可以看到您正在从 superview 中删除表视图,但我不确定您是否将它们全部清除。查看您的代码,可能有一些执行方式允许某些表视图堆叠(未清理)。一段时间后某些事情变得缓慢的原因通常是视图堆叠,所以尝试代码演练我猜你会发现泄漏...... 嗯 - 肯定会看到堆叠视图不好。我做了一些基本的测试(触发问题,然后是断点和 po self.subviews),但没有看到额外的表格视图。另一个线索 - 当情况出现时,我的单元格拖动事件最终会进入错误的单元格 - 通常比我正在拖动的单元格高出大约 2 个......??? 您可能对此感兴趣,How to Make a Gesture-Driven To-Do List App Like Clear 你可以发布一些截图吗,很难想象你在这里做什么...... 【参考方案1】:当用户向左或向右滑动时,为什么要创建一个新的UITableView
?使用内置的deleteRowsAtIndexPath:
和insertRowsAtIndexPath:
方法对滑入和滑出的行进行动画处理会更有效,而无需滑入和滑出整个UITableView
。
您可以查看at this for a reference,了解当您在日期之间滑动时为表格视图内容更改设置动画。
我不知道您当前的设置,但是当我尝试使用可滑动的UITableViewCell
进行完全相同的操作并且它的超级视图可以滑动时,我在获得我想要的交互时遇到了问题。如果这导致您的问题,您可以查看检测用户滑动的位置。如果用户在 UITableViewCell 左右边缘的特定偏移量内滑动,则它会滑动单元格,该偏移量(单元格中心)之外的任何内容都会使 UITableView 本身滑动。然后,您将动画出新行的删除和插入。
此外,通过每次重新实例化一个新的 UITableView,您也迫使您的 UITableViewCell 每次都重新实例化。通过使用删除和插入方法,您可以通过dequeueReusableCellWithIdentifier
方法继续使用现有单元格。每次您重新实例化 UITableView 时,您的单元格都设置为 nil 并被丢弃,需要新的 tableview 来创建单元格的完整新实例。这是非常低效的。
【讨论】:
感谢您的回复....我肯定创建了太多对象;但是,性能还不是问题。您发送的链接很有趣....不过,我将在每次滑动时更改每个单元格-该技术仍然有效吗?还;我可以做部分拖动吗? 您可以执行类似于@Rufel 提到的动画部分拖动的操作(使用选项 C)。如果你走那条路,我会实例化 2 个 UITableView,然后重新使用它们。表视图 A 滑出站点,表视图 B 滑入。这提供了部分拖动功能。然后每次滑动时,将相反的 TableView 移动到正确的一侧并进行动画处理。这使您可以继续重复使用已经实例化的 UITableViewCells 并节省大量表视图的实例化。【参考方案2】:您正在深入了解触摸跟踪和点击检测。改用更高级别的 API - 手势识别器。通过手势,您可以设置优先级(此操作必须先失败,此操作才能开始)。
因此,您需要在容器视图上使用几个自定义手势(不再需要是控件)。如果初始接触点不在视图的边缘(在某个范围内),则这些操作会失败。这些触发了整个表格视图的变化。
表格单元格具有滑动手势,每个手势都需要自定义手势失败。这些会触发您的单元格删除逻辑。
【讨论】:
【参考方案3】:您的想法与 ios7 解锁屏幕和日历的构建方式有一些相似之处。在解锁界面,您可以滑动通知,上下移动通知列表,滑动解锁。在日历应用程序中,您可以滑动以更改当前日期,或滑动标题以更改星期。因此,我建议观看 WWDC 2013 Session 视频 #217“Exploring Scroll Views on iOS 7”,该视频解释并演示了他们是如何做到的,以及如何复制该功能。
基本上,您可以做的(以及他们对解锁屏幕和日历屏幕所做的)是:(A) 将 UIScrollViews 嵌套在 UITableView 中或 (B) 在每个单元格上使用滑动/平移手势委托,并 (C) 将该 UITableView 与代表天数的所有其他 UITableView 嵌套在启用分页控件的 UIScrollView 中。
(A) 将允许您在单元格中显示删除按钮。您可以将删除按钮放在位于单元格中的 UIScrollView 下,以滑动内容将其揭开。
(B) 将允许您跟踪单元格中的手势,在需要时取消它,并使用单元格的 contentView 来显示删除按钮。
(C) 将允许您非常轻松地从一个 UITableView 滑动到另一个,并且通过启用分页,您将获得一个免费的“卡入到位或弹回”动画加上您可以轻松地重复使用两个UITableViews 而不是每天分配一个。
就个人而言,我会选择 (B) 和 (C)。
【讨论】:
【参考方案4】:原来我的 hitTest 方法有问题。用 [super hitTest...] 替换 [self.mainTableView hitTest...] 解决了我的问题。我想我弄乱了一些坐标,导致这个方法在它应该返回视图时返回 nil。我不能 100% 确定它到底出了什么问题,但它现在确实工作得更好了。
【讨论】:
以上是关于如何使表格滚动工作:UIControl 内的 UITableView 内的 UIControl的主要内容,如果未能解决你的问题,请参考以下文章
React - Material UI:如何从表格中删除滚动条
JQGrid当autowidth = true时,如何删除恼人的水平滚动条? (在IE中)
用于水平滚动的 UIScrollView 内的 UITableView 无法按预期工作
javascript element-ui@1.x版本,如果需要把表第一列作为汇总列,并且表格高度大于屏幕高度,向下滚动式使第一行固定在屏幕顶部