Google Now喜欢iOS上的界面

时间:2013-05-04 16:58:18

标签: ios iphone objective-c google-now

所以,我非常喜欢Android上的Google Now卡片界面。最近它甚至还来到了iOS。

是否有任何教程或示例项目可以帮助我为我的iOS应用程序创建卡片界面?

根据我的研究,我能够使用自定义UICollectionViewFlowLayout复制“堆叠”卡片。

- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
{
    NSArray *allAttributesInRect = [super layoutAttributesForElementsInRect:rect];

    CGPoint centerPoint = CGPointMake(CGRectGetMidX(self.collectionView.bounds), CGRectGetMidY(self.collectionView.bounds));

    for (UICollectionViewLayoutAttributes *cellAttributes in allAttributesInRect)
    {
        if (CGRectContainsPoint(cellAttributes.frame, centerPoint))
        {
            cellAttributes.transform = CGAffineTransformIdentity;
            cellAttributes.zIndex = 1.0;
        }
        else
        {
            cellAttributes.transform = CGAffineTransformMakeScale(0.75, 0.75);
        }
    }

    return allAttributesInRect;
}

然后我将最小行间距设置为负值,使它们显示为“堆叠”。

滚动时,我希望卡片保持在底部,只有1辆汽车可以向上扩展并在屏幕中居中。然后我会将该卡从屏幕上滚动,堆栈中的下一张“卡片”将从堆栈向上滚动并在屏幕上居中。我猜这会动态调整最小行间距?

1 个答案:

答案 0 :(得分:13)

我认为没有任何教程或课程完全符合您的要求。但是,如果您不介意仅针对iOS6及更高版本,则可以使用UICollectionView。使用标准的垂直流布局,您应该很难做到您想要实现的目标。看看:

我知道这些例子看起来并不像你想要达到的那样。但是一旦掌握了UICollectionView使用这些网站的基本概念,您就可以立即构建卡片布局。

更新

我创建了一个快速示例来展示处理单元格“平移”的潜在方法。确保添加必要的代码以从//Insert code to delete the cell here的集合视图中删除该项目,然后它将填充您通过删除单元格创建的间隙。

<强> CLCollectionViewCell.h

#import <QuartzCore/QuartzCore.h>
#import <UIKit/UIKit.h>

@interface CLCollectionViewCell : UICollectionViewCell <UIGestureRecognizerDelegate>

@property (assign, setter = setDeleted:) BOOL isDeleted;
@property (strong) UIPanGestureRecognizer *panGestureRecognizer;

@end

<强> CLCollectionViewCell.m

#import "CLCollectionViewCell.h"

@implementation CLCollectionViewCell

- (id)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super initWithCoder:aDecoder]) {
        // Create a pan gesture recognizer with self set as the delegate and add it the cell
        _panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureRecognizerDidChange:)];
        [_panGestureRecognizer setDelegate:self]; 
        [self addGestureRecognizer:_panGestureRecognizer];

        // Don't clip to bounds since we want the content view to be visible outside the bounds of the cell
        [self setClipsToBounds:NO];

        // For debugging purposes only: set the color of the content view
        [[self contentView] setBackgroundColor:[UIColor greenColor]];
    }
    return self;
}

- (void)panGestureRecognizerDidChange:(UIPanGestureRecognizer *)panGestureRecognizer {
    if ([self isDeleted]) {
        // The cell should be deleted, leave the state of the cell as it is 
        return;
    }

    // Percent holds a float value between -1 and 1 that indicates how much the user moved his finger relative to the width of the cell
    CGFloat percent = [panGestureRecognizer translationInView:self].x / [self frame].size.width;

    switch ([panGestureRecognizer state]) {
        case UIGestureRecognizerStateChanged: {
            // Create the 'throw animation' and base its current state on the percent
            CGAffineTransform moveTransform = CGAffineTransformMakeTranslation(percent * [self frame].size.width, 0.f);
            CGAffineTransform rotateTransform = CGAffineTransformMakeRotation(percent * M_PI / 20.f);
            CGAffineTransform transform = CGAffineTransformConcat(moveTransform, rotateTransform) ;

            // Apply the transformation to the content view
            [[self contentView] setTransform:transform];

            break;
        }

        case UIGestureRecognizerStateFailed:
        case UIGestureRecognizerStateEnded:
        case UIGestureRecognizerStateCancelled: {
            // Delete the current cell if the absolute value of the percent is above O.7 or the absolute value of the velocity of the gesture is above 600
            if (fabsf(percent) > 0.7f || fabsf([panGestureRecognizer velocityInView:self].x) > 600.f) {
                // The direction is -1 if the gesture is going left and 1 if it's going right
                CGFloat direction = percent < 0.f ? -1.f : 1.f;
                // Multiply the direction to make sure the content view will be removed entirely from the screen
                direction *= 1.5f;

                // Create the transform based on the direction of the gesture
                CGAffineTransform moveTransform = CGAffineTransformMakeTranslation(direction * [self frame].size.width , 0.f);
                CGAffineTransform rotateTransform = CGAffineTransformMakeRotation(direction * M_PI / 20.f);
                CGAffineTransform transform = CGAffineTransformConcat(moveTransform, rotateTransform);

                // Calculate the duration of the animation based on the velocity of the pan gesture recognizer and normalize abnormal high and low values
                CGFloat duration = fabsf(1000.f / [panGestureRecognizer velocityInView:self].x);
                duration = duration > 2.f  ? duration = 2.f  : duration;
                duration = duration < 0.2f ? duration = 0.2f : duration;

                // Animate the 'throwing away' of the cell and update the collection view once it's completed
                [UIView animateWithDuration:duration
                                 animations:^(){
                                     [[self contentView] setTransform:transform];
                                }
                                 completion:^(BOOL finished){
                                     [self setDeleted:YES];

                                     // Insert code to delete the cell here
                                     // e.g. [collectionView deleteItemsAtIndexPaths:@[[collectionView indexPathForCell:self]]];
                }];

            } else {
                // The cell shouldn't be deleted: animate the content view back to its original position
                [UIView animateWithDuration:1.f animations:^(){
                    [[self contentView] setTransform:CGAffineTransformIdentity];
                }];
            }

            break;
        }

        default: {
            break;
        }
    }
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    // Return YES to make sure the pan gesture recognizer doesn't interfere with the gesture recognizer of the collection view
    return YES;
}

@end