Вертикальный UIScrollView? - PullRequest
       4

Вертикальный UIScrollView?

0 голосов
/ 21 февраля 2012

Я борюсь с некоторыми разработками для iOS, и я надеюсь, что кто-нибудь сможет помочь мне советом. Я хочу реализовать стек UIViews, который будет выглядеть как колода карт. Пользователь должен иметь возможность смахивать «карты» с прикосновением. Я подумал о UIScrollViews с pagingEnabled, чтобы создать впечатление, что карты являются отдельными. Тем не менее, UIScrollView можно проводить по горизонтали, где что-то появляется с левой или правой стороны. Я хочу, чтобы моя колода карт находилась посередине экрана, чтобы пользователь мог смахивать карты из колоды, не видя карты с левой или правой стороны. Кроме того, я думал об использовании Touch Methods (touchesBegan, touchesMoved и т. Д.) Для перемещения видов. Это может сработать ... но я беспокоюсь, что не смогу воспроизвести правильную механику карты (трение, эффект отскакивания, когда удар коротким и т. Д.)

Может кто-нибудь подсказать мне правильную технику или посоветовать несколько уроков? Я уже провел некоторые исследования, но не смог найти ничего полезного.

Вот изображение эффекта, которого я хотел бы достичь.

enter image description here

И чего я хочу добиться, так это убрать карты из колоды.

Большое спасибо!

Ответы [ 2 ]

13 голосов
/ 21 февраля 2012

Полагаю, вы хотите, чтобы при взятии верхней карты в стопку она была перетасована в нижнюю часть стопки, открывая вторую карту в стопке, например:

card moving from top to bottom of deck

В реальной жизни анимация намного плавнее.Мне пришлось использовать низкую частоту кадров, чтобы сделать анимированный GIF маленьким.

Один из способов сделать это - создать подкласс UIView, который управляет стеком карточных представлений.Давайте назовем менеджера просмотра BoardView.Мы дадим ему два открытых метода: один для перетаскивания верхней карты внизу, а другой - для перетаскивания нижней карты вверх.

@interface BoardView : UIView

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

@end

@implementation BoardView {
    BOOL _isRestacking;
}

Мы будем использовать переменную _isRestacking для отслеживанияявляется ли представление доски движением карты в сторону.

A 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.И вам нужно добавить несколько подпредставлений, которые будут действовать как карты.

Еще одна деталь: чтобы края карт выглядели гладкими, когда они толкаются, вам нужно установить UIViewEdgeAntialiasing в YES в вашемInfo.plist.

Вы можете найти мой тестовый проект здесь: http://dl.dropbox.com/u/26919672/cardstack.zip

0 голосов
/ 21 февраля 2012

В Icarousel есть точная встроенная прокрутка с использованием UIImageViews.

https://github.com/nicklockwood/iCarousel. Там есть несколько различных эффектов, я забыл название того, который вы описываете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...