如何使表格滚动工作: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:如何从表格中删除滚动条

如何使 UITableView 内容围绕中心点滚动

JQGrid当autowidth = true时,如何删除恼人的水平滚动条? (在IE中)

用于水平滚动的 UIScrollView 内的 UITableView 无法按预期工作

javascript element-ui@1.x版本,如果需要把表第一列作为汇总列,并且表格高度大于屏幕高度,向下滚动式使第一行固定在屏幕顶部

滚动视图没有重力。如何使滚动视图内的内容为中心