6

我有一个有两个标签的视图。当我向左滑动时,我填充下一个内容以标记文本。同样向右滑动会加载以前的内容。我想对标签产生影响,就像它们从左或右滚动一样。我以前使用过滚动视图,但它有内存问题。所以我使用一个视图,滑动手势加载下一个或上一个内容。我想给标签添加滚动视图的滑动效果。我怎样才能做到这一点?

4

2 回答 2

15

我不太确定你在寻找什么效果,但你可以做这样的事情,创建一个新的临时标签,把它从屏幕上移开,将它移动到你在屏幕上的标签上,然后完成后,重置旧标签并删除临时标签。这是非自动布局实现的样子:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    UISwipeGestureRecognizer *left = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(leftSwipe:)];
    [left setDirection:UISwipeGestureRecognizerDirectionLeft];
    [self.view addGestureRecognizer:left];
    // if non-ARC, release it
    // [release left];

    self.label1.text = @"Mo";
}

- (void)leftSwipe:(UISwipeGestureRecognizer *)gesture
{
    NSString *newText;
    UILabel  *existingLabel = self.label1;

    // in my example, I'm just going to toggle the value between Mo and Curly

    if ([existingLabel.text isEqualToString:@"Curly"])
        newText = @"Mo";
    else
        newText = @"Curly";

    // create new label

    UILabel *tempLabel = [[UILabel alloc] initWithFrame:existingLabel.frame];
    [existingLabel.superview addSubview:tempLabel];
    tempLabel.text = newText;

    // move the new label off-frame to the right

    tempLabel.transform = CGAffineTransformMakeTranslation(tempLabel.superview.bounds.size.width, 0);

    // animate the sliding of them into place

    [UIView animateWithDuration:0.5
                     animations:^{
                         tempLabel.transform = CGAffineTransformIdentity;
                         existingLabel.transform = CGAffineTransformMakeTranslation(-existingLabel.superview.bounds.size.width, 0);
                     }
                     completion:^(BOOL finished) {
                         existingLabel.text = newText;
                         existingLabel.transform = CGAffineTransformIdentity;
                         [tempLabel removeFromSuperview];
                     }];

    // if non-ARC, release it
    // [release tempLabel];
}

此动画相对于其父视图为标签设置动画。您可能希望确保将其superview设置为“剪辑子视图”。这样,动画将被限制在 that 的范围内superview,从而产生稍微更精致的外观。

注意,如果使用自动布局,想法是一样的(虽然执行更复杂)。基本上配置您的约束,使新视图位于右侧,然后,在动画块中更新/替换约束,使原始标签位于左侧,新标签位于原始标签的位置,最后,在完成块重置原始标签的约束并删除临时标签。


顺便说一句,如果您对其中一种内置转换感到满意,这一切都会变得更加容易:

- (void)leftSwipe:(UISwipeGestureRecognizer *)gesture
{
    NSString *newText;
    UILabel  *existingLabel = self.label1;

    // in my example, I'm just going to toggle the value between Mo and Curly

    if ([existingLabel.text isEqualToString:@"Curly"])
        newText = @"Mo";
    else
        newText = @"Curly";

    [UIView transitionWithView:existingLabel  // or try `existingLabel.superview`
                      duration:0.5
                       options:UIViewAnimationOptionTransitionFlipFromRight
                    animations:^{
                        existingLabel.text = newText;
                    }
                    completion:nil];
}
于 2012-06-15T21:02:37.993 回答
6

如果您更喜欢表现得更像滚动视图的动画(即,对手势有连续反馈),它可能类似于以下内容:

- (void)viewDidLoad
{
    [super viewDidLoad];

    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panHandler:)];
    [self.view addGestureRecognizer:pan];

    self.label1.text = @"Mo";
}

- (void)panHandler:(UIPanGestureRecognizer *)sender
{
    if (sender.state == UIGestureRecognizerStateBegan)
    {
        _panLabel = [[UILabel alloc] init];

        // in my example, I'm just going to toggle the value between Mo and Curly
        // you'll presumably set the label contents based upon the direction of the
        // pan (if positive, swiping to the right, grab the "previous" label, if negative
        // pan, grab the "next" label)

        if ([self.label1.text isEqualToString:@"Curly"])
            _newText = @"Mo";
        else
            _newText = @"Curly";

        // set the text

        _panLabel.text = _newText;

        // set the frame to just be off screen

        _panLabel.frame = CGRectMake(self.label1.frame.origin.x + self.containerView.frame.size.width,
                                     self.label1.frame.origin.y, 
                                     self.label1.frame.size.width, 
                                     self.label1.frame.size.height);

        [self.containerView addSubview:_panLabel];

        _originalCenter = self.label1.center; // save where the original label originally was
    }
    else if (sender.state == UIGestureRecognizerStateChanged)
    {
        CGPoint translate = [sender translationInView:self.containerView];

        if (translate.x > 0)
        {
            _panLabel.center = CGPointMake(_originalCenter.x - self.containerView.frame.size.width + translate.x, _originalCenter.y);
            self.label1.center = CGPointMake(_originalCenter.x + translate.x, _originalCenter.y);
        }
        else 
        {
            _panLabel.center = CGPointMake(_originalCenter.x + self.containerView.frame.size.width + translate.x, _originalCenter.y);
            self.label1.center = CGPointMake(_originalCenter.x + translate.x, _originalCenter.y);
        }
    }
    else if (sender.state == UIGestureRecognizerStateEnded || sender.state == UIGestureRecognizerStateFailed || sender.state == UIGestureRecognizerStateCancelled)
    {
        CGPoint translate = [sender translationInView:self.containerView];
        CGPoint finalNewFieldLocation;
        CGPoint finalOriginalFieldLocation;
        BOOL panSucceeded;

        if (sender.state == UIGestureRecognizerStateFailed ||
            sender.state == UIGestureRecognizerStateCancelled)
        {
            panSucceeded = NO;
        }
        else 
        {
            // by factoring in the velocity, we can capture a flick more accurately
            //
            // (by the way, I don't like iOS's velocity, because if you stop moving, it records the velocity
            // prior to stopping the move rather than noting that you actually stopped, so I usually calculate my own, 
            // but I'll leave this as is for purposes of this example)

            CGPoint velocity  = [sender velocityInView:self.containerView];

            if (translate.x < 0)
                panSucceeded = ((translate.x + velocity.x * 0.5) < -(self.containerView.frame.size.width / 2));
            else
                panSucceeded = ((translate.x + velocity.x * 0.5) > (self.containerView.frame.size.width / 2));
        }

        if (panSucceeded)
        {
            // if we succeeded, finish moving the stuff

            finalNewFieldLocation = _originalCenter;
            if (translate.x < 0)
                finalOriginalFieldLocation = CGPointMake(_originalCenter.x - self.containerView.frame.size.width, _originalCenter.y);
            else
                finalOriginalFieldLocation = CGPointMake(_originalCenter.x + self.containerView.frame.size.width, _originalCenter.y);
        }
        else
        {
            // if we didn't, then just return everything to where it was

            finalOriginalFieldLocation = _originalCenter;

            if (translate.x < 0)
                finalNewFieldLocation = CGPointMake(_originalCenter.x + self.containerView.frame.size.width, _originalCenter.y);
            else
                finalNewFieldLocation = CGPointMake(_originalCenter.x - self.containerView.frame.size.width, _originalCenter.y);
        }

        // animate the moving of stuff to their final locations, and on completion, clean everything up

        [UIView animateWithDuration:0.3
                              delay:0.0 
                            options:UIViewAnimationOptionCurveEaseOut
                         animations:^{
                             _panLabel.center = finalNewFieldLocation;
                             self.label1.center = finalOriginalFieldLocation;
                         }
                         completion:^(BOOL finished) {
                             if (panSucceeded)
                                 self.label1.text = _newText;
                             self.label1.center = _originalCenter;
                             [_panLabel removeFromSuperview];
                             _panLabel = nil; // in non-ARC, release instead
                         }
         ];
    }
}   

请注意,我已将原始标签和正在平移的新标签都放在容器 UIView(称为 containerView,令人惊讶的是)中,以便我可以将动画剪辑到该容器。

于 2012-06-16T19:36:58.453 回答