Last active
March 9, 2017 10:01
-
-
Save byJeevan/11fe90cb0a27c2b94225ec3539bdfe7a to your computer and use it in GitHub Desktop.
Gist of my solution for question posted in Stackoverflow https://goo.gl/oKsZAB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//****************TJBinder.h**************// | |
// | |
// BindProxy.h | |
// HelloBind | |
// | |
// Created by Janos Tolgyesi on 29/03/14. | |
// Copyright (c) 2014 Neosperience SpA. All rights reserved. | |
// | |
#import "UIView+TJBinder.h" | |
#import <Foundation/Foundation.h> | |
@interface TJBinder : NSObject | |
-(instancetype)initWithView:(UIView*)view; | |
-(void)resetKeyPath; | |
@property (strong, nonatomic) id dataObject; | |
-(void)updateView; | |
-(void)updateModel; | |
@end | |
//****************TJBinder.m**************// | |
// BindProxy.m | |
// HelloBind | |
// | |
// Created by Janos Tolgyesi on 29/03/14. | |
// Copyright (c) 2014 Neosperience SpA. All rights reserved. | |
// | |
#import "TJBinder.h" | |
#import "UIView+TJBinder.h" | |
#import "TJBinderKeyPathParser.h" | |
#import "TJBinderLogger.h" | |
#pragma mark - BindEntry | |
@interface TJBindEntry : NSObject | |
@property (strong) NSString* dataObjectKeyPath; | |
@property (strong) NSString* viewKey; | |
@property (weak) UIView* view; | |
@property (assign) BOOL registered; | |
-(instancetype)initWithDataObjectKeyPath:(NSString*)dataObjectKeyPath viewKey:(NSString*)viewKey view:(UIView*)view; | |
@end | |
@implementation TJBindEntry | |
- (instancetype)initWithDataObjectKeyPath:(NSString *)dataObjectKeyPath viewKey:(NSString *)viewKey view:(UIView *)view | |
{ | |
self = [super init]; | |
if (self) { | |
self.dataObjectKeyPath = dataObjectKeyPath; | |
self.viewKey = viewKey; | |
self.view = view; | |
} | |
return self; | |
} | |
-(NSString *)description | |
{ | |
return [NSString stringWithFormat:@"%@ view: %@, dataObjectKeyPath: %@, viewKey: %@, registered: %@", | |
[super description], [self.view shortDescription], self.dataObjectKeyPath, self.viewKey, self.registered ? @"YES" : @"NO"]; | |
} | |
@end | |
#pragma mark - BindProxy | |
@interface TJBinder () | |
@property (weak) UIView* view; | |
@property (strong) NSMutableArray* bindings; | |
@property (strong) NSMutableArray* dataObjectKeyPath; | |
@end | |
@implementation TJBinder | |
- (instancetype)init | |
{ | |
self = [super init]; | |
if (self) { | |
self.bindings = [NSMutableArray array]; | |
self.dataObjectKeyPath = [NSMutableArray array]; | |
} | |
return self; | |
} | |
- (instancetype)initWithView:(UIView *)view | |
{ | |
self = [self init]; | |
if (self) { | |
self.view = view; | |
} | |
return self; | |
} | |
-(void)updateView | |
{ | |
for (TJBindEntry* entry in self.bindings) | |
{ | |
[self updateViewForBindingEntry:entry]; | |
} | |
} | |
-(void)updateModel{ | |
for (TJBindEntry* entry in self.bindings) | |
{ | |
[self updateBindingEntryForView:entry]; | |
} | |
} | |
-(void)setDataObject:(id)dataObject | |
{ | |
_dataObject = dataObject; | |
[self updateView]; | |
} | |
-(void)resetKeyPath | |
{ | |
[self.dataObjectKeyPath removeAllObjects]; | |
} | |
-(void)flushBindingPathForFinalDataObjectKey:(NSString*)finalDataObjectKey viewKey:(NSString*)viewKey | |
{ | |
UIView* dataObjectHolderView = self.view; | |
NSMutableArray* reconstructedDataObjectKeyPath = [NSMutableArray array]; | |
BOOL didPassDataObjectKey = NO; | |
for (NSString* pathComponent in self.dataObjectKeyPath) | |
{ | |
if ([pathComponent isEqualToString:@"dataObject"]) | |
{ | |
didPassDataObjectKey = YES; | |
continue; | |
} | |
if (!didPassDataObjectKey) | |
{ | |
dataObjectHolderView = [self viewForExtendedKeyPathComponent:pathComponent forView:dataObjectHolderView]; | |
NSAssert([dataObjectHolderView isKindOfClass:[UIView class]], | |
@"Found a non-UIView object %@ proceeding the key path: %@", dataObjectHolderView, self.dataObjectKeyPath); | |
} | |
else | |
{ | |
[reconstructedDataObjectKeyPath addObject:pathComponent]; | |
} | |
} | |
[reconstructedDataObjectKeyPath addObject:finalDataObjectKey]; | |
NSString* dataObjectKeyPathString = [reconstructedDataObjectKeyPath componentsJoinedByString:@"."]; | |
[self.dataObjectKeyPath removeAllObjects]; | |
TJBindEntry* entry = [[TJBindEntry alloc] initWithDataObjectKeyPath:dataObjectKeyPathString viewKey:viewKey view:self.view]; | |
[dataObjectHolderView.bindTo.bindings addObject:entry]; | |
[dataObjectHolderView.bindTo updateViewForBindingEntry:entry]; | |
} | |
+ (NSArray*)parsers | |
{ | |
static dispatch_once_t onceToken; | |
static NSArray* parsers; | |
dispatch_once(&onceToken, ^{ | |
parsers = @[ | |
[TJBinderKeyPathParser parserWithFunction:@"@superview" withTransformerBlock:^UIView *(UIView *view, NSString *originalKeyPathComponent) | |
{ | |
NSAssert(view.superview, @"%@ has no superview", view); | |
return view.superview; | |
}], | |
[TJBinderKeyPathParser parserWithFunction:@"@rootview" withTransformerBlock:^UIView *(UIView *view, NSString *originalKeyPathComponent) | |
{ | |
UIView* currentView = view; | |
while (currentView.superview) currentView = currentView.superview; | |
return currentView; | |
}], | |
[TJBinderKeyPathParser intParserWithFunction:@"@superviewWithTag" withTransformerBlock:^UIView *(UIView *view, NSInteger argument) | |
{ | |
UIView* currentView = view.superview; | |
while (currentView && (currentView.tag != argument)) currentView = currentView.superview; | |
NSAssert(currentView, @"No superview found with tag: %@", @(argument)); | |
return currentView; | |
}], | |
[TJBinderKeyPathParser stringParserWithFunction:@"@superviewWithRestorationID" withTransformerBlock:^UIView *(UIView *view, NSString *argument) | |
{ | |
UIView* currentView = view.superview; | |
while (currentView && (![currentView.restorationIdentifier isEqualToString:argument])) currentView = view.superview; | |
NSAssert(currentView, @"No superview found with restorationID: %@", argument); | |
return currentView; | |
}], | |
[TJBinderKeyPathParser stringParserWithFunction:@"@superviewWithClass" withTransformerBlock:^UIView *(UIView *view, NSString *argument) | |
{ | |
UIView* currentView = view.superview; | |
Class superviewClass = NSClassFromString(argument); | |
NSAssert(superviewClass, @"Invalid class: %@", argument); | |
while (currentView && (![currentView isKindOfClass:superviewClass])) currentView = currentView.superview; | |
NSAssert(currentView, @"No superview found with class: '%@'", argument); | |
return currentView; | |
}], | |
[TJBinderKeyPathParser intParserWithFunction:@"@subviews" | |
withTransformerBlock:^UIView *(UIView *view, NSInteger argument) | |
{ | |
return view.subviews[argument]; | |
}], | |
[TJBinderKeyPathParser intParserWithFunction:@"@subviewWithTag" | |
withTransformerBlock:^UIView *(UIView *view, NSInteger argument) | |
{ | |
NSPredicate* predicate = [NSPredicate predicateWithFormat:@"tag == %@", @(argument)]; | |
NSUInteger matchingViewIndex = [view.subviews indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { | |
return [predicate evaluateWithObject:obj]; | |
}]; | |
NSAssert(matchingViewIndex != NSNotFound, @"No subview found with tag: %@", @(argument)); | |
return view.subviews[matchingViewIndex]; | |
}], | |
[TJBinderKeyPathParser stringParserWithFunction:@"@subviewWithRestorationID" | |
withTransformerBlock:^UIView *(UIView *view, NSString *argument) | |
{ | |
NSPredicate* predicate = [NSPredicate predicateWithFormat:@"restorationIdentifier == %@", argument]; | |
NSUInteger matchingViewIndex = [view.subviews indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { | |
return [predicate evaluateWithObject:obj]; | |
}]; | |
NSAssert(matchingViewIndex != NSNotFound, @"No subview found with restorationID: '%@'", argument); | |
return view.subviews[matchingViewIndex]; | |
}], | |
[TJBinderKeyPathParser stringParserWithFunction:@"@subviewWithClass" withTransformerBlock:^UIView *(UIView *view, NSString *argument) | |
{ | |
Class superviewClass = NSClassFromString(argument); | |
NSUInteger matchingViewIndex = [view.subviews indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { | |
return [obj isKindOfClass:superviewClass]; | |
}]; | |
NSAssert(matchingViewIndex != NSNotFound, @"No subview found with class: '%@'", argument); | |
return view.subviews[matchingViewIndex]; | |
}], | |
[TJBinderKeyPathParser parserWithFunction:@"@cell" withTransformerBlock:^UIView *(UIView *view, NSString *originalKeyPathComponent) { | |
UIView* currentView = view.superview; | |
while (currentView && (![currentView isKindOfClass:[UITableViewCell class]] && ![currentView isKindOfClass:[UICollectionViewCell class]])) | |
currentView = currentView.superview; | |
NSAssert(currentView, @"No superview found with class UITableViewCell or UICollectionViewCell"); | |
return currentView; | |
}], | |
[TJBinderKeyPathParser parserWithFunction:@"*" withTransformerBlock:^UIView *(UIView *view, NSString *originalKeyPathComponent) | |
{ | |
return [view valueForKey:originalKeyPathComponent]; | |
}] | |
]; | |
}); | |
return parsers; | |
} | |
-(UIView*)viewForExtendedKeyPathComponent:(NSString*)pathComponent forView:(UIView*)view | |
{ | |
UIView* result = nil; | |
for (TJBinderKeyPathParser* parser in [[self class] parsers]) | |
{ | |
result = [parser tryToParseWithKeyPathComponent:pathComponent forView:view]; | |
if (result) break; | |
} | |
NSAssert(result, @"Unable to parse key path component: %@", pathComponent); | |
return result; | |
} | |
-(void)updateViewForBindingEntry:(TJBindEntry*)entry | |
{ | |
id extractedValue = [self.dataObject valueForKeyPath:entry.dataObjectKeyPath]; | |
[entry.view setValue:extractedValue forKey:entry.viewKey]; | |
} | |
-(void) updateBindingEntryForView:(TJBindEntry*)entry | |
{ | |
id extractedValue = [entry.view valueForKey:entry.viewKey]; | |
[self.dataObject setValue:extractedValue forKey:entry.dataObjectKeyPath]; | |
} | |
#pragma mark NSObject overrides | |
-(id)valueForUndefinedKey:(NSString *)key | |
{ | |
TJBinderLogVerbose(@"%s %@", __PRETTY_FUNCTION__, key); | |
[self.dataObjectKeyPath addObject:key]; | |
return self; | |
} | |
-(id)valueForKey:(NSString *)key | |
{ | |
if ([key isEqualToString:@"dataObject"]) | |
{ | |
[self.dataObjectKeyPath addObject:key]; | |
return self; | |
} | |
else | |
{ | |
return [super valueForKey:key]; | |
} | |
} | |
-(void)setValue:(id)value forUndefinedKey:(NSString *)key | |
{ | |
TJBinderLogVerbose(@"%s { %@ : %@ } \n%@", __PRETTY_FUNCTION__, key, value, self.dataObjectKeyPath); | |
[self flushBindingPathForFinalDataObjectKey:key viewKey:value]; | |
} | |
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context | |
{ | |
TJBindEntry* entry = (__bridge TJBindEntry*)context; | |
[self updateViewForBindingEntry:entry]; | |
} | |
-(NSString *)description | |
{ | |
return [NSString stringWithFormat:@"%@, view: %@, dataObject: %@, bindings: %@", | |
[super description], [self.view shortDescription], self.dataObject, self.bindings]; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Referred code from : https://github.com/Neosperience/TJBinder