2 つのラベルを持つビューがあります。左にスワイプすると、次のコンテンツを入力してテキストにラベルを付けます。同様に、右にスワイプすると、前のコンテンツが読み込まれます。左または右からスクロールするような効果をラベルに与えたいです。以前はスクロールビューを使用していましたが、メモリの問題がありました。したがって、私は 1 つのビューを使用しており、スワイプ ジェスチャは次または前のコンテンツをロードします。スクロールビューのスライド効果をラベルに追加したい。どうやってやるの?
2 に答える
あなたが探している効果が正確にはわかりませんが、新しい一時的なラベルを作成し、それを画面の外に置き、画面上のラベルの上に移動することをアニメーション化してから、次のようなことを行うことができます完了したら、古いものをリセットし、一時的なラベルを削除します。非自動レイアウトの実装は次のようになります。
- (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];
}
よりスクロール ビューのように動作する (つまり、ジェスチャに対する継続的なフィードバックがある) アニメーションを好む場合は、次のようになります。
- (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 と呼ばれます) に配置したことに注意してください。