Commits

José Manuel Sánchez Peñarroja committed a8ad24e

Sincronized array proxy access. Added search bar behavior. Added index path to item tap behavior. Added themes

  • Participants
  • Parent commits 9a9bf7b

Comments (0)

Files changed (24)

     end
 	end
 	
+	s.subspec "Themes" do |themes|
+		themes.source_files = 'bmf/shared/themes/**/*.{h,m}'
+	end
+  
 	s.subspec "Sqlite" do |sqlite|
 		sqlite.source_files = 'bmf/shared/subspecs/fmdb/**/*.{h,m}'
   	sqlite.ios.source_files = 'bmf/ios/subspecs/fmdb/**/*.{h,m}'

File Example/Example.xcworkspace/contents.xcworkspacedata

-<?xml version='1.0' encoding='UTF-8'?><Workspace version='1.0'><FileRef location='group:Example/Example.xcodeproj'/><FileRef location='group:ExampleMac/ExampleMac.xcodeproj'/><FileRef location='group:Pods/Pods.xcodeproj'/></Workspace>
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "group:../bmf/shared/base/BMFRegistrableProtocol.h">
+   </FileRef>
+   <FileRef
+      location = "group:Example/Example.xcodeproj">
+   </FileRef>
+   <FileRef
+      location = "group:ExampleMac/ExampleMac.xcodeproj">
+   </FileRef>
+   <FileRef
+      location = "group:Pods/Pods.xcodeproj">
+   </FileRef>
+</Workspace>

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

Binary file modified.

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

             landmarkType = "6">
          </BreakpointContent>
       </BreakpointProxy>
+      <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>
    </Breakpoints>
 </Bucket>

File bmf/ios/behaviors/BMFItemTapBehavior.h

 
 #import "BMFViewControllerBehavior.h"
 
+#import "BMFTypes.h"
+
 /// Abstract behavior for subclassing. Responds to table selections, collection view selections
 @interface BMFItemTapBehavior : BMFViewControllerBehavior <UITableViewDelegate, UICollectionViewDelegate>
 
 @property (nonatomic, assign) BOOL deselectItemOnTap;
 
 /// Template method. Should be implemented by subclasses
-- (void) itemTapped:(id) item;
+- (void) itemTapped:(id) item atIndexPath:(NSIndexPath *) indexPath;
 
 @end

File bmf/ios/behaviors/BMFItemTapBehavior.m

 	if ([dataSource conformsToProtocol:@protocol(BMFDataSourceProtocol)]) {
 		id<BMFDataReadProtocol> dataStore = dataSource.dataStore;
 		id item = [dataStore itemAt:indexPath];
-		[self itemTapped:item];
+		[self itemTapped:item atIndexPath:indexPath];
 	}
 	else {
 		[NSException raise:@"View data source should conform to protocol BMFDataSourceProtocol" format:@"%@",dataSource];
 	}
 }
 
-- (void) itemTapped:(id) item {
+- (void) itemTapped:(id) item atIndexPath:(NSIndexPath *)indexPath {
 	[NSException raise:@"Behavior subclass should implement itemTapped:" format:@"%@",self];
 }
 

File bmf/ios/behaviors/BMFItemTapBlockBehavior.h

 
 @interface BMFItemTapBlockBehavior : BMFItemTapBehavior
 
-@property (nonatomic, copy) BMFActionBlock itemTapBlock;
+@property (nonatomic, copy) BMFItemActionBlock itemTapBlock;
 
-- (instancetype) initWithTapBlock:(BMFActionBlock)tapBlock;
+- (instancetype) initWithTapBlock:(BMFItemActionBlock)tapBlock;
 
 @end

File bmf/ios/behaviors/BMFItemTapBlockBehavior.m

 
 #import "BMFItemTapBlockBehavior.h"
 
+#import "BMF.h"
 #import "BMFDataSourceProtocol.h"
 #import "BMFDataReadProtocol.h"
 
 @implementation BMFItemTapBlockBehavior
 
-- (instancetype) initWithTapBlock:(BMFActionBlock)tapBlock {
+- (instancetype) initWithTapBlock:(BMFItemActionBlock)tapBlock {
 	BMFAssertReturnNil(tapBlock);
 	
     self = [super init];
 	return nil;
 }
 
-- (void) itemTapped:(id)item {
-	self.itemTapBlock(item);
+- (void) itemTapped:(id)item atIndexPath:(NSIndexPath *) indexPath {
+	self.itemTapBlock(item,indexPath);
 }
 
 @end

File bmf/ios/behaviors/BMFSearchBarViewControllerBehavior.h

+//
+//  BMFSearchBarViewControllerBehavior.h
+//  Pods
+//
+//  Created by Jose Manuel Sánchez Peñarroja on 18/06/14.
+//
+//
+
+#import "BMFViewControllerBehavior.h"
+
+#import "BMFValueProtocol.h"
+
+@interface BMFSearchBarViewControllerBehavior : BMFViewControllerBehavior <UISearchBarDelegate>
+
+@property (nonatomic, assign) BOOL searchActive;
+@property (nonatomic, readonly) id<BMFValueProtocol> textValue;
+
+@property (nonatomic, strong) UISearchBar *searchBar;
+
+- (instancetype) initWithSearchBar:(UISearchBar *) searchBar;
+
+@end

File bmf/ios/behaviors/BMFSearchBarViewControllerBehavior.m

+//
+//  BMFSearchBarViewControllerBehavior.m
+//  Pods
+//
+//  Created by Jose Manuel Sánchez Peñarroja on 18/06/14.
+//
+//
+
+#import "BMFSearchBarViewControllerBehavior.h"
+
+#import "BMF.h"
+#import "BMFFixedValue.h"
+
+@interface BMFSearchBarViewControllerBehavior()
+
+@property (nonatomic, strong) BMFFixedValue *textValue;
+
+@end
+
+@implementation BMFSearchBarViewControllerBehavior
+
+- (instancetype) initWithSearchBar:(UISearchBar *) searchBar {
+	BMFAssertReturnNil(searchBar);
+	
+	self = [super init];
+	if (self) {
+		_searchBar = searchBar;
+		_searchBar.delegate = self;
+		_textValue = [BMFFixedValue new];
+	}
+	return self;
+}
+
+
+- (instancetype) init {
+	BMFInvalidInit(initWithSearchBar:);
+}
+
+- (void) setSearchBar:(UISearchBar *)searchBar {
+	BMFAssertReturn(searchBar);
+	_searchBar = searchBar;
+}
+
+- (void) viewWillDisappear:(BOOL)animated {
+	self.searchActive = NO;
+	[self.searchBar resignFirstResponder];
+}
+
+#pragma mark UISearchBarDelegate
+
+- (void) searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
+	if (searchText.length>0) self.searchActive = YES;
+	else self.searchActive = NO;
+	
+	BMFFixedValue *value = self.textValue;
+	value.value = searchText;
+}
+
+- (void) searchBarSearchButtonClicked:(UISearchBar *)searchBar {
+	[self.searchBar resignFirstResponder];
+}
+
+- (void) searchBarCancelButtonClicked:(UISearchBar *)searchBar {
+	self.searchActive = NO;
+	[self.searchBar resignFirstResponder];
+}
+
+
+@end

File bmf/ios/factories/BMFIOSDefaultFactory.h

 - (UIBarItem<BMFLoaderViewProtocol> *) navBarLoaderItem:(id) sender;
 
 #pragma mark More customizable
+- (id) tableViewDataSourceWithStore:(id<BMFDataReadProtocol>) store cellRegister:(id<BMFCellRegisterProtocol>) cellRegister animatedUpdates:(BOOL) animatedUpdates sender:(id) sender;
 - (id) tableViewDataSourceWithStore:(id<BMFDataReadProtocol>) store cellClassOrNib:(id) classOrNib animatedUpdates:(BOOL) animatedUpdates sender:(id) sender;
 - (id) tableViewDataSourceWithStore:(id<BMFDataReadProtocol>) store cellClassOrNib:(id) classOrNib sender:(id) sender;
 - (id) tableViewControllerWithDataStore:(id<BMFDataReadProtocol>) dataStore cellClassOrNib:(id) classOrNib sender:(id) sender;

File bmf/ios/factories/BMFIOSDefaultFactory.m

 	
 	BMFSimpleViewRegister *viewRegister = [[BMFSimpleViewRegister alloc] initWithInfos:@[ info ]];
 	return viewRegister;
-	
+}
+
+- (id) tableViewDataSourceWithStore:(id<BMFDataReadProtocol>) store cellRegister:(id<BMFCellRegisterProtocol>) cellRegister animatedUpdates:(BOOL) animatedUpdates sender:(id) sender {
+	BMFTableViewDataSource *dataSource = [[BMFTableViewDataSource alloc] initWithDataStore:store animatedUpdates:animatedUpdates];
+	dataSource.cellRegister = cellRegister;
+	return dataSource;
 }
 
 - (id) tableViewDataSourceWithStore:(id<BMFDataReadProtocol>) store cellClassOrNib:(id) classOrNib animatedUpdates:(BOOL) animatedUpdates sender:(id) sender {
 
 	BMFTableViewDataSource *dataSource = [[BMFTableViewDataSource alloc] initWithDataStore:store animatedUpdates:animatedUpdates];
-
-	
-	/*BMFTableViewDataSource *dataSource = [[BMFTableViewDataSource alloc] initWithDataStore:store configureCellBlock:^(id cell, id item) {
-		id<BMFCellProtocol> tableCell = cell;
-		tableCell.detailItem = item;
-		
-	} animatedUpdates:animatedUpdates];*/
 	
 	dataSource.cellRegister = [self cellRegister:@"cell" classOrNib:classOrNib sender:sender];
 	

File bmf/ios/factories/BMFUIFactoryProtocol.h

 - (UIBarItem<BMFLoaderViewProtocol> *) navBarLoaderItem:(id) sender;
 
 #pragma mark More customizable
+- (id) tableViewDataSourceWithStore:(id<BMFDataReadProtocol>) store cellRegister:(id<BMFCellRegisterProtocol>) cellRegister animatedUpdates:(BOOL) animatedUpdates sender:(id) sender;
 - (id) tableViewDataSourceWithStore:(id<BMFDataReadProtocol>) store cellClassOrNib:(id) classOrNib animatedUpdates:(BOOL) animatedUpdates sender:(id) sender;
 - (id) tableViewDataSourceWithStore:(id<BMFDataReadProtocol>) store cellClassOrNib:(id) classOrNib sender:(id) sender;
 - (id) tableViewControllerWithDataStore:(id<BMFDataReadProtocol>) dataStore cellClassOrNib:(id) classOrNib sender:(id) sender;

File bmf/ios/views/cell configuration/BMFViewConfigurator.h

 
 #import <Foundation/Foundation.h>
 
+#import "BMFRegistrableProtocol.h"
 #import "BMFViewConfiguratorProtocol.h"
 
-@interface BMFViewConfigurator : NSObject
+@interface BMFViewConfigurator : NSObject <BMFRegistrableProtocol>
 
-+ (NSArray *) availableConfigurators;
++ (NSArray *) registeredClasses;
 
 /// configuratorClass should conform to protocol BMFCellConfiguratorProtocol
 + (void) register;

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

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

File bmf/ios/views/cell factory/BMFBlockCellRegister.m

 @implementation BMFBlockCellRegister
 
 - (instancetype) initWithRegisterBlock:(BMFCellRegisterCellsBlock) registerBlock dequeueBlock:(BMFCellIdBlock)cellIdBlock {
-	BMFAssertReturnNil(registerBlock);
 	BMFAssertReturnNil(cellIdBlock);
 	
     self = [super init];
 }
 
 - (void) registerCells:(UIView *) view {
-	self.registerBlock(view);
+	if (self.registerBlock) self.registerBlock(view);
 }
 
 

File bmf/shared/base/BMFArrayProxy.m

 
 @property (nonatomic, strong) NSMutableSet *destinationObjects;
 @property (nonatomic, strong) RACSubject *destinationsSignal;
+@property (nonatomic, strong) dispatch_queue_t serialQueue;
 
 @end
 
 
 - (instancetype)init {
     if (self) {
-        self.destinationObjects = [NSMutableSet set];
+		_serialQueue = dispatch_queue_create("Proxy queue", DISPATCH_QUEUE_SERIAL);
+		
+        _destinationObjects = [NSMutableSet set];
 		_destinationsSignal = [RACSubject subject];
     }
     return self;
 
 #if TARGET_OS_IPHONE
 - (void) setViewController:(UIViewController *)viewController {
-	for (id obj in self.destinationObjects) {
-		if ([obj respondsToSelector:@selector(setViewController:)]) {
-			[obj setViewController:viewController];
+	dispatch_async(self.serialQueue, ^{
+		for (id obj in self.destinationObjects) {
+			if ([obj respondsToSelector:@selector(setViewController:)]) {
+				dispatch_async(dispatch_get_main_queue(), ^{
+					[obj setViewController:viewController];
+				});
+			}
 		}
-	}
+	});
 	
 	_viewController = viewController;
 }
 	}
 	
 	#endif
+
+	__block NSSet *destinationsCopy = nil;
+	dispatch_sync(self.serialQueue, ^{
+		[self.destinationObjects addObject:object];
+		destinationsCopy = [self.destinationObjects copy];
+	});
 	
-	if ([self.destinationObjects containsObject:object]) return;
-	
-	[self.destinationObjects addObject:object];
-	
-	[(RACSubject *)self.destinationsSignal sendNext:_destinationObjects];
+	[(RACSubject *)self.destinationsSignal sendNext:destinationsCopy];
 }
 
 - (void) removeDestinationObject:(id) object {
-	if (object) [self.destinationObjects removeObject:object];
+
+	__block NSSet *destinationsCopy = nil;
+	dispatch_sync(self.serialQueue, ^{
+		if (object) [self.destinationObjects removeObject:object];
+		destinationsCopy = [self.destinationObjects copy];
+	});
 	
-	[(RACSubject *)self.destinationsSignal sendNext:_destinationObjects];
+	[(RACSubject *)self.destinationsSignal sendNext:destinationsCopy];
 }
 
 - (void) removeAllDestinationObjects {
-	[self.destinationObjects removeAllObjects];
+	dispatch_sync(self.serialQueue, ^{
+		[self.destinationObjects removeAllObjects];
+	});
 
-	[(RACSubject *)self.destinationsSignal sendNext:_destinationObjects];
+	[(RACSubject *)self.destinationsSignal sendNext:[NSSet set]];
 }
 
 - (BOOL)respondsToSelector:(SEL)aSelector {
-	for (id obj in self.destinationObjects) {
-		if ([obj respondsToSelector:aSelector]) return YES;
-	}
+	__block BOOL result = NO;
+	dispatch_sync(self.serialQueue, ^{
+		for (id obj in self.destinationObjects) {
+			if ([obj respondsToSelector:aSelector]) {
+				result = YES;
+				return;
+			}
+		}
+	});
 	
-    return NO;
+    return result;
 }
 
 - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
-	NSMethodSignature *sig = nil;
-	for(id obj in self.destinationObjects) {
-		sig = [obj methodSignatureForSelector:sel];
-		if (sig) break;
-	}
+	__block NSMethodSignature *sig = nil;
+
+	dispatch_sync(self.serialQueue, ^{
+		for(id obj in self.destinationObjects) {
+			sig = [obj methodSignatureForSelector:sel];
+			if (sig) return;
+		}
+	});
 
 	return sig;
 }
 
 - (void)forwardInvocation:(NSInvocation *)inv {
-	for(id obj in self.destinationObjects) {
+	__block NSArray *objects = nil;
+	dispatch_sync(self.serialQueue, ^{
+		objects = self.destinationObjects.allObjects;
+	});
+	
+	for(id obj in objects) {
 		if ([obj respondsToSelector:inv.selector]) {
-			[inv invokeWithTarget:obj];	
+			[inv invokeWithTarget:obj];
 		}
 	}		
 }

File bmf/shared/base/BMFBase.h

 
 @property (nonatomic, strong) NSBundle *bundle;
 
+/// This is nil by default. You can define your own themes and use this property to store the current one
+@property (nonatomic, strong) id theme;
+
 #if TARGET_OS_IPHONE
 @property (nonatomic, strong) BMFKeyboardManager *keyboardManager;
 @property (nonatomic, weak) UIStoryboard *storyboard;

File bmf/shared/base/BMFRegistrableProtocol.h

+//
+//  BMFRegistrableProtocol.h
+//  
+//
+//  Created by Jose Manuel Sánchez Peñarroja on 18/06/14.
+//
+//
+
+#import <Foundation/Foundation.h>
+
+/// This protocol defines a registrable class, where subclasses can be registered and listed. This is used for view configurators and themes, for example
+@protocol BMFRegistrableProtocol <NSObject>
+
++ (NSArray *) registeredClasses;
+
++ (void) register;
+
+@optional
+
++ (void) unregister;
+
+@end

File bmf/shared/base/BMFTypes.h

 typedef void(^BMFBlock)();
 typedef void(^BMFCompletionBlock)(id result,NSError *error);
 typedef void(^BMFActionBlock)(id sender);
+typedef void(^BMFItemActionBlock)(id item,NSIndexPath *indexPath);
 typedef void(^BMFOperationBlock)(id sender,BMFCompletionBlock completionBlock);
 typedef NSComparisonResult(^BMFComparisonBlock)(id obj1,id obj2);
 

File bmf/shared/themes/BMFTheme.h

+//
+//  BMFThemeBase.h
+//  Pods
+//
+//  Created by Jose Manuel Sánchez Peñarroja on 18/06/14.
+//
+//
+
+#import <Foundation/Foundation.h>
+
+#import "BMFThemeProtocol.h"
+
+@interface BMFTheme : NSObject <BMFThemeProtocol>
+
++ (NSString *) name;
+
++ (void) setupInitialAppearance;
++ (void) setupView:(id) view;
+
++ (Class<BMFThemeProtocol>) currentTheme;
++ (void) setCurrentTheme:(Class<BMFThemeProtocol>) theme;
+
+
+@end

File bmf/shared/themes/BMFTheme.m

+//
+//  BMFThemeBase.m
+//  Pods
+//
+//  Created by Jose Manuel Sánchez Peñarroja on 18/06/14.
+//
+//
+
+#import "BMFTheme.h"
+
+#import "BMF.h"
+
+static NSMutableArray *registeredList = nil;
+static Class<BMFThemeProtocol> currentThemeInstance = nil;
+
+@implementation BMFTheme
+
++ (void) load {
+	if (!registeredList) {
+		registeredList = [NSMutableArray array];
+	}
+}
+
++ (NSString *) name {
+	BMFAbstractMethod();
+}
+
++ (void) setupInitialAppearance {
+	BMFAbstractMethod();
+}
+
++ (void) setupView:(id) view {
+	BMFAbstractMethod();
+}
+
++ (Class<BMFThemeProtocol>) currentTheme {
+	return currentThemeInstance;
+}
+
++ (void) setCurrentTheme:(Class<BMFThemeProtocol>) theme {
+	currentThemeInstance = theme;
+}
+
+#pragma mark BMFThemeProtocol
+
++ (NSArray *) registeredClasses {
+	return [NSArray arrayWithArray:registeredList];
+}
+
++ (void) register {
+	BMFAssertReturn([self conformsToProtocol:@protocol(BMFThemeProtocol)]);
+	
+	[registeredList addObject:self];
+}
+
++ (void) unregister {
+	BMFAssertReturn([self conformsToProtocol:@protocol(BMFThemeProtocol)]);
+	
+	[registeredList removeObject:self];
+}
+
+
+@end

File bmf/shared/themes/BMFThemeProtocol.h

+//
+//  BMFThemeProtocol.h
+//  Pods
+//
+//  Created by Jose Manuel Sánchez Peñarroja on 18/06/14.
+//
+//
+
+#import <Foundation/Foundation.h>
+
+#import "BMFRegistrableProtocol.h"
+
+@protocol BMFThemeProtocol <BMFRegistrableProtocol>
+
++ (id<BMFThemeProtocol>) currentTheme;
++ (void) setCurrentTheme:(Class<BMFThemeProtocol>) theme;
+
++ (NSString *) name;
+
++ (void) setupInitialAppearance;
+
++ (void) setupView:(id) view;
+
+@end

File bmf/shared/values/BMFFixedValue.h

 
 @interface BMFFixedValue : BMFValue
 
+/// Can be nil
 @property (nonatomic, strong) id value;
 
+/// Allows value nil, so you can use new or init instead
 - (instancetype) initWithValue:(id) value;
 
 @end