我有一个视图,里面有两个标签。当我向左滑动时,会将下一个内容填充到标签文本中。同样地,向右滑动会加载上一个内容。我想给标签添加一个效果,就好像它们从左边或右边滚动过来一样。
我之前使用了一个scrollview,但是它存在内存问题。所以我现在使用一个视图,通过滑动手势加载下一个或上一个内容。我希望可以将scrollview的滑动效果添加到标签中。我该怎么做?
我有一个视图,里面有两个标签。当我向左滑动时,会将下一个内容填充到标签文本中。同样地,向右滑动会加载上一个内容。我想给标签添加一个效果,就好像它们从左边或右边滚动过来一样。
我之前使用了一个scrollview,但是它存在内存问题。所以我现在使用一个视图,通过滑动手势加载下一个或上一个内容。我希望可以将scrollview的滑动效果添加到标签中。我该怎么做?
我不太确定您要实现的具体效果,但是您可以像这样做:创建一个新的临时标签并将其放在屏幕外,然后通过动画将其移动到您屏幕上的标签上方,完成后重新设置旧标签并删除临时标签。以下是一个非自动布局实现的示例:
- (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
边界的限制,从而产生稍微更加精细的外观。
请注意,如果使用自动布局,想法是相同的(尽管执行起来更加复杂)。基本上配置您的约束条件,以便新视图位于右侧,然后,在动画块中更新/替换约束条件,以使原始标签位于左侧,新标签处于原始标签的位置,最后,在完成块中重置原始标签的约束条件并删除临时标签。
顺便说一句,如果您熟悉内置的转换之一,则所有这些都会变得非常容易:
- (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];
}
UIView
内,并确保其父视图已打开“剪切子视图”设置。我已经修改了上面的代码示例,以便所有动画都与标签的 superview
相关。 - Rob如果您更喜欢更像滚动视图(即通过手势提供连续反馈)的动画效果,则可能如下所示:
- (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),以便我可以将动画限制在该容器内。