Tuesday, December 17, 2013

Article: Google's 2013 Zeitgeist: Nelson Mandela, Paul Walker, iPhone 5s Top Searches

In its 13th year, Google has released the findings of its Year-End Zeitgeist, which intends to highlight the "people, places, and events that captured the world's attention" this year. This year has been filled with many different topics of conversation, but only a few make it to Google's trendin...
Sent via Flipboard

Monday, October 21, 2013

Hang Gliding video collection

Recently I took a new hobby, Hang Gliding.
Check out my awesome youtube videos.

UICollectionView tutorial

UICollectionView is one of the new exciting features introduced in iOS 6. It enables you to organize your views on the screen dynamically. You can implement grids, stacks, circular or any other arrangement you like.


Collection View building blocks

UICollectionView is very similar to  UITableView. The UICollectionView has a delegate and dataSource protocols and a UICollectionViewLayout object that provides essential information for the collection's construction and workflow.
1. Class Structure
the dataSource protocol is very similar to the tableView dataSource protocol, it breaks down the structure to sections and items in each section. Although its up to the UICollectionViewLayout object to decide how to present them on the content area. To do so the layout object can choose to extend the UICollectionViewDelegate just like in the case of the flow layout where it extends the UICollectionViewDelegate into a new protocol called UICollectionViewDelegateFlowLayout. If you choose to provide your own layout and you need to customize your delegate then you should also extend the UICollectionViewDelegate to your own needs. The layout's job is as follow: 1. prepare the layout (which we'll dig into at a later point)  2. provide the content size 3. provide layout attributes objects for the cells that are displayed in some rect on the collection view These layout attributes of class UICollectionViewLayoutAttributes provide basic information such as frame, center, alpha, zIndex attributes that are later applied on the cell. Which will result in positioning these cells in the appropriate place in the collection view.

Flow Layout

What is UICollectionFlowLayout ? 

Flow layout object provides a customizable way to organize the cells in the collection view. Layout objects in general are designed to calculate and provide specific attributes to cells and decoration views.
2. FlowLayout's arrangement of the view elements in the collectionView
Courtesy of Apple.com
as you can see from the diagram above, the cells are laid out one after the other and they flow from the left to right.
3. notice the count
 In the screen shot above you can see how each cell displays its section and index above to demonstrate their position in relation to the index. Flow layout can change the scrolling direction to horizontal as well as vertical.

When to use UICollectionViewFlowLayout ?

Flow layout is useful if you want to display your objects in a grid, single file/line anything that has a linear flow to it could be implemented using the flow layout.

Configuring flow layout

Flow layout allows you to configure:
- scrollDirection: horizontal or vertical
- line spacings, which you can see in the example above that a minimum spacing is maintained.
- item size
- header and footer reference sizes
4. header and footers for each section
So, lets start building this collection example step by step.
FlowLayoutExample.h
@interface FlowLayoutExample : UIViewController <UICollectionViewDataSource, UICollectionViewDelegateFlowLayout>
@property (retain, nonatomic) IBOutlet UICollectionView *collectionView;
@end
Straight forward as you can see, my view controller implements the UICollectionViewDataSource and the UICollectionViewDelegateFlowLayout for the layout and datasource. And we have an outlet for our UICollectionView.
lets configure our collection view
FlowLayoutExample.m
- (void)viewDidLoad
{
    [super viewDidLoad];
    self.collectionView.dataSource = self;//1
    self.collectionView.delegate = self;//1
    //register cell
    [self.collectionView registerClass:[SimpleCell class] forCellWithReuseIdentifier:@"SIMPLE_CELL_ID"];
    //register header
    UINib *headerNib = [UINib nibWithNibName:@"HeaderView" bundle:nil];
    [self.collectionView registerNib:headerNib forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"HEADER_ID"];
    //register footer
    UINib *footerNib = [UINib nibWithNibName:@"FooterView" bundle:nil];
    [self.collectionView registerNib:footerNib forSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:@"FOOTER_ID"];
     //adjust layout
    UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout*)self.collectionView.collectionViewLayout;
    layout.minimumLineSpacing = 20.0;
  layout.itemSize = CGSizeMake(100, 100);
    layout.sectionInset = UIEdgeInsetsMake(20, 20, 40, 20);
    layout.headerReferenceSize = CGSizeMake(50, 65);
    layout.footerReferenceSize = CGSizeMake(50, 75);
}
Lets go through this:
1 - set the collectionView's delegate and dataSource to be my viewController's responsibilities. we declared in the header that we'll be implementing these protocols.
2- registering a class for the cell, in this step we tell the UICollectionView which class will be our cell made of and assign it a reuse identifier. UICollectionView will later instantiate cells of that class when we need them.
3- register header, just to demonstrate the different ways I registered the header using a Nib and not a class. UICollectionView allows you either. You notice that we registered this header for: SupplementaryViewOfKind:UICollectionElementKindSectionHeader. UICollectionView enables you to display some meta data on your cells and FlowLayout specifically comes equipped with header and footer. As you build your own layouts you can define more supplementary views of your own.
4- register footer, same as the header.
5- adjust layout. here we tweak our layout object
    UICollectionViewFlowLayout *layout = (UICollectionViewFlowLayout*)self.collectionView.collectionViewLayout;
Since we never assigned the layout object our selfs, it was assigned for us in the interface builder:
5. layout config in Xib
Layout: Flow layout
Scroll Direction: Vertical
Also you could setup the Header & Footer from Xib to some degree here.
you can see that we configured the layout such that it keeps a minimum 20 px between the lines. We set each item to be of size 100x100, which later you can change on a per item basis in a method called: collectionView:layout:sizeForItemAtIndexPath: where you can give a specific size to each item.
you can also set the insets of each section using: sectionInset property.
Important note about the header/footer reference size, when the flow layout scrolls vertically the layout will use the height value to keep the header/footer in that height and stretch the footer to the collection widths. when scrolling horizontally it will do exactly the opposite, it will take the width and stretch on the height.
DataSource implementation:
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return 50;
}
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
    return 3;
}
As you can see we have 3 sections with 50 items in each section.

Now we'll need to implement a method for the cells:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
    SimpleCell *cell = (SimpleCell *)[collectionView dequeueReusableCellWithReuseIdentifier:@"SIMPLE_CELL_ID" forIndexPath:indexPath];
    cell.label = [NSString stringWithFormat:@"%d,%d", indexPath.section, indexPath.row];
    cell.transform = CGAffineTransformMakeRotation(arc4random()%1*M_2_PI);
    return cell;
}
So we dequeue a reusable cell with identifier SIMPLE_CELL_ID, same as the cell class we registered before. The cool thing about the UICollectionView is that you dont need to check the if the dequeued cell is null , the collection will instantiate one for you in that case automatically.
Lets provide some final implementation for the supplementary elements:
- (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath {
    if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
        HeaderView *header = [collectionView dequeueReusableSupplementaryViewOfKind:kind withReuseIdentifier:@"HEADER_ID" forIndexPath:indexPath];
        header.label.text = [NSString stringWithFormat:@"Header for section: %d", indexPath.section];
        
        return header;
    }
    else if ([kind isEqualToString:UICollectionElementKindSectionFooter]){
        UICollectionReusableView *footer = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:@"FOOTER_ID" forIndexPath:indexPath];
        return footer;
    }
    return nil;
}
same logic as with the cells, we dequeue a supplementary element based on the kind of element and configure it as needed. The result will look like image 4 above.

Customizing your own layout

If the flow layout is not working for you, you can implement your own layout object. you will need to subclass UICollectionViewLayout and implement the following methods at a minimum:
- (void)prepareLayout 
This method is called before the layout is drawn on screen to allow you to prepare any calculations ahead of times.
prepareLayout will also be called when ever the layout is invalidated. Invalidating the layout will throw away all the existing layout information and forces the layout object to start over again. the layout can call this on itself or can trigger this automatically when the bounds change, see shouldInvalidateLayoutForBoundsChange for more details.
- (CGSize)collectionViewContentSize
the second step is to figure out our content size area. here you will tell the UICollectionView what is the size of the content area you will be needing to display all your cells and decorations.
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect
the last step is supplying an array of layout attributed for the displayed cells in the provided rect.
Since UICollectionView is a subclass of a UIScrollView displays only portion of the items and allows you to scroll to see the rest of the elements. so at any time there is only a certain area provided by the rect that is visible and needing the layout attributes.
6. layout process
Curtesy of Apple.com
UICollectionViewLayoutAttributes
The layout uses objects of class UICollectionViewLayoutAttributes (for short layout attributes) to describe the specific layout attributes for a given item in a collection view. the layout object will create layout attributes as needed by the collection view. Layout attributes include attributes such as frame, center, size, transform3D, alpha, zIndex and hidden.
zIndex is especially useful when cells overlap and you need to provide some order on the z-axis, deciding which cell is displayed on top.
Circular layout Example One of the cool demonstrations at Apple's WWDC 2012 was a circular layout.  Its a layout where the cells are displayed in a circle in the middle of the collection view.
for this layout to work a few things are required from our new custom layout object. we need to know the number of items displayed so we can calculate the angle increment between each cell. which should be done in the prepareLayout method:
- (void)prepareLayout {
    [super prepareLayout];
    //calculate the degree increment
    //360 / items_count
    if (self.numItems) {
        angle = (CGFloat)2*M_PI/self.numItems;
    }
    else{
        angle = 0;
    }
}
Next, we need to provide layout attributes for each cell in the visible rect area. Since we want to display all the cells at once for this demo, we'll provide layout attributes for all of them.
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
    if (self.numItems == 0) {
        return nil;
    }
    
    CGRect collectionFrame   = self.collectionView.frame;
    CGPoint center = CGPointMake(collectionFrame.size.width/2, collectionFrame.size.height/2);
    
    CGFloat R  = fminf(collectionFrame.size.width, collectionFrame.size.height)/2.0 - 100;
    
    NSMutableArray *array = [NSMutableArray new];
    CGFloat x,y,alpha;
    UICollectionViewLayoutAttributes *attrs;
    int z=0;
    for (int i=0; i<self.numItems; i++) {
        NSIndexPath *indexPath = [NSIndexPath indexPathForItem:i inSection:0];
        attrs = [UICollectionViewLayoutAttributes layoutAttributesForCellWithIndexPath:indexPath];
        alpha = i * angle;
        x = floorf(R * cosf(alpha) + center.x);
        y = floorf(R * cosf(M_PI_2-alpha) + center.y);
        attrs.center = CGPointMake(x, y);
        attrs.size = CGSizeMake(100, 100);
        
        if ((alpha > 0 && alpha <= M_PI_2) || (alpha > 1.5*M_PI && alpha <= 2*M_PI)) {
            attrs.zIndex = z++;
        }
        else if (alpha > M_PI_2 && alpha <= 1.5*M_PI)
        {
            attrs.zIndex = z--;
        }
        [array addObject:attrs];
    }
    
    NSArray *result = [NSArray arrayWithArray:array];
    [array release];
    return result;
}
So above you will see that I iterate over all the items and calculate the (x,y) coordinates of that item on the circle plus some normalization for the items to be positioned in the collection view and then some zIndex calculation to make sure that the cells overlap in a nice way.
Feel free to download the demo project from GitHub at: 
https://github.com/edwardIshaq/CollectionViewDemo