ios 进入编辑模式时动画自定义绘制的 UITableViewCell

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/742829/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 15:48:22  来源:igfitidea点击:

Animating custom-drawn UITableViewCell when entering edit mode

iphoneobjective-ccocoa-touchios

提问by Adam Alexander

Background

背景

First of all, much gratitude to atebitsfor their very informative blog post Fast Scrolling in Tweetie with UITableView. The post explains in detail how the developers were able to squeeze as much scrolling performance as possible out of the UITableViews in Tweetie.

首先,非常感谢atebits提供的非常有用的博文Fast Scrolling in Tweetie with UITableView。这篇文章详细解释了开发人员如何从Tweetie中的 UITableViews 中获得尽可能多的滚动性能。

Goals

目标

Beginning with the source code linked from the blog post (original) (my github repo):

从博客文章(原始)(我的 github 存储库)中链接的源代码开始:

  1. Allow a UITableView using these custom cells to switch to edit mode, exposing the UI for deleting an item from the table. (github commit)

  2. Move the cell's text aside as the deletion control slides in from the left. This is complete, although the text jumps back and forth without animation. (github commit)

  3. Apply animation to the text movement in goal 2 above for a smooth user experience. This is the step where I became stuck.

  1. 允许使用这些自定义单元格的 UITableView 切换到编辑模式,公开用于从表中删除项目的 UI。( github 提交)

  2. 当删除控件从左侧滑入时,将单元格的文本移到一边。这已经完成了,尽管文本在没有动画的情况下来回跳跃。( github 提交)

  3. 将动画应用于上述目标 2 中的文本移动,以获得流畅的用户体验。这是我卡住的步骤。

The Question

问题

What is the best way to introduce this animation to complete goal 3? It would be nice if this could be done in a manner that keeps the logic from my last commitbecause I would love the option to move the conflicting part of the view only, while any non-conflicting portions (such as right-justified text) stay in the same place or move a different number of pixels. If the above is not possible then undoing my last commit and replacing it with an option that slides the entire view to the right would be a workable solution also.

引入此动画以完成目标 3 的最佳方式是什么?如果这可以以一种保持我上次提交的逻辑的方式完成,那就太好了,因为我希望选择仅移动视图的冲突部分,而任何非冲突部分(例如右对齐文本)留在同一个地方或移动不同数量的像素。如果上述操作是不可能的,那么撤消我的最后一次提交并将其替换为将整个视图向右滑动的选项也将是一个可行的解决方案。

I appreciate any help anyone can provide, from quick pointers and ideas all the way to code snippets or github commits. Of course you are welcome to fork my repoif you would like. I will be staying involved with this question to make sure any successful resolution is committed to github and fully documented here. Thanks very much for your time!

我感谢任何人可以提供的任何帮助,从快速指针和想法一直到代码片段或 github 提交。当然,如果你愿意,欢迎你 fork我的 repo。我将继续参与这个问题,以确保任何成功的解决方案都提交给 github 并在此处完整记录。非常感谢你花时间陪伴!

Updated thoughts

更新的想法

I have been thinking about this a lot since my first post and realized that moving some text items relative to others in the view could undo some of the original performance goals solved in the original blog post. So at this point I am thinking a solution where the entire single subview is animated to its new postion may be the best one.

自从我的第一篇文章以来,我一直在思考这个问题,并意识到在视图中相对于其他文本项移动一些文本项可能会取消原始博客文章中解决的一些原始性能目标。所以在这一点上,我正在考虑将整个单个子视图动画到其新位置的解决方案可能是最好的解决方案。

Second, if it is done in this way there may be an instance where the subview has a custom color or gradient background. Hopefully this can be done in a way that in its normal position the background extends unseen off to the left enough so that when the view is slid to the right the custom background is still visible across the entire cell.

其次,如果以这种方式完成,则可能存在子视图具有自定义颜色或渐变背景的实例。希望这可以通过一种方式来完成,在其正常位置,背景向左延伸到足够看不见的地方,以便当视图向右滑动时,自定义背景在整个单元格中仍然可见。

采纳答案by Adam Alexander

Thanks to Craig's answerwhich pointed me in the right direction, I have a solution for this. I reverted my commitwhich moved the text position based on the editing mode and replaced it with a new solutionthat sets the entire content view to the correct position any time layoutSubviews is called, which results in an automatic animation when switching to and from edit mode:

感谢Craig 的回答为我指明了正确的方向,我对此有一个解决方案。我恢复了我的提交,它根据编辑模式移动了文本位置,并将其替换为一个新的解决方案,该解决方案在调用 layoutSubviews 时将整个内容视图设置为正确的位置,这会在切换到编辑模式和从编辑模式切换时产生自动动画:

- (void)layoutSubviews
{
    CGRect b = [self bounds];
    b.size.height -= 1; // leave room for the separator line
    b.size.width += 30; // allow extra width to slide for editing
    b.origin.x -= (self.editing) ? 0 : 30; // start 30px left unless editing
    [contentView setFrame:b];
    [super layoutSubviews];
}

By doing it this way I was able to remove the setFrame: override found in ABTableViewCell.m because its former logic plus my additions are now found in layoutSubviews.

通过这样做,我能够删除在 ABTableViewCell.m 中找到的 setFrame: 覆盖,因为它以前的逻辑加上我的添加现在可以在 layoutSubviews 中找到。

I set a light grey background on the cells to verify a custom background works properly without allowing us to see behind it as it moves back and forth and it seems to work great.

我在单元格上设置了浅灰色背景以验证自定义背景是否正常工作,而不允许我们在它来回移动时看到它的背后,它似乎工作得很好。

Thanks again to Craig and anyone else who has looked into this.

再次感谢 Craig 和其他任何对此进行调查的人。

GitHub commit for this solution: (link)

此解决方案的 GitHub 提交:(链接

回答by Craig Otis

How are you moving the text around currently? Or more specifically, in which UITableViewCell method are you performing the move?

您目前如何移动文本?或者更具体地说,您在哪个 UITableViewCell 方法中执行移动?

From my experience, overriding the layoutSubviewsmethod and setting the frame here will automatically be wrapped in an animation.

根据我的经验,在layoutSubviews此处覆盖方法并设置框架将自动包装在动画中。

Eg:

例如:

- (void)layoutSubviews {
    if (self.editing) {
        [titleLabel setFrame:CGRectMake(62, 6, 170, 24)];
    }
    else {
        [titleLabel setFrame:CGRectMake(30, 6, 200, 24)];
    }
    [super layoutSubviews];
}

回答by UIBuilder

For fully control on editing in your custom cell, you should override willTransitionToState method in your UITableViewCell subclass and check state mask

为了完全控制自定义单元格中的编辑,您应该覆盖 UITableViewCell 子类中的 willTransitionToState 方法并检查状态掩码

- (void)willTransitionToState:(UITableViewCellStateMask)state
{
    NSString *logStr = @"Invoked";
    if ((state & UITableViewCellStateShowingEditControlMask)
        != 0) {
        // you need to move the controls in left
        logStr = [NSString stringWithFormat:@"%@
                  %@",logStr,@"UITableViewCellStateShowingEditControlMask"];
    }
    if ((state & UITableViewCellStateShowingDeleteConfirmationMask)
        != 0) {
        // you need to hide the controls for the delete button
        logStr = [NSString stringWithFormat:@"%@
                  %@",logStr,@"UITableViewCellStateShowingDeleteConfirmationMask"];
    }
    NSLog(@"%@",logStr);
    [super willTransitionToState:state];
}

also you can override layoutSubviews

你也可以覆盖 layoutSubviews

- (void)layoutSubviews {
    // default place for label
    CGRect alarmTimeRect = CGRectMake(37, 7, 75, 30);
    if (self.editing && !self.showingDeleteConfirmation) {
        // move rect in left
        alarmTimeRect = CGRectMake(77, 7, 75, 30);
    }
    [alarmTimeLabel setFrame:alarmTimeRect];
    [super layoutSubviews];
}

回答by ben

To handle swiping as well: (self.editing && !self.showingDeleteConfirmation)

还要处理滑动:(self.editing && !self.showingDeleteConfirmation)

回答by Sauleil

I just had this question too: how to animate a custom editing mode? I didn't really like the solution here, so I decided to think a little bit and found a different solution. I don't know if it's better, but I prefer that one. So I decided to share it here:

我也有这个问题:如何为自定义编辑模式设置动画?我不是很喜欢这里的解决方案,所以我决定稍微思考一下,找到了一个不同的解决方案。我不知道它是否更好,但我更喜欢那个。所以我决定在这里分享:

In the custom cell (inherit from UITableViewCell), just overload the setEditing:

在自定义单元格中(从 UITableViewCell 继承),只需重载 setEditing:

- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
    [super setEditing:editing animated:animated];

    if (animated) {
        [UIView beginAnimations:@"setEditingAnimation" context:nil];
        [UIView setAnimationDuration:0.3];
    }

    if (editing) {
        /* do your offset and resize here */
    } else {
        /* return to the original here*/
    }

    if (animated)
        [UIView commitAnimations];
}

I don't check if the editing value is the same, but that's just an idea how I did it.

我不检查编辑值是否相同,但这只是我如何做到的一个想法。

回答by Henry

There are actually four edite states.

实际上有四种编辑状态。

UITableViewCellStateDefaultMask = 0

UITableViewCellStateDefaultMask = 0

UITableViewCellStateShowingEditControlMask = 1<<0 = 0b01 = 1 by setting the tableView.edit = YES

UITableViewCellStateShowingEditControlMask = 1<<0 = 0b01 = 1 通过设置 tableView.edit = YES

UITableViewCellStateShowingDeleteConfirmationMask = 1<<1 = 0b10 = 2 by swiping to delete

UITableViewCellStateShowingDeleteConfirmationMask = 1<<1 = 0b10 = 2 滑动删除

UITableViewCellStateShowingEditControlMask and UITableViewCellStateShowingDeleteConfirmationMask = (1<<1)&(1<<0) = 0b11 = 3 by setting the tableView.edit = YES and press the "stop" button

UITableViewCellStateShowingEditControlMask 和 UITableViewCellStateShowingDeleteConfirmationMask = (1<<1)&(1<<0) = 0b11 = 3 通过设置 tableView.edit = YES 并按下“停止”按钮

What confused me is that when state 3 transfer to state 1, no selector would get called which make no use of self.showingDeleteConfirmation to change layout in iOS7.Therefore, in order to make clear between state 2 and state 3, I added a instance variable to implement it. It works great for me.

是什么让我感到困惑的是,当状态3转移到状态1,没有选择将被调用,这使得没有用self.showingDeleteConfirmation来改变布局在iOS7。因此,为了使状态2和状态3之间清晰的,我加了一个实例变量来实现它。这对我很有效。

-(void)willTransitionToState:(UITableViewCellStateMask)state{
    [super willTransitionToState:state];

    //Custom delete button from
    //http://stackoverflow.com/questions/19159476/uitableviewcelldeleteconfirmationcontrol-issue
    if((state & UITableViewCellStateShowingDeleteConfirmationMask) == UITableViewCellStateShowingDeleteConfirmationMask){
        [self recurseAndReplaceSubViewIfDeleteConfirmationControl:self.subviews];
        [self performSelector:@selector(recurseAndReplaceSubViewIfDeleteConfirmationControl:) withObject:self.subviews afterDelay:0];
    }

    _swipeDelete = NO;

    if(state==2){
        //Swipe to delete confirm
        _swipeDelete = YES;
    }

    if(state==3){
        //Edit state to delete confirm
    }
}

- (void)layoutSubviews{

    [super layoutSubviews];

    self.contentView.frame = CGRectMake(10, 5.0, 300, self.frame.size.height - 10);

    if(self.editing){
        self.contentView.frame = CGRectMake(40, 5.0, 270, self.frame.size.height - 10);
    }

    if(_swipeDelete){
        self.contentView.frame = CGRectMake(10, 5.0, 300, self.frame.size.height - 10);
    }
}