diff --git a/README.md b/README.md index 203de7a..837adea 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Get your `UITableView` up and running in several lines of code: // Create the manager and assign a UITableView // - _manager = [[RETableViewManager alloc] initWithTableView:self.tableView]; + self.manager = [[RETableViewManager alloc] initWithTableView:self.tableView]; // Add a section // @@ -43,7 +43,7 @@ Get your `UITableView` up and running in several lines of code: // Custom items / cells // - _manager[@"CustomItem"] = @"CustomCell"; + self.manager[@"CustomItem"] = @"CustomCell"; [section addItem:[CustomItem item]]; } @@ -99,7 +99,7 @@ Edit your Podfile and add RETableViewManager: ``` bash platform :ios, '6.0' -pod 'RETableViewManager', '~> 1.1.2' +pod 'RETableViewManager', '~> 1.2.5' ``` Install into your Xcode project: @@ -220,35 +220,35 @@ Section without a title: ``` objective-c RETableViewSection *section = [RETableViewSection section]; -[_tableViewManager addSection:section]; +[self.manager addSection:section]; ``` Section with a title: ``` objective-c RETableViewSection *section = [RETableViewSection sectionWithHeaderTitle:@"Header"]; -[_tableViewManager addSection:section]; +[self.manager addSection:section]; ``` Section with a title and a footer: ``` objective-c RETableViewSection *section = [RETableViewSection sectionWithHeaderTitle:@"Header" footerTitle:@"Footer"]; -[_tableViewManager addSection:section]; +[self.manager addSection:section]; ``` Section with a custom header view: ``` objective-c RETableViewSection *section = [RETableViewSection sectionWithHeaderView:myCustomSectionHeaderView]; -[_tableViewManager addSection:section]; +[self.manager addSection:section]; ``` Section with a custom header and footer view: ``` objective-c RETableViewSection *section = [RETableViewSection sectionWithHeaderView:myCustomSectionHeaderView footerView:myCustomSectionFooterView]; -[_tableViewManager addSection:section]; +[self.manager addSection:section]; ``` ### Text (UITextField) and Number (REFormattedNumberField) Item Example @@ -256,35 +256,35 @@ RETableViewSection *section = [RETableViewSection sectionWithHeaderView:myCustom ``` objective-c // Create the manager // -_tableViewManager = [[RETableViewManager alloc] initWithTableView:self.tableView]; +self.manager = [[RETableViewManager alloc] initWithTableView:self.tableView]; // Add a section // RETableViewSection *section = [RETableViewSection sectionWithHeaderTitle:@"Test"]; -[_tableViewManager addSection:section]; +[self.manager addSection:section]; // Add items to the section // -_textItem = [RETextItem itemWithTitle:@"Enter text" value:@""]; -[section addItem:_textItem]; +self.textItem = [RETextItem itemWithTitle:@"Enter text" value:@""]; +[section addItem:self.textItem]; -_numberItem = [RENumberItem itemWithTitle:@"Enter text" value:@"" placeholder:@"(123) 456-7890" format:@"(XXX) XXX-XXXX"]; -[section addItem:_numberItem]; +self.numberItem = [RENumberItem itemWithTitle:@"Enter text" value:@"" placeholder:@"(123) 456-7890" format:@"(XXX) XXX-XXXX"]; +[section addItem:self.numberItem]; ``` -You can read `_textItem.value` and `_numberItem.value` later whenever you need them. +You can read `self.textItem.value` and `self.numberItem.value` later whenever you need them. ### Bool Item (UISwitch) Example ``` objective-c // Create the manager // -_tableViewManager = [[RETableViewManager alloc] initWithTableView:self.tableView]; +self.manager = [[RETableViewManager alloc] initWithTableView:self.tableView]; // Add a section // RETableViewSection *section = [RETableViewSection sectionWithHeaderTitle:@"Test"]; -[_tableViewManager addSection:section]; +[self.manager addSection:section]; // Add a bool value cell (using UISwitch) // @@ -298,12 +298,12 @@ RETableViewSection *section = [RETableViewSection sectionWithHeaderTitle:@"Test" ``` objective-c // Create the manager // -_tableViewManager = [[RETableViewManager alloc] initWithTableView:self.tableView]; +self.manager = [[RETableViewManager alloc] initWithTableView:self.tableView]; // Add a section // RETableViewSection *section = [RETableViewSection sectionWithHeaderTitle:@"Test"]; -[_tableViewManager addSection:section]; +[self.manager addSection:section]; // Add radio cell (options) // @@ -333,12 +333,12 @@ RERadioItem *optionItem = [RERadioItem itemWithTitle:@"Radio" value:@"Option 4" ``` objective-c // Create the manager // -_tableViewManager = [[RETableViewManager alloc] initWithTableView:self.tableView]; +self.manager = [[RETableViewManager alloc] initWithTableView:self.tableView]; // Add a section // RETableViewSection *section = [RETableViewSection sectionWithHeaderTitle:@"Test"]; -[_tableViewManager addSection:section]; +[self.manager addSection:section]; // Add a float item // @@ -352,25 +352,44 @@ RETableViewSection *section = [RETableViewSection sectionWithHeaderTitle:@"Test" ``` objective-c // Create the manager // -_tableViewManager = [[RETableViewManager alloc] initWithTableView:self.tableView]; +self.manager = [[RETableViewManager alloc] initWithTableView:self.tableView]; // Add a section // RETableViewSection *section = [RETableViewSection sectionWithHeaderTitle:@"Test"]; -[_tableViewManager addSection:section]; +[self.manager addSection:section]; // Add a date item // [section addItem:[REDateTimeItem itemWithTitle:@"Date / Time" value:[NSDate date] placeholder:nil format:@"MM/dd/yyyy hh:mm a" datePickerMode:UIDatePickerModeDateAndTime]]; ``` +### Validations + +Validations are performed using [REValidation](https://github.com/romaonthego/REValidation) library. + +Example: + +```objective-c +self.textItem = [RETextItem itemWithTitle:@"Text" value:@"" placeholder:@"Text item"]; +self.textItem.validators = @[@"presence", @"length(3, 10)"]; + +self.emailItem = [RETextItem itemWithTitle:@"Email" value:@"" placeholder:@"Email item"]; +self.emailItem.name = @"Your email"; +self.emailItem.validators = @[@"presence", @"email"]; +``` + +Each item, each section and the manager have property `errors`. This property is always up to date with errors on each level. +For example, an `RETableViewItem` would only have its own validation errors, `RETableViewSection` would have all errors that occured in that section (one per item). +`RETableViewManager`'s property `errors` would reflect all errors. + ### Custom Cells `RETableViewManager` allows to map custom objects to custom cells. In order to map your custom object (an item) to a cell, simply write: ```objective-c -_manager[@"CustomItem"] = @"CustomCell"; +self.manager[@"CustomItem"] = @"CustomCell"; ``` If you take a look at [RETableViewManager Source Code](https://github.com/romaonthego/RETableViewManager/blob/master/RETableViewManager/RETableViewManager.m) you may @@ -445,16 +464,29 @@ Quick example: ```objective-c - (void)cellDidLoad { - _testLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 44)]; - [self.contentView addSubview:_testLabel]; + [super cellDidLoad]; + self.testLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 44)]; + [self.contentView addSubview:self.testLabel]; } - (void)cellWillAppear { - _testLabel.text = self.item.someVariable; + [super cellWillAppear]; + self.testLabel.text = self.item.someVariable; } ``` +### Interface Builder Support + +Interface builder cells are supported out of the box, no special set up needed. +Cells and items are being automatically registered like any other custom cell in `RETableViewManager`: + +```objective-c +self.manager[@"XIBTestItem"] = @"XIBTestCell"; +``` + +Here `XIBTestItem` would be your cell identifier and you should have the `XIBTestCell.xib` file in your bundle. That's it. + ### Styling It's super easy to customize different offsets and cell background images of diff --git a/RETableViewManager.podspec b/RETableViewManager.podspec index 30c8175..8fe9d54 100644 --- a/RETableViewManager.podspec +++ b/RETableViewManager.podspec @@ -1,11 +1,11 @@ Pod::Spec.new do |s| s.name = 'RETableViewManager' - s.version = '1.1.2' + s.version = '1.2.5' s.authors = { 'Roman Efimov' => 'romefimov@gmail.com' } s.homepage = 'https://github.com/romaonthego/RETableViewManager' s.summary = 'Powerful data driven content manager for UITableView.' s.source = { :git => 'https://github.com/romaonthego/RETableViewManager.git', - :tag => '1.1.2' } + :tag => '1.2.5' } s.license = { :type => "MIT", :file => "LICENSE" } s.platform = :ios, '6.0' @@ -17,4 +17,5 @@ Pod::Spec.new do |s| s.ios.deployment_target = '6.0' s.dependency 'REFormattedNumberField', '~> 1.0.4' + s.dependency 'REValidation', '~> 0.1.3' end diff --git a/RETableViewManager/Items/RERadioItem.h b/RETableViewManager/Items/RERadioItem.h index 73b7f06..61ac590 100644 --- a/RETableViewManager/Items/RERadioItem.h +++ b/RETableViewManager/Items/RERadioItem.h @@ -27,7 +27,7 @@ @interface RERadioItem : RETableViewItem -@property (strong, readwrite, nonatomic, getter = detailLabelText, setter = setDetailLabelText:) NSString *value; +@property (copy, readwrite, nonatomic) NSString *value; + (instancetype)itemWithTitle:(NSString *)title value:(NSString *)value selectionHandler:(void(^)(RERadioItem *item))selectionHandler; - (id)initWithTitle:(NSString *)title value:(NSString *)value selectionHandler:(void(^)(RERadioItem *item))selectionHandler; diff --git a/RETableViewManager/Items/RERadioItem.m b/RETableViewManager/Items/RERadioItem.m index 32b6919..0a2df8c 100644 --- a/RETableViewManager/Items/RERadioItem.m +++ b/RETableViewManager/Items/RERadioItem.m @@ -47,4 +47,10 @@ return self; } +- (void)setValue:(NSString *)value +{ + _value = value; + self.detailLabelText = value; +} + @end diff --git a/RETableViewManager/Items/RETextItem.m b/RETableViewManager/Items/RETextItem.m index 965b721..8a5f9f5 100644 --- a/RETableViewManager/Items/RETextItem.m +++ b/RETableViewManager/Items/RETextItem.m @@ -55,4 +55,12 @@ return self; } +#pragma mark - +#pragma mark Error validation + +- (NSArray *)errors +{ + return [REValidation validateObject:self.value name:self.name ? self.name : self.title validators:self.validators]; +} + @end diff --git a/RETableViewManager/REPlaceholderTextView.m b/RETableViewManager/REPlaceholderTextView.m index 97bcb98..dae1128 100644 --- a/RETableViewManager/REPlaceholderTextView.m +++ b/RETableViewManager/REPlaceholderTextView.m @@ -83,8 +83,12 @@ [self addSubview:_placeholderLabel]; } + _placeholderLabel.textAlignment = self.textAlignment; _placeholderLabel.text = self.placeholder; [_placeholderLabel sizeToFit]; + CGRect frame = _placeholderLabel.frame; + frame.size.width = self.bounds.size.width - self.frame.origin.x * 2; + _placeholderLabel.frame = frame; [self sendSubviewToBack:_placeholderLabel]; } diff --git a/RETableViewManager/RETableViewCell.h b/RETableViewManager/RETableViewCell.h old mode 100644 new mode 100755 index 736c82a..f2999c8 --- a/RETableViewManager/RETableViewCell.h +++ b/RETableViewManager/RETableViewCell.h @@ -96,4 +96,6 @@ typedef NS_ENUM(NSInteger, RETableViewCellType) { - (void)cellWillAppear; - (void)cellDidDisappear; +@property (assign, readonly, nonatomic) BOOL loaded; + @end diff --git a/RETableViewManager/RETableViewCell.m b/RETableViewManager/RETableViewCell.m old mode 100644 new mode 100755 index 9ad62a3..e7147c6 --- a/RETableViewManager/RETableViewCell.m +++ b/RETableViewManager/RETableViewCell.m @@ -26,6 +26,12 @@ #import "RETableViewCell.h" #import "RETableViewManager.h" +@interface RETableViewCell () + +@property (assign, readwrite, nonatomic) BOOL loaded; + +@end + @implementation RETableViewCell + (BOOL)canFocusWithItem:(RETableViewItem *)item @@ -46,7 +52,7 @@ #pragma mark - UI --(void)_addBackgroundImage +- (void)addBackgroundImage { self.tableViewManager.tableView.separatorStyle = UITableViewCellSeparatorStyleNone; self.backgroundView = [[UIView alloc] initWithFrame:self.contentView.bounds]; @@ -56,7 +62,7 @@ [self.backgroundView addSubview:_backgroundImageView]; } --(void)_addSelectedBackgroundImage +- (void)addSelectedBackgroundImage { self.selectedBackgroundView = [[UIView alloc] initWithFrame:self.contentView.bounds]; self.selectedBackgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; @@ -71,15 +77,16 @@ - (void)cellDidLoad { + self.loaded = YES; self.actionBar = [[REActionBar alloc] initWithDelegate:self]; self.selectionStyle = self.tableViewManager.style.defaultCellSelectionStyle; if ([self.tableViewManager.style hasCustomBackgroundImage]) { - [self _addBackgroundImage]; + [self addBackgroundImage]; } if ([self.tableViewManager.style hasCustomSelectedBackgroundImage]) { - [self _addSelectedBackgroundImage]; + [self addSelectedBackgroundImage]; } } @@ -132,15 +139,15 @@ if ([self.section.style hasCustomBackgroundImage]) { self.backgroundColor = [UIColor clearColor]; - if (_backgroundImageView==nil) { - [self _addBackgroundImage]; + if (!_backgroundImageView) { + [self addBackgroundImage]; } _backgroundImageView.image = [self.section.style backgroundImageForCellType:self.type]; } if ([self.section.style hasCustomSelectedBackgroundImage]) { - if (_selectedBackgroundImageView==nil) { - [self _addSelectedBackgroundImage]; + if (!_selectedBackgroundImageView) { + [self addSelectedBackgroundImage]; } _selectedBackgroundImageView.image = [self.section.style selectedBackgroundImageForCellType:self.type]; } diff --git a/RETableViewManager/RETableViewItem.h b/RETableViewManager/RETableViewItem.h index 869e343..5d391ab 100644 --- a/RETableViewManager/RETableViewItem.h +++ b/RETableViewManager/RETableViewItem.h @@ -25,6 +25,7 @@ #import #import "RETableViewCellStyle.h" +#import "REValidation.h" @class RETableViewSection; @@ -54,6 +55,12 @@ @property (assign, readwrite, nonatomic) CGFloat cellHeight; @property (copy, readwrite, nonatomic) NSString *cellIdentifier; +// Error validation +// +@property (copy, readwrite, nonatomic) NSString *name; +@property (strong, readwrite, nonatomic) NSArray *validators; +@property (strong, readonly, nonatomic) NSArray *errors; + + (instancetype)item; + (instancetype)itemWithTitle:(NSString *)title; + (instancetype)itemWithTitle:(NSString *)title accessoryType:(UITableViewCellAccessoryType)accessoryType selectionHandler:(void(^)(RETableViewItem *item))selectionHandler; diff --git a/RETableViewManager/RETableViewManager.h b/RETableViewManager/RETableViewManager.h index e1ff3ef..9365078 100644 --- a/RETableViewManager/RETableViewManager.h +++ b/RETableViewManager/RETableViewManager.h @@ -44,6 +44,7 @@ #import "REFloatItem.h" #import "REDateTimeItem.h" #import "REMultipleChoiceItem.h" +#import "REValidation.h" @protocol RETableViewManagerDelegate; @@ -69,7 +70,7 @@ BOOL REDeviceIsUIKit7(); /** The array of sections. See RETableViewSection reference for details. */ -@property (strong, readwrite, nonatomic) NSMutableArray *sections; +@property (strong, readonly, nonatomic) NSArray *sections; /** The `UITableView` that needs to be managed using this `RETableViewManager`. @@ -334,6 +335,12 @@ BOOL REDeviceIsUIKit7(); */ - (void)sortSectionsUsingSelector:(SEL)comparator; +///----------------------------- +/// @name Checking for Validation Errors +///----------------------------- + +@property (strong, readonly, nonatomic) NSArray *errors; + @end diff --git a/RETableViewManager/RETableViewManager.m b/RETableViewManager/RETableViewManager.m old mode 100644 new mode 100755 index 7096c19..d77cc55 --- a/RETableViewManager/RETableViewManager.m +++ b/RETableViewManager/RETableViewManager.m @@ -43,8 +43,24 @@ BOOL REDeviceIsUIKit7() { return NO; } +@interface RETableViewManager () + +/** + The array of pairs of items / cell classes. + */ +@property (strong, readwrite, nonatomic) NSMutableDictionary *registeredXIBs; +@property (strong, readwrite, nonatomic) NSMutableArray *mutableSections; + +@end + @implementation RETableViewManager ++ (void)initialize +{ + [REValidation registerDefaultValidators]; + [REValidation registerDefaultErrorMessages]; +} + - (id)init { @throw [NSException exceptionWithName:NSGenericException reason:@"init not supported, use initWithTableView: instead." userInfo:nil]; @@ -73,9 +89,10 @@ BOOL REDeviceIsUIKit7() { self.tableView = tableView; - _sections = [[NSMutableArray alloc] init]; - _registeredClasses = [[NSMutableDictionary alloc] init]; - _style = [[RETableViewCellStyle alloc] init]; + self.mutableSections = [[NSMutableArray alloc] init]; + self.registeredClasses = [[NSMutableDictionary alloc] init]; + self.registeredXIBs = [[NSMutableDictionary alloc] init]; + self.style = [[RETableViewCellStyle alloc] init]; [self registerDefaultClasses]; @@ -103,12 +120,19 @@ BOOL REDeviceIsUIKit7() { { NSAssert(NSClassFromString(objectClass), ([NSString stringWithFormat:@"Item class '%@' does not exist.", identifier])); NSAssert(NSClassFromString(identifier), ([NSString stringWithFormat:@"Cell class '%@' does not exist.", identifier])); - [_registeredClasses setObject:identifier forKey:objectClass]; + self.registeredClasses[objectClass] = identifier; + + // Perform check if a XIB exists with the same name as the cell class + // + if ([[NSBundle mainBundle] pathForResource:identifier ofType:@"nib"]) { + self.registeredXIBs[identifier] = objectClass; + [self.tableView registerNib:[UINib nibWithNibName:identifier bundle:nil] forCellReuseIdentifier:objectClass]; + } } - (id)objectAtKeyedSubscript:(id )key { - return [_registeredClasses objectForKey:key]; + return [self.registeredClasses objectForKey:key]; } - (void)setObject:(id)obj forKeyedSubscript:(id )key @@ -118,61 +142,83 @@ BOOL REDeviceIsUIKit7() { - (Class)classForCellAtIndexPath:(NSIndexPath *)indexPath { - RETableViewSection *section = [_sections objectAtIndex:indexPath.section]; + RETableViewSection *section = [self.mutableSections objectAtIndex:indexPath.section]; NSObject *item = [section.items objectAtIndex:indexPath.row]; Class cellClass; - for (NSString *className in _registeredClasses) { + for (NSString *className in self.registeredClasses) { NSString *itemClass = NSStringFromClass([item class]); if ([itemClass isEqualToString:className]) { - cellClass = NSClassFromString([_registeredClasses objectForKey:className]); + cellClass = NSClassFromString([self.registeredClasses objectForKey:className]); break; } } return cellClass; } +- (NSArray *)sections +{ + return self.mutableSections; +} + #pragma mark - #pragma mark Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { - return _sections.count; + return self.mutableSections.count; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)sectionIndex { - return ((RETableViewSection *)[_sections objectAtIndex:sectionIndex]).items.count; + return ((RETableViewSection *)[self.mutableSections objectAtIndex:sectionIndex]).items.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - RETableViewSection *section = [_sections objectAtIndex:indexPath.section]; + RETableViewSection *section = [self.mutableSections objectAtIndex:indexPath.section]; RETableViewItem *item = [section.items objectAtIndex:indexPath.row]; UITableViewCellStyle cellStyle = UITableViewCellStyleDefault; if ([item isKindOfClass:[RETableViewItem class]]) cellStyle = ((RETableViewItem *)item).style; - NSString *cellIdentifier = [item respondsToSelector:@selector(cellIdentifier)] && item.cellIdentifier ? item.cellIdentifier : [NSString stringWithFormat:@"RETableViewManager_%@_%i", [item class], cellStyle]; - + NSString *cellIdentifier = [NSString stringWithFormat:@"RETableViewManager_%@_%i", [item class], cellStyle]; + Class cellClass = [self classForCellAtIndexPath:indexPath]; + if (self.registeredXIBs[NSStringFromClass(cellClass)]) { + cellIdentifier = self.registeredXIBs[NSStringFromClass(cellClass)]; + } + + if ([item respondsToSelector:@selector(cellIdentifier)] && item.cellIdentifier) { + cellIdentifier = item.cellIdentifier; + } + RETableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier]; - if (cell == nil) { - cell = [[cellClass alloc] initWithStyle:cellStyle reuseIdentifier:cellIdentifier]; + + void (^loadCell)(RETableViewCell *cell) = ^(RETableViewCell *cell) { cell.tableViewManager = self; // RETableViewManagerDelegate // - if ([_delegate conformsToProtocol:@protocol(RETableViewManagerDelegate)] && [_delegate respondsToSelector:@selector(tableView:willLoadCell:forRowAtIndexPath:)]) - [_delegate tableView:tableView willLoadCell:cell forRowAtIndexPath:indexPath]; + if ([self.delegate conformsToProtocol:@protocol(RETableViewManagerDelegate)] && [self.delegate respondsToSelector:@selector(tableView:willLoadCell:forRowAtIndexPath:)]) + [self.delegate tableView:tableView willLoadCell:cell forRowAtIndexPath:indexPath]; [cell cellDidLoad]; // RETableViewManagerDelegate // - if ([_delegate conformsToProtocol:@protocol(RETableViewManagerDelegate)] && [_delegate respondsToSelector:@selector(tableView:didLoadCell:forRowAtIndexPath:)]) - [_delegate tableView:tableView didLoadCell:cell forRowAtIndexPath:indexPath]; + if ([self.delegate conformsToProtocol:@protocol(RETableViewManagerDelegate)] && [self.delegate respondsToSelector:@selector(tableView:didLoadCell:forRowAtIndexPath:)]) + [self.delegate tableView:tableView didLoadCell:cell forRowAtIndexPath:indexPath]; + }; + + if (cell == nil) { + cell = [[cellClass alloc] initWithStyle:cellStyle reuseIdentifier:cellIdentifier]; + loadCell(cell); + } + + if ([cell isKindOfClass:[RETableViewCell class]] && [cell respondsToSelector:@selector(loaded)] && !cell.loaded) { + loadCell(cell); } cell.rowIndex = indexPath.row; @@ -193,14 +239,14 @@ BOOL REDeviceIsUIKit7() { - (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView { NSMutableArray *titles; - for (RETableViewSection *section in self.sections) { + for (RETableViewSection *section in self.mutableSections) { if (section.indexTitle) { titles = [NSMutableArray array]; break; } } if (titles) { - for (RETableViewSection *section in self.sections) { + for (RETableViewSection *section in self.mutableSections) { [titles addObject:section.indexTitle ? section.indexTitle : @""]; } } @@ -210,23 +256,23 @@ BOOL REDeviceIsUIKit7() { - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)sectionIndex { - RETableViewSection *section = [_sections objectAtIndex:sectionIndex]; + RETableViewSection *section = [self.mutableSections objectAtIndex:sectionIndex]; return section.headerTitle; } - (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)sectionIndex { - RETableViewSection *section = [_sections objectAtIndex:sectionIndex]; + RETableViewSection *section = [self.mutableSections objectAtIndex:sectionIndex]; return section.footerTitle; } - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath { - RETableViewSection *sourceSection = [_sections objectAtIndex:sourceIndexPath.section]; + RETableViewSection *sourceSection = [self.mutableSections objectAtIndex:sourceIndexPath.section]; RETableViewItem *item = [sourceSection.items objectAtIndex:sourceIndexPath.row]; [sourceSection removeItemAtIndex:sourceIndexPath.row]; - RETableViewSection *destinationSection = [_sections objectAtIndex:destinationIndexPath.section]; + RETableViewSection *destinationSection = [self.mutableSections objectAtIndex:destinationIndexPath.section]; [destinationSection insertItem:item atIndex:destinationIndexPath.row]; if (item.moveCompletionHandler) @@ -235,14 +281,14 @@ BOOL REDeviceIsUIKit7() { - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath { - RETableViewSection *section = [_sections objectAtIndex:indexPath.section]; + RETableViewSection *section = [self.mutableSections objectAtIndex:indexPath.section]; RETableViewItem *item = [section.items objectAtIndex:indexPath.row]; return item.moveHandler != nil; } - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { - RETableViewSection *section = [_sections objectAtIndex:indexPath.section]; + RETableViewSection *section = [self.mutableSections objectAtIndex:indexPath.section]; RETableViewItem *item = [section.items objectAtIndex:indexPath.row]; if ([item isKindOfClass:[RETableViewItem class]]) { return item.editingStyle != UITableViewCellEditingStyleNone || item.moveHandler; @@ -254,23 +300,23 @@ BOOL REDeviceIsUIKit7() { - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { - RETableViewSection *section = [_sections objectAtIndex:indexPath.section]; + RETableViewSection *section = [self.mutableSections objectAtIndex:indexPath.section]; RETableViewItem *item = [section.items objectAtIndex:indexPath.row]; if (item.deletionHandlerWithCompletion) { item.deletionHandlerWithCompletion(item, ^{ - [section.items removeObjectAtIndex:indexPath.row]; + [section removeItemAtIndex:indexPath.row]; [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; }); } else { if (item.deletionHandler) item.deletionHandler(item); - [section.items removeObjectAtIndex:indexPath.row]; + [section removeItemAtIndex:indexPath.row]; [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; } } if (editingStyle == UITableViewCellEditingStyleInsert) { - RETableViewSection *section = [_sections objectAtIndex:indexPath.section]; + RETableViewSection *section = [self.mutableSections objectAtIndex:indexPath.section]; RETableViewItem *item = [section.items objectAtIndex:indexPath.row]; if (item.insertionHandler) item.insertionHandler(item); @@ -286,24 +332,24 @@ BOOL REDeviceIsUIKit7() { { // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:willDisplayCell:forRowAtIndexPath:)]) - [_delegate tableView:tableView willDisplayCell:cell forRowAtIndexPath:indexPath]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:willDisplayCell:forRowAtIndexPath:)]) + [self.delegate tableView:tableView willDisplayCell:cell forRowAtIndexPath:indexPath]; } - (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section { // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:willDisplayHeaderView:forSection:)]) - [_delegate tableView:tableView willDisplayHeaderView:view forSection:section]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:willDisplayHeaderView:forSection:)]) + [self.delegate tableView:tableView willDisplayHeaderView:view forSection:section]; } - (void)tableView:(UITableView *)tableView willDisplayFooterView:(UIView *)view forSection:(NSInteger)section { // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:willDisplayFooterView:forSection:)]) - [_delegate tableView:tableView willDisplayFooterView:view forSection:section]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:willDisplayFooterView:forSection:)]) + [self.delegate tableView:tableView willDisplayFooterView:view forSection:section]; } - (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath*)indexPath @@ -313,65 +359,65 @@ BOOL REDeviceIsUIKit7() { // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:didEndDisplayingCell:forRowAtIndexPath:)]) - [_delegate tableView:tableView didEndDisplayingCell:cell forRowAtIndexPath:indexPath]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:didEndDisplayingCell:forRowAtIndexPath:)]) + [self.delegate tableView:tableView didEndDisplayingCell:cell forRowAtIndexPath:indexPath]; } - (void)tableView:(UITableView *)tableView didEndDisplayingHeaderView:(UIView *)view forSection:(NSInteger)section { // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:didEndDisplayingHeaderView:forSection:)]) - [_delegate tableView:tableView didEndDisplayingHeaderView:view forSection:section]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:didEndDisplayingHeaderView:forSection:)]) + [self.delegate tableView:tableView didEndDisplayingHeaderView:view forSection:section]; } - (void)tableView:(UITableView *)tableView didEndDisplayingFooterView:(UIView *)view forSection:(NSInteger)section { // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:didEndDisplayingFooterView:forSection:)]) - [_delegate tableView:tableView didEndDisplayingFooterView:view forSection:section]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:didEndDisplayingFooterView:forSection:)]) + [self.delegate tableView:tableView didEndDisplayingFooterView:view forSection:section]; } // Variable height support - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { - RETableViewSection *section = [_sections objectAtIndex:indexPath.section]; + RETableViewSection *section = [self.mutableSections objectAtIndex:indexPath.section]; id item = [section.items objectAtIndex:indexPath.row]; // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:heightForRowAtIndexPath:)]) - return [_delegate tableView:tableView heightForRowAtIndexPath:indexPath]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:heightForRowAtIndexPath:)]) + return [self.delegate tableView:tableView heightForRowAtIndexPath:indexPath]; return [[self classForCellAtIndexPath:indexPath] heightWithItem:item tableViewManager:self]; } - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)sectionIndex { - RETableViewSection *section = [_sections objectAtIndex:sectionIndex]; + RETableViewSection *section = [self.mutableSections objectAtIndex:sectionIndex]; if (section.headerView) return section.headerView.frame.size.height; // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:heightForHeaderInSection:)]) - return [_delegate tableView:tableView heightForHeaderInSection:sectionIndex]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:heightForHeaderInSection:)]) + return [self.delegate tableView:tableView heightForHeaderInSection:sectionIndex]; return UITableViewAutomaticDimension; } - (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)sectionIndex { - RETableViewSection *section = [_sections objectAtIndex:sectionIndex]; + RETableViewSection *section = [self.mutableSections objectAtIndex:sectionIndex]; if (section.footerView) return section.footerView.frame.size.height; // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:heightForFooterInSection:)]) - return [_delegate tableView:tableView heightForFooterInSection:sectionIndex]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:heightForFooterInSection:)]) + return [self.delegate tableView:tableView heightForFooterInSection:sectionIndex]; return UITableViewAutomaticDimension; } @@ -380,24 +426,24 @@ BOOL REDeviceIsUIKit7() { - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)sectionIndex { - RETableViewSection *section = [_sections objectAtIndex:sectionIndex]; + RETableViewSection *section = [self.mutableSections objectAtIndex:sectionIndex]; // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:viewForHeaderInSection:)]) - return [_delegate tableView:tableView viewForHeaderInSection:sectionIndex]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:viewForHeaderInSection:)]) + return [self.delegate tableView:tableView viewForHeaderInSection:sectionIndex]; return section.headerView; } - (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)sectionIndex { - RETableViewSection *section = [_sections objectAtIndex:sectionIndex]; + RETableViewSection *section = [self.mutableSections objectAtIndex:sectionIndex]; // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:viewForFooterInSection:)]) - return [_delegate tableView:tableView viewForFooterInSection:sectionIndex]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:viewForFooterInSection:)]) + return [self.delegate tableView:tableView viewForFooterInSection:sectionIndex]; return section.footerView; } @@ -406,7 +452,7 @@ BOOL REDeviceIsUIKit7() { - (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath { - RETableViewSection *section = [_sections objectAtIndex:indexPath.section]; + RETableViewSection *section = [self.mutableSections objectAtIndex:indexPath.section]; id item = [section.items objectAtIndex:indexPath.row]; if ([item respondsToSelector:@selector(setAccessoryButtonTapHandler:)]) { RETableViewItem *actionItem = (RETableViewItem *)item; @@ -416,8 +462,8 @@ BOOL REDeviceIsUIKit7() { // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:accessoryButtonTappedForRowWithIndexPath:)]) - [_delegate tableView:tableView accessoryButtonTappedForRowWithIndexPath:indexPath]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:accessoryButtonTappedForRowWithIndexPath:)]) + [self.delegate tableView:tableView accessoryButtonTappedForRowWithIndexPath:indexPath]; } // Selection @@ -426,8 +472,8 @@ BOOL REDeviceIsUIKit7() { { // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:shouldHighlightRowAtIndexPath:)]) - return [_delegate tableView:tableView shouldHighlightRowAtIndexPath:indexPath]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:shouldHighlightRowAtIndexPath:)]) + return [self.delegate tableView:tableView shouldHighlightRowAtIndexPath:indexPath]; return YES; } @@ -436,24 +482,24 @@ BOOL REDeviceIsUIKit7() { { // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:didHighlightRowAtIndexPath:)]) - [_delegate tableView:tableView didHighlightRowAtIndexPath:indexPath]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:didHighlightRowAtIndexPath:)]) + [self.delegate tableView:tableView didHighlightRowAtIndexPath:indexPath]; } - (void)tableView:(UITableView *)tableView didUnhighlightRowAtIndexPath:(NSIndexPath *)indexPath { // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:didUnhighlightRowAtIndexPath:)]) - [_delegate tableView:tableView didUnhighlightRowAtIndexPath:indexPath]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:didUnhighlightRowAtIndexPath:)]) + [self.delegate tableView:tableView didUnhighlightRowAtIndexPath:indexPath]; } - (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath { // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:willSelectRowAtIndexPath:)]) - return [_delegate tableView:tableView willSelectRowAtIndexPath:indexPath]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:willSelectRowAtIndexPath:)]) + return [self.delegate tableView:tableView willSelectRowAtIndexPath:indexPath]; return indexPath; } @@ -462,15 +508,15 @@ BOOL REDeviceIsUIKit7() { { // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:willDeselectRowAtIndexPath:)]) - return [_delegate tableView:tableView willDeselectRowAtIndexPath:indexPath]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:willDeselectRowAtIndexPath:)]) + return [self.delegate tableView:tableView willDeselectRowAtIndexPath:indexPath]; return indexPath; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - RETableViewSection *section = [_sections objectAtIndex:indexPath.section]; + RETableViewSection *section = [self.mutableSections objectAtIndex:indexPath.section]; id item = [section.items objectAtIndex:indexPath.row]; if ([item respondsToSelector:@selector(setSelectionHandler:)]) { RETableViewItem *actionItem = (RETableViewItem *)item; @@ -480,29 +526,32 @@ BOOL REDeviceIsUIKit7() { // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:didSelectRowAtIndexPath:)]) - [_delegate tableView:tableView didSelectRowAtIndexPath:indexPath]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:didSelectRowAtIndexPath:)]) + [self.delegate tableView:tableView didSelectRowAtIndexPath:indexPath]; } - (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath { // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:didDeselectRowAtIndexPath:)]) - [_delegate tableView:tableView didDeselectRowAtIndexPath:indexPath]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:didDeselectRowAtIndexPath:)]) + [self.delegate tableView:tableView didDeselectRowAtIndexPath:indexPath]; } // Editing - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath { - RETableViewSection *section = [_sections objectAtIndex:indexPath.section]; + RETableViewSection *section = [self.mutableSections objectAtIndex:indexPath.section]; RETableViewItem *item = [section.items objectAtIndex:indexPath.row]; + if (![item isKindOfClass:[RETableViewItem class]]) + return UITableViewCellEditingStyleNone; + // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:editingStyleForRowAtIndexPath:)]) - return [_delegate tableView:tableView editingStyleForRowAtIndexPath:indexPath]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:editingStyleForRowAtIndexPath:)]) + return [self.delegate tableView:tableView editingStyleForRowAtIndexPath:indexPath]; return item.editingStyle; } @@ -511,8 +560,8 @@ BOOL REDeviceIsUIKit7() { { // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:titleForDeleteConfirmationButtonForRowAtIndexPath:)]) - return [_delegate tableView:tableView titleForDeleteConfirmationButtonForRowAtIndexPath:indexPath]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:titleForDeleteConfirmationButtonForRowAtIndexPath:)]) + return [self.delegate tableView:tableView titleForDeleteConfirmationButtonForRowAtIndexPath:indexPath]; return NSLocalizedString(@"Delete", @"Delete"); } @@ -521,8 +570,8 @@ BOOL REDeviceIsUIKit7() { { // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:shouldIndentWhileEditingRowAtIndexPath:)]) - return [_delegate tableView:tableView shouldIndentWhileEditingRowAtIndexPath:indexPath]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:shouldIndentWhileEditingRowAtIndexPath:)]) + return [self.delegate tableView:tableView shouldIndentWhileEditingRowAtIndexPath:indexPath]; return YES; } @@ -531,23 +580,23 @@ BOOL REDeviceIsUIKit7() { { // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:willBeginEditingRowAtIndexPath:)]) - [_delegate tableView:tableView willBeginEditingRowAtIndexPath:indexPath]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:willBeginEditingRowAtIndexPath:)]) + [self.delegate tableView:tableView willBeginEditingRowAtIndexPath:indexPath]; } - (void)tableView:(UITableView*)tableView didEndEditingRowAtIndexPath:(NSIndexPath *)indexPath { // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:didEndEditingRowAtIndexPath:)]) - [_delegate tableView:tableView didEndEditingRowAtIndexPath:indexPath]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:didEndEditingRowAtIndexPath:)]) + [self.delegate tableView:tableView didEndEditingRowAtIndexPath:indexPath]; } // Moving/reordering - (NSIndexPath *)tableView:(UITableView *)tableView targetIndexPathForMoveFromRowAtIndexPath:(NSIndexPath *)sourceIndexPath toProposedIndexPath:(NSIndexPath *)proposedDestinationIndexPath { - RETableViewSection *sourceSection = [_sections objectAtIndex:sourceIndexPath.section]; + RETableViewSection *sourceSection = [self.mutableSections objectAtIndex:sourceIndexPath.section]; RETableViewItem *item = [sourceSection.items objectAtIndex:sourceIndexPath.row]; if (item.moveHandler) { BOOL allowed = item.moveHandler(item, sourceIndexPath, proposedDestinationIndexPath); @@ -557,8 +606,8 @@ BOOL REDeviceIsUIKit7() { // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:targetIndexPathForMoveFromRowAtIndexPath:toProposedIndexPath:)]) - return [_delegate tableView:tableView targetIndexPathForMoveFromRowAtIndexPath:sourceIndexPath toProposedIndexPath:proposedDestinationIndexPath]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:targetIndexPathForMoveFromRowAtIndexPath:toProposedIndexPath:)]) + return [self.delegate tableView:tableView targetIndexPathForMoveFromRowAtIndexPath:sourceIndexPath toProposedIndexPath:proposedDestinationIndexPath]; return proposedDestinationIndexPath; } @@ -569,8 +618,8 @@ BOOL REDeviceIsUIKit7() { { // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:indentationLevelForRowAtIndexPath:)]) - return [_delegate tableView:tableView indentationLevelForRowAtIndexPath:indexPath]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:indentationLevelForRowAtIndexPath:)]) + return [self.delegate tableView:tableView indentationLevelForRowAtIndexPath:indexPath]; return 0; } @@ -579,7 +628,7 @@ BOOL REDeviceIsUIKit7() { - (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath { - RETableViewSection *section = [_sections objectAtIndex:indexPath.section]; + RETableViewSection *section = [self.mutableSections objectAtIndex:indexPath.section]; id anItem = [section.items objectAtIndex:indexPath.row]; if ([anItem respondsToSelector:@selector(setCopyHandler:)]) { RETableViewItem *item = anItem; @@ -589,15 +638,15 @@ BOOL REDeviceIsUIKit7() { // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:shouldShowMenuForRowAtIndexPath:)]) - return [_delegate tableView:tableView shouldShowMenuForRowAtIndexPath:indexPath]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:shouldShowMenuForRowAtIndexPath:)]) + return [self.delegate tableView:tableView shouldShowMenuForRowAtIndexPath:indexPath]; return NO; } - (BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { - RETableViewSection *section = [_sections objectAtIndex:indexPath.section]; + RETableViewSection *section = [self.mutableSections objectAtIndex:indexPath.section]; id anItem = [section.items objectAtIndex:indexPath.row]; if ([anItem respondsToSelector:@selector(setCopyHandler:)]) { RETableViewItem *item = anItem; @@ -613,15 +662,15 @@ BOOL REDeviceIsUIKit7() { // Forward to UITableViewDelegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:canPerformAction:forRowAtIndexPath:withSender:)]) - return [_delegate tableView:tableView canPerformAction:action forRowAtIndexPath:indexPath withSender:sender]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:canPerformAction:forRowAtIndexPath:withSender:)]) + return [self.delegate tableView:tableView canPerformAction:action forRowAtIndexPath:indexPath withSender:sender]; return NO; } - (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender { - RETableViewSection *section = [_sections objectAtIndex:indexPath.section]; + RETableViewSection *section = [self.mutableSections objectAtIndex:indexPath.section]; RETableViewItem *item = [section.items objectAtIndex:indexPath.row]; if (action == @selector(copy:)) { @@ -641,8 +690,8 @@ BOOL REDeviceIsUIKit7() { // Forward to UITableView delegate // - if ([_delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [_delegate respondsToSelector:@selector(tableView:performAction:forRowAtIndexPath:withSender:)]) - [_delegate tableView:tableView performAction:action forRowAtIndexPath:indexPath withSender:sender]; + if ([self.delegate conformsToProtocol:@protocol(UITableViewDelegate)] && [self.delegate respondsToSelector:@selector(tableView:performAction:forRowAtIndexPath:withSender:)]) + [self.delegate tableView:tableView performAction:action forRowAtIndexPath:indexPath withSender:sender]; } #pragma mark - @@ -652,72 +701,72 @@ BOOL REDeviceIsUIKit7() { { // Forward to UIScrollView delegate // - if ([_delegate conformsToProtocol:@protocol(UIScrollViewDelegate)] && [_delegate respondsToSelector:@selector(scrollViewDidScroll:)]) - [_delegate scrollViewDidScroll:self.tableView]; + if ([self.delegate conformsToProtocol:@protocol(UIScrollViewDelegate)] && [self.delegate respondsToSelector:@selector(scrollViewDidScroll:)]) + [self.delegate scrollViewDidScroll:self.tableView]; } - (void)scrollViewDidZoom:(UIScrollView *)scrollView { // Forward to UIScrollView delegate // - if ([_delegate conformsToProtocol:@protocol(UIScrollViewDelegate)] && [_delegate respondsToSelector:@selector(scrollViewDidZoom:)]) - [_delegate scrollViewDidZoom:self.tableView]; + if ([self.delegate conformsToProtocol:@protocol(UIScrollViewDelegate)] && [self.delegate respondsToSelector:@selector(scrollViewDidZoom:)]) + [self.delegate scrollViewDidZoom:self.tableView]; } - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { // Forward to UIScrollView delegate // - if ([_delegate conformsToProtocol:@protocol(UIScrollViewDelegate)] && [_delegate respondsToSelector:@selector(scrollViewWillBeginDragging:)]) - [_delegate scrollViewWillBeginDragging:self.tableView]; + if ([self.delegate conformsToProtocol:@protocol(UIScrollViewDelegate)] && [self.delegate respondsToSelector:@selector(scrollViewWillBeginDragging:)]) + [self.delegate scrollViewWillBeginDragging:self.tableView]; } - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset { // Forward to UIScrollView delegate // - if ([_delegate conformsToProtocol:@protocol(UIScrollViewDelegate)] && [_delegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]) - [_delegate scrollViewWillEndDragging:self.tableView withVelocity:velocity targetContentOffset:targetContentOffset]; + if ([self.delegate conformsToProtocol:@protocol(UIScrollViewDelegate)] && [self.delegate respondsToSelector:@selector(scrollViewWillEndDragging:withVelocity:targetContentOffset:)]) + [self.delegate scrollViewWillEndDragging:self.tableView withVelocity:velocity targetContentOffset:targetContentOffset]; } - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { // Forward to UIScrollView delegate // - if ([_delegate conformsToProtocol:@protocol(UIScrollViewDelegate)] && [_delegate respondsToSelector:@selector(scrollViewDidEndDragging:willDecelerate:)]) - [_delegate scrollViewDidEndDragging:self.tableView willDecelerate:decelerate]; + if ([self.delegate conformsToProtocol:@protocol(UIScrollViewDelegate)] && [self.delegate respondsToSelector:@selector(scrollViewDidEndDragging:willDecelerate:)]) + [self.delegate scrollViewDidEndDragging:self.tableView willDecelerate:decelerate]; } - (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView { // Forward to UIScrollView delegate // - if ([_delegate conformsToProtocol:@protocol(UIScrollViewDelegate)] && [_delegate respondsToSelector:@selector(scrollViewWillBeginDecelerating:)]) - [_delegate scrollViewWillBeginDecelerating:self.tableView]; + if ([self.delegate conformsToProtocol:@protocol(UIScrollViewDelegate)] && [self.delegate respondsToSelector:@selector(scrollViewWillBeginDecelerating:)]) + [self.delegate scrollViewWillBeginDecelerating:self.tableView]; } - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { // Forward to UIScrollView delegate // - if ([_delegate conformsToProtocol:@protocol(UIScrollViewDelegate)] && [_delegate respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) - [_delegate scrollViewDidEndDecelerating:self.tableView]; + if ([self.delegate conformsToProtocol:@protocol(UIScrollViewDelegate)] && [self.delegate respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) + [self.delegate scrollViewDidEndDecelerating:self.tableView]; } - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView { // Forward to UIScrollView delegate // - if ([_delegate conformsToProtocol:@protocol(UIScrollViewDelegate)] && [_delegate respondsToSelector:@selector(scrollViewDidEndScrollingAnimation:)]) - [_delegate scrollViewDidEndScrollingAnimation:self.tableView]; + if ([self.delegate conformsToProtocol:@protocol(UIScrollViewDelegate)] && [self.delegate respondsToSelector:@selector(scrollViewDidEndScrollingAnimation:)]) + [self.delegate scrollViewDidEndScrollingAnimation:self.tableView]; } - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView { // Forward to UIScrollView delegate // - if ([_delegate conformsToProtocol:@protocol(UIScrollViewDelegate)] && [_delegate respondsToSelector:@selector(viewForZoomingInScrollView:)]) - return [_delegate viewForZoomingInScrollView:self.tableView]; + if ([self.delegate conformsToProtocol:@protocol(UIScrollViewDelegate)] && [self.delegate respondsToSelector:@selector(viewForZoomingInScrollView:)]) + return [self.delegate viewForZoomingInScrollView:self.tableView]; return nil; } @@ -726,24 +775,24 @@ BOOL REDeviceIsUIKit7() { { // Forward to UIScrollView delegate // - if ([_delegate conformsToProtocol:@protocol(UIScrollViewDelegate)] && [_delegate respondsToSelector:@selector(scrollViewWillBeginZooming:withView:)]) - [_delegate scrollViewWillBeginZooming:self.tableView withView:view]; + if ([self.delegate conformsToProtocol:@protocol(UIScrollViewDelegate)] && [self.delegate respondsToSelector:@selector(scrollViewWillBeginZooming:withView:)]) + [self.delegate scrollViewWillBeginZooming:self.tableView withView:view]; } - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale { // Forward to UIScrollView delegate // - if ([_delegate conformsToProtocol:@protocol(UIScrollViewDelegate)] && [_delegate respondsToSelector:@selector(scrollViewDidEndZooming:withView:atScale:)]) - [_delegate scrollViewDidEndZooming:self.tableView withView:view atScale:scale]; + if ([self.delegate conformsToProtocol:@protocol(UIScrollViewDelegate)] && [self.delegate respondsToSelector:@selector(scrollViewDidEndZooming:withView:atScale:)]) + [self.delegate scrollViewDidEndZooming:self.tableView withView:view atScale:scale]; } - (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView { // Forward to UIScrollView delegate // - if ([_delegate conformsToProtocol:@protocol(UIScrollViewDelegate)] && [_delegate respondsToSelector:@selector(scrollViewShouldScrollToTop:)]) - return [_delegate scrollViewShouldScrollToTop:self.tableView]; + if ([self.delegate conformsToProtocol:@protocol(UIScrollViewDelegate)] && [self.delegate respondsToSelector:@selector(scrollViewShouldScrollToTop:)]) + return [self.delegate scrollViewShouldScrollToTop:self.tableView]; return YES; } @@ -751,8 +800,8 @@ BOOL REDeviceIsUIKit7() { { // Forward to UIScrollView delegate // - if ([_delegate conformsToProtocol:@protocol(UIScrollViewDelegate)] && [_delegate respondsToSelector:@selector(scrollViewDidScrollToTop:)]) - [_delegate scrollViewDidScrollToTop:self.tableView]; + if ([self.delegate conformsToProtocol:@protocol(UIScrollViewDelegate)] && [self.delegate respondsToSelector:@selector(scrollViewDidScrollToTop:)]) + [self.delegate scrollViewDidScrollToTop:self.tableView]; } #pragma mark - @@ -761,83 +810,83 @@ BOOL REDeviceIsUIKit7() { - (void)addSection:(RETableViewSection *)section { section.tableViewManager = self; - [_sections addObject:section]; + [self.mutableSections addObject:section]; } - (void)addSectionsFromArray:(NSArray *)array { for (RETableViewSection *section in array) section.tableViewManager = self; - [_sections addObjectsFromArray:array]; + [self.mutableSections addObjectsFromArray:array]; } - (void)insertSection:(RETableViewSection *)section atIndex:(NSUInteger)index { section.tableViewManager = self; - [_sections insertObject:section atIndex:index]; + [self.mutableSections insertObject:section atIndex:index]; } - (void)insertSections:(NSArray *)sections atIndexes:(NSIndexSet *)indexes { for (RETableViewSection *section in sections) section.tableViewManager = self; - [_sections insertObjects:sections atIndexes:indexes]; + [self.mutableSections insertObjects:sections atIndexes:indexes]; } - (void)removeSection:(RETableViewSection *)section { - [_sections removeObject:section]; + [self.mutableSections removeObject:section]; } - (void)removeAllSections { - [_sections removeAllObjects]; + [self.mutableSections removeAllObjects]; } - (void)removeSectionIdenticalTo:(RETableViewSection *)section inRange:(NSRange)range { - [_sections removeObjectIdenticalTo:section inRange:range]; + [self.mutableSections removeObjectIdenticalTo:section inRange:range]; } - (void)removeSectionIdenticalTo:(RETableViewSection *)section { - [_sections removeObjectIdenticalTo:section]; + [self.mutableSections removeObjectIdenticalTo:section]; } - (void)removeSectionsInArray:(NSArray *)otherArray { - [_sections removeObjectsInArray:otherArray]; + [self.mutableSections removeObjectsInArray:otherArray]; } - (void)removeSectionsInRange:(NSRange)range { - [_sections removeObjectsInRange:range]; + [self.mutableSections removeObjectsInRange:range]; } - (void)removeSection:(RETableViewSection *)section inRange:(NSRange)range { - [_sections removeObject:section inRange:range]; + [self.mutableSections removeObject:section inRange:range]; } - (void)removeLastSection { - [_sections removeLastObject]; + [self.mutableSections removeLastObject]; } - (void)removeSectionAtIndex:(NSUInteger)index { - [_sections removeObjectAtIndex:index]; + [self.mutableSections removeObjectAtIndex:index]; } - (void)removeSectionsAtIndexes:(NSIndexSet *)indexes { - [_sections removeObjectsAtIndexes:indexes]; + [self.mutableSections removeObjectsAtIndexes:indexes]; } - (void)replaceSectionAtIndex:(NSUInteger)index withSection:(RETableViewSection *)section { section.tableViewManager = self; - [_sections replaceObjectAtIndex:index withObject:section]; + [self.mutableSections replaceObjectAtIndex:index withObject:section]; } - (void)replaceSectionsWithSectionsFromArray:(NSArray *)otherArray @@ -850,34 +899,52 @@ BOOL REDeviceIsUIKit7() { { for (RETableViewSection *section in sections) section.tableViewManager = self; - [_sections replaceObjectsAtIndexes:indexes withObjects:sections]; + [self.mutableSections replaceObjectsAtIndexes:indexes withObjects:sections]; } - (void)replaceSectionsInRange:(NSRange)range withSectionsFromArray:(NSArray *)otherArray range:(NSRange)otherRange { for (RETableViewSection *section in otherArray) section.tableViewManager = self; - [_sections replaceObjectsInRange:range withObjectsFromArray:otherArray range:otherRange]; + [self.mutableSections replaceObjectsInRange:range withObjectsFromArray:otherArray range:otherRange]; } - (void)replaceSectionsInRange:(NSRange)range withSectionsFromArray:(NSArray *)otherArray { - [_sections replaceObjectsInRange:range withObjectsFromArray:otherArray]; + [self.mutableSections replaceObjectsInRange:range withObjectsFromArray:otherArray]; } - (void)exchangeSectionAtIndex:(NSUInteger)idx1 withSectionAtIndex:(NSUInteger)idx2 { - [_sections exchangeObjectAtIndex:idx1 withObjectAtIndex:idx2]; + [self.mutableSections exchangeObjectAtIndex:idx1 withObjectAtIndex:idx2]; } - (void)sortSectionsUsingFunction:(NSInteger (*)(id, id, void *))compare context:(void *)context { - [_sections sortUsingFunction:compare context:context]; + [self.mutableSections sortUsingFunction:compare context:context]; } - (void)sortSectionsUsingSelector:(SEL)comparator { - [_sections sortUsingSelector:comparator]; + [self.mutableSections sortUsingSelector:comparator]; +} + +#pragma mark - +#pragma mark Checking for errors + +- (NSArray *)errors +{ + NSMutableArray *errors; + for (RETableViewSection *section in self.mutableSections) { + NSArray *sectionErrors = section.errors; + if (sectionErrors) { + if (!errors) { + errors = [[NSMutableArray alloc] init]; + } + [errors addObjectsFromArray:sectionErrors]; + } + } + return errors; } @end diff --git a/RETableViewManager/RETableViewOptionsController.m b/RETableViewManager/RETableViewOptionsController.m index 279376b..d4c51d8 100644 --- a/RETableViewManager/RETableViewOptionsController.m +++ b/RETableViewManager/RETableViewOptionsController.m @@ -28,6 +28,9 @@ @interface RETableViewOptionsController () +@property (strong, readwrite, nonatomic) RETableViewManager *tableViewManager; +@property (strong, readwrite, nonatomic) RETableViewSection *mainSection; + @end @implementation RETableViewOptionsController @@ -51,12 +54,12 @@ { [super viewDidLoad]; - _tableViewManager = [[RETableViewManager alloc] initWithTableView:self.tableView delegate:self.delegate]; - _mainSection = [[RETableViewSection alloc] init]; - [_tableViewManager addSection:_mainSection]; + self.tableViewManager = [[RETableViewManager alloc] initWithTableView:self.tableView delegate:self.delegate]; + self.mainSection = [[RETableViewSection alloc] init]; + [self.tableViewManager addSection:self.mainSection]; if (self.style) - _tableViewManager.style = self.style; + self.tableViewManager.style = self.style; __typeof (&*self) __weak weakSelf = self; void (^refreshItems)(void) = ^{ @@ -84,9 +87,17 @@ } } } - [_mainSection addItem:[RETableViewItem itemWithTitle:title accessoryType:accessoryType selectionHandler:^(RETableViewItem *selectedItem) { + [self.mainSection addItem:[RETableViewItem itemWithTitle:title accessoryType:accessoryType selectionHandler:^(RETableViewItem *selectedItem) { UITableViewCell *cell = [weakSelf.tableView cellForRowAtIndexPath:selectedItem.indexPath]; if (!weakSelf.multipleChoice) { + for (NSIndexPath *indexPath in [weakSelf.tableView indexPathsForVisibleRows]) { + UITableViewCell *cell = [weakSelf.tableView cellForRowAtIndexPath:indexPath]; + cell.accessoryType = UITableViewCellAccessoryNone; + } + for (RETableViewItem *item in weakSelf.mainSection.items) { + item.accessoryType = UITableViewCellAccessoryNone; + } + selectedItem.accessoryType = UITableViewCellAccessoryCheckmark; cell.accessoryType = UITableViewCellAccessoryCheckmark; RERadioItem * __weak item = (RERadioItem *)weakSelf.item; item.value = selectedItem.title; diff --git a/RETableViewManager/RETableViewSection.h b/RETableViewManager/RETableViewSection.h index 8f8d452..5702df0 100644 --- a/RETableViewManager/RETableViewSection.h +++ b/RETableViewManager/RETableViewSection.h @@ -36,7 +36,7 @@ /** An array of section items (rows). */ -@property (strong, readwrite, nonatomic) NSMutableArray *items; +@property (strong, readonly, nonatomic) NSArray *items; /** The title of the header of the specified section of the table view. @@ -344,4 +344,10 @@ */ - (void)reloadSectionWithAnimation:(UITableViewRowAnimation)animation; +///----------------------------- +/// @name Checking for Validation Errors +///----------------------------- + +@property (strong, readonly, nonatomic) NSArray *errors; + @end \ No newline at end of file diff --git a/RETableViewManager/RETableViewSection.m b/RETableViewManager/RETableViewSection.m index 8fad822..a1ddc1c 100644 --- a/RETableViewManager/RETableViewSection.m +++ b/RETableViewManager/RETableViewSection.m @@ -26,6 +26,12 @@ #import "RETableViewSection.h" #import "RETableViewManager.h" +@interface RETableViewSection () + +@property (strong, readwrite, nonatomic) NSMutableArray *mutableItems; + +@end + @implementation RETableViewSection #pragma mark - @@ -62,7 +68,7 @@ if (!self) return nil; - _items = [[NSMutableArray alloc] init]; + self.mutableItems = [[NSMutableArray alloc] init]; return self; } @@ -121,7 +127,7 @@ - (CGFloat)maximumTitleWidthWithFont:(UIFont *)font { CGFloat width = 0; - for (RETableViewItem *item in self.items) { + for (RETableViewItem *item in self.mutableItems) { if ([item isMemberOfClass:[RETextItem class]] || [item isMemberOfClass:[REDateTimeItem class]] || [item isMemberOfClass:[RENumberItem class]]) { CGSize size = [item.title sizeWithFont:font]; width = MAX(width, size.width); @@ -133,12 +139,17 @@ #pragma mark - #pragma mark Managing items +- (NSArray *)items +{ + return self.mutableItems; +} + - (void)addItem:(id)item { if ([item isKindOfClass:[RETableViewItem class]]) ((RETableViewItem *)item).section = self; - [_items addObject:item]; + [self.mutableItems addObject:item]; } - (void)addItemsFromArray:(NSArray *)array @@ -147,7 +158,7 @@ if ([item isKindOfClass:[RETableViewItem class]]) ((RETableViewItem *)item).section = self; - [_items addObjectsFromArray:array]; + [self.mutableItems addObjectsFromArray:array]; } - (void)insertItem:(id)item atIndex:(NSUInteger)index @@ -155,7 +166,7 @@ if ([item isKindOfClass:[RETableViewItem class]]) ((RETableViewItem *)item).section = self; - [_items insertObject:item atIndex:index]; + [self.mutableItems insertObject:item atIndex:index]; } - (void)insertItems:(NSArray *)items atIndexes:(NSIndexSet *)indexes @@ -164,57 +175,57 @@ if ([item isKindOfClass:[RETableViewItem class]]) ((RETableViewItem *)item).section = self; - [_items insertObjects:items atIndexes:indexes]; + [self.mutableItems insertObjects:items atIndexes:indexes]; } - (void)removeItem:(id)item inRange:(NSRange)range { - [_items removeObject:item inRange:range]; + [self.mutableItems removeObject:item inRange:range]; } - (void)removeLastItem { - [_items removeLastObject]; + [self.mutableItems removeLastObject]; } - (void)removeItemAtIndex:(NSUInteger)index { - [_items removeObjectAtIndex:index]; + [self.mutableItems removeObjectAtIndex:index]; } - (void)removeItem:(id)item { - [_items removeObject:item]; + [self.mutableItems removeObject:item]; } - (void)removeAllItems { - [_items removeAllObjects]; + [self.mutableItems removeAllObjects]; } - (void)removeItemIdenticalTo:(id)item inRange:(NSRange)range { - [_items removeObjectIdenticalTo:item inRange:range]; + [self.mutableItems removeObjectIdenticalTo:item inRange:range]; } - (void)removeItemIdenticalTo:(id)item { - [_items removeObjectIdenticalTo:item]; + [self.mutableItems removeObjectIdenticalTo:item]; } - (void)removeItemsInArray:(NSArray *)otherArray { - [_items removeObjectsInArray:otherArray]; + [self.mutableItems removeObjectsInArray:otherArray]; } - (void)removeItemsInRange:(NSRange)range { - [_items removeObjectsInRange:range]; + [self.mutableItems removeObjectsInRange:range]; } - (void)removeItemsAtIndexes:(NSIndexSet *)indexes { - [_items removeObjectsAtIndexes:indexes]; + [self.mutableItems removeObjectsAtIndexes:indexes]; } - (void)replaceItemAtIndex:(NSUInteger)index withItem:(id)item @@ -222,7 +233,7 @@ if ([item isKindOfClass:[RETableViewItem class]]) ((RETableViewItem *)item).section = self; - [_items replaceObjectAtIndex:index withObject:item]; + [self.mutableItems replaceObjectAtIndex:index withObject:item]; } - (void)replaceItemsWithItemsFromArray:(NSArray *)otherArray @@ -237,7 +248,7 @@ if ([item isKindOfClass:[RETableViewItem class]]) ((RETableViewItem *)item).section = self; - [_items replaceObjectsInRange:range withObjectsFromArray:otherArray range:otherRange]; + [self.mutableItems replaceObjectsInRange:range withObjectsFromArray:otherArray range:otherRange]; } - (void)replaceItemsInRange:(NSRange)range withItemsFromArray:(NSArray *)otherArray @@ -246,7 +257,7 @@ if ([item isKindOfClass:[RETableViewItem class]]) ((RETableViewItem *)item).section = self; - [_items replaceObjectsInRange:range withObjectsFromArray:otherArray]; + [self.mutableItems replaceObjectsInRange:range withObjectsFromArray:otherArray]; } - (void)replaceItemsAtIndexes:(NSIndexSet *)indexes withItems:(NSArray *)items @@ -255,22 +266,22 @@ if ([item isKindOfClass:[RETableViewItem class]]) ((RETableViewItem *)item).section = self; - [_items replaceObjectsAtIndexes:indexes withObjects:items]; + [self.mutableItems replaceObjectsAtIndexes:indexes withObjects:items]; } - (void)exchangeItemAtIndex:(NSUInteger)idx1 withItemAtIndex:(NSUInteger)idx2 { - [_items exchangeObjectAtIndex:idx1 withObjectAtIndex:idx2]; + [self.mutableItems exchangeObjectAtIndex:idx1 withObjectAtIndex:idx2]; } - (void)sortItemsUsingFunction:(NSInteger (*)(id, id, void *))compare context:(void *)context { - [_items sortUsingFunction:compare context:context]; + [self.mutableItems sortUsingFunction:compare context:context]; } - (void)sortItemsUsingSelector:(SEL)comparator { - [_items sortUsingSelector:comparator]; + [self.mutableItems sortUsingSelector:comparator]; } #pragma mark - @@ -281,4 +292,25 @@ [self.tableViewManager.tableView reloadSections:[NSIndexSet indexSetWithIndex:self.index] withRowAnimation:animation]; } +#pragma mark - +#pragma mark Checking for errors + +- (NSArray *)errors +{ + NSMutableArray *errors; + for (RETableViewItem *item in self.mutableItems) { + if ([item respondsToSelector:@selector(errors)]) { + NSArray *itemErrors = item.errors; + if (itemErrors) { + if (!errors) { + errors = [[NSMutableArray alloc] init]; + } + if (itemErrors.count > 0) + [errors addObject:itemErrors[0]]; + } + } + } + return errors; +} + @end diff --git a/RETableViewManagerExample/RETableViewManagerExample.xcodeproj/project.pbxproj b/RETableViewManagerExample/RETableViewManagerExample.xcodeproj/project.pbxproj index bebcb30..ffcce23 100644 --- a/RETableViewManagerExample/RETableViewManagerExample.xcodeproj/project.pbxproj +++ b/RETableViewManagerExample/RETableViewManagerExample.xcodeproj/project.pbxproj @@ -39,6 +39,14 @@ 30939CF317949A10004D95A9 /* userpic5.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 30939CE717949A10004D95A9 /* userpic5.jpg */; }; 30939CF417949A10004D95A9 /* userpic6.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 30939CE817949A10004D95A9 /* userpic6.jpg */; }; 30939CF917949B2D004D95A9 /* REFormattedNumberField.m in Sources */ = {isa = PBXBuildFile; fileRef = 30939CF817949B2D004D95A9 /* REFormattedNumberField.m */; }; + 30B00D5E17C56EEF0010D439 /* NSError+REValidation.m in Sources */ = {isa = PBXBuildFile; fileRef = 30B00D5217C56EEF0010D439 /* NSError+REValidation.m */; }; + 30B00D5F17C56EEF0010D439 /* REValidation.m in Sources */ = {isa = PBXBuildFile; fileRef = 30B00D5417C56EEF0010D439 /* REValidation.m */; }; + 30B00D6017C56EEF0010D439 /* REValidator.m in Sources */ = {isa = PBXBuildFile; fileRef = 30B00D5617C56EEF0010D439 /* REValidator.m */; }; + 30B00D6117C56EEF0010D439 /* REEmailValidator.m in Sources */ = {isa = PBXBuildFile; fileRef = 30B00D5917C56EEF0010D439 /* REEmailValidator.m */; }; + 30B00D6217C56EEF0010D439 /* RELengthValidator.m in Sources */ = {isa = PBXBuildFile; fileRef = 30B00D5B17C56EEF0010D439 /* RELengthValidator.m */; }; + 30B00D6317C56EEF0010D439 /* REPresenceValidator.m in Sources */ = {isa = PBXBuildFile; fileRef = 30B00D5D17C56EEF0010D439 /* REPresenceValidator.m */; }; + 30B00D7A17C667A90010D439 /* ValidationsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 30B00D7917C667A90010D439 /* ValidationsViewController.m */; }; + 30B00DEE17D8E9CE0010D439 /* REURLValidator.m in Sources */ = {isa = PBXBuildFile; fileRef = 30B00DED17D8E90D0010D439 /* REURLValidator.m */; }; 30D1C7D617AC08F4001F731C /* First_Alt@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 30D1C7D217AC08F3001F731C /* First_Alt@2x.png */; }; 30D1C7D717AC08F4001F731C /* Last_Alt@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 30D1C7D317AC08F3001F731C /* Last_Alt@2x.png */; }; 30D1C7D817AC08F4001F731C /* Middle_Alt@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 30D1C7D417AC08F3001F731C /* Middle_Alt@2x.png */; }; @@ -135,6 +143,22 @@ 30939CE817949A10004D95A9 /* userpic6.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = userpic6.jpg; sourceTree = ""; }; 30939CF717949B2D004D95A9 /* REFormattedNumberField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = REFormattedNumberField.h; sourceTree = ""; }; 30939CF817949B2D004D95A9 /* REFormattedNumberField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = REFormattedNumberField.m; sourceTree = ""; }; + 30B00D5117C56EEF0010D439 /* NSError+REValidation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+REValidation.h"; sourceTree = ""; }; + 30B00D5217C56EEF0010D439 /* NSError+REValidation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+REValidation.m"; sourceTree = ""; }; + 30B00D5317C56EEF0010D439 /* REValidation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = REValidation.h; sourceTree = ""; }; + 30B00D5417C56EEF0010D439 /* REValidation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = REValidation.m; sourceTree = ""; }; + 30B00D5517C56EEF0010D439 /* REValidator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = REValidator.h; sourceTree = ""; }; + 30B00D5617C56EEF0010D439 /* REValidator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = REValidator.m; sourceTree = ""; }; + 30B00D5817C56EEF0010D439 /* REEmailValidator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = REEmailValidator.h; sourceTree = ""; }; + 30B00D5917C56EEF0010D439 /* REEmailValidator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = REEmailValidator.m; sourceTree = ""; }; + 30B00D5A17C56EEF0010D439 /* RELengthValidator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RELengthValidator.h; sourceTree = ""; }; + 30B00D5B17C56EEF0010D439 /* RELengthValidator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RELengthValidator.m; sourceTree = ""; }; + 30B00D5C17C56EEF0010D439 /* REPresenceValidator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = REPresenceValidator.h; sourceTree = ""; }; + 30B00D5D17C56EEF0010D439 /* REPresenceValidator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = REPresenceValidator.m; sourceTree = ""; }; + 30B00D7817C667A90010D439 /* ValidationsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ValidationsViewController.h; sourceTree = ""; }; + 30B00D7917C667A90010D439 /* ValidationsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ValidationsViewController.m; sourceTree = ""; }; + 30B00DEC17D8E90D0010D439 /* REURLValidator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = REURLValidator.h; sourceTree = ""; }; + 30B00DED17D8E90D0010D439 /* REURLValidator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = REURLValidator.m; sourceTree = ""; }; 30D1C7D217AC08F3001F731C /* First_Alt@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "First_Alt@2x.png"; path = "Resources/First_Alt@2x.png"; sourceTree = ""; }; 30D1C7D317AC08F3001F731C /* Last_Alt@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Last_Alt@2x.png"; path = "Resources/Last_Alt@2x.png"; sourceTree = ""; }; 30D1C7D417AC08F3001F731C /* Middle_Alt@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Middle_Alt@2x.png"; path = "Resources/Middle_Alt@2x.png"; sourceTree = ""; }; @@ -250,6 +274,8 @@ 303D967117B7CDE300A418F3 /* XIBTestViewController.m */, 30E5F50A17BBD163005CF1FE /* IndexedListViewController.h */, 30E5F50B17BBD163005CF1FE /* IndexedListViewController.m */, + 30B00D7817C667A90010D439 /* ValidationsViewController.h */, + 30B00D7917C667A90010D439 /* ValidationsViewController.m */, ); path = Controllers; sourceTree = ""; @@ -301,6 +327,7 @@ 30939CF517949B2D004D95A9 /* Vendor */ = { isa = PBXGroup; children = ( + 30B00D5017C56EEF0010D439 /* REValidation */, 30939CF617949B2D004D95A9 /* REFormattedNumberField */, ); path = Vendor; @@ -315,6 +342,35 @@ path = REFormattedNumberField; sourceTree = ""; }; + 30B00D5017C56EEF0010D439 /* REValidation */ = { + isa = PBXGroup; + children = ( + 30B00D5117C56EEF0010D439 /* NSError+REValidation.h */, + 30B00D5217C56EEF0010D439 /* NSError+REValidation.m */, + 30B00D5317C56EEF0010D439 /* REValidation.h */, + 30B00D5417C56EEF0010D439 /* REValidation.m */, + 30B00D5517C56EEF0010D439 /* REValidator.h */, + 30B00D5617C56EEF0010D439 /* REValidator.m */, + 30B00D5717C56EEF0010D439 /* Validators */, + ); + path = REValidation; + sourceTree = ""; + }; + 30B00D5717C56EEF0010D439 /* Validators */ = { + isa = PBXGroup; + children = ( + 30B00D5817C56EEF0010D439 /* REEmailValidator.h */, + 30B00D5917C56EEF0010D439 /* REEmailValidator.m */, + 30B00D5A17C56EEF0010D439 /* RELengthValidator.h */, + 30B00D5B17C56EEF0010D439 /* RELengthValidator.m */, + 30B00D5C17C56EEF0010D439 /* REPresenceValidator.h */, + 30B00D5D17C56EEF0010D439 /* REPresenceValidator.m */, + 30B00DEC17D8E90D0010D439 /* REURLValidator.h */, + 30B00DED17D8E90D0010D439 /* REURLValidator.m */, + ); + path = Validators; + sourceTree = ""; + }; 30DB064616E934AD006C9530 /* RETableViewManager */ = { isa = PBXGroup; children = ( @@ -593,6 +649,14 @@ 303D966B17B7CCF800A418F3 /* XIBTestCell.m in Sources */, 303D967217B7CDE300A418F3 /* XIBTestViewController.m in Sources */, 30E5F50C17BBD163005CF1FE /* IndexedListViewController.m in Sources */, + 30B00D5E17C56EEF0010D439 /* NSError+REValidation.m in Sources */, + 30B00D5F17C56EEF0010D439 /* REValidation.m in Sources */, + 30B00D6017C56EEF0010D439 /* REValidator.m in Sources */, + 30B00D6117C56EEF0010D439 /* REEmailValidator.m in Sources */, + 30B00DEE17D8E9CE0010D439 /* REURLValidator.m in Sources */, + 30B00D6217C56EEF0010D439 /* RELengthValidator.m in Sources */, + 30B00D6317C56EEF0010D439 /* REPresenceValidator.m in Sources */, + 30B00D7A17C667A90010D439 /* ValidationsViewController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/RETableViewManagerExample/RETableViewManagerExample/Classes/Controllers/IndexedListViewController.m b/RETableViewManagerExample/RETableViewManagerExample/Classes/Controllers/IndexedListViewController.m index 13fceac..5d57327 100644 --- a/RETableViewManagerExample/RETableViewManagerExample/Classes/Controllers/IndexedListViewController.m +++ b/RETableViewManagerExample/RETableViewManagerExample/Classes/Controllers/IndexedListViewController.m @@ -32,6 +32,8 @@ RETableViewSection *section = [RETableViewSection sectionWithHeaderTitle:sectionTitle]; section.indexTitle = sectionTitle; // assign index title + // Add 5 items with name `section title + item index` + // for (NSInteger i = 1; i <= 5; i++) [section addItem:[NSString stringWithFormat:@"%@%i", sectionTitle, i]]; diff --git a/RETableViewManagerExample/RETableViewManagerExample/Classes/Controllers/RootViewController.m b/RETableViewManagerExample/RETableViewManagerExample/Classes/Controllers/RootViewController.m index 01b03a2..290ed74 100644 --- a/RETableViewManagerExample/RETableViewManagerExample/Classes/Controllers/RootViewController.m +++ b/RETableViewManagerExample/RETableViewManagerExample/Classes/Controllers/RootViewController.m @@ -14,6 +14,7 @@ #import "StylingViewController.h" #import "XIBTestViewController.h" #import "IndexedListViewController.h" +#import "ValidationsViewController.h" @interface RootViewController () @@ -71,6 +72,11 @@ [item deselectRowAnimated:YES]; [weakSelf.navigationController pushViewController:[[IndexedListViewController alloc] initWithStyle:UITableViewStylePlain] animated:YES]; }]]; + + [section addItem:[RETableViewItem itemWithTitle:@"Validations" accessoryType:UITableViewCellAccessoryDisclosureIndicator selectionHandler:^(RETableViewItem *item) { + [item deselectRowAnimated:YES]; + [weakSelf.navigationController pushViewController:[[ValidationsViewController alloc] initWithStyle:UITableViewStyleGrouped] animated:YES]; + }]]; } @end diff --git a/RETableViewManagerExample/RETableViewManagerExample/Classes/Controllers/ValidationsViewController.h b/RETableViewManagerExample/RETableViewManagerExample/Classes/Controllers/ValidationsViewController.h new file mode 100644 index 0000000..0762806 --- /dev/null +++ b/RETableViewManagerExample/RETableViewManagerExample/Classes/Controllers/ValidationsViewController.h @@ -0,0 +1,16 @@ +// +// ValidationsViewController.h +// RETableViewManagerExample +// +// Created by Roman Efimov on 8/22/13. +// Copyright (c) 2013 Roman Efimov. All rights reserved. +// + +#import +#import "RETableViewManager.h" + +@interface ValidationsViewController : UITableViewController + +@property (strong, readonly, nonatomic) RETableViewManager *manager; + +@end diff --git a/RETableViewManagerExample/RETableViewManagerExample/Classes/Controllers/ValidationsViewController.m b/RETableViewManagerExample/RETableViewManagerExample/Classes/Controllers/ValidationsViewController.m new file mode 100644 index 0000000..d8b549b --- /dev/null +++ b/RETableViewManagerExample/RETableViewManagerExample/Classes/Controllers/ValidationsViewController.m @@ -0,0 +1,88 @@ +// +// ValidationsViewController.m +// RETableViewManagerExample +// +// Created by Roman Efimov on 8/22/13. +// Copyright (c) 2013 Roman Efimov. All rights reserved. +// + +#import "ValidationsViewController.h" + +@interface ValidationsViewController () + +@property (strong, readwrite, nonatomic) RETextItem *textItem; +@property (strong, readwrite, nonatomic) RETextItem *emailItem; +@property (strong, readwrite, nonatomic) RETextItem *urlItem; +@property (strong, readwrite, nonatomic) RETextItem *inlineTestItem; + +@end + +@implementation ValidationsViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + self.title = @"Controls"; + self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Validate" style:UIBarButtonItemStyleBordered target:self action:@selector(validateButtonPressed:)]; + + // Create manager + // + _manager = [[RETableViewManager alloc] initWithTableView:self.tableView]; + + // Add a section + // + RETableViewSection *section = [RETableViewSection sectionWithHeaderTitle:@"Basic controls"]; + [_manager addSection:section]; + + // Add items + // + self.textItem = [RETextItem itemWithTitle:@"Text" value:@"" placeholder:@"Text item"]; + self.textItem.validators = @[@"presence", @"length(3, 10)"]; + + self.emailItem = [RETextItem itemWithTitle:@"Email" value:@"" placeholder:@"Email item"]; + self.emailItem.name = @"Your email"; + self.emailItem.keyboardType = UIKeyboardTypeEmailAddress; + self.emailItem.autocapitalizationType = UITextAutocapitalizationTypeNone; + self.emailItem.validators = @[@"presence", @"email"]; + + self.urlItem = [RETextItem itemWithTitle:@"URL" value:@"http://invalid-url.co%m" placeholder:@"URL item"]; + self.urlItem.validators = @[@"url"]; + + // Inline Validation Example + // + REValidator *nameValidator = [REValidator validatorWithInlineValidation:^NSError *(NSString *string, NSString *name) { + NSString *cleanString = [string stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + if ([cleanString componentsSeparatedByString:@" "].count < 2) { + return [NSError errorWithDomain:@"" code:0 userInfo:@{ NSLocalizedDescriptionKey: @"Please enter first and last name." }]; + } + return nil; + }]; + self.inlineTestItem = [RETextItem itemWithTitle:@"Name" value:@"" placeholder:@"First & Last Name"]; + self.inlineTestItem.validators = @[@"presence", nameValidator]; + + [section addItem:self.textItem]; + [section addItem:self.emailItem]; + [section addItem:self.urlItem]; + [section addItem:self.inlineTestItem]; +} + +#pragma mark - +#pragma mark Button actions + +- (void)validateButtonPressed:(UIButton *)sender +{ + NSArray *managerErrors = self.manager.errors; + if (managerErrors.count > 0) { + NSMutableArray *errors = [NSMutableArray array]; + for (NSError *error in managerErrors) { + [errors addObject:error.localizedDescription]; + } + NSString *errorString = [errors componentsJoinedByString:@"\n"]; + UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Errors" message:errorString delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; + [alert show]; + } else { + NSLog(@"All good, no errors!"); + } +} + +@end diff --git a/RETableViewManagerExample/RETableViewManagerExample/Classes/Controllers/XIBTestViewController.m b/RETableViewManagerExample/RETableViewManagerExample/Classes/Controllers/XIBTestViewController.m index 428ef36..57461f1 100644 --- a/RETableViewManagerExample/RETableViewManagerExample/Classes/Controllers/XIBTestViewController.m +++ b/RETableViewManagerExample/RETableViewManagerExample/Classes/Controllers/XIBTestViewController.m @@ -24,11 +24,7 @@ // _manager = [[RETableViewManager alloc] initWithTableView:self.tableView delegate:self]; - // Register XIB file - // - [self.tableView registerNib:[UINib nibWithNibName:@"XIBTestCell" bundle:nil] forCellReuseIdentifier:@"XIBTestItem"]; - - // Map item to a cell + // Map item to a cell, this will also register the XIBTestCell.xib for the XIBTestItem identifier // _manager[@"XIBTestItem"] = @"XIBTestCell"; @@ -44,9 +40,16 @@ selectionHandler:^(RETableViewItem *item) { [item deselectRowAnimated:YES]; }]; - item.cellIdentifier = @"XIBTestItem"; [section addItem:item]; } + + // Cell is being assigned an automatic identifier + // + // You can manually set it if you want to: + // item.cellIdentifier = @"CustomIdentifier"; + // + // You'll need to register a cell class for it as well: + // [self.tableView registerNib:[UINib nibWithNibName:@"XIBTestCell" bundle:nil] forCellReuseIdentifier:@"CustomIdentifier"]; } @end diff --git a/RETableViewManagerExample/RETableViewManagerExample/Classes/Views/ListImageCell.m b/RETableViewManagerExample/RETableViewManagerExample/Classes/Views/ListImageCell.m index 81395c8..5436f4c 100644 --- a/RETableViewManagerExample/RETableViewManagerExample/Classes/Views/ListImageCell.m +++ b/RETableViewManagerExample/RETableViewManagerExample/Classes/Views/ListImageCell.m @@ -17,6 +17,7 @@ - (void)cellDidLoad { + [super cellDidLoad]; _pictureView = [[UIImageView alloc] initWithFrame:CGRectMake(7, 0, 306, 306)]; _pictureView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; [self addSubview:_pictureView]; diff --git a/RETableViewManagerExample/Vendor/REValidation/NSError+REValidation.h b/RETableViewManagerExample/Vendor/REValidation/NSError+REValidation.h new file mode 100644 index 0000000..86dfaf6 --- /dev/null +++ b/RETableViewManagerExample/Vendor/REValidation/NSError+REValidation.h @@ -0,0 +1,32 @@ +// +// NSError+REValidation.h +// REValidation +// +// Copyright (c) 2013 Roman Efimov (https://github.com/romaonthego) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import + +@interface NSError (REValidation) + ++ (NSError *)re_validationErrorForDomain:(NSString *)domain, ...; + +@end diff --git a/RETableViewManagerExample/Vendor/REValidation/NSError+REValidation.m b/RETableViewManagerExample/Vendor/REValidation/NSError+REValidation.m new file mode 100644 index 0000000..2e8f3c0 --- /dev/null +++ b/RETableViewManagerExample/Vendor/REValidation/NSError+REValidation.m @@ -0,0 +1,40 @@ +// +// NSError+REValidation.m +// REValidation +// +// Copyright (c) 2013 Roman Efimov (https://github.com/romaonthego) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "NSError+REValidation.h" +#import "REValidation.h" + +@implementation NSError (REValidation) + ++ (NSError *)re_validationErrorForDomain:(NSString *)domain, ... +{ + va_list args; + va_start(args, domain); + NSError *error = [NSError errorWithDomain:domain code:0 userInfo:@{ NSLocalizedDescriptionKey:[[NSString alloc] initWithFormat:[REValidation errorMessageForDomain:domain] arguments:args] }]; + va_end(args); + return error; +} + +@end diff --git a/RETableViewManagerExample/Vendor/REValidation/REValidation.h b/RETableViewManagerExample/Vendor/REValidation/REValidation.h new file mode 100644 index 0000000..a2c2d61 --- /dev/null +++ b/RETableViewManagerExample/Vendor/REValidation/REValidation.h @@ -0,0 +1,60 @@ +// +// REValidation.h +// REValidation +// +// Copyright (c) 2013 Roman Efimov (https://github.com/romaonthego) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import +#import "REValidator.h" +#import "NSError+REValidation.h" +#import "REPresenceValidator.h" +#import "RELengthValidator.h" +#import "REEmailValidator.h" +#import "REURLValidator.h" + +@interface REValidation : NSObject + +///----------------------------- +/// @name Registering Validators +///----------------------------- + ++ (void)registerDefaultValidators; ++ (void)registerDefaultErrorMessages; ++ (void)registerValidator:(Class)validatorClass; + +///----------------------------- +/// @name Validating Objects +///----------------------------- + ++ (NSError *)validateObject:(NSObject *)object name:(NSString *)name validatorString:(NSString *)string; ++ (NSError *)validateObject:(NSObject *)object name:(NSString *)name validator:(REValidator *)validator; ++ (NSArray *)validateObject:(NSObject *)object name:(NSString *)name validators:(NSArray *)validators; + +///----------------------------- +/// @name Handling Error Messages +///----------------------------- + ++ (NSString *)errorMessageForDomain:(NSString *)domain; ++ (void)setErrorMessage:(NSString *)message forDomain:(NSString *)domain; ++ (void)setErrorMessages:(NSDictionary *)messages; + +@end diff --git a/RETableViewManagerExample/Vendor/REValidation/REValidation.m b/RETableViewManagerExample/Vendor/REValidation/REValidation.m new file mode 100644 index 0000000..ec2c939 --- /dev/null +++ b/RETableViewManagerExample/Vendor/REValidation/REValidation.m @@ -0,0 +1,146 @@ +// +// REValidation.m +// REValidation +// +// Copyright (c) 2013 Roman Efimov (https://github.com/romaonthego) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "REValidation.h" + +@interface REValidation () + +@property (strong, readwrite, nonatomic) NSMutableDictionary *registeredValidators; +@property (strong, readwrite, nonatomic) NSMutableDictionary *errorMessages; + +@end + +@implementation REValidation + ++ (instancetype)sharedObject +{ + static REValidation *_sharedClient = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _sharedClient = [[REValidation alloc] init]; + }); + + return _sharedClient; +} + ++ (void)registerValidator:(Class)validatorClass +{ + [REValidation sharedObject].registeredValidators[[validatorClass name]] = NSStringFromClass(validatorClass); +} + ++ (void)registerDefaultValidators +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + [REValidation registerValidator:[REPresenceValidator class]]; + [REValidation registerValidator:[RELengthValidator class]]; + [REValidation registerValidator:[REEmailValidator class]]; + [REValidation registerValidator:[REURLValidator class]]; + }); +} + ++ (void)registerDefaultErrorMessages +{ + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSDictionary *messages = @{ + @"com.REValidation.presence": @"%@ can't be blank.", + @"com.REValidation.minimumLength": @"%@ is too short (minimum is %i characters).", + @"com.REValidation.maximumLength": @"%@ is too long (maximum is %i characters).", + @"com.REValidation.email": @"%@ is not a valid email.", + @"com.REValidation.url": @"%@ is not a valid url." + }; + [REValidation sharedObject].errorMessages = [NSMutableDictionary dictionaryWithDictionary:messages]; + }); +} + ++ (NSString *)errorMessageForDomain:(NSString *)domain +{ + return NSLocalizedString([REValidation sharedObject].errorMessages[domain], [REValidation sharedObject].errorMessages[domain]); +} + ++ (void)setErrorMessage:(NSString *)message forDomain:(NSString *)domain +{ + [REValidation sharedObject].errorMessages[domain] = message; +} + ++ (void)setErrorMessages:(NSDictionary *)messages +{ + [REValidation sharedObject].errorMessages = [NSMutableDictionary dictionaryWithDictionary:messages]; +} + ++ (NSError *)validateObject:(NSObject *)object name:(NSString *)name validatorString:(NSString *)string +{ + NSString *validatorStringName = [string componentsSeparatedByString:@"("][0]; + for (NSString *validatorName in [REValidation sharedObject].registeredValidators) { + if ([validatorName isEqualToString:validatorStringName]) { + Class validator = NSClassFromString([REValidation sharedObject].registeredValidators[validatorName]); + return [[validator class] validateObject:object variableName:name parameters:[validator parseParameterString:string]]; + } + } + return nil; +} + ++ (NSError *)validateObject:(NSObject *)object name:(NSString *)name validator:(REValidator *)validator +{ + return [[validator class] validateObject:object variableName:name parameters:validator.parameters]; +} + ++ (NSArray *)validateObject:(NSObject *)object name:(NSString *)name validators:(NSArray *)validators +{ + NSMutableArray *errors = [NSMutableArray array]; + + for (id validator in validators) { + NSError *error; + if ([validator isKindOfClass:[NSString class]]) { + error = [self validateObject:object name:name validatorString:(NSString *)validator]; + } else { + REValidator *v = (REValidator *)validator; + if (v.inlineValidation) { + error = [[v class] validateObject:object variableName:name validation:v.inlineValidation]; + } else { + error = [self validateObject:object name:name validator:validator]; + } + } + if (error) + [errors addObject:error]; + } + + return errors; +} + + +- (id)init +{ + self = [super init]; + if (!self) + return nil; + + self.registeredValidators = [[NSMutableDictionary alloc] init]; + + return self; +} + +@end diff --git a/RETableViewManagerExample/Vendor/REValidation/REValidator.h b/RETableViewManagerExample/Vendor/REValidation/REValidator.h new file mode 100644 index 0000000..7a3b1cc --- /dev/null +++ b/RETableViewManagerExample/Vendor/REValidation/REValidator.h @@ -0,0 +1,55 @@ +// +// REValidator.h +// REValidation +// +// Copyright (c) 2013 Roman Efimov (https://github.com/romaonthego) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import + +@interface REValidator : NSObject + +@property (strong, readonly, nonatomic) NSDictionary *parameters; +@property (copy, readwrite, nonatomic) NSError *(^inlineValidation)(id object, NSString *name); + +///----------------------------- +/// @name Getting Validator Instance +///----------------------------- + ++ (instancetype)validator; ++ (instancetype)validatorWithParameters:(NSDictionary *)parameters; ++ (instancetype)validatorWithInlineValidation:(NSError *(^)(id object, NSString *name))validation; + +///----------------------------- +/// @name Configuring Representation +///----------------------------- + ++ (NSString *)name; ++ (NSDictionary *)parseParameterString:(NSString *)string; + +///----------------------------- +/// @name Validating Objects +///----------------------------- + ++ (NSError *)validateObject:(NSObject *)object variableName:(NSString *)name parameters:(NSDictionary *)parameters; ++ (NSError *)validateObject:(NSObject *)object variableName:(NSString *)name validation:(NSError *(^)(id object, NSString *name))validation; + +@end diff --git a/RETableViewManagerExample/Vendor/REValidation/REValidator.m b/RETableViewManagerExample/Vendor/REValidation/REValidator.m new file mode 100644 index 0000000..a0407f3 --- /dev/null +++ b/RETableViewManagerExample/Vendor/REValidation/REValidator.m @@ -0,0 +1,77 @@ +// +// REValidator.m +// REValidation +// +// Copyright (c) 2013 Roman Efimov (https://github.com/romaonthego) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "REValidator.h" + +@interface REValidator () + +@property (strong, readwrite, nonatomic) NSDictionary *parameters; + +@end + +@implementation REValidator + ++ (instancetype)validator +{ + return [[self alloc] init]; +} + ++ (instancetype)validatorWithParameters:(NSDictionary *)parameters +{ + REValidator *validator = [[self alloc] init]; + validator.parameters = parameters; + return validator; +} + ++ (instancetype)validatorWithInlineValidation:(NSError *(^)(id object, NSString *name))validation +{ + REValidator *validator = [[self alloc] init]; + validator.inlineValidation = validation; + return validator; +} + ++ (NSString *)name +{ + return @""; +} + ++ (NSError *)validateObject:(NSObject *)object variableName:(NSString *)name parameters:(NSDictionary *)parameters +{ + return nil; +} + ++ (NSError *)validateObject:(NSObject *)object variableName:(NSString *)name validation:(NSError *(^)(id object, NSString *name))validation +{ + if (validation) + return validation(object, name); + return nil; +} + ++ (NSDictionary *)parseParameterString:(NSString *)string +{ + return nil; +} + +@end diff --git a/RETableViewManagerExample/Vendor/REValidation/Validators/REEmailValidator.h b/RETableViewManagerExample/Vendor/REValidation/Validators/REEmailValidator.h new file mode 100644 index 0000000..705b354 --- /dev/null +++ b/RETableViewManagerExample/Vendor/REValidation/Validators/REEmailValidator.h @@ -0,0 +1,30 @@ +// +// REEmailValidator.h +// REValidation +// +// Copyright (c) 2013 Roman Efimov (https://github.com/romaonthego) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "REValidator.h" + +@interface REEmailValidator : REValidator + +@end diff --git a/RETableViewManagerExample/Vendor/REValidation/Validators/REEmailValidator.m b/RETableViewManagerExample/Vendor/REValidation/Validators/REEmailValidator.m new file mode 100644 index 0000000..56176af --- /dev/null +++ b/RETableViewManagerExample/Vendor/REValidation/Validators/REEmailValidator.m @@ -0,0 +1,52 @@ +// +// REEmailValidator.m +// REValidation +// +// Copyright (c) 2013 Roman Efimov (https://github.com/romaonthego) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "REEmailValidator.h" +#import "REValidation.h" + +@implementation REEmailValidator + ++ (NSString *)name +{ + return @"email"; +} + ++ (NSError *)validateObject:(NSString *)object variableName:(NSString *)name parameters:(NSDictionary *)parameters +{ + NSString *string = object ? object : @""; + if (string.length == 0) + return nil; + + NSError *error = NULL; + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}" options:NSRegularExpressionCaseInsensitive error:&error]; + NSTextCheckingResult *match = [regex firstMatchInString:string options:0 range:NSMakeRange(0, string.length)]; + + if (!match) + return [NSError re_validationErrorForDomain:@"com.REValidation.email", name]; + + return nil; +} + +@end diff --git a/RETableViewManagerExample/Vendor/REValidation/Validators/RELengthValidator.h b/RETableViewManagerExample/Vendor/REValidation/Validators/RELengthValidator.h new file mode 100644 index 0000000..0f0ba7e --- /dev/null +++ b/RETableViewManagerExample/Vendor/REValidation/Validators/RELengthValidator.h @@ -0,0 +1,30 @@ +// +// RELengthValidator.h +// REValidation +// +// Copyright (c) 2013 Roman Efimov (https://github.com/romaonthego) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "REValidator.h" + +@interface RELengthValidator : REValidator + +@end diff --git a/RETableViewManagerExample/Vendor/REValidation/Validators/RELengthValidator.m b/RETableViewManagerExample/Vendor/REValidation/Validators/RELengthValidator.m new file mode 100644 index 0000000..bf1b7fd --- /dev/null +++ b/RETableViewManagerExample/Vendor/REValidation/Validators/RELengthValidator.m @@ -0,0 +1,67 @@ +// +// RELengthValidator.m +// REValidation +// +// Copyright (c) 2013 Roman Efimov (https://github.com/romaonthego) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "RELengthValidator.h" +#import "NSError+REValidation.h" + +@implementation RELengthValidator + ++ (NSString *)name +{ + return @"length"; +} + ++ (NSDictionary *)parseParameterString:(NSString *)string +{ + NSError *error = NULL; + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(\\d+)" options:0 error:&error]; + NSArray *matches = [regex matchesInString:string options:0 range:NSMakeRange(0, string.length)]; + NSMutableArray *results = [NSMutableArray array]; + for (NSTextCheckingResult *matchResult in matches) { + NSString *match = [string substringWithRange:[matchResult range]]; + [results addObject:match]; + } + if (results.count == 2) { + return @{ @"min": results[0], @"max": results[1]}; + } + + return nil; +} + ++ (NSError *)validateObject:(NSString *)object variableName:(NSString *)name parameters:(NSDictionary *)parameters +{ + NSUInteger minimumValue = [parameters[@"min"] integerValue]; + NSUInteger maximumValue = [parameters[@"max"] integerValue]; + + if (object.length < minimumValue && minimumValue > 0) + return [NSError re_validationErrorForDomain:@"com.REValidation.minimumLength", name, minimumValue]; + + if (object.length > maximumValue && maximumValue > 0) + return [NSError re_validationErrorForDomain:@"com.REValidation.maximumLength", name, maximumValue]; + + return nil; +} + +@end diff --git a/RETableViewManagerExample/Vendor/REValidation/Validators/REPresenceValidator.h b/RETableViewManagerExample/Vendor/REValidation/Validators/REPresenceValidator.h new file mode 100644 index 0000000..4ab42bb --- /dev/null +++ b/RETableViewManagerExample/Vendor/REValidation/Validators/REPresenceValidator.h @@ -0,0 +1,30 @@ +// +// REPresenceValidator.h +// REValidation +// +// Copyright (c) 2013 Roman Efimov (https://github.com/romaonthego) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "REValidator.h" + +@interface REPresenceValidator : REValidator + +@end diff --git a/RETableViewManagerExample/Vendor/REValidation/Validators/REPresenceValidator.m b/RETableViewManagerExample/Vendor/REValidation/Validators/REPresenceValidator.m new file mode 100644 index 0000000..7a907ff --- /dev/null +++ b/RETableViewManagerExample/Vendor/REValidation/Validators/REPresenceValidator.m @@ -0,0 +1,44 @@ +// +// REPresenceValidator.m +// REValidation +// +// Copyright (c) 2013 Roman Efimov (https://github.com/romaonthego) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "REPresenceValidator.h" +#import "NSError+REValidation.h" + +@implementation REPresenceValidator + ++ (NSString *)name +{ + return @"presence"; +} + ++ (NSError *)validateObject:(NSString *)object variableName:(NSString *)name parameters:(NSDictionary *)parameters +{ + if (![object isKindOfClass:[NSString class]] || object.length == 0) + return [NSError re_validationErrorForDomain:@"com.REValidation.presence", name]; + + return nil; +} + +@end diff --git a/RETableViewManagerExample/Vendor/REValidation/Validators/REURLValidator.h b/RETableViewManagerExample/Vendor/REValidation/Validators/REURLValidator.h new file mode 100644 index 0000000..3990fd1 --- /dev/null +++ b/RETableViewManagerExample/Vendor/REValidation/Validators/REURLValidator.h @@ -0,0 +1,30 @@ +// +// REURLValidator.h +// REValidation +// +// Copyright (c) 2013 Roman Efimov (https://github.com/romaonthego) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "REValidator.h" + +@interface REURLValidator : REValidator + +@end diff --git a/RETableViewManagerExample/Vendor/REValidation/Validators/REURLValidator.m b/RETableViewManagerExample/Vendor/REValidation/Validators/REURLValidator.m new file mode 100644 index 0000000..6954e38 --- /dev/null +++ b/RETableViewManagerExample/Vendor/REValidation/Validators/REURLValidator.m @@ -0,0 +1,51 @@ +// +// REURLValidator.m +// REValidation +// +// Copyright (c) 2013 Roman Efimov (https://github.com/romaonthego) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#import "REURLValidator.h" +#import "REValidation.h" + +@implementation REURLValidator + ++ (NSString *)name +{ + return @"url"; +} + ++ (NSError *)validateObject:(NSString *)object variableName:(NSString *)name parameters:(NSDictionary *)parameters +{ + NSString *string = object ? object : @""; + if (string.length == 0) + return nil; + + NSError *error = NULL; + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^(?i)(?:(?:https?|ftp):\\/\\/)?(?:\\S+(?::\\S*)?@)?(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))(?::\\d{2,5})?(?:\\/[^\\s]*)?$" options:NSRegularExpressionCaseInsensitive error:&error]; + NSTextCheckingResult *match = [regex firstMatchInString:string options:0 range:NSMakeRange(0, string.length)]; + if (!match) + return [NSError re_validationErrorForDomain:@"com.REValidation.url", name]; + + return nil; +} + +@end