3
- (void)setStrokeLabel:(BOOL)strokeLabel
{
    _strokeLabel = strokeLabel;

    if (_strokeLabel) {
        _timer = [NSTimer scheduledTimerWithTimeInterval:0.4 target:self selector:@selector(setStrokeThrough) userInfo:nil repeats:NO];
    } else {
        [self cancelStrokeThrough];
    }
}

- (void)setStrokeThrough
{
    NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithAttributedString:self.attributedText];

    for (NSUInteger i = 1; i <= [attributedString length]; i++) {
        [attributedString addAttribute:NSStrikethroughStyleAttributeName
                                 value:[NSNumber numberWithInt:1]
                                 range:NSMakeRange(0, i)];
        self.attributedText = attributedString;
    }
}

- (void)cancelStrokeThrough
{
    NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithAttributedString:self.attributedText];
    [attributedString removeAttribute:NSStrikethroughStyleAttributeName
                                range:NSMakeRange(0, [attributedString length])];
    self.attributedText = attributedString;

}

I want to animate strike-through, like todo done animation. As I set timer for it, the timer only handle how to show the stoke through letter by letter??

andyleehao
  • 2,412
  • 1
  • 15
  • 8
  • 1
    This is not an animatable property. The best you can do is setup a timer yourself and go letter by letter. – borrrden Apr 01 '13 at 10:20
  • Thank you @borrrden, even though I set a NSTimer for it but the result is not obvious. – andyleehao Apr 02 '13 at 02:10
  • I don't know what you mean by "the result is not obvious" – borrrden Apr 02 '13 at 02:18
  • I want it strike slowly letter by letter, _timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(setStrokeThrough) userInfo:nil repeats:NO]; something has error? – andyleehao Apr 02 '13 at 02:44
  • That timer will not repeat, for one thing, but you should update your question with all the relevant code. – borrrden Apr 02 '13 at 03:03

1 Answers1

2

Here are two functions which do the job.

    BOOL setStrokethrough(UILabel *label, NSRange range)
    {
        if (range.location >= [label.attributedText length])
            return FALSE;

        if (range.location + range.length > [label.attributedText length])
            range.length = [label.attributedText length] - range.location;

        NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithAttributedString:label.attributedText];

        [attributedString addAttribute:NSStrikethroughStyleAttributeName
                                 value:@(NSUnderlineStyleSingle)
                                 range:range];

        label.attributedText = attributedString;
        return TRUE;
    }

    -(void)animateSetStrokethroughDuration:(float)duration
    {
        __block float const stepDuration = 0.1;
        float steps = duration / stepDuration;
        __block NSRange range = NSMakeRange(0, ceil((float)[self.label.attributedText length] / steps));

        void (^__block fn)();
        void (^__block __weak weakfn)();

        weakfn = fn = ^(){
            if (!setStrokethrough(self.label, range))
                return;
            range = NSMakeRange(range.location + range.length, range.length);
            [self performBlock:weakfn afterDelay:stepDuration];
        };
        fn();
    }

Notes

  1. To put a limit on animation time I animate by blocks of characters, rather than charater by character. The longer the string the longer the block of characters.
  2. __block __weak business explained in here
  3. self performBlock is an extension of NSObject by Mike Ash and also here
  4. The code assumes you have self.label member defined
Community
  • 1
  • 1
dmitri
  • 3,183
  • 23
  • 28