Commits

José Manuel Sánchez Peñarroja committed 658c853

Added view controller context to pass parameters between view controllers. Added behavior to pass vc contexts. Added zoom to collection view selected cells. Added utility to calculate a rect from a rect, a container rect and a content mode. Added messages presenter to bmfviewcontroller

Comments (0)

Files changed (20)

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.FileBreakpoint">
          <BreakpointContent
-            shouldBeEnabled = "Yes"
-            ignoreCount = "0"
-            continueAfterRunningActions = "No"
-            filePath = "../bmf/shared/base/BMFTypes.h"
-            timestampString = "424779243.784715"
-            startingColumnNumber = "9223372036854775807"
-            endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "53"
-            endingLineNumber = "53">
-         </BreakpointContent>
-      </BreakpointProxy>
-      <BreakpointProxy
-         BreakpointExtensionID = "Xcode.Breakpoint.FileBreakpoint">
-         <BreakpointContent
             shouldBeEnabled = "No"
             ignoreCount = "0"
             continueAfterRunningActions = "No"

bmf/ios/behaviors/BMFCollectionViewZoomSelectedCellBehavior.h

+//
+//  BMFCollectionViewZoomSelectedCellBehavior.h
+//  Pods
+//
+//  Created by Jose Manuel Sánchez Peñarroja on 21/07/14.
+//
+//
+
+#import "BMFItemTapBehavior.h"
+
+#import "BMFObjectControllerProtocol.h"
+
+@interface BMFCollectionViewZoomSelectedCellBehavior : BMFItemTapBehavior <BMFObjectControllerProtocol>
+
+@property (nonatomic, strong) BMFObjectDataStore *objectStore;
+
+@end

bmf/ios/behaviors/BMFCollectionViewZoomSelectedCellBehavior.m

+//
+//  BMFCollectionViewZoomSelectedCellBehavior.m
+//  Pods
+//
+//  Created by Jose Manuel Sánchez Peñarroja on 21/07/14.
+//
+//
+
+#import "BMFCollectionViewZoomSelectedCellBehavior.h"
+
+#import "BMFDataSourceProtocol.h"
+#import "BMF.h"
+
+@interface BMFCollectionViewZoomSelectedCellBehavior()
+
+@property (nonatomic, assign) CGRect oldFrame;
+
+@end
+
+@implementation BMFCollectionViewZoomSelectedCellBehavior
+
+- (instancetype)init
+{
+    self = [super init];
+    if (self) {
+        self.objectStore = [BMFObjectDataStore new];
+    }
+    return self;
+}
+
+#pragma mark UITableViewDelegate
+
+- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
+	[NSException raise:BMFLocalized(@"Unsupported view",nil) format:@"Table views are not supported"];
+}
+
+#pragma mark UICollectionViewDelegate
+
+- (void) itemTapped:(id) item atIndexPath:(NSIndexPath *) indexPath containerView:(UICollectionView *)containerView {
+	BMFAssertReturn([containerView isKindOfClass:[UICollectionView class]]);
+	
+	UICollectionView *collectionView = containerView;
+	
+	UICollectionViewCell *cell = [collectionView cellForItemAtIndexPath:indexPath];
+	if (self.objectStore.currentValue==item) {
+		[self stopZooming:cell containerView:containerView];
+	}
+	else {
+		self.objectStore.currentValue = item;
+		[self startZooming:cell containerView:containerView];
+	}
+}
+
+- (void) startZooming:(UIView *) view containerView:(UIView *) containerView {
+	self.oldFrame = view.frame;
+
+	[containerView bringSubviewToFront:view];
+	view.frame = self.oldFrame;
+	
+	[UIView animateWithDuration:0.5 animations:^{
+		view.frame = [BMFUtils rectToFitRect:view.frame toRect:containerView.bounds mode:BMFContentModeScaleAspectFit];
+	}];
+}
+
+- (void) stopZooming:(UIView *) view containerView:(UIView *) containerView {
+	[UIView animateWithDuration:0.5 animations:^{
+		view.frame = self.oldFrame;
+	}];
+}
+
+@end

bmf/ios/behaviors/BMFItemTapBehavior.h

 @property (nonatomic, assign) BOOL deselectItemOnTap;
 
 /// Template method. Should be implemented by subclasses
-- (void) itemTapped:(id) item atIndexPath:(NSIndexPath *) indexPath;
+- (void) itemTapped:(id) item atIndexPath:(NSIndexPath *) indexPath containerView:(UIView *) containerView;
 
 @end

bmf/ios/behaviors/BMFItemTapBehavior.m

 
 - (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
 	if (self.deselectItemOnTap) [tableView deselectRowAtIndexPath:indexPath animated:YES];
-	[self tapInDataSource:(id<BMFDataSourceProtocol>)tableView.dataSource indexPath:indexPath];
+	[self tapInDataSource:(id<BMFDataSourceProtocol>)tableView.dataSource indexPath:indexPath containerView:tableView];
 }
 
 #pragma mark UICollectionViewDelegate
 
 - (void) collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
 	if (self.deselectItemOnTap) [collectionView deselectItemAtIndexPath:indexPath animated:YES];
-	[self tapInDataSource:(id<BMFDataSourceProtocol>)collectionView.dataSource indexPath:indexPath];
+	[self tapInDataSource:(id<BMFDataSourceProtocol>)collectionView.dataSource indexPath:indexPath containerView:collectionView];
 }
 
 #pragma mark Item tap
 
-- (void) tapInDataSource:(id<BMFDataSourceProtocol>) dataSource indexPath:(NSIndexPath *)indexPath {
-	if ([dataSource conformsToProtocol:@protocol(BMFDataSourceProtocol)]) {
-		id<BMFDataReadProtocol> dataStore = dataSource.dataStore;
-		id item = [dataStore itemAt:indexPath];
-		[self itemTapped:item atIndexPath:indexPath];
-	}
-	else {
-		[NSException raise:@"View data source should conform to protocol BMFDataSourceProtocol" format:@"%@",dataSource];
-	}
+- (void) tapInDataSource:(id<BMFDataSourceProtocol>) dataSource indexPath:(NSIndexPath *)indexPath containerView:(UIView *) containerView {
+	BMFAssertReturn([dataSource conformsToProtocol:@protocol(BMFDataSourceProtocol)]);
+	
+	id<BMFDataReadProtocol> dataStore = dataSource.dataStore;
+	id item = [dataStore itemAt:indexPath];
+	[self itemTapped:item atIndexPath:indexPath containerView:containerView];
 }
 
-- (void) itemTapped:(id) item atIndexPath:(NSIndexPath *)indexPath {
-	[NSException raise:@"Behavior subclass should implement itemTapped:" format:@"%@",self];
+- (void) itemTapped:(id) item atIndexPath:(NSIndexPath *)indexPath containerView:(UIView *)containerView {
+	BMFAbstractMethod();
 }
 
 @end

bmf/ios/behaviors/BMFItemTapBlockBehavior.m

 	return nil;
 }
 
-- (void) itemTapped:(id)item atIndexPath:(NSIndexPath *) indexPath {
+- (void) itemTapped:(id)item atIndexPath:(NSIndexPath *) indexPath containerView:(UIView *)containerView {
 	self.itemTapBlock(item,indexPath);
 }
 

bmf/ios/behaviors/BMFItemTapPresentsViewControllerBehavior.m

     return self;
 }
 
-- (void) itemTapped:(id) item atIndexPath:(NSIndexPath *)indexPath {
+- (void) itemTapped:(id) item atIndexPath:(NSIndexPath *)indexPath containerView:(UIView *)containerView {
 
 	BMFAssertReturn(self.object);
 

bmf/ios/behaviors/BMFViewControllerContextBehavior.h

+//
+//  BMFViewControllerContextBehavior.h
+//  Pods
+//
+//  Created by Jose Manuel Sánchez Peñarroja on 21/07/14.
+//
+//
+
+#import "BMFViewControllerBehavior.h"
+
+#import "BMFViewControllerContext.h"
+
+typedef BMFViewControllerContext *(^BMFViewControllerContextCreationBlock)();
+typedef (^BMFViewControllerContextApplyBlock)(BMFViewControllerContext *context,UIViewController *vc);
+
+@interface BMFViewControllerContextBehavior : BMFViewControllerBehavior
+
+/// Optional. Allows creating a subclass of BMFViewControllerContext
+@property (nonatomic, copy) BMFViewControllerContextCreationBlock creationBlock;
+
+/// Optional. You can use this to apply properties that you added in your subclass after the BMFViewControllerContext properties are applied
+@property (nonatomic, copy) BMFViewControllerContextApplyBlock applyBlock;
+
+
+/// If you aren't using a storyboard segue you must use this method to apply the context to the next view controller
+- (void) applyTo:(UIViewController *) destinationVC;
+
+@end

bmf/ios/behaviors/BMFViewControllerContextBehavior.m

+//
+//  BMFViewControllerContextBehavior.m
+//  Pods
+//
+//  Created by Jose Manuel Sánchez Peñarroja on 21/07/14.
+//
+//
+
+#import "BMFViewControllerContextBehavior.h"
+
+#import "BMF.h"
+
+@implementation BMFViewControllerContextBehavior
+
+- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
+	UIViewController *destinationVC = [BMFUtils extractDetailViewController:segue.destinationViewController];
+	[self applyTo:segue.destinationViewController];
+}
+
+- (void) applyTo:(UIViewController *) destinationVC {
+	
+	BMFAssertReturn([self.object isKindOfClass:[UIViewController class]]);
+	
+	BMFViewControllerContext *context = nil;
+	
+	if (self.creationBlock) {
+		context = self.creationBlock();
+	}
+	else {
+		context = [BMFViewControllerContext contextFromViewController:self.object];
+	}
+	
+	[context apply:destinationVC];
+	
+	if (self.applyBlock) self.applyBlock(context,destinationVC);
+
+}
+
+@end

bmf/ios/view controllers/BMFMatrixFlowLayoutDelegate.m

     self = [super init];
     if (self) {
 		self.numColumns = [[BMFDeviceValue alloc] initWithDefaultValue:@2];
-//		[self.numColumns setValue:@3 forFamily:BMFDeviceFamilyIPhone orientationAxis:BMFDeviceOrientationAxisLandscape];
-//		[self.numColumns setValue:@3 forFamily:BMFDeviceFamilyIPad];
-//		[self.numColumns setValue:@4 forFamily:BMFDeviceFamilyIPad orientationAxis:BMFDeviceOrientationAxisLandscape];
-		
-//        self.numColumns = 2;
-//		self.numColumnsIPad = 3;
-//		self.numColumnsLandscape = 3;
-//		self.numColumnsLandscapeIPad = 4;
 		
 		self.outerMargin = 10;
 		self.innerMargin = 10;
 		self.verticalMode = BMFMatrixFlowLayoutDelegateVerticalModeRows;
 		
 		self.numRows = [[BMFDeviceValue alloc] initWithDefaultValue:@4];
-//		[self.numRows setValue:@2 forFamily:BMFDeviceFamilyIPhone orientationAxis:BMFDeviceOrientationAxisLandscape];
-//		[self.numRows setValue:@6 forFamily:BMFDeviceFamilyIPad];
-//		[self.numRows setValue:@4 forFamily:BMFDeviceFamilyIPad orientationAxis:BMFDeviceOrientationAxisLandscape];
 
 		self.itemHeight = [[BMFDeviceValue alloc] initWithDefaultValue:@120];
-//		[self.itemHeight setValue:@70 forFamily:BMFDeviceFamilyIPhone orientationAxis:BMFDeviceOrientationAxisLandscape];
-//		[self.itemHeight setValue:@150 forFamily:BMFDeviceFamilyIPad];
-//		[self.itemHeight setValue:@120 forFamily:BMFDeviceFamilyIPad orientationAxis:BMFDeviceOrientationAxisLandscape];
-
-		
-		// self.numRows = 4;
-// 		self.numRowsIPad = 6;
-// 		self.numRowsLandscape = 2;
-// 		self.numRowsLandscapeIPad = 4;
 		
 		self.itemAspectRatio = 1;
-		
-		// self.itemHeight = 120;
-// 		self.itemHeightIPad = 150;
-// 		self.itemHeightLandscape = 70;
-// 		self.itemHeightLandscapeIPad = 120;
     }
     return self;
 }
 
-/*- (NSUInteger) deviceNumColumns {
-	if ([[UIDevice currentDevice] userInterfaceIdiom]==UIUserInterfaceIdiomPad) {
-		if (UIInterfaceOrientationIsLandscape([UIDevice currentDevice].orientation)) {
-			return self.numColumnsLandscapeIPad;
-		}
-		else {
-			return self.numColumnsIPad;
-		}
-	}
-	else {
-		if (UIInterfaceOrientationIsLandscape([UIDevice currentDevice].orientation)) {
-			return self.numColumnsLandscape;
-		}
-		else {
-			return self.numColumns;
-		}
-	}
-}
-
-- (CGFloat) deviceNumRows {
-	if ([[UIDevice currentDevice] userInterfaceIdiom]==UIUserInterfaceIdiomPad) {
-		if (UIInterfaceOrientationIsLandscape([UIDevice currentDevice].orientation)) {
-			return self.numColumnsLandscapeIPad;
-		}
-		else {
-			return self.numRowsIPad;
-		}
-	}
-	else {
-		if (UIInterfaceOrientationIsLandscape([UIDevice currentDevice].orientation)) {
-			return self.numRowsLandscape;
-		}
-		else {
-			return self.numRows;
-		}
-	}
-}
-
-- (CGFloat) deviceItemHeight {
-	if ([[UIDevice currentDevice] userInterfaceIdiom]==UIUserInterfaceIdiomPad) {
-		if (UIInterfaceOrientationIsLandscape([UIDevice currentDevice].orientation)) {
-			return self.itemHeightLandscapeIPad;
-		}
-		else {
-			return self.itemHeightIPad;
-		}
-	}
-	else {
-		if (UIInterfaceOrientationIsLandscape([UIDevice currentDevice].orientation)) {
-			return self.itemHeightLandscape;
-		}
-		else {
-			return self.itemHeight;
-		}
-	}
-}
-*/
-
 - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath {
 	
 	CGFloat itemWidth = [self itemWidth:collectionView.bounds.size.width];

bmf/ios/view controllers/BMFViewController.h

 
 #import "BMFViewControllerBehaviorProtocol.h"
 #import "BMFArrayProxy.h"
+#import "BMFMessagesPresenterViewControllerProtocol.h"
 
 enum BMFViewControllerNavigationDirection {
 	BMFViewControllerNavigationDirectionForward = 1,
 	BMFViewControllerNavigationDirectionBackward = 2
 };
 
-@interface BMFViewController : UIViewController
+@interface BMFViewController : UIViewController <BMFMessagesPresenterViewControllerProtocol>
+
+@property (nonatomic, strong) id<BMFUserMessagesPresenterProtocol> messagePresenter;
 
 /// Reference for when it's used inside a popoverViewController
 @property (nonatomic, weak) UIPopoverController *BMF_popoverController;

bmf/ios/view controllers/BMFViewControllerContext.h

+//
+//  BMFViewControllerContext.h
+//  Pods
+//
+//  Created by Jose Manuel Sánchez Peñarroja on 21/07/14.
+//
+//
+
+#import <Foundation/Foundation.h>
+
+#import "BMFUserMessagesPresenterProtocol.h"
+
+//@class BMFViewControllerContext;
+
+//@protocol BMFViewControllerContextProtocol <NSObject>
+//
+//- (void) setContext:(BMFViewControllerContext *) context;
+//
+//@end
+
+
+@interface BMFViewControllerContext : NSObject
+
+@property (nonatomic, strong) id<BMFUserMessagesPresenterProtocol> messagePresenter;
+@property (nonatomic, strong) id object;
+
+- (void) apply:(UIViewController *) viewController;
++ (BMFViewControllerContext *) contextFromViewController:(UIViewController *) viewController;
+
+@end

bmf/ios/view controllers/BMFViewControllerContext.m

+//
+//  BMFViewControllerContext.m
+//  Pods
+//
+//  Created by Jose Manuel Sánchez Peñarroja on 21/07/14.
+//
+//
+
+#import "BMFViewControllerContext.h"
+
+#import "BMF.h"
+#import "BMFMessagesPresenterViewControllerProtocol.h"
+#import "BMFObjectControllerProtocol.h"
+
+@implementation BMFViewControllerContext
+
+- (void) apply:(UIViewController *) viewController {
+	
+	viewController = [BMFUtils extractDetailViewController:viewController];
+		
+	if ([viewController conformsToProtocol:@protocol(BMFMessagesPresenterViewControllerProtocol)]) {
+		UIViewController<BMFMessagesPresenterViewControllerProtocol> *vc = (UIViewController<BMFMessagesPresenterViewControllerProtocol> *)viewController;
+		vc.messagePresenter = self.messagePresenter;
+	}
+		
+	if ([viewController conformsToProtocol:@protocol(BMFObjectControllerProtocol)]) {
+		UIViewController<BMFObjectControllerProtocol> *vc = (UIViewController<BMFObjectControllerProtocol> *)viewController;
+		vc.objectStore.currentValue = self.object;
+	}
+}
+
++ (BMFViewControllerContext *) contextFromViewController:(UIViewController *) viewController {
+	BMFViewControllerContext *context = [BMFViewControllerContext new];
+	
+	if ([viewController conformsToProtocol:@protocol(BMFMessagesPresenterViewControllerProtocol)]) {
+		UIViewController<BMFMessagesPresenterViewControllerProtocol> *vc = (UIViewController<BMFMessagesPresenterViewControllerProtocol> *)viewController;
+		context.messagePresenter = vc.messagePresenter;
+	}
+		
+	if ([viewController conformsToProtocol:@protocol(BMFObjectControllerProtocol)]) {
+		UIViewController<BMFObjectControllerProtocol> *vc = (UIViewController<BMFObjectControllerProtocol> *)viewController;
+		context.object = vc.objectStore.currentValue;
+	}
+	
+	return context;
+}
+
+@end

bmf/shared/base/BMFTypes.h

 	BMFDeviceOrientationAxisLandscape
 };
 
+typedef NS_ENUM(NSInteger, BMFContentMode) {
+    BMFContentModeScaleToFill,
+    BMFContentModeScaleAspectFit,      // contents scaled to fit with fixed aspect. remainder is transparent
+    BMFContentModeScaleAspectFill,     // contents scaled to fill with fixed aspect. some portion of content may be clipped.
+    BMFContentModeCenter,              // contents remain same size. positioned adjusted.
+    BMFContentModeTop,
+    BMFContentModeBottom,
+    BMFContentModeLeft,
+    BMFContentModeRight,
+    BMFContentModeTopLeft,
+    BMFContentModeTopRight,
+    BMFContentModeBottomLeft,
+    BMFContentModeBottomRight,
+};
+
+
 #if TARGET_OS_IPHONE
 
 typedef NS_ENUM(NSUInteger, BMFTextAlignment) {

bmf/shared/controllers/BMFMessagesPresenterViewControllerProtocol.h

+//
+//  BMFMessagesPresenterViewControllerProtocol.h
+//  Pods
+//
+//  Created by Jose Manuel Sánchez Peñarroja on 21/07/14.
+//
+//
+
+#import <Foundation/Foundation.h>
+
+#import "BMFUserMessagesPresenterProtocol.h"
+
+@protocol BMFMessagesPresenterViewControllerProtocol <NSObject>
+
+@property (nonatomic, strong) id<BMFUserMessagesPresenterProtocol> messagePresenter;
+
+@end

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

 //
 //
 
-#import <UIKit/UIKit.h>
+#import <Foundation/Foundation.h>
 
 typedef NS_ENUM(NSUInteger, BMFDataStoreSelectionMode) {
     BMFDataStoreSelectionModeSingle,

bmf/shared/themes/BMFThemeProtocol.h

 
 #import <Foundation/Foundation.h>
 
+#import "BMFTypes.h"
 #import "BMFRegistrableProtocol.h"
 
 @protocol BMFThemeProtocol <BMFRegistrableProtocol>
 
-- (UIColor *) tintColor;
+- (BMFIXColor *) tintColor;
 
 - (NSString *) name;
 

bmf/shared/utils/BMFUtils.h

 + (NSData *) archiveImage:(BMFIXImage *) image;
 + (BMFIXImage *) unarchiveImage:(NSData *) imageData;
 
++ (CGRect) rectToFitRect:(CGRect) rect toRect:(CGRect) containerRect mode:(BMFContentMode)mode;
+
 #if TARGET_OS_IPHONE
 + (void) showNavigationBarLoading: (UIViewController *)vc;
 + (void) hideNavigationBarLoading: (UIViewController *)vc;

bmf/shared/utils/BMFUtils.m

 
 #import "BMFUtils.h"
 
+#import "BMF.h"
+
 #import <QuartzCore/QuartzCore.h>
 #import <ReactiveCocoa/ReactiveCocoa.h>
 
 	}
 }
 
++ (CGRect) rectToFitRect:(CGRect) rect toRect:(CGRect) containerRect mode:(BMFContentMode)mode {
+
+	CGFloat x;
+	CGFloat y;
+	CGFloat width = rect.size.width;
+	CGFloat height = rect.size.height;
+	
+	if (mode==BMFContentModeTopLeft) {
+		x = 0;
+		y = 0;
+	}
+	else if (mode==BMFContentModeTop) {
+		y = 0;
+	}
+	else if (mode==BMFContentModeTopRight) {
+		x = (containerRect.size.width-width);
+		y = 0;
+	}
+	else if (mode==BMFContentModeRight) {
+		x = (containerRect.size.width-width);
+	}
+	else if (mode==BMFContentModeBottomRight) {
+		x = (containerRect.size.width-width);
+		y = (containerRect.size.height-height);
+	}
+	else if (mode==BMFContentModeBottom) {
+		y = (containerRect.size.height-height);
+	}
+	else if (mode==BMFContentModeBottomLeft) {
+		x = 0;
+		y = (containerRect.size.height-height);
+	}
+	else if (mode==BMFContentModeLeft) {
+		x = 0;
+	}
+	else if (mode==BMFContentModeCenter) {
+		x = (containerRect.size.width-width)/2;
+		y = (containerRect.size.height-height)/2;
+	}
+	else if (mode==BMFContentModeScaleAspectFill) {
+		height = containerRect.size.height;
+		width = rect.size.width*height/rect.size.height;
+		if (width<containerRect.size.width) {
+			width = containerRect.size.width;
+			height = rect.size.height*width/rect.size.width;
+			x = 0;
+			y = (height-containerRect.size.height)/2;
+		}
+		else {
+			y = 0;
+			x = (width-containerRect.size.width)/2;
+		}
+	}
+	else if (mode==BMFContentModeScaleAspectFit) {
+		height = containerRect.size.height;
+		width = rect.size.width*height/rect.size.height;
+		if (width>containerRect.size.width) {
+			width = containerRect.size.width;
+			height = rect.size.height*width/rect.size.width;
+			x = 0;
+			y = (containerRect.size.height-height)/2;
+		}
+		else {
+			y = 0;
+			x = (containerRect.size.width-width)/2;
+		}
+		
+	}
+	else if (mode==BMFContentModeScaleToFill) {
+		x = 0;
+		y = 0;
+		width = containerRect.size.width;
+		height = containerRect.size.height;
+	}
+	else {
+		[NSException raise:BMFLocalized(@"Unknown content mode",nil) format:@"%d",mode];
+	}
+	
+	return CGRectMake(x, y, width, height);
+}
+
 #pragma mark iPhone only methods
 #if TARGET_OS_IPHONE