在iOS上实现Cover Flow (三)

本文档详细介绍了如何在iOS应用中实现Cover Flow视图动画,包括设置透视、初始化对象、注册手势、创建模板、布局图片以及显示图片和倒影的步骤。通过这些操作,用户可以滑动查看图片,实现类似封面流动的效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

miracle_of_thinking原创,欢迎转载!原文地址:

http://blog.youkuaiyun.com/miracle_of_thinking/article/details/7208415

转载时请在明显处注明,谢谢!


三、实现

       在第一部分中,我们定义了UICoverFlowView类的唯一一个类方法:

+ (id)coverFlowViewWithFrame:(CGRect)frame
                   andImages:(NSArray*)arrImages
                  sidePieces:(int)SIDEPIECES
                   sideScale:(CGFloat)SIDESCALE
                 middleScale:(CGFloat)MIDDLESCALE;

       它看上去有点长…是的,我在这里传递image数组、两侧显示的张数,以及尺寸等参数。将其定义成类方法可以方便使用者,使用者只需利用该方法来实例化UICoverFlowView对象而不必关心内存管理,因为在其中我们已经将其设为autorelease。下面是该方法的具体实现:

+ (id)coverFlowViewWithFrame:(CGRect)frame
                   andImages:(NSArray*)arrImages
                  sidePieces:(int)SIDEPIECES
                   sideScale:(CGFloat)SIDESCALE
                 middleScale:(CGFloat)MIDDLESCALE
{
    UICoverFlowView *cfv = [[[UICoverFlowView alloc] initWithFrame:frame] autorelease];
   
    // 左右两侧各显示SIDEPIECES张image,中间显示1张image
    cfv._SIDEPIECES = SIDEPIECES;
    // 设定左右两侧image的缩放程度,以及中间image的缩放程度
    cfv._SIDESCALE = SIDESCALE;
    cfv._MIDDLESCALE = MIDDLESCALE;
    // 默认初始时显示的是第一张image
    cfv._curImageIndex = 0;
 
    // 初始化数组
    cfv._arrImages = [NSMutableArray arrayWithArray:arrImages];
    cfv._arrLayers = [[NSMutableArray alloc] initWithCapacity:cfv._SIDEPIECES*2+1];
    cfv._arrTempleteLayers = [[NSMutableArray alloc] initWithCapacity:(cfv._SIDEPIECES+1)*2+1];
   
    // 注册手势
    UIPanGestureRecognizer *gestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:cfv action:@selector(handlePanFrom:)];
    [cfv addGestureRecognizer:gestureRecognizer];
   
    // 设置模板
    [cfv setTempleteLayers];
   
    // 布局images
    [cfv arrangeImages];
   
    [cfv addPageControl];
   
    return cfv;
}

       我们首先调用了基类方法:initWithFrame:,因此,我们可以在该方法中对其设置透视:

// 设置sublayer为透视
CATransform3D perspectiveTransform3D = CATransform3DIdentity;
perspectiveTransform3D.m34 = -1.0 / 500.0;  // 效果比1000要好
self.layer.sublayerTransform = perspectiveTransform3D;

       接着,我们对对象的属性值进行了赋值和初始化(注意三个array,我们要定义成NSMutableArray)。然后我们在UICoverFlowView对象中注册了pan手势,通过该手势,用户可以左右滑动来更换image,其中的selector方法我们将在后面给出。

       接下来的工作尤为关键,那就是设置模板,模板中记录着每个位置上CALayer的position、zPosition、transform等。每当用户滑动图片后,每个layer就可以根据模板来进行调整。其实现如下:

// 设置模板
- (void)setTempleteLayers
{
    // 1 - UICoverFlowView的中心坐标
    CGFloat centerX = self.bounds.size.width/2;
    CGFloat centerY = self.bounds.size.height/2;
   
    // 2 - 设定左右两侧image旋转的角度
    CGFloat leftRadian = M_PI / 3;
    CGFloat rightRadian = -M_PI / 3;
   
    // 3 - 设定左右两侧image之间的间隔
    CGFloat space = 20;
   
    // 4 - 设定正中间的image与左右相邻image的间隔
    CGFloat theGap = 200.0f;
   
    // 5 - 布置layer模板
    // 首先 Left 部分
    for (int i = 0; i < _SIDEPIECES + 1; i++) {
        CALayer *layer = [CALayer layer];
        layer.position = CGPointMake(centerX-theGap-space*(_SIDEPIECES-i), centerY);
        layer.zPosition = (i - _SIDEPIECES - 1) * 10;
        layer.transform = CATransform3DMakeRotation(leftRadian, 0, 1, 0);
        [self._arrTempleteLayers addObject:layer];
    }
    // 然后 Middle 部分
    {
        CALayer *layer = [CALayer layer];
        layer.position = CGPointMake(centerX, centerY);
        [self._arrTempleteLayers addObject:layer];
    }
    // 最后 Right 部分
    for (int i = 0; i <= _SIDEPIECES + 1; i++) {
        CALayer *layer = [CALayer layer];
        layer.position = CGPointMake(centerX+theGap+space*i, centerY);
        layer.zPosition = -i * 10;
        layer.transform = CATransform3DMakeRotation(rightRadian, 0, 1, 0);
        [self._arrTempleteLayers addObject:layer];
    }
}

       除了代码中的注释,这里还有个值得注意的地方,那就是布置layer模板的left以及right部分时,都要比实际两侧显示的image张数多1。这样做的原因是,当用户滑动image后最左侧或最右侧的image就可能超出显示范围,此时moveOneStep:方法(后面即将介绍)可以得到良好的执行。

       在设置完模板后,我们就可以将image按照模板进行布局了,这在arrangeImages方法中完成:

// 布局images
- (void)arrangeImages
{
    // 1 - 设定可浏览范围内,image的起始下标及结束下标
    int startIndex = (_curImageIndex-_SIDEPIECES <= 0) ? 0 : _curImageIndex-_SIDEPIECES;
    int endIndex = (_curImageIndex+_SIDEPIECES >= _arrImages.count-1) ? _arrImages.count-1 : _curImageIndex+_SIDEPIECES;
   
    // 2 - 布局image
    for (int i=startIndex; i<=endIndex; i++) {
        UIImage *aImage = (UIImage*)[_arrImages objectAtIndex:i];
        CALayer *imgLayer = [CALayer layer];
        imgLayer.contents = (id)aImage.CGImage;
        CGFloat scale = (i==_curImageIndex) ? _MIDDLESCALE : _SIDESCALE;
        imgLayer.bounds = CGRectMake(0, 0, aImage.size.width*scale, aImage.size.height*scale);
        [self._arrLayers addObject:imgLayer];
    }
    // 3 - 根据模板设置position、zPosition、transform等
    // 计算对应在模板layer的起始下标
    int targetStartIndex = (_curImageIndex < _SIDEPIECES) ? (_SIDEPIECES+1-_curImageIndex) : 1;
    for (int i=0; i<_arrLayers.count; i++) {
        CALayer *tempLayer = (CALayer*)[_arrTempleteLayers objectAtIndex:(i+targetStartIndex)];
        CALayer *layer = (CALayer*)[_arrLayers objectAtIndex:i];
        layer.position = tempLayer.position;
        layer.zPosition = tempLayer.zPosition;
        layer.transform = tempLayer.transform;
        // 显示layer及其“倒影”
        [self showImageAndReflection:layer];
    }
}

       上面代码中的注释已经很清楚地介绍了各个步骤的工作,应该比较容易理解。要注意的是数组下标的计算,不能有任何差错哦。完成position、zPosition、transform等设置后,我们需要显示layer及其倒影。showImageAndReflection:具体实现如下:

// 添加layer及其“倒影”
- (void)showImageAndReflection:(CALayer*)layer
{  
    // 制作reflection
    CALayer *reflectLayer = [CALayer layer];
    reflectLayer.contents = layer.contents;
    reflectLayer.bounds = layer.bounds;
    reflectLayer.position = CGPointMake(layer.bounds.size.width/2, layer.bounds.size.height*1.5);
    reflectLayer.transform = CATransform3DMakeRotation(M_PI, 1, 0, 0);
   
    // 给该reflection加个半透明的layer
    CALayer *blackLayer = [CALayer layer];
    blackLayer.backgroundColor = [UIColor blackColor].CGColor;
    blackLayer.bounds = reflectLayer.bounds;
    blackLayer.position = CGPointMake(blackLayer.bounds.size.width/2, blackLayer.bounds.size.height/2);
    blackLayer.opacity = 0.6;
    [reflectLayer addSublayer:blackLayer];
   
    // 给该reflection加个mask
    CAGradientLayer *mask = [CAGradientLayer layer];
    mask.bounds = reflectLayer.bounds;
    mask.position = CGPointMake(mask.bounds.size.width/2, mask.bounds.size.height/2);
    mask.colors = [NSArray arrayWithObjects:
                   (id)[UIColor clearColor].CGColor,
                   (id)[UIColor whiteColor].CGColor, nil];
    mask.startPoint = CGPointMake(0.5, 0.35);
    mask.endPoint = CGPointMake(0.5, 1.0);
    reflectLayer.mask = mask;
   
    // 作为layer的sublayer
    [layer addSublayer:reflectLayer];
    // 加入UICoverFlowView的sublayers
    [self.layer addSublayer:layer];
}

至此,我们的“cover flow”已经可以显示了,如果你已经迫不及待想看一下效果的话,你可以先将那些未介绍的方法注释掉(并取消pan手势的注册),然后实例化UICoverFlowView后作为subView加入你工程中已存在的view中。不过,我觉得你还是因该再忍耐一下,毕竟看着一堆静态且不能移动的图片毫无乐趣可言,这离我们的目标相去甚远!


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值