iphonic iphonic - 3 months ago 90
Objective-C Question

Scale items with iCarousel

I was trying to use iCarousel for one my solutions, I need to achieve something like the image below enter image description here

It should be exactly the way

iCarouselOptionFadeMin
iCarouselOptionFadeMax
iCarouselOptionFadeRange
iCarouselOptionFadeMinAlpha
works using

- (CGFloat)carousel:(iCarousel *)carousel valueForOption:(iCarouselOption)option withDefault:(CGFloat)value


I tried to create a function exactly like

- (CGFloat)alphaForItemWithOffset:(CGFloat)offset


I discovered that it cane be done using
offset
values, but things are not working me, can any one can help me achieving this?

Thanks.

Answer

You can do this via the iCarousel's iCarouselTypeCustom type in the delegate method

- (CATransform3D)carousel:(iCarousel *)carousel itemTransformForOffset:(CGFloat)offset baseTransform:(CATransform3D)transform

Just set the type of the carousel (e.g. in viewDidLoad of the carousel's view controller):

self.carousel.type = iCarouselTypeCustom;

And calculate the transform as you like. I've laid the objects on a hyperbola, and shrink them in addition a bit as they move away from the center. That quite resembles your image, I think:

- (CATransform3D)carousel:(iCarousel *)carousel itemTransformForOffset:(CGFloat)offset baseTransform:(CATransform3D)transform
{
    const CGFloat offsetFactor = [self carousel:carousel valueForOption:iCarouselOptionSpacing withDefault:1.0f]*carousel.itemWidth;

    //The larger these values, as the items move away from the center ...

    //... the faster they move to the back
    const CGFloat zFactor = 150.0f;

    //... the faster they move to the bottom of the screen
    const CGFloat normalFactor = 50.0f;

    //... the faster they shrink
    const CGFloat shrinkFactor = 3.0f;

    //hyperbola
    CGFloat f = sqrtf(offset*offset+1)-1;

    transform = CATransform3DTranslate(transform, offset*offsetFactor, f*normalFactor, f*(-zFactor));
    transform = CATransform3DScale(transform, 1/(f/shrinkFactor+1.0f), 1/(f/shrinkFactor+1.0f), 1.0);
    return transform;
}

and the result: result

you can adjust the float constants to your liking.

For moving items around a circle while scaling them just use goniometric functions for translation, then rotate and scale:

- (CGFloat)carousel:(iCarousel *)carousel valueForOption:(iCarouselOption)option withDefault:(CGFloat)value
{
    if (option == iCarouselOptionSpacing)
    {
        return value * 2.0f;
    }
    if(option == iCarouselOptionVisibleItems)
    {
        return 11;
    }
    if(option == iCarouselOptionWrap) return YES;
    return value;
}

- (CATransform3D)carousel:(iCarousel *)carousel itemTransformForOffset:(CGFloat)offset baseTransform:(CATransform3D)transform
{
    const CGFloat radius = [self carousel:carousel valueForOption:iCarouselOptionRadius withDefault:200.0];
    const CGFloat offsetFactor = [self carousel:carousel valueForOption:iCarouselOptionSpacing withDefault:1.0f]*carousel.itemWidth;
    const CGFloat angle = offset*offsetFactor/radius;

    //... the faster they shrink
    const CGFloat shrinkFactor = 2.0f;
    //hyperbola (now only for shrinking purposes)
    CGFloat f = sqrtf(offset*offset+1)-1;


    transform = CATransform3DTranslate(transform, radius*sinf(angle), radius*(1-cosf(angle)), 0.0);
    transform = CATransform3DRotate(transform, angle, 0, 0, 1);
    transform = CATransform3DScale(transform, 1/(f*shrinkFactor+1.0f), 1/(f*shrinkFactor+1.0f), 1.0);
    return transform;
} 

and again, the result: result2

you can adjust the spacing and the radius in the carousel:valueForOption:withDefault: method.

Enjoy! :)