6

2 つのラベルを持つビューがあります。左にスワイプすると、次のコンテンツを入力してテキストにラベルを付けます。同様に、右にスワイプすると、前のコンテンツが読み込まれます。左または右からスクロールするような効果をラベルに与えたいです。以前はスクロールビューを使用していましたが、メモリの問題がありました。したがって、私は 1 つのビューを使用しており、スワイプ ジェスチャは次または前のコンテンツをロードします。スクロールビューのスライド効果をラベルに追加したい。どうやってやるの?

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が「クリップ サブビュー」に設定されていることを確認することをお勧めします。このようにして、アニメーションはその境界に制限されsuperview、少し洗練された外観になります。

自動レイアウトを使用する場合、考え方は同じです (ただし、実行はより複雑になります)。基本的に、新しいビューが右にずれるように制約を構成し、アニメーション ブロックで制約を更新/置換して、元のラベルが左にずれ、新しいラベルが元のラベルの場所にくるようにし、最後に完了ブロックは元のラベルの制約をリセットし、一時的なラベルを削除します。


ちなみに、組み込みのトランジションの 1 つに慣れていれば、これはすべて非常に簡単です。

- (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 に答える