Merge pull request #1 from romaonthego/master

update
This commit is contained in:
Marin Todorov
2013-09-06 04:38:36 -07:00
35 changed files with 1336 additions and 222 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -47,4 +47,10 @@
return self;
}
- (void)setValue:(NSString *)value
{
_value = value;
self.detailLabelText = value;
}
@end

View File

@@ -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

View File

@@ -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];
}

2
RETableViewManager/RETableViewCell.h Normal file → Executable file
View File

@@ -96,4 +96,6 @@ typedef NS_ENUM(NSInteger, RETableViewCellType) {
- (void)cellWillAppear;
- (void)cellDidDisappear;
@property (assign, readonly, nonatomic) BOOL loaded;
@end

23
RETableViewManager/RETableViewCell.m Normal file → Executable file
View File

@@ -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];
}

View File

@@ -25,6 +25,7 @@
#import <Foundation/Foundation.h>
#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;

View File

@@ -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

363
RETableViewManager/RETableViewManager.m Normal file → Executable file
View File

@@ -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 <NSCopying>)key
{
return [_registeredClasses objectForKey:key];
return [self.registeredClasses objectForKey:key];
}
- (void)setObject:(id)obj forKeyedSubscript:(id <NSCopying>)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

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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 = "<group>"; };
30939CF717949B2D004D95A9 /* REFormattedNumberField.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = REFormattedNumberField.h; sourceTree = "<group>"; };
30939CF817949B2D004D95A9 /* REFormattedNumberField.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = REFormattedNumberField.m; sourceTree = "<group>"; };
30B00D5117C56EEF0010D439 /* NSError+REValidation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+REValidation.h"; sourceTree = "<group>"; };
30B00D5217C56EEF0010D439 /* NSError+REValidation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+REValidation.m"; sourceTree = "<group>"; };
30B00D5317C56EEF0010D439 /* REValidation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = REValidation.h; sourceTree = "<group>"; };
30B00D5417C56EEF0010D439 /* REValidation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = REValidation.m; sourceTree = "<group>"; };
30B00D5517C56EEF0010D439 /* REValidator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = REValidator.h; sourceTree = "<group>"; };
30B00D5617C56EEF0010D439 /* REValidator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = REValidator.m; sourceTree = "<group>"; };
30B00D5817C56EEF0010D439 /* REEmailValidator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = REEmailValidator.h; sourceTree = "<group>"; };
30B00D5917C56EEF0010D439 /* REEmailValidator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = REEmailValidator.m; sourceTree = "<group>"; };
30B00D5A17C56EEF0010D439 /* RELengthValidator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RELengthValidator.h; sourceTree = "<group>"; };
30B00D5B17C56EEF0010D439 /* RELengthValidator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RELengthValidator.m; sourceTree = "<group>"; };
30B00D5C17C56EEF0010D439 /* REPresenceValidator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = REPresenceValidator.h; sourceTree = "<group>"; };
30B00D5D17C56EEF0010D439 /* REPresenceValidator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = REPresenceValidator.m; sourceTree = "<group>"; };
30B00D7817C667A90010D439 /* ValidationsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ValidationsViewController.h; sourceTree = "<group>"; };
30B00D7917C667A90010D439 /* ValidationsViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ValidationsViewController.m; sourceTree = "<group>"; };
30B00DEC17D8E90D0010D439 /* REURLValidator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = REURLValidator.h; sourceTree = "<group>"; };
30B00DED17D8E90D0010D439 /* REURLValidator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = REURLValidator.m; sourceTree = "<group>"; };
30D1C7D217AC08F3001F731C /* First_Alt@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "First_Alt@2x.png"; path = "Resources/First_Alt@2x.png"; sourceTree = "<group>"; };
30D1C7D317AC08F3001F731C /* Last_Alt@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Last_Alt@2x.png"; path = "Resources/Last_Alt@2x.png"; sourceTree = "<group>"; };
30D1C7D417AC08F3001F731C /* Middle_Alt@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Middle_Alt@2x.png"; path = "Resources/Middle_Alt@2x.png"; sourceTree = "<group>"; };
@@ -250,6 +274,8 @@
303D967117B7CDE300A418F3 /* XIBTestViewController.m */,
30E5F50A17BBD163005CF1FE /* IndexedListViewController.h */,
30E5F50B17BBD163005CF1FE /* IndexedListViewController.m */,
30B00D7817C667A90010D439 /* ValidationsViewController.h */,
30B00D7917C667A90010D439 /* ValidationsViewController.m */,
);
path = Controllers;
sourceTree = "<group>";
@@ -301,6 +327,7 @@
30939CF517949B2D004D95A9 /* Vendor */ = {
isa = PBXGroup;
children = (
30B00D5017C56EEF0010D439 /* REValidation */,
30939CF617949B2D004D95A9 /* REFormattedNumberField */,
);
path = Vendor;
@@ -315,6 +342,35 @@
path = REFormattedNumberField;
sourceTree = "<group>";
};
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 = "<group>";
};
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 = "<group>";
};
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;
};

View File

@@ -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]];

View File

@@ -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

View File

@@ -0,0 +1,16 @@
//
// ValidationsViewController.h
// RETableViewManagerExample
//
// Created by Roman Efimov on 8/22/13.
// Copyright (c) 2013 Roman Efimov. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "RETableViewManager.h"
@interface ValidationsViewController : UITableViewController
@property (strong, readonly, nonatomic) RETableViewManager *manager;
@end

View File

@@ -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

View File

@@ -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

View File

@@ -17,6 +17,7 @@
- (void)cellDidLoad
{
[super cellDidLoad];
_pictureView = [[UIImageView alloc] initWithFrame:CGRectMake(7, 0, 306, 306)];
_pictureView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
[self addSubview:_pictureView];

View File

@@ -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 <Foundation/Foundation.h>
@interface NSError (REValidation)
+ (NSError *)re_validationErrorForDomain:(NSString *)domain, ...;
@end

View File

@@ -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

View File

@@ -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 <Foundation/Foundation.h>
#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

View File

@@ -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

View File

@@ -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 <Foundation/Foundation.h>
@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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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