iphone - 垂直 UIScrollView?

标签 iphone objective-c ios uiscrollview

我正在努力进行一些 iOS 开发,我希望有人能够帮助我提出建议。我想实现一堆看起来像一副纸牌的 UIView。用户应该能够通过触摸刷出“卡片”。我想到了带有 pagingEnabled 的 UIScrollViews 给人一种卡片是分开的印象。但是,UIScrollView 可以从左侧或右侧出现的内容水平滑动。我希望我的牌组位于屏幕中间,这样用户就可以从牌组中刷卡而不会看到卡片出现在左侧或右侧。 此外,我正在考虑使用触摸方法(touchesBegan、touchesMoved 等)来移动 View 。它可以工作......但我担心我无法重现卡的正确机制(摩擦,滑动时的弹跳效果等......)。

有人可以指点我正确的技术或建议一些教程吗?我已经做了一些研究,但找不到任何有用的东西。

这是我想要达到的效果的图像。

enter image description here

我想要实现的是将牌从牌堆中刷出来。

非常感谢!

最佳答案

我猜你想要轻扫以取出牌堆顶部的牌并将其“洗牌”到牌堆底部,露出牌堆中的第二张牌,如下所示:

card moving from top to bottom of deck

动画在现实生活中要流畅得多。我不得不使用低帧率来使动画 GIF 变小。

一种方法是创建 UIView 的子类来管理卡片 View 堆栈。我们将管理器 View 称为 BoardView。我们将为其提供两种公共(public)方法:一种用于将顶牌洗到底层,一种用于将底牌洗牌至顶层。

@interface BoardView : UIView

- (IBAction)goToNextCard;
- (IBAction)goToPriorCard;

@end

@implementation BoardView {
    BOOL _isRestacking;
}

我们将使用 _isRestacking 变量来跟踪棋盘 View 是否正在为卡片移动到一边制作动画。

BoardView 将其所有 subview 视为卡片 View 。它负责将它们堆叠起来,顶部卡片居中。在您的屏幕截图中,您将较低的卡片偏移了稍微随机的数量。我们可以通过随机旋转下面的卡片让它看起来更性感一点。此方法对 View 应用小的随机旋转:

- (void)jostleSubview:(UIView *)subview {
    subview.transform = CGAffineTransformMakeRotation((((double)arc4random() / UINT32_MAX) - .5) * .2);
}

我们希望将它应用于每个添加的 subview :

- (void)didAddSubview:(UIView *)subview {
    [self jostleSubview:subview];
}

每当 View 的大小发生变化,或者当 View 被赋予新的 subview 时,系统都会发送 layoutSubviews。我们将利用这一点将所有卡片放在棋盘 View 边界中间的堆栈中。但是,如果 View 当前正在为卡片设置动画,我们不想进行布局,因为它会终止动画。

- (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;
    }
}

现在我们准备好处理改组了。要“转到下一张卡片”,我们需要将最上面的卡片移动到一边,然后将其移动到 subview 堆叠顺序的底部,然后将其动画化回中间。我们还需要微调所有其他卡片的位置,因为它们都更靠近堆栈的顶部。

- (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);
    }

在完成 block 中,我们将卡片移到堆栈底部。

    completion:^(BOOL finished) {
        _isRestacking = NO;
        [self sendSubviewToBack:movingView];

为了将现在位于底部的卡片移回堆栈,并插入所有其他卡片,我们只需调用 layoutSubviews。但我们不应该直接调用 layoutSubviews,因此我们使用适当的 API:setNeedsLayout 后跟 layoutIfNeeded。我们在动画 block 中调用 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 消息。并且你需要添加一些 subview 来充当卡片。

另一个细节:为了让卡片的边缘在推挤时看起来平滑,您需要在 Info 中将 UIViewEdgeAntialiasing 设置为 YES。 plist.

你可以在这里找到我的测试项目:http://dl.dropbox.com/u/26919672/cardstack.zip

关于iphone - 垂直 UIScrollView?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9369034/

相关文章:

iphone - 如何更改 QLPreviewController 中的文件名?

iphone - objective-c : Create arrays from first array based on value

ios - 如何根据 View 高度按比例增加字体大小

javascript - 全屏不适用于 Iphone 6s 和 5s

iPhone - 这会泄漏吗?

iphone - 如何管理多个异步 NSURLConnection 委托(delegate)?

ios - 如何停止 uisearch 即时搜索?

iphone - UIView 的 drawRect 没有被调用

ios - 从 Swift 3.2 迁移到 Swift 4 出现 "failed to import bridging header "错误

ios - 如何将按钮添加到 UITableView 页脚 View 但保留最后一个单元格分隔符?