我正在为一些iOS开发而苦苦挣扎,我希望有人能给我一个建议。我想实现一个看起来像一副纸牌的UIViews堆栈。用户应该能够刷出“卡”与触摸。我把UIScrollViews和pagingEnabled结合起来,给人的印象是卡片是分开的。但是,UIScrollView可以从左侧或右侧水平滑动对象。我希望我的卡片组在屏幕的中间,这样用户就可以从卡片组中刷卡,而不会看到卡片出现在左边或右边。此外,我还在考虑使用Touch方法( touchesBegan、touchesMoved等)。移动视图。它可以工作的..。但我担心我不能重现卡片的正确机制(摩擦力,当刷卡太短时的反弹效果等等)。
有没有人能给我指点合适的技巧或建议一些教程?我已经做了一些研究,但我找不到任何有用的东西。
这是我想要达到的效果的图像。

我想要实现的是将卡片从卡片组中刷出来。
非常感谢!
发布于 2012-02-21 12:16:02
我猜你想要一次刷卡,将堆叠的顶部卡片“洗牌”到堆叠的底部,露出堆叠中的第二张卡片,如下所示:

动画在现实生活中要流畅得多。我不得不使用较低的帧速率来使动画GIF变小。
要做到这一点,一种方法是创建一个管理卡片视图堆栈的UIView子类。让我们将管理器视图称为BoardView。我们将为它提供两个公共方法:一个用于将顶部卡片洗牌到底部,另一个用于将底部卡片洗牌到顶部。
@interface BoardView : UIView
- (IBAction)goToNextCard;
- (IBAction)goToPriorCard;
@end
@implementation BoardView {
BOOL _isRestacking;
}我们将使用_isRestacking变量来跟踪棋盘视图是否正在设置卡片向一侧移动的动画。
BoardView将其所有子视图视为卡片视图。它负责将它们堆叠起来,最上面的卡片居中。在您的屏幕截图中,您通过稍微随机的数量来偏移较低的牌。我们可以通过随机旋转下面的牌来让它看起来更性感一点。此方法对视图应用小的随机旋转:
- (void)jostleSubview:(UIView *)subview {
subview.transform = CGAffineTransformMakeRotation((((double)arc4random() / UINT32_MAX) - .5) * .2);
}我们希望在添加子视图时将其应用于每个子视图:
- (void)didAddSubview:(UIView *)subview {
[self jostleSubview:subview];
}每当视图的大小发生变化时,或者当视图被赋予了新的子视图时,系统都会发送layoutSubviews。我们将利用这一点将所有卡片放在板视图边界中间的堆栈中。但是,如果视图当前正在对一张卡进行动画处理,我们不想进行布局,因为这会扼杀动画。
- (void)layoutSubviews {
if (_isRestacking)
return;
CGRect bounds = self.bounds;
CGPoint center = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
UIView *topView = self.subviews.lastObject;
CGFloat offset = 10.0f / self.subviews.count;
for (UIView *view in self.subviews.reverseObjectEnumerator) {
view.center = center;
if (view == topView) {
// Make the top card be square to the edges of the screen.
view.transform = CGAffineTransformIdentity;
}
center.x -= offset;
center.y -= offset;
}
}现在我们准备好处理洗牌了。要“转到下一张卡片”,我们需要动画将顶部的卡片移到一侧,然后将其移动到子视图堆叠顺序的底部,然后将其动画移回中间。我们还需要轻推所有其他卡片的位置,因为它们都移动到了堆栈的顶部。
- (void)goToNextCard {
if (self.subviews.count < 2)
return;首先,我们动画顶端的卡片移动到一边。为了让它看起来更性感,我们在移动卡片的时候旋转它。
UIView *movingView = self.subviews.lastObject;
[UIView animateWithDuration:1 delay:0 options:UIViewAnimationCurveEaseInOut animations:^{
_isRestacking = YES;
CGPoint center = movingView.center;
center.x = -hypotf(movingView.frame.size.width / 2, movingView.frame.size.height / 2);
movingView.center = center;
movingView.transform = CGAffineTransformMakeRotation(-M_PI_4);
}在完成块中,我们将卡片移动到堆栈的底部。
completion:^(BOOL finished) {
_isRestacking = NO;
[self sendSubviewToBack:movingView];要将现在最底层的卡片移回到堆栈中,并轻推所有其他卡片,我们只需调用layoutSubviews。但是我们不应该直接调用layoutSubviews,所以我们使用正确的API:setNeedsLayout后跟layoutIfNeeded。我们在动画块中调用layoutIfNeeded,这样卡片就会被动画到它们的新位置。
[self setNeedsLayout];
[UIView animateWithDuration:1 delay:0 options:UIViewAnimationCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction animations:^{
[self jostleSubview:movingView];
[self layoutIfNeeded];
} completion:nil];
}];
}这就是goToNextCard的终结。我们可以用类似的方法来做goToPriorCard:
- (void)goToPriorCard {
if (self.subviews.count < 2)
return;
UIView *movingView = [self.subviews objectAtIndex:0];
[UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
_isRestacking = YES;
CGPoint center = movingView.center;
center.x = -movingView.frame.size.height / 2;
movingView.center = center;
movingView.transform = CGAffineTransformMakeRotation(-M_PI_4);
} completion:^(BOOL finished) {
_isRestacking = NO;
UIView *priorTopView = self.subviews.lastObject;
[self bringSubviewToFront:movingView];
[self setNeedsLayout];
[UIView animateWithDuration:1 delay:0 options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction animations:^{
[self jostleSubview:priorTopView];
[self layoutIfNeeded];
} completion:nil];
}];
}一旦你有了BoardView,你只需要添加一个向其发送goToNextCard消息的滑动手势识别器,以及另一个向其发送goToPriorCard消息的滑动手势识别器。并且您需要添加一些子视图来充当卡片。
另一个细节:为了让卡片的边缘在碰撞时看起来很平滑,需要在Info.plist中将UIViewEdgeAntialiasing设置为YES。
你可以在这里找到我的测试项目:http://dl.dropbox.com/u/26919672/cardstack.zip
发布于 2012-02-21 07:08:19
Icarousel使用UIImageViews实现了精确的滚动功能。
https://github.com/nicklockwood/iCarousel。这里有几种不同的效果,我忘记了你描述的那个效果的名字。
https://stackoverflow.com/questions/9369034
复制相似问题