Commits

José Manuel Sánchez Peñarroja committed 7dadb44

Added datastoreselection. Added containerviewclass to singleviewconfigurator

Comments (0)

Files changed (37)

Example/Example.xcworkspace/xcuserdata/josanchez.xcuserdatad/UserInterfaceState.xcuserstate

Binary file modified.

Example/Example.xcworkspace/xcuserdata/josanchez.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist

       <BreakpointProxy
          BreakpointExtensionID = "Xcode.Breakpoint.ExceptionBreakpoint">
          <BreakpointContent
-            shouldBeEnabled = "No"
+            shouldBeEnabled = "Yes"
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             scope = "0"
             shouldBeEnabled = "No"
             ignoreCount = "0"
             continueAfterRunningActions = "No"
-            filePath = "../bmf/ios/view controllers/BMFBannerViewController.m"
-            timestampString = "425827810.798741"
+            filePath = "../bmf/ios/views/cell configuration/BMFBannerImageConfigurator.m"
+            timestampString = "425905614.903681"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "44"
-            endingLineNumber = "44"
-            landmarkName = "-viewDidLoad"
+            startingLineNumber = "29"
+            endingLineNumber = "29"
+            landmarkName = "+viewClass"
+            landmarkType = "5">
+         </BreakpointContent>
+      </BreakpointProxy>
+      <BreakpointProxy
+         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+         <BreakpointContent
+            shouldBeEnabled = "No"
+            ignoreCount = "0"
+            continueAfterRunningActions = "No"
+            filePath = "../bmf/ios/views/cell configuration/BMFBannerImageConfigurator.m"
+            timestampString = "425905615.815677"
+            startingColumnNumber = "9223372036854775807"
+            endingColumnNumber = "9223372036854775807"
+            startingLineNumber = "25"
+            endingLineNumber = "25"
+            landmarkName = "+itemClass"
             landmarkType = "5">
          </BreakpointContent>
       </BreakpointProxy>
+      <BreakpointProxy
+         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+         <BreakpointContent
+            shouldBeEnabled = "No"
+            ignoreCount = "0"
+            continueAfterRunningActions = "No"
+            filePath = "../bmf/ios/views/cell configuration/BMFBannerImageConfigurator.m"
+            timestampString = "425905618.337172"
+            startingColumnNumber = "9223372036854775807"
+            endingColumnNumber = "9223372036854775807"
+            startingLineNumber = "33"
+            endingLineNumber = "33"
+            landmarkName = "+containerViewClass"
+            landmarkType = "5">
+         </BreakpointContent>
+      </BreakpointProxy>
+      <BreakpointProxy
+         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
+         <BreakpointContent
+            shouldBeEnabled = "No"
+            ignoreCount = "0"
+            continueAfterRunningActions = "No"
+            filePath = "Example/bmfTests/BMFDataStoreSelectionTests.m"
+            timestampString = "425916654.373629"
+            startingColumnNumber = "9223372036854775807"
+            endingColumnNumber = "9223372036854775807"
+            startingLineNumber = "78"
+            endingLineNumber = "78"
+            landmarkName = "describe()"
+            landmarkType = "6">
+         </BreakpointContent>
+      </BreakpointProxy>
    </Breakpoints>
 </Bucket>

Example/Example/Example.xcodeproj/project.pbxproj

 		AC1D14CB187D700E008598EB /* bmfTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AC1D14CA187D700E008598EB /* bmfTests.m */; };
 		AC1D1521187D9F69008598EB /* Base.m in Sources */ = {isa = PBXBuildFile; fileRef = AC1D1520187D9F69008598EB /* Base.m */; };
 		AC472F5F1929E968004FCBEC /* bmfObjectDataStoreTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AC472F5E1929E968004FCBEC /* bmfObjectDataStoreTests.m */; };
+		AC4B04F81962F2DF00D27486 /* BMFDataStoreSelectionTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AC4B04F71962F2DF00D27486 /* BMFDataStoreSelectionTests.m */; };
 		AC4FE88518F8298B00D8E845 /* bmfLoaderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AC4FE88418F8298B00D8E845 /* bmfLoaderTests.m */; };
 		AC4FE88718F82AA400D8E845 /* blah.txt in Resources */ = {isa = PBXBuildFile; fileRef = AC4FE88618F82AA400D8E845 /* blah.txt */; };
 		AC52FBEF18F42ECF007FA26E /* bmfDataStoreTests.m in Sources */ = {isa = PBXBuildFile; fileRef = AC52FBEE18F42ECF007FA26E /* bmfDataStoreTests.m */; };
 		AC1D154F187DA41B008598EB /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		AC1D1550187DA41B008598EB /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = "<group>"; };
 		AC472F5E1929E968004FCBEC /* bmfObjectDataStoreTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = bmfObjectDataStoreTests.m; sourceTree = "<group>"; };
+		AC4B04F71962F2DF00D27486 /* BMFDataStoreSelectionTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BMFDataStoreSelectionTests.m; sourceTree = "<group>"; };
 		AC4FE88418F8298B00D8E845 /* bmfLoaderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = bmfLoaderTests.m; sourceTree = "<group>"; };
 		AC4FE88618F82AA400D8E845 /* blah.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = blah.txt; sourceTree = "<group>"; };
 		AC52FBEE18F42ECF007FA26E /* bmfDataStoreTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = bmfDataStoreTests.m; sourceTree = "<group>"; };
 				AC66CE4F18F2B45000B633C9 /* bmfConditionTests.m */,
 				ACCFF1481910E65B002BC1FD /* bmfUtilsTests.m */,
 				ACAEE81D193747760020C42B /* bmfStatTests.m */,
+				AC4B04F71962F2DF00D27486 /* BMFDataStoreSelectionTests.m */,
 				AC1D14C5187D700E008598EB /* Supporting Files */,
 			);
 			path = bmfTests;
 				AC66CE5018F2B45000B633C9 /* bmfConditionTests.m in Sources */,
 				AC52FBF218F43993007FA26E /* BMFDataStoresModel.xcdatamodeld in Sources */,
 				AC472F5F1929E968004FCBEC /* bmfObjectDataStoreTests.m in Sources */,
+				AC4B04F81962F2DF00D27486 /* BMFDataStoreSelectionTests.m in Sources */,
 				AC4FE88518F8298B00D8E845 /* bmfLoaderTests.m in Sources */,
 				ACA63AD41961874900247D56 /* bmfEnumerateDataSourceAspectTests.m in Sources */,
 			);

Example/Example/Example/Base.lproj/Main.storyboard

 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="5056" systemVersion="13C64" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" initialViewController="bwM-Ru-wQf">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="5056" systemVersion="13D65" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" initialViewController="bwM-Ru-wQf">
     <dependencies>
         <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="3733"/>
     </dependencies>
                         <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                         <subviews>
                             <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" translatesAutoresizingMaskIntoConstraints="NO" id="bhG-7d-NNA">
-                                <rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
+                                <rect key="frame" x="0.0" y="174" width="320" height="394"/>
                                 <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                                 <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
                             </tableView>
                                 <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
                                 <nil key="highlightedColor"/>
                             </label>
+                            <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="4ws-Hx-PBY">
+                                <rect key="frame" x="0.0" y="64" width="320" height="110"/>
+                                <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                                <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="110" id="Wti-mP-6I9"/>
+                                </constraints>
+                            </view>
                         </subviews>
                         <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
                         <constraints>
+                            <constraint firstAttribute="trailing" secondItem="4ws-Hx-PBY" secondAttribute="trailing" id="1M5-bl-8uA"/>
                             <constraint firstAttribute="centerY" secondItem="4eY-nB-ini" secondAttribute="centerY" id="GuW-hk-1fD"/>
                             <constraint firstAttribute="trailing" secondItem="bhG-7d-NNA" secondAttribute="trailing" id="aLS-o7-a6O"/>
                             <constraint firstAttribute="centerX" secondItem="4eY-nB-ini" secondAttribute="centerX" id="c45-tG-HKz"/>
-                            <constraint firstItem="bhG-7d-NNA" firstAttribute="top" secondItem="xN5-uW-q9o" secondAttribute="top" id="hlF-Hr-CMx"/>
+                            <constraint firstItem="bhG-7d-NNA" firstAttribute="top" secondItem="4ws-Hx-PBY" secondAttribute="bottom" id="f9Z-9P-v5J"/>
                             <constraint firstItem="a8Y-70-yoV" firstAttribute="top" secondItem="bhG-7d-NNA" secondAttribute="bottom" id="ns5-PY-nb7"/>
                             <constraint firstItem="bhG-7d-NNA" firstAttribute="leading" secondItem="xN5-uW-q9o" secondAttribute="leading" id="p97-Zu-Sey"/>
+                            <constraint firstItem="4ws-Hx-PBY" firstAttribute="top" secondItem="UMF-jx-4gt" secondAttribute="bottom" id="rjZ-a8-V46"/>
+                            <constraint firstItem="4ws-Hx-PBY" firstAttribute="leading" secondItem="xN5-uW-q9o" secondAttribute="leading" id="uEy-Gw-LN3"/>
                         </constraints>
                     </view>
                     <navigationItem key="navigationItem" title="Test" id="geE-HS-Bfb"/>
                     <connections>
+                        <outlet property="bannerView" destination="4ws-Hx-PBY" id="3r4-pw-YXW"/>
                         <outlet property="label" destination="4eY-nB-ini" id="KLf-U8-me6"/>
                         <outlet property="tableView" destination="bhG-7d-NNA" id="KAs-jn-kTo"/>
                         <segue destination="DEu-nb-IOp" kind="push" identifier="collection" id="ow2-4j-70E"/>

Example/Example/Example/ExampleTestViewController.h

 @interface ExampleTestViewController : BMFTableViewController
 
 @property (weak, nonatomic) IBOutlet UILabel *label;
+@property (weak, nonatomic) IBOutlet UIView *bannerView;
 
 @end

Example/Example/Example/ExampleTestViewController.m

 
 #import "BMFTableViewDataSource.h"
 
+#import <BMF/BMF.h>
+#import <BMF/BMFImageBannerViewController.h>
+#import <BMF/BMFBannerImage.h>
+
 #import <ReactiveCocoa/ReactiveCocoa.h>
 
 @interface ExampleTestViewController ()
 - (void) viewDidLoad {
 	[super viewDidLoad];
 	
+	BMFImageBannerViewController *bannerVC = [BMFImageBannerViewController new];
+	[self BMF_addChild:bannerVC addSubviewBlock:^(id sender) {
+		[self.bannerView addSubview:bannerVC.view];
+		[BMFAutoLayoutUtils fill:self.bannerView with:bannerVC.view margin:0];
+	}];
+	
+	bannerVC.items = @[
+					   [[BMFBannerImage alloc] initWithUrlString:@"http://media.nbcdfw.com/images/654*368/oak-tree.jpg"],
+   					   [[BMFBannerImage alloc] initWithUrlString:@"http://levoleague-wordpress.s3.amazonaws.com/wp-content/uploads/2013/04/Career-Path-Like-Climbing-Tree.jpg"],
+   					   [[BMFBannerImage alloc] initWithUrlString:@"http://www.growingagreenerworld.com/wp-content/uploads/2011/09/212-Tree-Majestic.jpg"]
+					   ];
+	
+	
 	BMFScrollHidesNavigationBarBehavior *behavior = [[BMFScrollHidesNavigationBarBehavior alloc] initWithView:self.tableView];
 	[self addBehavior:behavior];
 

Example/Example/bmfTests/BMFDataStoreSelectionTests.m

+//
+//  BMFDataStoreSelectionTests.m
+//  Example
+//
+//  Created by Jose Manuel Sánchez Peñarroja on 01/07/14.
+//  Copyright (c) 2014 José Manuel Sánchez. All rights reserved.
+//
+
+#import <XCTest/XCTest.h>
+
+#import <Specta/Specta.h>
+#define EXP_SHORTHAND
+#import <Expecta/Expecta.h>
+#import <OCMock/OCMock.h>
+
+#import <BMF/BMFDataStoreSelection.h>
+
+SpecBegin(BMFDataStoreSelection)
+
+describe(@"Single Selection", ^{
+	
+	__block BMFDataStoreSelection *selection = nil;
+	
+	beforeEach(^{
+		selection = [BMFDataStoreSelection new];
+		selection.mode = BMFDataStoreSelectionModeSingle;
+	});
+	
+	it(@"should never have more than one element selected", ^{
+		expect(selection.selection.count).to.equal(0);
+		
+		[selection select:[NSIndexPath indexPathForRow:0 inSection:0]];
+		
+		expect(selection.selection.count).to.equal(1);
+		
+		NSIndexPath *indexPath = selection.selection.firstObject;
+		expect(indexPath.section).to.equal(0);
+		expect(indexPath.row).to.equal(0);
+		
+		[selection select:[NSIndexPath indexPathForRow:1 inSection:0]];
+
+		expect(selection.selection.count).to.equal(1);
+		
+		indexPath = selection.selection.firstObject;
+		expect(indexPath.section).to.equal(0);
+		expect(indexPath.row).to.equal(1);
+	});
+	
+	it(@"should deselect elements correctly", ^{
+		expect(selection.selection.count).to.equal(0);
+
+		[selection select:[NSIndexPath indexPathForRow:0 inSection:0]];
+		
+		expect(selection.selection.count).to.equal(1);
+		
+		NSIndexPath *indexPath = selection.selection.firstObject;
+		expect(indexPath.section).to.equal(0);
+		expect(indexPath.row).to.equal(0);
+
+		[selection deselect:[NSIndexPath indexPathForRow:0 inSection:0]];
+		
+		expect(selection.selection.count).to.equal(0);
+	});
+});
+
+
+describe(@"Single Selection per Section", ^{
+	__block BMFDataStoreSelection *selection = nil;
+	
+	beforeEach(^{
+		selection = [BMFDataStoreSelection new];
+		selection.mode = BMFDataStoreSelectionModeSinglePerSection;
+	});
+	
+	it(@"should never have more than one element selected per section", ^{
+		expect(selection.selection.count).to.equal(0);
+		
+		[selection select:[NSIndexPath indexPathForRow:0 inSection:0]];
+		
+		expect(selection.selection.count).to.equal(1);
+
+		NSIndexPath *indexPath = selection.selection.firstObject;
+		expect(indexPath.section).to.equal(0);
+		expect(indexPath.row).to.equal(0);
+		
+		[selection select:[NSIndexPath indexPathForRow:1 inSection:0]];
+		
+		expect(selection.selection.count).to.equal(1);
+		
+		indexPath = selection.selection.firstObject;
+		expect(indexPath.section).to.equal(0);
+		expect(indexPath.row).to.equal(1);
+
+		[selection select:[NSIndexPath indexPathForRow:1 inSection:1]];
+		
+		expect(selection.selection.count).to.equal(2);
+		
+		indexPath = selection.selection[1];
+		expect(indexPath.section).to.equal(1);
+		expect(indexPath.row).to.equal(1);
+
+		[selection select:[NSIndexPath indexPathForRow:3 inSection:1]];
+		
+		expect(selection.selection.count).to.equal(2);
+		
+		indexPath = selection.selection[1];
+		expect(indexPath.section).to.equal(1);
+		expect(indexPath.row).to.equal(3);
+	});
+
+	it(@"should deselect elements correctly", ^{
+		expect(selection.selection.count).to.equal(0);
+		
+		[selection select:[NSIndexPath indexPathForRow:0 inSection:0]];
+		
+		expect(selection.selection.count).to.equal(1);
+		
+		NSIndexPath *indexPath = selection.selection.firstObject;
+		expect(indexPath.section).to.equal(0);
+		expect(indexPath.row).to.equal(0);
+
+		[selection select:[NSIndexPath indexPathForRow:1 inSection:1]];
+		
+		expect(selection.selection.count).to.equal(2);
+		
+		indexPath = selection.selection[1];
+		expect(indexPath.section).to.equal(1);
+		expect(indexPath.row).to.equal(1);
+
+		[selection deselect:[NSIndexPath indexPathForRow:0 inSection:0]];
+
+		expect(selection.selection.count).to.equal(1);
+		
+		indexPath = selection.selection.firstObject;
+		expect(indexPath.section).to.equal(1);
+		expect(indexPath.row).to.equal(1);
+		
+		[selection deselect:[NSIndexPath indexPathForRow:1 inSection:1]];
+		
+		expect(selection.selection.count).to.equal(0);
+	});
+});
+
+describe(@"Multiple Selection", ^{
+	__block BMFDataStoreSelection *selection = nil;
+	
+	beforeEach(^{
+		selection = [BMFDataStoreSelection new];
+		selection.mode = BMFDataStoreSelectionModeMultiple;
+	});
+	
+	it(@"should allow multiple selected indexes", ^{
+		expect(selection.selection.count).to.equal(0);
+		
+		[selection select:[NSIndexPath indexPathForRow:0 inSection:0]];
+		
+		expect(selection.selection.count).to.equal(1);
+		
+		NSIndexPath *indexPath = selection.selection.firstObject;
+		expect(indexPath.section).to.equal(0);
+		expect(indexPath.row).to.equal(0);
+		
+		[selection select:[NSIndexPath indexPathForRow:1 inSection:0]];
+		
+		expect(selection.selection.count).to.equal(2);
+		
+		indexPath = selection.selection[1];
+		expect(indexPath.section).to.equal(0);
+		expect(indexPath.row).to.equal(1);
+		
+		[selection select:[NSIndexPath indexPathForRow:1 inSection:1]];
+		
+		expect(selection.selection.count).to.equal(3);
+		
+		indexPath = selection.selection[2];
+		expect(indexPath.section).to.equal(1);
+		expect(indexPath.row).to.equal(1);
+		
+		[selection select:[NSIndexPath indexPathForRow:3 inSection:1]];
+		
+		expect(selection.selection.count).to.equal(4);
+		
+		indexPath = selection.selection[3];
+		expect(indexPath.section).to.equal(1);
+		expect(indexPath.row).to.equal(3);
+	});
+
+	it(@"should deselect elements correctly", ^{
+		expect(selection.selection.count).to.equal(0);
+		
+		[selection select:[NSIndexPath indexPathForRow:0 inSection:0]];
+		
+		expect(selection.selection.count).to.equal(1);
+		
+		NSIndexPath *indexPath = selection.selection.firstObject;
+		expect(indexPath.section).to.equal(0);
+		expect(indexPath.row).to.equal(0);
+		
+		[selection select:[NSIndexPath indexPathForRow:1 inSection:0]];
+		
+		expect(selection.selection.count).to.equal(2);
+		
+		indexPath = selection.selection[1];
+		expect(indexPath.section).to.equal(0);
+		expect(indexPath.row).to.equal(1);
+
+		[selection deselect:[NSIndexPath indexPathForRow:1 inSection:0]];
+		
+		expect(selection.selection.count).to.equal(1);
+		
+		indexPath = selection.selection.firstObject;
+		expect(indexPath.section).to.equal(0);
+		expect(indexPath.row).to.equal(0);
+		
+		[selection deselect:[NSIndexPath indexPathForRow:0 inSection:0]];
+
+		expect(selection.selection.count).to.equal(0);
+		
+		[selection select:[NSIndexPath indexPathForRow:0 inSection:0]];
+
+		expect(selection.selection.count).to.equal(1);
+		
+		indexPath = selection.selection.firstObject;
+		expect(indexPath.section).to.equal(0);
+		expect(indexPath.row).to.equal(0);
+	});
+
+});
+
+SpecEnd

bmf/ios/data/data sources/BMFCollectionViewDataSource.m

 
 #import "UICollectionView+BMF.h"
 
+@interface BMFCollectionViewDataSource() <UICollectionViewDelegate>
+
+@end
+
 @implementation BMFCollectionViewDataSource {
 	id dataDidChangeObservationToken;
 	id dataBatchChangeObservationToken;

bmf/ios/data/data sources/BMFDataSource.h

 @property (nonatomic, strong) id<BMFDataReadProtocol> dataStore;
 @property (nonatomic, copy) BMFCompletionBlock dataChangedBlock;
 
+@property (nonatomic, strong) id<BMFDataStoreSelectionProtocol> selection;
+
 - (instancetype) initWithDataStore:(id<BMFDataReadProtocol>) dataStore;
 
 @end

bmf/ios/data/data sources/BMFDataSource.m

 	return nil;
 }
 
+- (void) setSelection:(id<BMFDataStoreSelectionProtocol>)selection {
+	self.dataStore.selection = selection;
+}
+
+- (id<BMFDataStoreSelectionProtocol>)selection {
+	return self.dataStore.selection;
+}
+
 #pragma mark BMFDataReadProtocol
 
 - (NSInteger) numberOfSections {

bmf/ios/data/data sources/BMFTableViewDataSource.m

 	return [self tableView:tableView heightForViewKind:BMFViewKindSectionFooter atIndexPath:indexPath];
 }
 
+
+//
+//- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+//	[self.selection select:indexPath];
+//}
+//
+//- (void) tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
+//	[self.selection deselect:indexPath];
+//}
+
 @end

bmf/ios/subspecs/coredata/BMFFRCDataStore.h

 
 #import <CoreData/CoreData.h>
 
-#import "BMFNode.h"
-#import "BMFDataReadProtocol.h"
+#import "BMFDataRead.h"
 
-@interface BMFFRCDataStore : BMFNode <BMFDataReadProtocol,NSFetchedResultsControllerDelegate>
+@interface BMFFRCDataStore : BMFDataRead <NSFetchedResultsControllerDelegate>
 
 @property (nonatomic, strong) NSFetchedResultsController *fr;
 

bmf/ios/subspecs/coredata/BMFFRCDataStore.m

 //
 
 #import "BMFFRCDataStore.h"
+#import "BMFDataStoreSelection.h"
 
 @implementation BMFFRCDataStore
 
 //}
 
 #pragma mark TNNode
-
+/*
 - (BOOL) performProcess:(id)input completion:(BMFNodeProcessCompletionBlock)completionBlock {
 	return [self loadData:completionBlock];
 }
 	if (completionBlock) completionBlock(self.fr.fetchedObjects,error);
 	
 	return result;
-}
+}*/
 
 @end

bmf/ios/view controllers/BMFBannerViewController.h

 @property (nonatomic, assign) NSUInteger slideDuration;
 @property (nonatomic, assign) BMFBannerViewControllerScrollDirection scrollDirection;
 
+@property (nonatomic, strong) Class cellClass;
+
+/// This items should conform to protocol BMFBannerItemProtocol
+@property (nonatomic, strong) NSArray *items;
 
 @end

bmf/ios/view controllers/BMFBannerViewController.m

 #import "BMFTimerViewControllerBehavior.h"
 #import "BMFEnumerateDataSourceAspect.h"
 
+#import "BMFUpdateCollectionViewBehavior.h"
+
+#import "BMFArrayDataStore.h"
+
+#import "BMFBannerItemProtocol.h"
+
 #import "BMF.h"
 
 @interface BMFBannerViewController () <UICollectionViewDelegate>
 
-@property (nonatomic, weak) BMFTimerViewControllerBehavior *timerBehavior;
+@property (nonatomic, strong) BMFTimerViewControllerBehavior *timerBehavior;
 
 @end
 
-@implementation BMFBannerViewController
+@implementation BMFBannerViewController {
+	id orientationObserver;
+}
+
+- (NSArray *) items {
+	BMFArrayDataStore *dataStore = self.dataSource.dataStore;
+	return dataStore.items;
+}
 
+- (void) setItems:(NSArray *)items {
+	if (items.count>0) {
+		BMFAssertReturn([items.firstObject conformsToProtocol:@protocol(BMFBannerItemProtocol)]);
+	}
+	
+	BMFArrayDataStore *dataStore = self.dataSource.dataStore;
+	dataStore.items = items;
+}
 
 - (void) viewDidLoad {
 	[super viewDidLoad];
 	
+	BMFAssertReturn(self.cellClass);
+	
+	self.collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:[UICollectionViewFlowLayout new]];
+	[self.view addSubview:self.collectionView];
+	self.collectionView.pagingEnabled = YES;
+	[BMFAutoLayoutUtils fill:self.view with:self.collectionView margin:0];
+	
+	BMFArrayDataStore *dataStore = [[BMFBase sharedInstance].factory dataStoreWithParameter:@[] sender:nil];
+	self.dataSource = [[BMFBase sharedInstance].factory collectionViewDataSourceWithStore:dataStore cellClassOrNib:self.cellClass sender:self];
+	
 	self.scrollDirection = BMFBannerViewControllerScrollDirectionHorizontal;
 	
 	UICollectionViewFlowLayout *flowLayout = [UICollectionViewFlowLayout BMF_cast:self.collectionView.collectionViewLayout];
 	
 	flowLayout.minimumInteritemSpacing = 0;
 	flowLayout.minimumLineSpacing = 0;
+	flowLayout.sectionInset = UIEdgeInsetsZero;
 	
 	self.collectionView.delegate = self;
 	
 		}];
 	};
 	[self addBehavior:self.timerBehavior];
+	
+	[self addBehavior:[[BMFUpdateCollectionViewBehavior alloc] initWithView:self.collectionView]];
+}
+
+- (void) didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
+	[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
+	
+	UICollectionViewFlowLayout *layout =  self.collectionView.collectionViewLayout;
+	layout.itemSize = self.view.bounds.size;
+	[self.collectionView selectItemAtIndexPath:[self.collectionView indexPathsForSelectedItems].firstObject animated:NO scrollPosition:UICollectionViewScrollPositionLeft];
+	[self.collectionView reloadData];
 }
 
 - (void) setSlideDuration:(NSUInteger)slideDuration {
 	
 	if (self.scrollDirection==BMFBannerViewControllerScrollDirectionHorizontal)	flowLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal;
 	else flowLayout.scrollDirection = UICollectionViewScrollDirectionVertical;
-
 }
 
 #pragma mark UICollectionViewDelegate

bmf/ios/view controllers/BMFImageBannerViewController.h

+//
+//  BMFImageBannerViewController.h
+//  Pods
+//
+//  Created by Jose Manuel Sánchez Peñarroja on 01/07/14.
+//
+//
+
+#import "BMFBannerViewController.h"
+
+@interface BMFImageBannerViewController : BMFBannerViewController
+
+
+@end

bmf/ios/view controllers/BMFImageBannerViewController.m

+//
+//  BMFImageBannerViewController.m
+//  Pods
+//
+//  Created by Jose Manuel Sánchez Peñarroja on 01/07/14.
+//
+//
+
+#import "BMFImageBannerViewController.h"
+
+#import "BMFCollectionImageViewCell.h"
+#import "BMFBannerItemImageProtocol.h"
+
+@interface BMFImageBannerViewController ()
+
+@end
+
+@implementation BMFImageBannerViewController
+
+- (void) setItems:(NSArray *)items {
+	if (items.count>0) {
+		BMFAssertReturn([items.firstObject conformsToProtocol:@protocol(BMFBannerItemImageProtocol)]);
+	}
+	
+	[super setItems:items];
+}
+
+- (void) performInit {
+	[super performInit];
+	self.cellClass = [BMFCollectionImageViewCell class];
+}
+
+- (void) viewDidLoad {
+	[super viewDidLoad];
+	
+	
+}
+
+@end

bmf/ios/view controllers/BMFViewController.h

 
 - (void) performSegueWithIdentifier:(NSString *)identifier prepareBlock:(BMFActionBlock) block;
 
-/// Teplate method
+/// Template method
 - (void) performInit __attribute((objc_requires_super));
 
 @end

bmf/ios/views/cell configuration/BMFBannerImageConfigurator.h

+//
+//  BMFBannerImageConfigurator.h
+//  Pods
+//
+//  Created by Jose Manuel Sánchez Peñarroja on 01/07/14.
+//
+//
+
+#import "BMFSingleViewConfigurator.h"
+
+@interface BMFBannerImageConfigurator : BMFSingleViewConfigurator
+
+@end

bmf/ios/views/cell configuration/BMFBannerImageConfigurator.m

+//
+//  BMFBannerImageConfigurator.m
+//  Pods
+//
+//  Created by Jose Manuel Sánchez Peñarroja on 01/07/14.
+//
+//
+
+#import "BMFBannerImageConfigurator.h"
+
+#import "BMFBannerImage.h"
+#import "BMFCollectionImageViewCell.h"
+
+#import "BMF.h"
+
+#import <AFNetworking/UIImageView+AFNetworking.h>
+
+@implementation BMFBannerImageConfigurator
+
++ (void) load {
+	[self register];
+}
+
++ (Class) itemClass {
+	return [BMFBannerImage class];
+}
+
++ (Class) viewClass {
+	return [BMFCollectionImageViewCell class];
+}
+
++ (Class) containerViewClass {
+	return [UICollectionView class];
+}
+
++ (void) configure:(BMFCollectionImageViewCell *)view kind:(BMFViewKind)kind withItem:(BMFBannerImage *)item inView:(UICollectionView *)containerView atIndexPath:(NSIndexPath *)indexPath controller:(id)controller {
+	BMFAssertReturn([containerView isKindOfClass:[UICollectionView class]]);
+	
+	if (item.image) {
+		view.imageView.image = item.image;
+	}
+	else if (item.urlString.length>0) {
+		if (item.placeholderImage) view.imageView.image = item.placeholderImage;
+		
+		id<BMFTaskProtocol> task = [[BMFBase sharedInstance].factory imageLoadTask:item.urlString parameters:nil sender:self];
+		[task start:^(id result, NSError *error) {
+			if (error) {
+				DDLogError(@"Error loading banner image: %@",error);
+				return;
+			}
+			
+			item.image = result;
+			[containerView reloadItemsAtIndexPaths:@[ indexPath ]];
+		}];
+	}
+}
+
+@end

bmf/ios/views/cell configuration/BMFSingleViewConfigurator.h

 + (Class) viewClass;
 + (Class) itemClass;
 
+
+/// If this is not implemented it will be used for any container view. A container view can be a table, a collection view, etc
++ (Class) containerViewClass;
+
 @end

bmf/ios/views/cell configuration/BMFSingleViewConfigurator.m

 	return nil;
 }
 
++ (Class) containerViewClass {
+	return nil;
+}
 
 + (BOOL) canConfigure:(id) view kind:(BMFViewKind)kind withItem:(id) item inView:(id)containerView {
+
+	Class allowedContainerClass = [self containerViewClass];
+	if (allowedContainerClass && ![containerView isKindOfClass:allowedContainerClass]) return NO;
+	
 	if ([view isKindOfClass:[self viewClass]] && [item isKindOfClass:[self itemClass]]) return YES;
 	return NO;
 }

bmf/ios/views/cell configuration/BMFViewConfigurator.m

 + (Class<BMFViewConfiguratorProtocol>) configuratorForView:(id) view kind:(BMFViewKind)kind item:(id) item inView:(id)containerView {
 	NSArray *available = [self registeredClasses];
 	for (Class<BMFViewConfiguratorProtocol> configuratorClass in available) {
-		if ([configuratorClass canConfigure:view kind:kind withItem:item inView:view]) {
+		if ([configuratorClass canConfigure:view kind:kind withItem:item inView:containerView]) {
 			return configuratorClass;
 		}
 	}

bmf/shared/data/data stores/BMFArrayDataStore.h

 
 #import "BMFNode.h"
 #import "BMFDataStoreProtocol.h"
-#import "BMFDataReadProtocol.h"
+#import "BMFDataRead.h"
 
-@interface BMFArrayDataStore : BMFNode <BMFDataStoreProtocol, BMFDataReadProtocol>
+@interface BMFArrayDataStore : BMFDataRead <BMFDataStoreProtocol>
 
 @property (nonatomic, copy) BMFCompletionBlock dataChangedBlock;
 

bmf/shared/data/data stores/BMFComplexDataStore.h

 //
 
 #import <Foundation/Foundation.h>
-#import "BMFDataReadProtocol.h"
+#import "BMFDataRead.h"
 
-@interface BMFComplexDataStore : NSObject <BMFDataReadProtocol>
+@interface BMFComplexDataStore : BMFDataRead
 
 @property (nonatomic, strong) NSArray *dataStores;
 

bmf/shared/data/data stores/BMFDataRead.h

+//
+//  BMFDataRead.h
+//  Pods
+//
+//  Created by Jose Manuel Sánchez Peñarroja on 01/07/14.
+//
+//
+
+#import <Foundation/Foundation.h>
+
+#import "BMFDataReadProtocol.h"
+
+@interface BMFDataRead : NSObject <BMFDataReadProtocol>
+
+@property (nonatomic, strong) id<BMFDataStoreSelectionProtocol> selection;
+
+@end

bmf/shared/data/data stores/BMFDataRead.m

+//
+//  BMFDataRead.m
+//  Pods
+//
+//  Created by Jose Manuel Sánchez Peñarroja on 01/07/14.
+//
+//
+
+#import "BMFDataRead.h"
+
+#import "BMFDataStoreSelection.h"
+
+@implementation BMFDataRead
+
+- (instancetype)init
+{
+    self = [super init];
+    if (self) {
+        _selection = [BMFDataStoreSelection new];
+    }
+    return self;
+}
+
+- (NSInteger) numberOfSections {
+	BMFAbstractMethod();
+	return 0;
+}
+
+- (NSInteger) numberOfRowsInSection:(NSUInteger) section {
+	BMFAbstractMethod();
+	return 0;
+}
+
+- (NSString *) titleForSection:(NSUInteger) section kind:(BMFViewKind)kind {
+	return nil;
+}
+
+- (id) itemAt:(NSInteger) section row:(NSInteger) row {
+	return [self itemAt:[NSIndexPath BMF_indexPathForRow:row inSection:section]];
+}
+
+- (id) itemAt:(NSIndexPath *) indexPath {
+	BMFAbstractMethod();
+	return nil;
+}
+
+- (NSIndexPath *) indexOfItem:(id) item {
+	BMFAbstractMethod();
+	return nil;
+}
+
+- (NSArray *) allItems {
+	BMFAbstractMethod();
+	return nil;
+}
+
+- (BOOL) isEmpty {
+	BMFAbstractMethod();
+	return YES;
+}
+
+@end

bmf/shared/data/data stores/BMFDataReadProtocol.h

 
 #import "BMFTypes.h"
 
+#import "BMFDataStoreSelectionProtocol.h"
+
 @protocol BMFDataReadProtocol <NSObject>
 
+@property (nonatomic, strong) id<BMFDataStoreSelectionProtocol> selection;
+
 - (NSInteger) numberOfSections;
 - (NSInteger) numberOfRowsInSection:(NSUInteger) section;
 - (NSString *) titleForSection:(NSUInteger) section kind:(BMFViewKind)kind;

bmf/shared/data/data stores/BMFDataStoreSelection.h

+//
+//  BMFDataStoreSelection.h
+//  Pods
+//
+//  Created by Jose Manuel Sánchez Peñarroja on 01/07/14.
+//
+//
+
+#import <Foundation/Foundation.h>
+
+#import "BMFDataStoreSelectionProtocol.h"
+
+@interface BMFDataStoreSelection : NSObject <BMFDataStoreSelectionProtocol>
+
+/// BMFDataStoreSelectionModeMultiple by default
+@property (nonatomic, assign) BMFDataStoreSelectionMode mode;
+
+@property (nonatomic, strong) NSArray *selection;
+
+@end

bmf/shared/data/data stores/BMFDataStoreSelection.m

+//
+//  BMFDataStoreSelection.m
+//  Pods
+//
+//  Created by Jose Manuel Sánchez Peñarroja on 01/07/14.
+//
+//
+
+#import "BMFDataStoreSelection.h"
+
+#import "BMF.h"
+
+@interface BMFDataStoreSelection()
+
+@property (nonatomic, strong) NSMutableArray *selectedIndexPaths;
+
+@end
+
+@implementation BMFDataStoreSelection
+
+- (instancetype)init
+{
+    self = [super init];
+    if (self) {
+        _selectedIndexPaths = [NSMutableArray array];
+		_mode = BMFDataStoreSelectionModeMultiple;
+    }
+    return self;
+}
+
+- (void) setSelection:(NSArray *)selection {
+	_selectedIndexPaths = [selection mutableCopy];
+}
+
+- (NSArray *) selection {
+	return [self.selectedIndexPaths copy];
+}
+
+- (void) select:(NSIndexPath *) indexPath {
+	BMFAssertReturn(indexPath);
+	
+	if (_mode==BMFDataStoreSelectionModeSingle) {
+		[self selectSingle:indexPath];
+	}
+	else if (_mode==BMFDataStoreSelectionModeSinglePerSection) {
+		[self selectSinglePerSection:indexPath];
+	}
+	else if (_mode==BMFDataStoreSelectionModeMultiple) {
+		[self selectMultiple:indexPath];
+	}
+}
+
+- (void) selectSingle:(NSIndexPath *) indexPath {
+	[_selectedIndexPaths removeAllObjects];
+	[_selectedIndexPaths addObject:indexPath];
+}
+
+- (void) selectSinglePerSection:(NSIndexPath *) indexPath {
+	NSMutableArray *indexesToRemove = [NSMutableArray array];
+	for (NSIndexPath *selectedIndexPath in _selectedIndexPaths) {
+		if (selectedIndexPath.section==indexPath.section) [indexesToRemove addObject:selectedIndexPath];
+	}
+	
+	[_selectedIndexPaths removeObjectsInArray:indexesToRemove];
+	[_selectedIndexPaths addObject:indexPath];
+}
+
+- (void) selectMultiple:(NSIndexPath *) indexPath {
+	[_selectedIndexPaths addObject:indexPath];
+}
+
+
+- (void) deselect:(NSIndexPath *) indexPath {
+	[_selectedIndexPaths removeObject:indexPath];
+}
+
+
+@end

bmf/shared/data/data stores/BMFDataStoreSelectionProtocol.h

+//
+//  BMFDataStoreSelectionProtocol.h
+//  Pods
+//
+//  Created by Jose Manuel Sánchez Peñarroja on 01/07/14.
+//
+//
+
+#import <UIKit/UIKit.h>
+
+typedef NS_ENUM(NSUInteger, BMFDataStoreSelectionMode) {
+    BMFDataStoreSelectionModeSingle,
+    BMFDataStoreSelectionModeSinglePerSection,
+    BMFDataStoreSelectionModeMultiple,
+};
+
+@protocol BMFDataStoreSelectionProtocol <NSObject>
+
+@property (nonatomic, assign) BMFDataStoreSelectionMode mode;
+
+@property (nonatomic, strong) NSArray *selection;
+
+- (void) select:(NSIndexPath *) indexPath;
+- (void) deselect:(NSIndexPath *) indexPath;
+
+@end

bmf/shared/data/data stores/BMFFRDataStore.h

 
 #import <Foundation/Foundation.h>
 
-#import "BMFNode.h"
-#import "BMFDataReadProtocol.h"
+#import "BMFDataRead.h"
 
 @class NSFetchRequest;
 
-@interface BMFFRDataStore : BMFNode <BMFDataReadProtocol>
+@interface BMFFRDataStore : BMFDataRead
 
 @property (nonatomic, strong) NSFetchRequest *fr;
 @property (nonatomic, strong) NSManagedObjectContext *context;

bmf/shared/data/data stores/BMFFRDataStore.m

 }
 
 #pragma mark TNNode
-
+/*
 - (BOOL) performProcess:(id)input completion:(BMFNodeProcessCompletionBlock)completionBlock {
 	return [self loadData:completionBlock];
 }
 	
 	return _loaded;
 }
+*/
 
 @end

bmf/shared/factories/BMFDefaultFactory.m

 	
 	BMFAFURLSessionLoader *loader = [BMFAFURLSessionLoader new];
 	
-	loader.url = [NSURL URLWithString:urlString];
+	loader.url = [urlString BMF_url];
 	loader.parameters = parameters;
 	
 	return loader;

bmf/shared/model/BMFBannerImage.h

+//
+//  BannerImage.h
+//  Pods
+//
+//  Created by Jose Manuel Sánchez Peñarroja on 01/07/14.
+//
+//
+
+#import <Foundation/Foundation.h>
+
+#import "BMFTypes.h"
+#import "BMFBannerItemImageProtocol.h"
+
+@interface BMFBannerImage : NSObject <BMFBannerItemImageProtocol>
+
+@property (nonatomic, strong) BMFIXImage *image;
+@property (nonatomic, strong) BMFIXImage *placeholderImage;
+
+@property (nonatomic, copy) NSString *urlString;
+
+- (instancetype) initWithUrlString:(NSString *)urlString;
+- (instancetype) initWithImage:(BMFIXImage *) image;
+
+@end

bmf/shared/model/BMFBannerImage.m

+//
+//  BannerImage.m
+//  Pods
+//
+//  Created by Jose Manuel Sánchez Peñarroja on 01/07/14.
+//
+//
+
+#import "BMFBannerImage.h"
+
+@implementation BMFBannerImage
+
+- (instancetype) initWithUrlString:(NSString *)urlString {
+	self = [super init];
+    if (self) {
+        _urlString = urlString;
+    }
+    return self;
+}
+
+- (instancetype) initWithImage:(BMFIXImage *) image {
+	self = [super init];
+    if (self) {
+        _image = image;
+    }
+    return self;
+}
+
+
+- (instancetype)init {
+	BMFInvalidInit(initWithUrlString: or initWithImage:);
+}
+
+@end

bmf/shared/model/BMFBannerItemImageProtocol.h

 @import UIKit;
 
 #import "BMFTypes.h"
+#import "BMFBannerItemProtocol.h"
 
-@protocol BMFBannerItemImageProtocol <NSObject>
+@protocol BMFBannerItemImageProtocol <BMFBannerItemProtocol>
 
-@property (nonatomic, copy) NSString *imageUrl;
-@property (nonatomic, strong) BMFIXImage *image;
+@property (nonatomic, copy) NSString *urlString;
 
-- (void) load:(BMFCompletionBlock) completionBlock;
+@property (nonatomic, strong) BMFIXImage *image;
+@property (nonatomic, strong) BMFIXImage *placeholderImage;
 
 @end