├── .gitignore ├── CVLibrary ├── BoolCellHandler.h ├── BoolCellHandler.m ├── CGUtils.c ├── CGUtils.h ├── CVEllipseBorder.h ├── CVEllipseBorder.m ├── CVImage.h ├── CVImage.m ├── CVImageAdorner.h ├── CVImageAdorner.m ├── CVImageCache.h ├── CVImageCache.m ├── CVImageCacheTest.m ├── CVLibrary.h ├── CVLibrary.xcodeproj │ ├── TemplateIcon.icns │ └── project.pbxproj ├── CVLibrary_Prefix.pch ├── CVPolygonBorder.h ├── CVPolygonBorder.m ├── CVRoundedRectBorder.h ├── CVRoundedRectBorder.m ├── CVSettingsViewController.h ├── CVSettingsViewController.m ├── CVShadowStyle.h ├── CVShadowStyle.m ├── CVStyleProtocols.h ├── CVThumbnailView.h ├── CVThumbnailView.m ├── CVThumbnailViewCell.h ├── CVThumbnailViewCell.m ├── CVThumbnailViewCell_Private.h ├── CVThumbnailViewController.h ├── CVThumbnailViewController.m ├── CVTitleStyle.h ├── CVTitleStyle.m ├── CellHandler.h ├── EnumCellHandler.h ├── EnumCellHandler.m ├── EnumOptionsViewController.h ├── EnumOptionsViewController.m ├── Frameworks │ └── OCMock.framework │ │ ├── Headers │ │ ├── OCMock │ │ ├── Resources │ │ └── Versions │ │ ├── A │ │ ├── Headers │ │ │ ├── OCMConstraint.h │ │ │ ├── OCMock.h │ │ │ ├── OCMockObject.h │ │ │ └── OCMockRecorder.h │ │ ├── OCMock │ │ └── Resources │ │ │ ├── English.lproj │ │ │ └── InfoPlist.strings │ │ │ ├── Info.plist │ │ │ └── License.txt │ │ └── Current ├── GTMDefines.h ├── GTMIPhoneUnitTestDelegate.h ├── GTMIPhoneUnitTestDelegate.m ├── GTMIPhoneUnitTestMain.m ├── GTMSenTestCase.h ├── GTMSenTestCase.m ├── RunIPhoneUnitTest.sh ├── SynthesizeSingleton.h ├── TextCellHandler.h ├── TextCellHandler.m ├── UIImage+Adornments.h ├── UIImage+Adornments.m └── UnitTest-Info.plist └── CVLibraryDemo ├── AddItemViewController.h ├── AddItemViewController.m ├── AddItemViewController.xib ├── BGTile.png ├── CVLibraryDemo-Info.plist ├── CVLibraryDemo.xcodeproj └── project.pbxproj ├── CVLibraryDemo_Prefix.pch ├── Checkmark_Off.png ├── Checkmark_On.png ├── Classes ├── CVLibraryDemoAppDelegate.h ├── CVLibraryDemoAppDelegate.m ├── ColorToStringConverter.h ├── ColorToStringConverter.m ├── ConfigOptions.h ├── ConfigOptions.m ├── DataServices.h ├── DemoGridViewController.h ├── DemoGridViewController.m ├── DemoItem.h ├── DemoItem.m ├── FakeDataService.h ├── FakeDataService.m ├── FlickrDataService.h ├── FlickrDataService.m ├── FlickrDemoViewController.h ├── FlickrDemoViewController.m ├── LoadMoreControl.h ├── LoadMoreControl.m ├── RootViewController.h ├── RootViewController.m ├── TestView.h └── TestView.m ├── ConfigOptions.plist ├── LoadingIcon.png ├── MainWindow.xib ├── RootViewController.xib └── main.m /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | *.pbxuser 3 | *.mode1v3 4 | *.tm_build_errors 5 | .DS_Store 6 | SampleAPIKey.h -------------------------------------------------------------------------------- /CVLibrary/BoolCellHandler.h: -------------------------------------------------------------------------------- 1 | // 2 | // BoolCellHandler.h 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 7/26/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CellHandler.h" 11 | 12 | @interface BoolCellHandler : NSObject { 13 | id delegate_; 14 | NSString *label_; 15 | NSString *keyPath_; 16 | NSString *identifier_; 17 | } 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /CVLibrary/BoolCellHandler.m: -------------------------------------------------------------------------------- 1 | // 2 | // BoolCellHandler.m 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 7/26/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import "BoolCellHandler.h" 10 | 11 | @interface BoolCellHandler() 12 | - (UITableViewCell *) tableView:(UITableView *) tableView cellWithReuseIdentifier:(NSString *) identifier; 13 | - (IBAction) switchControlChanged:(id) sender; 14 | @end 15 | 16 | @implementation BoolCellHandler 17 | @synthesize delegate = delegate_; 18 | @synthesize label = label_; 19 | @synthesize keyPath = keyPath_; 20 | @synthesize identifier = identifier_; 21 | 22 | - (void) dealloc { 23 | [label_ release], label_ = nil; 24 | [keyPath_ release], keyPath_ = nil; 25 | [identifier_ release], identifier_ = nil; 26 | [super dealloc]; 27 | } 28 | 29 | #define LABEL_TAG 1 30 | #define SWITCH_TAG 2 31 | 32 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath usingData:(id) data { 33 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier_]; 34 | if (nil == cell) { 35 | cell = [self tableView:tableView cellWithReuseIdentifier:identifier_]; 36 | } 37 | 38 | UISwitch *switchControl = (UISwitch *) [cell viewWithTag:SWITCH_TAG]; 39 | if (nil == data) { 40 | switchControl.on = YES; 41 | } else if ([data isKindOfClass:[NSNumber class]]) { 42 | switchControl.on = [data boolValue]; 43 | } 44 | 45 | UILabel *labelField = (UILabel *) [cell viewWithTag:LABEL_TAG]; 46 | labelField.text = label_; 47 | 48 | return cell; 49 | } 50 | 51 | #define LEFT_COLUMN_OFFSET 10.0 52 | #define LEFT_COLUMN_WIDTH 160.0 53 | 54 | #define RIGHT_COLUMN_OFFSET 150.0 55 | #define RIGHT_COLUMN_WIDTH 80.0 56 | 57 | #define MAIN_FONT_SIZE 16.0 58 | #define LABEL_HEIGHT 26.0 59 | 60 | - (UITableViewCell *) tableView:(UITableView *) tableView cellWithReuseIdentifier:(NSString *) identifier { 61 | UITableViewCell *cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier] autorelease]; 62 | 63 | CGRect frame = CGRectMake(LEFT_COLUMN_OFFSET, (tableView.rowHeight - LABEL_HEIGHT) / 2.0, LEFT_COLUMN_WIDTH, LABEL_HEIGHT); 64 | UILabel *label = [[UILabel alloc] initWithFrame:frame]; 65 | label.tag = LABEL_TAG; 66 | label.font = [UIFont boldSystemFontOfSize:MAIN_FONT_SIZE]; 67 | label.adjustsFontSizeToFitWidth = YES; 68 | label.textAlignment = UITextAlignmentLeft; 69 | [cell.contentView addSubview:label]; 70 | [label release]; 71 | 72 | frame = CGRectMake(RIGHT_COLUMN_OFFSET, (tableView.rowHeight - LABEL_HEIGHT) / 2.0, RIGHT_COLUMN_WIDTH, LABEL_HEIGHT); 73 | UISwitch *switchControl = [[UISwitch alloc] initWithFrame:frame]; 74 | switchControl.tag = SWITCH_TAG; 75 | switchControl.on = YES; 76 | [switchControl addTarget:self action:@selector(switchControlChanged:) forControlEvents:UIControlEventValueChanged]; 77 | [cell.contentView addSubview:switchControl]; 78 | [switchControl release]; 79 | 80 | return cell; 81 | } 82 | 83 | - (IBAction) switchControlChanged:(id) sender { 84 | UISwitch *switchControl = (UISwitch *) sender; 85 | 86 | BOOL controlState = switchControl.on; 87 | NSNumber *packedValue = [NSNumber numberWithBool:controlState]; 88 | [delegate_ cellData:packedValue changedForHandler:self]; 89 | } 90 | 91 | @end 92 | -------------------------------------------------------------------------------- /CVLibrary/CGUtils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * CGUtils.c 3 | * CVLibrary 4 | * 5 | * Created by Kerem Karatal on 5/16/09. 6 | * Copyright 2009 Coding Ventures. All rights reserved. 7 | * 8 | */ 9 | 10 | #include "CGUtils.h" 11 | 12 | void CVAddRoundedRectToPath(CGContextRef context, CGRect rect, CGFloat ovalWidth, CGFloat ovalHeight) { 13 | CGFloat fw, fh; 14 | 15 | if (ovalWidth == 0.0 || ovalHeight == 0.0) { 16 | // Not rounded so just add the rectangle 17 | CGContextAddRect(context, rect); 18 | } else { 19 | CGContextSaveGState(context); 20 | 21 | // Translate the below to lower-left corner of the rectangle, so that origin is at 0,0 and we can work 22 | // with the width and height of the rectangle only. 23 | CGContextTranslateCTM(context, CGRectGetMinX(rect), CGRectGetMinY(rect)); 24 | 25 | // Do the below scaling so that if ovalWidth != ovalHeight, we create a normalized 26 | // system where they are equal and can use the CGContextAddArcToPoint which expects a circle not oval. 27 | // At this point ovalWidth and ovalHeight are normalized to 1.0 and hence the radius is 0.5 28 | CGContextScaleCTM(context, ovalWidth, ovalHeight); 29 | 30 | // Now unscale the width and height of the rectangle 31 | fw = CGRectGetWidth(rect) / ovalWidth; 32 | fh = CGRectGetHeight(rect) / ovalHeight; 33 | 34 | // Start at the right side of rectangle half point of the height and go counterclockwise 35 | CGContextMoveToPoint(context, fw, fh/2); 36 | 37 | CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 0.5); 38 | CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 0.5); 39 | CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 0.5); 40 | CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 0.5); 41 | 42 | CGContextClosePath(context); 43 | 44 | CGContextRestoreGState(context); 45 | } 46 | } 47 | 48 | void CVAddPolygonToPath(CGContextRef context, CGRect rect, CGFloat radius, unsigned int numOfSides, CGFloat angle) { 49 | if (numOfSides < 3 || radius < 0.0) 50 | return; 51 | 52 | CGContextSaveGState(context); 53 | 54 | CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect)); 55 | CGContextTranslateCTM(context, center.x, center.y); 56 | CGContextRotateCTM(context, angle); 57 | CGContextMoveToPoint(context, 0.0, - radius); 58 | for(unsigned int i = 1; i < numOfSides; ++i) { 59 | CGFloat x = - radius * sinf(i * 2.0 * M_PI / numOfSides); 60 | CGFloat y = - radius * cosf(i * 2.0 * M_PI / numOfSides); 61 | CGContextAddLineToPoint(context, x, y); 62 | } 63 | 64 | CGContextRestoreGState(context); 65 | } 66 | 67 | void CVAddCircleToPath(CGContextRef context, CGRect rect, CGFloat radius) { 68 | if (radius < 0.0) 69 | return; 70 | 71 | CGContextSaveGState(context); 72 | 73 | CGContextAddEllipseInRect(context, rect); 74 | 75 | CGContextRestoreGState(context); 76 | 77 | } 78 | 79 | void CVAddStarToPath(CGContextRef context, CGRect rect, CGFloat radius) { 80 | if (radius < 0.0) 81 | return; 82 | 83 | CGContextSaveGState(context); 84 | 85 | CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect)); 86 | for(unsigned int i = 1; i < 5; ++i) 87 | { 88 | CGFloat x = radius * sinf(i * 4.0 * M_PI / 5.0); 89 | CGFloat y = radius * cosf(i * 4.0 * M_PI / 5.0); 90 | CGContextAddLineToPoint(context, center.x + x, center.y + y); 91 | } 92 | 93 | CGContextRestoreGState(context); 94 | } 95 | 96 | void CVPathAddRoundedRect(CGMutablePathRef path, CGRect rect, CGFloat ovalWidth, CGFloat ovalHeight) { 97 | CGFloat fw, fh; 98 | 99 | if (ovalWidth == 0 || ovalHeight == 0) { 100 | // Not rounded so just add the rectangle 101 | CGPathAddRect(path, NULL, rect); 102 | } else { 103 | CGAffineTransform theTransform = CGAffineTransformMakeTranslation(CGRectGetMinX(rect), CGRectGetMinY(rect)); 104 | theTransform = CGAffineTransformScale(theTransform, ovalWidth, ovalHeight); 105 | 106 | // Now unscale the width and height of the rectangle 107 | fw = CGRectGetWidth(rect) / ovalWidth; 108 | fh = CGRectGetHeight(rect) / ovalHeight; 109 | 110 | CGPathMoveToPoint(path, &theTransform, fw, fh/2); 111 | 112 | CGPathAddArcToPoint(path, &theTransform, fw, fh, fw/2, fh, 0.5); 113 | CGPathAddArcToPoint(path, &theTransform, 0, fh, 0, fh/2, 0.5); 114 | CGPathAddArcToPoint(path, &theTransform, 0, 0, fw/2, 0, 0.5); 115 | CGPathAddArcToPoint(path, &theTransform, fw, 0, fw, fh/2, 0.5); 116 | 117 | CGPathCloseSubpath(path); 118 | } 119 | } 120 | 121 | void CVPathAddPolygon(CGMutablePathRef path, CGRect rect, CGFloat radius, unsigned int numOfSides) { 122 | if (numOfSides < 3 || radius < 0.0) 123 | return; 124 | 125 | CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect)); 126 | CGPathMoveToPoint(path, NULL, center.x, center.y - radius); 127 | for(unsigned int i = 1; i < numOfSides; ++i) { 128 | CGFloat x = - radius * sinf(i * 2.0 * M_PI / numOfSides); 129 | CGFloat y = - radius * cosf(i * 2.0 * M_PI / numOfSides); 130 | CGPathAddLineToPoint(path, NULL, center.x + x, center.y + y); 131 | } 132 | CGPathCloseSubpath(path); 133 | } 134 | 135 | void CVPathAddStar(CGMutablePathRef path, CGRect rect, CGFloat radius) { 136 | if (radius < 0.0) 137 | return; 138 | 139 | CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect)); 140 | for(unsigned int i = 1; i < 5; ++i) 141 | { 142 | CGFloat x = radius * sinf(i * 4.0 * M_PI / 5.0); 143 | CGFloat y = radius * cosf(i * 4.0 * M_PI / 5.0); 144 | CGPathAddLineToPoint(path, NULL, center.x + x, center.y + y); 145 | } 146 | CGPathCloseSubpath(path); 147 | } 148 | -------------------------------------------------------------------------------- /CVLibrary/CGUtils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CGUtils.h 3 | * CVLibrary 4 | * 5 | * Created by Kerem Karatal on 5/16/09. 6 | * Copyright 2009 Coding Ventures. All rights reserved. 7 | * 8 | */ 9 | 10 | #ifndef CGUTILS_H_ 11 | #define CGUTILS_H_ 12 | 13 | #include 14 | #include 15 | 16 | static inline double radians (double degrees) {return degrees * M_PI/180;} 17 | 18 | void CVAddRoundedRectToPath(CGContextRef context, CGRect rect, CGFloat ovalWidth, CGFloat ovalHeight); 19 | void CVAddPolygonToPath(CGContextRef context, CGRect rect, CGFloat radius, unsigned int numOfSides, CGFloat angle); 20 | void CVAddStarToPath(CGContextRef context, CGRect rect, CGFloat radius); 21 | 22 | void CVPathAddRoundedRect(CGMutablePathRef path, CGRect rect, CGFloat ovalWidth, CGFloat ovalHeight); 23 | void CVPathAddPolygon(CGMutablePathRef path, CGRect rect, CGFloat radius, unsigned int numOfSides); 24 | void CVPathAddStar(CGMutablePathRef path, CGRect rect, CGFloat radius); 25 | 26 | #endif -------------------------------------------------------------------------------- /CVLibrary/CVEllipseBorder.h: -------------------------------------------------------------------------------- 1 | // 2 | // CVEllipseBorder.h 3 | // CVLibrary 4 | // 5 | // Created by Kerem Karatal on 9/14/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CVStyleProtocols.h" 11 | 12 | /*! 13 | @abstract The CVEllipseBorder class describes how to provide an elliptical border for a given image. 14 | 15 | @discussion The image is cropped to stay within the border. The ellipse is automatically created to fit within the rectangular area of the required final image size. 16 | */ 17 | @interface CVEllipseBorder : NSObject { 18 | CGFloat width_; 19 | UIColor *color_; 20 | } 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /CVLibrary/CVEllipseBorder.m: -------------------------------------------------------------------------------- 1 | // 2 | // CVEllipseBorder.m 3 | // CVLibrary 4 | // 5 | // Created by Kerem Karatal on 9/14/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import "CVEllipseBorder.h" 10 | 11 | @interface CVEllipseBorder() 12 | - (CGSize) sizeAfterRenderingGivenInitialSize:(CGSize) size; 13 | @end 14 | 15 | 16 | @implementation CVEllipseBorder 17 | @synthesize color = color_; 18 | @synthesize width = width_; 19 | 20 | - (void) dealloc { 21 | [color_ release], color_ = nil; 22 | [super dealloc]; 23 | } 24 | 25 | #pragma mark CVRenderPath 26 | 27 | - (void) drawInContext:(CGContextRef) context forImageSize:(CGSize) imageSize { 28 | CGSize borderSize = [self sizeAfterRenderingGivenInitialSize:imageSize]; 29 | 30 | CGRect borderRect = CGRectMake(0.0, 0.0, borderSize.width, borderSize.height); 31 | if (self.width > 0.0) { 32 | CGContextBeginPath(context); 33 | CGContextAddEllipseInRect(context, borderRect); 34 | CGContextClosePath(context); 35 | CGContextSetFillColorWithColor(context, [self.color CGColor]); 36 | CGContextDrawPath(context, kCGPathFill); 37 | } 38 | 39 | // Calculate the offset based on the border: 40 | CGContextTranslateCTM(context, self.width, self.width); 41 | 42 | // Clip the image with inner circle 43 | CGRect innerRect = CGRectMake(0, 0, imageSize.width, imageSize.height); 44 | CGContextBeginPath(context); 45 | CGContextAddEllipseInRect(context, innerRect); 46 | CGContextClosePath(context); 47 | CGContextClip(context); 48 | } 49 | 50 | - (CGSize) sizeRequiredForRendering { 51 | 52 | return CGSizeMake(2 * self.width, 2 * self.width); 53 | } 54 | 55 | - (CGSize) sizeAfterRenderingGivenInitialSize:(CGSize) size { 56 | CGSize adornedImageSize = size; 57 | 58 | adornedImageSize.width += 2 * self.width; 59 | adornedImageSize.height += 2 * self.width; 60 | return adornedImageSize; 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /CVLibrary/CVImage.h: -------------------------------------------------------------------------------- 1 | // 2 | // CVImage.h 3 | // CVLibrary 4 | // 5 | // Created by Kerem Karatal on 5/24/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CVImageAdorner.h" 11 | 12 | @interface CVImage : NSObject { 13 | NSString *imageUrl_; 14 | UIImage *image_; 15 | NSIndexPath *indexPath_; 16 | NSUInteger previousMemorySize_; 17 | } 18 | 19 | @property (nonatomic, assign) UIImage *image; 20 | @property (nonatomic, copy) NSString *imageUrl; 21 | @property (nonatomic, copy) NSIndexPath *indexPath; 22 | @property (nonatomic, readonly) NSUInteger previousMemorySize; 23 | 24 | - (id) initWithUrl:(NSString *) url indexPath:(NSIndexPath *) indexPath; 25 | - (NSUInteger) memorySize; 26 | @end 27 | -------------------------------------------------------------------------------- /CVLibrary/CVImage.m: -------------------------------------------------------------------------------- 1 | // 2 | // CVImage.m 3 | // CVLibrary 4 | // 5 | // Created by Kerem Karatal on 5/24/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import "CVImage.h" 10 | #import "CVImageCache.h" 11 | #import "UIImage+Adornments.h" 12 | #include "CGUtils.h" 13 | 14 | @implementation CVImage 15 | @synthesize imageUrl = imageUrl_; 16 | @synthesize image = image_; 17 | @synthesize indexPath = indexPath_; 18 | @synthesize previousMemorySize = previousMemorySize_; 19 | 20 | - (void) dealloc { 21 | [image_ release], image_ = nil; 22 | [imageUrl_ release], imageUrl_ = nil; 23 | [indexPath_ release], indexPath_ = nil; 24 | [super dealloc]; 25 | } 26 | 27 | + (BOOL)automaticallyNotifiesObserversForKey:(NSString *)theKey { 28 | BOOL automatic = NO; 29 | 30 | if ([theKey isEqualToString:@"image"]) { 31 | automatic=NO; 32 | } else { 33 | automatic=[super automaticallyNotifiesObserversForKey:theKey]; 34 | } 35 | return automatic; 36 | } 37 | 38 | - (id) initWithUrl:(NSString *) url indexPath:(NSIndexPath *) indexPath { 39 | self = [super init]; 40 | if (self != nil) { 41 | self.imageUrl = url; 42 | self.indexPath = indexPath; 43 | image_ = nil; 44 | previousMemorySize_ = 0; 45 | } 46 | return self; 47 | } 48 | 49 | - (void) setImage:(UIImage *) image { 50 | if (image_ != image) { 51 | [self willChangeValueForKey:@"image"]; 52 | previousMemorySize_ = [self memorySize]; 53 | [image_ release]; 54 | image_ = [image retain]; 55 | [self didChangeValueForKey:@"image"]; 56 | } 57 | } 58 | 59 | - (NSUInteger) memorySize { 60 | NSUInteger imageSize = (nil != image_) ? [image_ imageMemorySize] : 0; 61 | return imageSize; 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /CVLibrary/CVImageAdorner.h: -------------------------------------------------------------------------------- 1 | // 2 | // CVStyle.h 3 | // CVLibrary 4 | // 5 | // Created by Kerem Karatal on 5/14/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CVShadowStyle.h" 11 | 12 | /*! 13 | @abstract The CVImageAdorner class is used for rendering border and shadow adornments on images. 14 | 15 | @discussion 16 | */ 17 | @interface CVImageAdorner : NSObject { 18 | id borderStyle_; 19 | CVShadowStyle *shadowStyle_; 20 | } 21 | 22 | /*! 23 | @abstract The border adornment to be used in rendering the image. 24 | 25 | @discussion 26 | */ 27 | @property (nonatomic, retain) id borderStyle; 28 | /*! 29 | @abstract The shadow adornment to be used in rendering the image. 30 | 31 | @discussion 32 | */ 33 | @property (nonatomic, retain) CVShadowStyle *shadowStyle; 34 | 35 | /*! 36 | @abstract Creates a new adorned image from a given image. 37 | 38 | @discussion 39 | @param image The instance of UIImage class to be adorned. 40 | @param targetImageSize The final bounding image size to be created. The inner image size (inside the border and shadow adornments) is calculated. 41 | @result The instance of UIImage class that represents the adorned image. 42 | */ 43 | - (UIImage *) adornedImageFromImage:(UIImage *) image usingTargetImageSize:(CGSize) targetImageSize; 44 | /*! 45 | @abstract The size of the padding required for an upper left badge. 46 | 47 | @discussion Usually the upper left badge can be things like a delete icon in editing mode of a thumbnail view. 48 | @param badgeSize The size of the badge that will be used. 49 | @result The size is calculated taking into account things like shadow direction and the shape of the border. 50 | */ 51 | - (CGSize) paddingRequiredForUpperLeftBadgeSize:(CGSize) badgeSize; 52 | @end 53 | -------------------------------------------------------------------------------- /CVLibrary/CVImageAdorner.m: -------------------------------------------------------------------------------- 1 | // 2 | // CVStyle.m 3 | // CVLibrary 4 | // 5 | // Created by Kerem Karatal on 5/14/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import "CVImageAdorner.h" 10 | #include "CGUtils.h" 11 | 12 | @interface CVImageAdorner() 13 | - (CGSize) innerImageSizeForTargetImageSize:(CGSize) targetImageSize; 14 | @end 15 | 16 | 17 | @implementation CVImageAdorner 18 | @synthesize borderStyle = borderStyle_; 19 | @synthesize shadowStyle = shadowStyle_; 20 | 21 | - (void) dealloc { 22 | [borderStyle_ release], borderStyle_ = nil; 23 | [shadowStyle_ release], shadowStyle_ = nil; 24 | [super dealloc]; 25 | } 26 | 27 | - (id) init { 28 | self = [super init]; 29 | if (self != nil) { 30 | shadowStyle_ = [[CVShadowStyle alloc] init]; 31 | } 32 | return self; 33 | } 34 | 35 | #define SHADOW_BLUR_PIXELS 3 36 | #define BITS_PER_COMPONENT 8 37 | #define NUM_OF_COMPONENTS 4 38 | 39 | - (UIImage *) adornedImageFromImage:(UIImage *) image usingTargetImageSize:(CGSize) targetImageSize { 40 | // IMPORTANT NOTE: 41 | // DONOT use UIGraphicsBeginImageContext here 42 | // This is done in the background thread and the UI* calls are not threadsafe with the 43 | // main UI thread. So use the pure CoreGraphics APIs instead. 44 | 45 | CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); 46 | CGContextRef context = CGBitmapContextCreate(NULL, targetImageSize.width, targetImageSize.height, 47 | BITS_PER_COMPONENT, 48 | NUM_OF_COMPONENTS * targetImageSize.width, // We need to have RGBA with alpha for shadow effects 49 | colorSpaceRef, 50 | kCGImageAlphaPremultipliedLast); 51 | CGColorSpaceRelease(colorSpaceRef); 52 | CGContextClearRect(context, CGRectMake(0.0, 0.0, targetImageSize.width, targetImageSize.height)); 53 | 54 | CGContextBeginTransparencyLayer(context, NULL); 55 | 56 | CGSize innerImageSize = [self innerImageSizeForTargetImageSize:targetImageSize]; 57 | [shadowStyle_ drawInContext:context forImageSize:CGSizeZero]; 58 | [borderStyle_ drawInContext:context forImageSize:innerImageSize]; 59 | 60 | // Draw the new image in 61 | CGContextDrawImage(context, CGRectMake(0.0, 0.0, innerImageSize.width, innerImageSize.height), [image CGImage]); 62 | 63 | CGContextEndTransparencyLayer(context); 64 | 65 | CGImageRef cgImage = CGBitmapContextCreateImage(context); 66 | UIImage *processedImage = [UIImage imageWithCGImage:cgImage]; 67 | CGImageRelease(cgImage); 68 | CGContextRelease(context); 69 | 70 | return processedImage; 71 | } 72 | 73 | - (CGSize) innerImageSizeForTargetImageSize:(CGSize) targetImageSize { 74 | CGSize sizeRequiredForShadow = shadowStyle_ ? [shadowStyle_ sizeRequiredForRendering] : CGSizeZero; 75 | CGSize sizeRequiredForBorder = borderStyle_ ? [borderStyle_ sizeRequiredForRendering] : CGSizeZero; 76 | 77 | CGFloat newWidth = targetImageSize.width - (sizeRequiredForBorder.width + sizeRequiredForShadow.width); 78 | CGFloat newHeight = targetImageSize.height - (sizeRequiredForBorder.height + sizeRequiredForShadow.height); 79 | 80 | return CGSizeMake(newWidth, newHeight); 81 | } 82 | 83 | - (CGSize) paddingRequiredForUpperLeftBadgeSize:(CGSize) badgeSize { 84 | // paddingRequiredForUpperLeftBadgeSize: depends on the shadow direction and the shape of the border 85 | 86 | CGSize shadowOffset = self.shadowStyle.offsetWithBlurPixels; 87 | 88 | CGFloat widthAdjustment = (shadowOffset.width < 0) ? abs(shadowOffset.width) : 0.0; 89 | CGFloat heightAdjustment = (shadowOffset.height > 0) ? shadowOffset.height : 0.0; 90 | 91 | CGFloat xPadding = widthAdjustment - (badgeSize.width / 2); 92 | xPadding = (xPadding > 0) ? 0.0 : abs(xPadding); 93 | 94 | CGFloat yPadding = heightAdjustment - (badgeSize.width / 2); 95 | yPadding = (yPadding > 0) ? 0.0 : abs(yPadding); 96 | 97 | return CGSizeMake(xPadding, yPadding); 98 | } 99 | 100 | @end 101 | -------------------------------------------------------------------------------- /CVLibrary/CVImageCache.h: -------------------------------------------------------------------------------- 1 | // 2 | // CVImageCache.h 3 | // CVLibrary 4 | // 5 | // Created by Kerem Karatal on 5/17/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CVImage.h" 11 | 12 | @interface CVImageCache : NSObject { 13 | @private 14 | NSMutableArray *imageCacheHistory_; // To manage to LRU cache management 15 | NSMutableDictionary *imageCache_; // Fast access to cache objects by id 16 | NSUInteger memoryCacheSize_; 17 | NSUInteger currentMemCacheSize_; 18 | } 19 | 20 | - (id) init; 21 | - (void) setImage:(CVImage *) image; 22 | - (CVImage *) imageForKey:(NSString *) key; 23 | - (void) clearMemoryCache; 24 | 25 | @property (nonatomic) NSUInteger memoryCacheSize; 26 | @property (nonatomic, readonly) NSUInteger currentMemoryCacheSize; 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /CVLibrary/CVImageCache.m: -------------------------------------------------------------------------------- 1 | // 2 | // CVImageCache.m 3 | // CVLibrary 4 | // 5 | // Created by Kerem Karatal on 5/17/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import "CVImageCache.h" 10 | #import "SynthesizeSingleton.h" 11 | 12 | @interface CVImageCache() 13 | - (void) removeObserversFromImages; 14 | - (void) checkAndHandleLowMemory; 15 | @end 16 | 17 | @implementation CVImageCache 18 | @synthesize memoryCacheSize = memoryCacheSize_; 19 | @synthesize currentMemoryCacheSize = currentMemCacheSize_; 20 | 21 | static NSUInteger const kDefaultMemCacheSize = 10000 * 1024; // 10MB 22 | 23 | - (void) dealloc { 24 | [self removeObserversFromImages]; 25 | [imageCache_ release], imageCache_ = nil; 26 | [imageCacheHistory_ release], imageCacheHistory_ = nil; 27 | [super dealloc]; 28 | } 29 | 30 | - (id) init { 31 | self = [super init]; 32 | if (self != nil) { 33 | memoryCacheSize_ = kDefaultMemCacheSize; 34 | imageCache_ = nil; 35 | imageCacheHistory_ = nil; 36 | [self clearMemoryCache]; 37 | } 38 | return self; 39 | } 40 | 41 | - (void) setMemoryCacheSize:(NSUInteger) newSize { 42 | memoryCacheSize_ = newSize; 43 | [self clearMemoryCache]; 44 | } 45 | 46 | static char imageSizeObservingContext; 47 | 48 | - (void) setImage:(CVImage *) image { 49 | if (nil == image) 50 | return; 51 | 52 | if ([imageCache_ objectForKey:image.imageUrl]) { 53 | return; 54 | } 55 | 56 | NSUInteger newImageMemSize = [image memorySize]; 57 | currentMemCacheSize_ += newImageMemSize; 58 | 59 | [imageCache_ setObject:image forKey:image.imageUrl]; 60 | if (newImageMemSize > 0) { 61 | [imageCacheHistory_ addObject:image.imageUrl]; 62 | } 63 | [image addObserver:self forKeyPath:@"image" options:NSKeyValueChangeSetting context:&imageSizeObservingContext]; 64 | 65 | [self checkAndHandleLowMemory]; 66 | } 67 | 68 | - (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { 69 | if (context == &imageSizeObservingContext) { 70 | CVImage *image = (CVImage *) object; 71 | 72 | currentMemCacheSize_ -= image.previousMemorySize; 73 | NSUInteger newImageMemSize = [image memorySize]; 74 | currentMemCacheSize_ += newImageMemSize; 75 | 76 | // Image is changes so add this to the top of history 77 | if (newImageMemSize > 0) { 78 | [imageCacheHistory_ addObject:image.imageUrl]; 79 | } 80 | [self checkAndHandleLowMemory]; 81 | } else { 82 | [super observeValueForKeyPath:keyPath ofObject:object change:change context:context]; 83 | } 84 | } 85 | 86 | - (CVImage *) imageForKey:(NSString *) key { 87 | CVImage *image = [imageCache_ objectForKey:key]; 88 | 89 | return image; 90 | } 91 | 92 | - (void) removeObserversFromImages { 93 | if (nil != imageCache_) { 94 | for (CVImage *image in [imageCache_ allValues]) { 95 | [image removeObserver:self forKeyPath:@"image"]; 96 | } 97 | } 98 | } 99 | 100 | - (void) clearMemoryCache { 101 | [self removeObserversFromImages]; 102 | [imageCache_ release]; 103 | [imageCacheHistory_ release]; 104 | 105 | imageCache_ = [[NSMutableDictionary alloc] init]; 106 | imageCacheHistory_ = [[NSMutableArray alloc] init]; 107 | currentMemCacheSize_ = 0; 108 | } 109 | 110 | - (void) checkAndHandleLowMemory { 111 | while ((currentMemCacheSize_ > memoryCacheSize_) && ([imageCacheHistory_ count] > 0)) { 112 | // Get the oldest image entry 113 | NSString *oldImageUrl = [imageCacheHistory_ objectAtIndex:0]; 114 | CVImage *oldImage = [imageCache_ objectForKey:oldImageUrl]; 115 | 116 | // Remove it 117 | currentMemCacheSize_ -= [oldImage memorySize]; 118 | [imageCacheHistory_ removeObjectAtIndex:0]; 119 | [imageCache_ removeObjectForKey:oldImage.imageUrl]; 120 | } 121 | } 122 | 123 | @end 124 | -------------------------------------------------------------------------------- /CVLibrary/CVImageCacheTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // CVImageCacheTest.m 3 | // CVLibrary 4 | // 5 | // Created by Kerem Karatal on 7/23/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | // Link to Google Toolbox For Mac (IPhone Unit Test): 9 | // http://code.google.com/p/google-toolbox-for-mac/wiki/iPhoneUnitTesting 10 | // Link to OCUnit: http://www.sente.ch/s/?p=276&lang=en 11 | // Link to OCMock: http://www.mulle-kybernetik.com/software/OCMock/ 12 | 13 | #import 14 | #import 15 | #import 16 | #import "GTMSenTestCase.h" 17 | #import "CVLibrary.h" 18 | 19 | @interface CVImageCacheTest : GTMTestCase { 20 | id mock; // Mock object used in tests 21 | CVImageCache *imageCache_; 22 | } 23 | 24 | - (UIImage *) fakeImageForText:(NSString *) text size:(CGSize) size; 25 | @end 26 | 27 | @implementation CVImageCacheTest 28 | 29 | #if TARGET_IPHONE_SIMULATOR // Only run when the target is simulator 30 | 31 | #define TEST_CACHE_SIZE 20000000 // 10M 32 | #define SMALL_CACHE_SIZE 10000 // 10K 33 | 34 | - (void) setUp { 35 | imageCache_ = [[CVImageCache alloc] init]; 36 | 37 | [imageCache_ setMemoryCacheSize:TEST_CACHE_SIZE]; 38 | } 39 | 40 | - (void) testCacheSize { 41 | NSUInteger size = [imageCache_ memoryCacheSize]; 42 | STAssertTrue(size == TEST_CACHE_SIZE, @"Size is not set correct"); 43 | 44 | CVImage *image = [[CVImage alloc] initWithUrl:@"1" indexPath:nil]; 45 | [imageCache_ setImage:image]; 46 | [image release]; 47 | image = nil; 48 | 49 | [imageCache_ setMemoryCacheSize:SMALL_CACHE_SIZE]; 50 | 51 | image = [imageCache_ imageForKey:@"1"]; 52 | STAssertTrue(image == nil, @"Changing memory size should clear cache"); 53 | 54 | size = [imageCache_ memoryCacheSize]; 55 | STAssertTrue(size == SMALL_CACHE_SIZE, @"Changing memory size, should return new memory size"); 56 | } 57 | 58 | - (void) testImageSizeInCache { 59 | UIImage *image = [self fakeImageForText:@"1" size:CGSizeMake(80.0, 80.0)]; 60 | NSUInteger imageSize = [image imageMemorySize]; 61 | 62 | CVImage *cvImage = [[CVImage alloc] initWithUrl:@"1" indexPath:nil]; 63 | [cvImage setImage:image]; 64 | [imageCache_ setImage:cvImage]; 65 | [cvImage release]; 66 | 67 | NSUInteger memoryCacheSize = [imageCache_ currentMemoryCacheSize]; 68 | STAssertTrue(memoryCacheSize == imageSize, @"Cache memory size should be equal to the sum of mem sizes of images"); 69 | } 70 | 71 | - (void) testImageSizeChange { 72 | // Step 1 73 | UIImage *image = [self fakeImageForText:@"1" size:CGSizeMake(80.0, 80.0)]; 74 | NSUInteger imageSize = [image imageMemorySize]; 75 | CVImage *cvImage = [[CVImage alloc] initWithUrl:@"1" indexPath:nil]; 76 | [imageCache_ setImage:cvImage]; 77 | NSUInteger memoryCacheSize = [imageCache_ currentMemoryCacheSize]; 78 | STAssertTrue(memoryCacheSize == 0, @"When there is no image contents, cache size should stay the same"); 79 | 80 | // Step 2 81 | [cvImage setImage:image]; 82 | memoryCacheSize = [imageCache_ currentMemoryCacheSize]; 83 | STAssertTrue(memoryCacheSize == imageSize, @"When the image contents change, cache should update"); 84 | 85 | // Step 3 86 | image = [self fakeImageForText:@"1" size:CGSizeMake(150.0, 150.0)]; 87 | imageSize = [image imageMemorySize]; 88 | [cvImage setImage:image]; 89 | memoryCacheSize = [imageCache_ currentMemoryCacheSize]; 90 | STAssertTrue(memoryCacheSize == imageSize, @"When the image contents change again, cache should update"); 91 | 92 | [cvImage release]; 93 | } 94 | 95 | - (void) testCacheAllocation { 96 | for (NSInteger i = 0; i < 1000; i++) { 97 | NSString *imageName = [NSString stringWithFormat:@"%d", i]; 98 | CVImage *cvImage = [[CVImage alloc] initWithUrl:imageName indexPath:nil]; 99 | [imageCache_ setImage:cvImage]; 100 | [cvImage release]; 101 | } 102 | NSUInteger memoryCacheSize = [imageCache_ currentMemoryCacheSize]; 103 | STAssertTrue(memoryCacheSize == 0, @"Should have 0 size, no actual images in memory"); 104 | 105 | NSUInteger totalSize = 0; 106 | for (NSInteger i = 0; i < 1000; i++) { 107 | CGFloat randomSize = (arc4random() % 501) + 30.0; 108 | NSString *imageName = [NSString stringWithFormat:@"%d", i]; 109 | UIImage *image = [self fakeImageForText:imageName size:CGSizeMake(randomSize, randomSize)]; 110 | NSUInteger imageSize = [image imageMemorySize]; 111 | totalSize += imageSize; 112 | 113 | CVImage *cvImage = [imageCache_ imageForKey:imageName]; 114 | [cvImage setImage:image]; 115 | } 116 | memoryCacheSize = [imageCache_ currentMemoryCacheSize]; 117 | if (totalSize > TEST_CACHE_SIZE) { 118 | STAssertTrue(memoryCacheSize < totalSize, @"Some images should be deleted and cache should be less than allowed max"); 119 | } else { 120 | STAssertTrue(memoryCacheSize == totalSize, @"No images deleted, cache should be equal total size of images"); 121 | } 122 | } 123 | 124 | - (void) tearDown { 125 | [imageCache_ clearMemoryCache]; 126 | [imageCache_ release], imageCache_ = nil; 127 | } 128 | 129 | #pragma mark Helper Functions 130 | 131 | - (UIImage *) fakeImageForText:(NSString *) text size:(CGSize) size{ 132 | UIGraphicsBeginImageContext(size); 133 | CGContextRef context = UIGraphicsGetCurrentContext(); 134 | CGRect rect = CGRectMake(0.0, 0.0, size.width, size.height); 135 | CGContextSetRGBFillColor(context, 0.0, 0.0, 1.0, 1.0); 136 | CGContextFillRect(context, rect); 137 | CGContextSetRGBFillColor(context, 0.0, 1.0, 1.0, 1.0); 138 | [text drawInRect:rect withFont:[UIFont boldSystemFontOfSize:64.0]]; 139 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 140 | UIGraphicsEndImageContext(); 141 | 142 | return image; 143 | } 144 | 145 | #endif 146 | 147 | @end 148 | -------------------------------------------------------------------------------- /CVLibrary/CVLibrary.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CVLibrary.h 3 | * CVLibrary 4 | * 5 | * Created by Kerem Karatal on 4/11/09. 6 | * Copyright 2009 Coding Ventures. All rights reserved. 7 | * 8 | */ 9 | 10 | #import "CVThumbnailView.h" 11 | #import "CVThumbnailViewCell.h" 12 | #import "CVThumbnailViewController.h" 13 | #import "CVImageAdorner.h" 14 | #import "CVRoundedRectBorder.h" 15 | #import "CVPolygonBorder.h" 16 | #import "CVEllipseBorder.h" 17 | #import "UIImage+Adornments.h" 18 | #import "CVSettingsViewController.h" 19 | #import "CellHandler.h" -------------------------------------------------------------------------------- /CVLibrary/CVLibrary.xcodeproj/TemplateIcon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keremk/CViPhoneLibrary/a845c169916c0dea05680773b10e85f8020ae700/CVLibrary/CVLibrary.xcodeproj/TemplateIcon.icns -------------------------------------------------------------------------------- /CVLibrary/CVLibrary_Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'CVLibrary' target in the 'CVLibrary' project. 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #import 8 | #endif 9 | 10 | // Taken from http://iPhoneIncubator.com/blog/debugging/the-evolution-of-a-replacement-for-nslog 11 | // DLog is almost a drop-in replacement for NSLog 12 | // DLog(); 13 | // DLog(@"here"); 14 | // DLog(@"value: %d", x); 15 | // Unfortunately this doesn't work DLog(aStringVariable); you have to do this instead DLog(@"%@", aStringVariable); 16 | #ifdef DEBUG 17 | # define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__); 18 | #else 19 | # define DLog(...) 20 | #endif 21 | 22 | // ALog always displays output regardless of the DEBUG setting 23 | #define ALog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__); -------------------------------------------------------------------------------- /CVLibrary/CVPolygonBorder.h: -------------------------------------------------------------------------------- 1 | // 2 | // CVPolygonBorder.h 3 | // CVLibrary 4 | // 5 | // Created by Kerem Karatal on 9/10/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CVStyleProtocols.h" 11 | 12 | /*! 13 | @abstract The CVPolygonBorder class describes how to provide a polygon border for a given image. 14 | 15 | @discussion The image is cropped to stay within the border. 16 | */ 17 | @interface CVPolygonBorder : NSObject { 18 | CGFloat width_; 19 | CGFloat rotationAngle_; 20 | UIColor *color_; 21 | NSUInteger numOfSides_; 22 | } 23 | 24 | /*! 25 | @abstract The number of sides for the polygon. 26 | 27 | @discussion Must be greater than or equal to 3. 28 | */ 29 | @property (nonatomic) NSUInteger numOfSides; 30 | /*! 31 | @abstract The rotation angle in radians when drawing the polygon. 32 | 33 | @discussion 34 | */ 35 | @property (nonatomic) CGFloat rotationAngle; 36 | @end 37 | -------------------------------------------------------------------------------- /CVLibrary/CVPolygonBorder.m: -------------------------------------------------------------------------------- 1 | // 2 | // CVPolygonBorder.m 3 | // CVLibrary 4 | // 5 | // Created by Kerem Karatal on 9/10/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import "CVPolygonBorder.h" 10 | #include "CGUtils.h" 11 | 12 | @interface CVPolygonBorder() 13 | - (CGSize) sizeAfterRenderingGivenInitialSize:(CGSize) size; 14 | @end 15 | 16 | @implementation CVPolygonBorder 17 | @synthesize numOfSides = numOfSides_; 18 | @synthesize color = color_; 19 | @synthesize width = width_; 20 | @synthesize rotationAngle = rotationAngle_; 21 | 22 | - (void) dealloc { 23 | [color_ release], color_ = nil; 24 | [super dealloc]; 25 | } 26 | 27 | #define DEFAULT_ANGLE M_PI 28 | #define DEFAULT_NUM_OF_SIDES 5 // Pentagon by default 29 | - (id) init { 30 | self = [super init]; 31 | if (self != nil) { 32 | // Set the defaults 33 | numOfSides_ = DEFAULT_NUM_OF_SIDES; 34 | rotationAngle_ = M_PI; 35 | } 36 | return self; 37 | } 38 | 39 | #pragma mark CVRenderPath 40 | 41 | - (void) drawInContext:(CGContextRef) context forImageSize:(CGSize) imageSize { 42 | CGSize borderSize = [self sizeAfterRenderingGivenInitialSize:imageSize]; 43 | CGFloat radius = MIN(borderSize.width, borderSize.height) / 2.0; 44 | 45 | CGRect borderRect = CGRectMake(0.0, 0.0, borderSize.width, borderSize.height); 46 | if (self.width > 0.0) { 47 | // Prepare the rounded rect path (or simple rect if radius = 0) 48 | 49 | CGContextBeginPath(context); 50 | CVAddPolygonToPath(context, borderRect, radius, numOfSides_, rotationAngle_); 51 | CGContextClosePath(context); 52 | CGContextSetFillColorWithColor(context, [self.color CGColor]); 53 | CGContextDrawPath(context, kCGPathFill); 54 | } 55 | 56 | // Clip the image with rounded rect 57 | CGContextBeginPath(context); 58 | CVAddPolygonToPath(context, borderRect, (radius - self.width), numOfSides_, rotationAngle_); 59 | CGContextClosePath(context); 60 | CGContextClip(context); 61 | } 62 | 63 | - (CGSize) sizeRequiredForRendering { 64 | 65 | return CGSizeMake(self.width, self.width); 66 | } 67 | 68 | - (CGSize) sizeAfterRenderingGivenInitialSize:(CGSize) size { 69 | CGSize adornedImageSize = size; 70 | 71 | adornedImageSize.width += self.width; 72 | adornedImageSize.height += self.width; 73 | return adornedImageSize; 74 | } 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /CVLibrary/CVRoundedRectBorder.h: -------------------------------------------------------------------------------- 1 | // 2 | // CVRoundedRectShape.h 3 | // CVLibrary 4 | // 5 | // Created by Kerem Karatal on 9/8/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CVStyleProtocols.h" 11 | 12 | struct CVBorderDimensions { 13 | CGFloat top; 14 | CGFloat right; 15 | CGFloat bottom; 16 | CGFloat left; 17 | }; 18 | typedef struct CVBorderDimensions CVBorderDimensions; 19 | 20 | static inline CVBorderDimensions 21 | CVBorderDimensionsMake(CGFloat top, CGFloat right, CGFloat bottom, CGFloat left) { 22 | CVBorderDimensions dimensions; 23 | dimensions.top = top; 24 | dimensions.right = right; 25 | dimensions.bottom = bottom; 26 | dimensions.left = left; 27 | 28 | return dimensions; 29 | } 30 | 31 | /*! 32 | @abstract The CVRoundedRectBorder class describes how to provide a rounded rectangle border for a given image. 33 | 34 | @discussion The image is cropped to stay within the border. 35 | */ 36 | @interface CVRoundedRectBorder : NSObject { 37 | @private 38 | CGFloat width_; 39 | UIColor *color_; 40 | 41 | CGFloat cornerOvalWidth_; 42 | CGFloat cornerOvalHeight_; 43 | CVBorderDimensions dimensions_; 44 | CGFloat radius_; 45 | } 46 | 47 | /*! 48 | @abstract The width of the border for the top,right, bottom and left. 49 | 50 | @discussion Specify this only if the border width differs on top,right, bottom or left. Otherwise if same, just use the width property, and all these values will be set to the same width property. 51 | */ 52 | @property (nonatomic) CVBorderDimensions dimensions; 53 | /*! 54 | @abstract Specify the oval width of the rounded corners of the rounded rectangle. 55 | 56 | @discussion Specify this if cornerOvalWidth != cornerOvalHeight. Otherwise use the radius property. 57 | */ 58 | @property (nonatomic) CGFloat cornerOvalWidth; 59 | /*! 60 | @abstract Specify the oval height of the rounded corners of the rounded rectangle. 61 | 62 | @discussion Specify this if cornerOvalWidth != cornerOvalHeight. Otherwise use the radius property. 63 | */ 64 | @property (nonatomic) CGFloat cornerOvalHeight; 65 | /*! 66 | @abstract Specify the radius for the rounded corners of the rounded rectangle. 67 | 68 | @discussion 69 | */ 70 | @property (nonatomic) CGFloat radius; 71 | 72 | @end 73 | -------------------------------------------------------------------------------- /CVLibrary/CVRoundedRectBorder.m: -------------------------------------------------------------------------------- 1 | // 2 | // CVRoundedRectShape.m 3 | // CVLibrary 4 | // 5 | // Created by Kerem Karatal on 9/8/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import "CVRoundedRectBorder.h" 10 | #include "CGUtils.h" 11 | 12 | @interface CVRoundedRectBorder() 13 | - (CGSize) sizeAfterRenderingGivenInitialSize:(CGSize) size; 14 | @end 15 | 16 | 17 | @implementation CVRoundedRectBorder 18 | @synthesize cornerOvalWidth = cornerOvalWidth_; 19 | @synthesize cornerOvalHeight = cornerOvalHeight_; 20 | @synthesize dimensions = dimensions_; 21 | @synthesize radius = radius_; 22 | @synthesize color = color_; 23 | @synthesize width = width_; 24 | 25 | - (void) dealloc { 26 | [color_ release], color_ = nil; 27 | [super dealloc]; 28 | } 29 | 30 | - (id) init { 31 | self = [super init]; 32 | if (self != nil) { 33 | // Set the defaults 34 | radius_ = 0.0; // Not rounded by default 35 | cornerOvalWidth_ = 0.0; 36 | cornerOvalHeight_ = 0.0; 37 | } 38 | return self; 39 | } 40 | 41 | - (void) setWidth:(CGFloat) width { 42 | // Set the dimensions as well 43 | dimensions_ = CVBorderDimensionsMake(width, width, width, width); 44 | width_ = width; 45 | } 46 | 47 | - (void) setRadius:(CGFloat) radius { 48 | cornerOvalWidth_ = radius * 2; 49 | cornerOvalHeight_ = cornerOvalWidth_; 50 | radius_ = radius; 51 | } 52 | 53 | - (void) setDimensions:(CVBorderDimensions) dimensions { 54 | dimensions_.top = abs(dimensions.top); 55 | dimensions_.bottom = abs(dimensions.bottom); 56 | dimensions_.left = abs(dimensions.left); 57 | dimensions_.right = abs(dimensions.right); 58 | } 59 | 60 | #pragma mark CVRenderPath 61 | 62 | - (void) drawInContext:(CGContextRef) context forImageSize:(CGSize) imageSize { 63 | CGSize borderSize = [self sizeAfterRenderingGivenInitialSize:imageSize]; 64 | 65 | CGRect borderRect; 66 | if (self.dimensions.left > 0.0 || self.dimensions.right > 0.0 || self.dimensions.top > 0.0 || self.dimensions.bottom > 0.0) { 67 | // Prepare the rounded rect path (or simple rect if radius = 0) 68 | borderRect = CGRectMake(0.0, 0.0, borderSize.width, borderSize.height); 69 | CGContextBeginPath(context); 70 | CVAddRoundedRectToPath(context, borderRect, self.cornerOvalWidth, self.cornerOvalHeight); 71 | CGContextClosePath(context); 72 | CGContextSetFillColorWithColor(context, [self.color CGColor]); 73 | CGContextDrawPath(context, kCGPathFill); 74 | } 75 | 76 | // Calculate the offset based on the border: 77 | CGPoint offset = CGPointMake(self.dimensions.left, self.dimensions.top); 78 | CGContextTranslateCTM(context, offset.x, offset.y); 79 | 80 | // Clip the image with rounded rect 81 | CGRect imageRect; 82 | if (self.cornerOvalWidth > 0.0 && self.cornerOvalHeight > 0.0) { 83 | imageRect = CGRectMake(0.0, 0.0, imageSize.width, imageSize.height); 84 | CGContextBeginPath(context); 85 | CVAddRoundedRectToPath(context, imageRect, self.cornerOvalWidth, self.cornerOvalHeight); 86 | CGContextClosePath(context); 87 | CGContextClip(context); 88 | } 89 | } 90 | 91 | - (CGSize) sizeAfterRenderingGivenInitialSize:(CGSize) size { 92 | CGSize adornedImageSize = size; 93 | 94 | adornedImageSize.width += self.dimensions.left + self.dimensions.right; 95 | adornedImageSize.height += self.dimensions.top + self.dimensions.bottom; 96 | return adornedImageSize; 97 | } 98 | 99 | - (CGSize) sizeRequiredForRendering { 100 | 101 | return CGSizeMake(self.dimensions.left + self.dimensions.right, self.dimensions.top + self.dimensions.bottom); 102 | } 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /CVLibrary/CVSettingsViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // CVSettingsViewController.h 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 7/2/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CellHandler.h" 11 | #import "CVLibrary.h" 12 | 13 | @class CVSettingsViewController; 14 | 15 | @protocol CVSettingsViewControllerDelegate 16 | - (void) configurationUpdatedForGridConfigViewController:(CVSettingsViewController *) controller; 17 | @end 18 | 19 | @interface CVSettingsViewController : UITableViewController { 20 | NSArray *configSections_; 21 | id delegate_; 22 | id settingsData_; 23 | NSMutableArray *cellHandlers_; 24 | NSMutableArray *sectionNames_; 25 | } 26 | 27 | @property (assign) id delegate; 28 | @property (nonatomic, retain) id settingsData; 29 | @end 30 | 31 | 32 | -------------------------------------------------------------------------------- /CVLibrary/CVSettingsViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // CVSettingsViewController.m 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 7/2/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import "CVSettingsViewController.h" 10 | 11 | @implementation CVSettingsViewController 12 | @synthesize delegate = delegate_; 13 | @synthesize settingsData = settingsData_; 14 | 15 | - (void)dealloc { 16 | [cellHandlers_ release], cellHandlers_ = nil; 17 | [sectionNames_ release], sectionNames_ = nil; 18 | [super dealloc]; 19 | } 20 | 21 | - (void) createCellHandlers { 22 | if (!cellHandlers_) { 23 | NSString *filePath = [[NSBundle mainBundle] pathForResource:@"ConfigOptions" ofType:@"plist"]; 24 | NSData *pListData = [NSData dataWithContentsOfFile:filePath]; 25 | NSPropertyListFormat format; 26 | NSString *error; 27 | NSArray *configSections = (NSArray *) [NSPropertyListSerialization propertyListFromData:pListData 28 | mutabilityOption:NSPropertyListImmutable 29 | format:&format 30 | errorDescription:&error]; 31 | if (!configSections) { 32 | // This is some catastrophic error 33 | // TODO: figure out what to do. 34 | NSLog(@"%@", error); 35 | [error release]; 36 | } else { 37 | cellHandlers_ = [[NSMutableArray alloc] init]; 38 | sectionNames_ = [[NSMutableArray alloc] init]; 39 | 40 | for (NSInteger i = 0; i < [configSections count]; i++) { 41 | NSMutableArray *cellHandlerRows = [[NSMutableArray alloc] init]; 42 | for (NSInteger j = 0; j < [[[configSections objectAtIndex:i] objectForKey:@"sectionOptions"] count]; j++) { 43 | NSDictionary *cellConfig = [[[configSections objectAtIndex:i] 44 | objectForKey:@"sectionOptions"] 45 | objectAtIndex:j]; 46 | 47 | Class cellHandlerClass = NSClassFromString([cellConfig objectForKey:@"cellHandlerClass"]); 48 | id cellHandler = [[cellHandlerClass alloc] init]; 49 | NSString *keyPath = [cellConfig objectForKey:@"keyPath"]; 50 | if (nil != keyPath) { 51 | cellHandler.keyPath = [NSString stringWithFormat:@"settingsData.%@", keyPath]; 52 | } 53 | cellHandler.label = [cellConfig objectForKey:@"label"]; 54 | cellHandler.identifier = [cellConfig objectForKey:@"identifier"]; 55 | NSString *optionsString; 56 | if (optionsString = [cellConfig objectForKey:@"options"]) { 57 | cellHandler.options = [optionsString componentsSeparatedByString:@","]; 58 | } 59 | cellHandler.delegate = self; 60 | NSString *converterClassName; 61 | if (converterClassName = [cellConfig objectForKey:@"dataConverter"]) { 62 | cellHandler.dataConverterClassName = converterClassName; 63 | } 64 | NSString *keyboardType; 65 | if (keyboardType = [cellConfig objectForKey:@"keyboardType"]) { 66 | cellHandler.keyboardType = keyboardType; 67 | } 68 | [cellHandlerRows addObject:cellHandler]; 69 | [cellHandler release]; 70 | } 71 | [cellHandlers_ addObject:cellHandlerRows]; 72 | [sectionNames_ addObject:[[configSections objectAtIndex:i] objectForKey:@"sectionName"]]; 73 | [cellHandlerRows release]; 74 | } 75 | 76 | } 77 | 78 | } 79 | } 80 | 81 | - (void)viewWillDisappear:(BOOL)animated { 82 | [super viewWillDisappear:animated]; 83 | 84 | [self.delegate configurationUpdatedForGridConfigViewController:self]; 85 | } 86 | 87 | 88 | - (void)didReceiveMemoryWarning { 89 | // Releases the view if it doesn't have a superview. 90 | [super didReceiveMemoryWarning]; 91 | 92 | // Release any cached data, images, etc that aren't in use. 93 | } 94 | 95 | - (void)viewDidUnload { 96 | // Release any retained subviews of the main view. 97 | // e.g. self.myOutlet = nil; 98 | } 99 | 100 | 101 | #pragma mark Table view methods 102 | 103 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 104 | if (!cellHandlers_) { 105 | [self createCellHandlers]; 106 | } 107 | return [cellHandlers_ count]; 108 | } 109 | 110 | 111 | // Customize the number of rows in the table view. 112 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 113 | if (!cellHandlers_) { 114 | [self createCellHandlers]; 115 | } 116 | return [[cellHandlers_ objectAtIndex:section] count]; 117 | } 118 | 119 | 120 | // Customize the appearance of table view cells. 121 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 122 | id cellHandler = [[cellHandlers_ objectAtIndex:indexPath.section] objectAtIndex:indexPath.row]; 123 | id data = [self valueForKeyPath:cellHandler.keyPath]; 124 | UITableViewCell *cell = [cellHandler tableView:tableView cellForRowAtIndexPath:indexPath usingData:data]; 125 | 126 | return cell; 127 | } 128 | 129 | - (id) valueForUndefinedKey:(NSString *) key { 130 | return nil; 131 | } 132 | 133 | - (void) setValue:(id) value forUndefinedKey:(NSString *) key { 134 | // Do nothing - no exception raised 135 | NSLog(@"Undefined Key = %@", key); 136 | } 137 | 138 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 139 | id cellHandler = [[cellHandlers_ objectAtIndex:indexPath.section] objectAtIndex:indexPath.row]; 140 | 141 | if ([cellHandler respondsToSelector:@selector(tableView:didSelectRowAtIndexPath:usingNavigationController:)]) { 142 | [cellHandler tableView:tableView didSelectRowAtIndexPath:indexPath usingNavigationController:[self navigationController]]; 143 | } 144 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 145 | } 146 | 147 | - (void) cellData:(id) data changedForHandler:(id) handler { 148 | id convertedData = data; 149 | if ([handler respondsToSelector:@selector(dataConverterClassName)]) { 150 | Class converterClass = NSClassFromString(handler.dataConverterClassName); 151 | 152 | if (nil != converterClass) { 153 | id dataConverter = [[converterClass alloc] init]; 154 | convertedData = [dataConverter convertFromString:data]; 155 | [dataConverter release]; 156 | } 157 | } 158 | [self setValue:convertedData forKeyPath:handler.keyPath]; 159 | [self.tableView reloadData]; 160 | } 161 | 162 | - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { 163 | if (!cellHandlers_) { 164 | [self createCellHandlers]; 165 | } 166 | return [sectionNames_ objectAtIndex:section]; 167 | } 168 | 169 | 170 | @end 171 | 172 | -------------------------------------------------------------------------------- /CVLibrary/CVShadowStyle.h: -------------------------------------------------------------------------------- 1 | // 2 | // CVShadowStyle.h 3 | // CVLibrary 4 | // 5 | // Created by Kerem Karatal on 8/10/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CVStyleProtocols.h" 11 | 12 | /*! 13 | @abstract The CVShadowStyle class is used for describing how the shadow adornments can be rendered on a given image. 14 | 15 | @discussion 16 | */ 17 | @interface CVShadowStyle : NSObject { 18 | @private 19 | CGSize offset_; 20 | CGFloat blur_; 21 | UIColor *color_; 22 | } 23 | 24 | /*! 25 | @abstract The color of the shadow to be rendered. 26 | 27 | @discussion 28 | */ 29 | @property (nonatomic, retain) UIColor *color; 30 | /*! 31 | @abstract Specifies a translation of the context’s coordinate system, to establish an offset for the shadow. 32 | 33 | @discussion For example, {0,0} specifies a light source immediately above the screen. Note that the coordinate system origin is the lower-left corner in this case (in line with Quartz coordinate system.) Default is {0, 0} - no shadow. 34 | */ 35 | @property (nonatomic) CGSize offset; 36 | /*! 37 | @abstract A non-negative number specifying the amount of blur. 38 | 39 | @discussion Default is 0.0. 40 | */ 41 | @property (nonatomic) CGFloat blur; 42 | 43 | /*! 44 | @abstract The offset that takes into account the gradient caused by non-zero blur. 45 | 46 | @discussion This is useful in calculating the bounding rectangle of the image. For example, as the shadow gradients from black to white due to blur, if we cut it off at the offset number of pixels, the image looks cut-off. 47 | @result Offset + a set number of pixels. 48 | */ 49 | - (CGSize) offsetWithBlurPixels; 50 | @end 51 | -------------------------------------------------------------------------------- /CVLibrary/CVShadowStyle.m: -------------------------------------------------------------------------------- 1 | // 2 | // CVShadowStyle.m 3 | // CVLibrary 4 | // 5 | // Created by Kerem Karatal on 8/10/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import "CVShadowStyle.h" 10 | 11 | @interface CVShadowStyle() 12 | - (CGPoint) zeroClippedOffsetAsPoint; 13 | @end 14 | 15 | 16 | @implementation CVShadowStyle 17 | @synthesize color = color_; 18 | @synthesize offset = offset_; 19 | @synthesize blur = blur_; 20 | 21 | - (void) dealloc { 22 | [color_ release], color_ = nil; 23 | [super dealloc]; 24 | } 25 | 26 | - (id) init { 27 | self = [super init]; 28 | if (self != nil) { 29 | offset_ = CGSizeZero; 30 | blur_ = 0.0; 31 | } 32 | return self; 33 | } 34 | 35 | #define SHADOW_BLUR_PIXELS 3 // Fudge factor used to take into account the fading of blur effect. This is a HACK!!! We need a better way to calculate this!!! 36 | 37 | - (CGPoint) zeroClippedOffsetAsPoint { 38 | // Take into account the shadow based on its direction 39 | 40 | CGPoint offset = CGPointZero; 41 | if (self.offset.width < 0) { 42 | offset.x = abs(self.offset.width) + SHADOW_BLUR_PIXELS; 43 | } 44 | if (self.offset.height < 0) { 45 | offset.y = abs(self.offset.height) + SHADOW_BLUR_PIXELS; 46 | } 47 | return offset; 48 | } 49 | 50 | - (CGSize) offsetWithBlurPixels { 51 | 52 | CGSize offset = CGSizeZero; 53 | if (self.offset.width < 0) { 54 | offset.width = self.offset.width - SHADOW_BLUR_PIXELS; 55 | } else if (self.offset.width > 0) { 56 | offset.width = self.offset.width + SHADOW_BLUR_PIXELS; 57 | } 58 | 59 | if (self.offset.height < 0) { 60 | offset.height = self.offset.height - SHADOW_BLUR_PIXELS; 61 | } else if (self.offset.height > 0) { 62 | offset.height = self.offset.height + SHADOW_BLUR_PIXELS; 63 | } 64 | return offset; 65 | } 66 | 67 | #pragma mark CVRenderStyle 68 | 69 | 70 | - (void) drawInContext:(CGContextRef) context forImageSize:(CGSize) imageSize { 71 | CGContextSetShadow(context, self.offset, self.blur); 72 | 73 | CGPoint effectiveOffset = [self zeroClippedOffsetAsPoint]; 74 | CGContextTranslateCTM(context, effectiveOffset.x, effectiveOffset.y); 75 | } 76 | 77 | - (CGSize) sizeAfterRenderingGivenInitialSize:(CGSize) size { 78 | CGSize adornedImageSize = size; 79 | 80 | adornedImageSize.width += abs(self.offset.width) + SHADOW_BLUR_PIXELS; 81 | adornedImageSize.height += abs(self.offset.height) + SHADOW_BLUR_PIXELS; 82 | return adornedImageSize; 83 | } 84 | 85 | - (CGSize) sizeRequiredForRendering { 86 | CGFloat width = abs(self.offset.width) + SHADOW_BLUR_PIXELS; 87 | CGFloat height = abs(self.offset.height) + SHADOW_BLUR_PIXELS; 88 | 89 | CGSize size = CGSizeMake(width, height); 90 | 91 | return size; 92 | } 93 | 94 | @end 95 | 96 | -------------------------------------------------------------------------------- /CVLibrary/CVStyleProtocols.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CVStyleProtocols.h 3 | * CVLibrary 4 | * 5 | * Created by Kerem Karatal on 8/10/09. 6 | * Copyright 2009 Coding Ventures. All rights reserved. 7 | * 8 | */ 9 | 10 | /*! 11 | @abstract Protocol required by all image adorner implementations. 12 | 13 | @discussion 14 | */ 15 | @protocol CVRenderStyle 16 | @required 17 | /*! 18 | @abstract Draw the image in the given context 19 | 20 | @discussion 21 | @param context Context to be drawn into. 22 | @param imageSize The original input image size. The final size is calculated based on the additional requirements of the adornment drawn. For example for a simple rectangular border the image size will grow by the width of the border. 23 | */ 24 | - (void) drawInContext:(CGContextRef) context forImageSize:(CGSize) imageSize; 25 | /*! 26 | @abstract The additional size required for rendering this adornment. 27 | 28 | @discussion For example for a rectangular border it is: width = 2 * width of border; height = 2 * width of border. 29 | @result The size required. 30 | */ 31 | - (CGSize) sizeRequiredForRendering; 32 | @end 33 | 34 | /*! 35 | @abstract Protocol required by the border style image adorner implementations. 36 | 37 | @discussion 38 | */ 39 | @protocol CVBorderStyle 40 | @required 41 | /*! 42 | @abstract The color of the border to be rendered. 43 | 44 | @discussion 45 | */ 46 | @property (nonatomic, retain) UIColor *color; 47 | /*! 48 | @abstract The width of the border to be rendered. 49 | 50 | @discussion 51 | */ 52 | @property (nonatomic) CGFloat width; 53 | @end -------------------------------------------------------------------------------- /CVLibrary/CVThumbnailViewCell.h: -------------------------------------------------------------------------------- 1 | // 2 | // CVThumbnailViewCell.h 3 | // CVLibrary 4 | // 5 | // Created by Kerem Karatal on 1/23/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CVImageAdorner.h" 11 | #import "CVTitleStyle.h" 12 | 13 | @protocol CVThumbnailViewCellDelegate; 14 | 15 | /*! 16 | @abstract The CVThumbnailViewCell class defines the attributes and behavior of the cells that appear in CVThumbnailView objects. 17 | 18 | @discussion 19 | */ 20 | @interface CVThumbnailViewCell : UIView { 21 | @private 22 | id delegate_; 23 | NSIndexPath *indexPath_; 24 | CGPoint touchLocation_; // Location of touch in own coordinates (stays constant during dragging). 25 | BOOL dragging_; 26 | BOOL editing_; 27 | BOOL selected_; 28 | CGRect home_; 29 | UIImage *thumbnailImage_; 30 | NSString *title_; 31 | CVImageAdorner *imageAdorner_; 32 | NSString *imageUrl_; 33 | } 34 | 35 | /*! 36 | @abstract The container object that CVThumbnailViewCells communicates with. (Implemented by CVThumbnailView) 37 | 38 | @discussion This is automatically set by the CVThumbnailView. Should not be set by the clients. 39 | */ 40 | @property (nonatomic, assign) id delegate; 41 | /*! 42 | @abstract Instance of NSIndexPath object that points to the index this cell corresponds to. 43 | 44 | @discussion 45 | */ 46 | @property (nonatomic, retain) NSIndexPath *indexPath; 47 | /*! 48 | @abstract Indicates whether the cell is selected. 49 | 50 | @discussion 51 | */ 52 | @property (nonatomic) BOOL selected; 53 | /*! 54 | @abstract The url of the image to be displayed in the cell. 55 | 56 | @discussion The clients should set this value to make sure that image can be loaded asynchronously, and also the image adornments are performed only for images that are loaded asynchronously by setting this property. 57 | */ 58 | @property (nonatomic, copy) NSString *imageUrl; 59 | /*! 60 | @abstract The title to be displayed in the lower bottom of the cell. 61 | 62 | @discussion 63 | */ 64 | @property (nonatomic, copy) NSString *title; 65 | /*! 66 | @abstract The image to be displayed in the cell. 67 | 68 | @discussion If you set this directly the image adornments will NOT be applied as specified in the thumbnail view. 69 | */ 70 | @property (nonatomic, assign) UIImage *image; 71 | 72 | /*! 73 | @abstract Initializes a thumbnail view cell with a frame and a reuse identifier and returns it to the caller. 74 | 75 | @discussion 76 | @param frame The frame rectangle of the cell. Because the table view automatically positions the cell and makes it the optimal size, you can pass in CGRectZero in most cases. 77 | @param identifier A string used to identify the cell object if it is to be reused for drawing multiple cells of a thumbnail view. 78 | @result An initialized CVThumbnailViewCell object or nil if the object could not be created. 79 | */ 80 | - (id) initWithFrame:(CGRect)frame reuseIdentifier:(NSString *) identifier; 81 | @end 82 | 83 | /*! 84 | @abstract This protocol is implemented by the CVThumbnailView and defines the contract between the thumbnail view and its cell. 85 | 86 | @discussion 87 | */ 88 | @protocol CVThumbnailViewCellDelegate 89 | @required 90 | /*! 91 | @abstract Instance of the class used to adorn thumbnails in the thumbnail view. 92 | 93 | @discussion All thumbnails except for the selected thumbnail is adorned using the instance of this class. 94 | */ 95 | @property (nonatomic, retain) CVImageAdorner *imageAdorner; 96 | /*! 97 | @abstract Instance of the class used to adorn the selected thumbnail in the thumbnail view. 98 | 99 | @discussion Selected thumbnail is adorned using the instance of this class. 100 | */ 101 | @property (nonatomic, retain) CVImageAdorner *selectedImageAdorner; 102 | /*! 103 | @abstract The delete sign icon displayed when the cell is in edit mode. 104 | 105 | @discussion The thumbnail view provides a default icon if this is not set explicitly by the client. 106 | */ 107 | @property (nonatomic, retain) UIImage *deleteSignIcon; 108 | /*! 109 | @abstract Border width for the default selection adornment provided by the thumbnail view. 110 | 111 | @discussion The default selection adornment is a rounded rectangle around the whole cell. See showDefaultSelectionEffect to turn this on/off. Default is 3 pixels. 112 | */ 113 | @property (nonatomic) CGFloat selectionBorderWidth; 114 | /*! 115 | @abstract Border color for the default selection adornment provided by the thumbnail view. 116 | 117 | @discussion The default selection adornment is a rounded rectangle around the whole cell. See showDefaultSelectionEffect to turn this on/off. Default is red. 118 | */ 119 | @property (nonatomic, copy) UIColor *selectionBorderColor; 120 | /*! 121 | @abstract Indicates if the edit mode is on/off. 122 | 123 | @discussion YES if the edit mode is enabled. Default is NO. If this is NO, the setting editing on thumbnail view has no effect. 124 | */ 125 | @property (nonatomic) BOOL editModeEnabled; 126 | /*! 127 | @abstract Indicates if the default selection adornment should be provided or not. 128 | 129 | @discussion Default is YES. If YES, then a simple rectangular border is shown around the selected cell if the allowsSelection is also YES. 130 | */ 131 | @property (nonatomic) BOOL showDefaultSelectionEffect; 132 | /*! 133 | @abstract Indicates whether the titles in the cell can be shown. 134 | 135 | @discussion Default is NO. If YES, then the titles are shown at the lower bottom of the cell. A certain spacing depending on the font size of the title is allocated. 136 | */ 137 | @property (nonatomic) BOOL showTitles; 138 | /*! 139 | @abstract Instance of CVTitleStyle class to be used for displaying titles in the cells. 140 | 141 | @discussion 142 | */ 143 | @property (nonatomic, retain) CVTitleStyle *titleStyle; 144 | 145 | @optional 146 | /*! 147 | @abstract Tells the thumbnail view that the delete sign for the cell is tapped. 148 | 149 | @discussion 150 | @param cell Instance of CVThumbnailViewCell for which the delete sign is tapped. 151 | */ 152 | - (void) deleteSignWasTapped:(CVThumbnailViewCell *) cell; 153 | /*! 154 | @abstract Tells the thumbnail view that the cell is tapped. 155 | 156 | @discussion 157 | @param cell Instance of CVThumbnailViewCell that is tapped. 158 | */ 159 | - (void) thumbnailViewCellWasTapped:(CVThumbnailViewCell *) cell; 160 | /*! 161 | @abstract Tells the thumbnail view that the cell is started to be tracked. 162 | 163 | @discussion 164 | @param cell Instance of CVThumbnailViewCell that is tapped. 165 | */ 166 | - (void) thumbnailViewCellStartedTracking:(CVThumbnailViewCell *) cell; 167 | /*! 168 | @abstract Tells the thumbnail view that the cell is moved. 169 | 170 | @discussion 171 | @param cell Instance of CVThumbnailViewCell that is tapped. 172 | */ 173 | - (void) thumbnailViewCellMoved:(CVThumbnailViewCell *) cell; 174 | /*! 175 | @abstract Tells the thumbnail view that the cell is stopped being tracked. 176 | 177 | @discussion 178 | @param cell Instance of CVThumbnailViewCell that is tapped. 179 | */ 180 | - (void) thumbnailViewCellStoppedTracking:(CVThumbnailViewCell *) cell; 181 | @end 182 | 183 | -------------------------------------------------------------------------------- /CVLibrary/CVThumbnailViewCell.m: -------------------------------------------------------------------------------- 1 | // 2 | // CVThumbnailViewCell.m 3 | // CVLibrary 4 | // 5 | // Created by Kerem Karatal on 1/23/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import "CVThumbnailViewCell.h" 10 | #import "CVThumbnailViewCell_Private.h" 11 | #import "CVThumbnailView.h" 12 | #import "CVTitleStyle.h" 13 | #include "CGUtils.h" 14 | 15 | #define DRAG_THRESHOLD 10 16 | 17 | CGFloat distanceBetweenPoints(CGPoint a, CGPoint b) { 18 | CGFloat deltaX = a.x - b.x; 19 | CGFloat deltaY = a.y - b.y; 20 | return sqrtf( (deltaX * deltaX) + (deltaY * deltaY) ); 21 | } 22 | 23 | @implementation CVThumbnailViewCell 24 | @synthesize delegate = delegate_; 25 | @synthesize indexPath = indexPath_; 26 | @synthesize home = home_; 27 | @synthesize touchLocation = touchLocation_; 28 | @synthesize thumbnailImage = thumbnailImage_; 29 | @synthesize editing = editing_; 30 | @synthesize imageUrl = imageUrl_; 31 | @synthesize selected = selected_; 32 | @synthesize title = title_; 33 | 34 | - (void)dealloc { 35 | [title_ release], title_ = nil; 36 | [indexPath_ release], indexPath_ = nil; 37 | [thumbnailImage_ release], thumbnailImage_ = nil; 38 | [imageUrl_ release], imageUrl_ = nil; 39 | [super dealloc]; 40 | } 41 | 42 | - (id)initWithFrame:(CGRect)frame reuseIdentifier:(NSString *) identifier { 43 | if (self = [super initWithFrame:frame]) { 44 | [self setUserInteractionEnabled:YES]; 45 | [self setOpaque:NO]; 46 | editing_ = NO; 47 | imageUrl_ = nil; 48 | selected_ = NO; 49 | } 50 | return self; 51 | } 52 | 53 | - (void) setEditing:(BOOL) editing { 54 | if (editing != editing_) { 55 | editing_ = editing; 56 | [self setNeedsDisplay]; 57 | } 58 | } 59 | 60 | - (void) setSelected:(BOOL) selected { 61 | if (selected != selected_) { 62 | selected_ = selected; 63 | [self setNeedsDisplay]; 64 | } 65 | } 66 | 67 | - (UIImage *) deleteSignIcon { 68 | UIImage *deleteSignIcon = nil; 69 | if ([delegate_ respondsToSelector:@selector(deleteSignIcon)]) { 70 | deleteSignIcon = [delegate_ deleteSignIcon]; 71 | } 72 | return deleteSignIcon; 73 | } 74 | 75 | - (CVImageAdorner *) imageAdorner { 76 | CVImageAdorner *imageAdorner; 77 | if (self.selected) { 78 | imageAdorner = [delegate_ selectedImageAdorner]; 79 | } else { 80 | imageAdorner = [delegate_ imageAdorner]; 81 | } 82 | return imageAdorner; 83 | } 84 | 85 | #define CORNER_OVAL_WIDTH 10 86 | #define CORNER_OVAL_HEIGHT 10 87 | 88 | - (void) drawRect:(CGRect) rect { 89 | CGSize padding = CGSizeZero; 90 | if ([delegate_ respondsToSelector:@selector(editModeEnabled)]) { 91 | if ([delegate_ editModeEnabled]) { 92 | padding = [self.imageAdorner paddingRequiredForUpperLeftBadgeSize:[[self deleteSignIcon] size]]; 93 | } 94 | } 95 | CGPoint pointToDrawImage = CGPointMake(padding.width, padding.height); 96 | [self.thumbnailImage drawAtPoint:pointToDrawImage]; 97 | 98 | if (editing_) { 99 | CGRect deleteSignRect = [self deleteSignRect]; 100 | [[self deleteSignIcon] drawAtPoint:deleteSignRect.origin]; 101 | } 102 | 103 | CGContextRef context = UIGraphicsGetCurrentContext(); 104 | if (self.selected && [self.delegate showDefaultSelectionEffect]) { 105 | 106 | CGContextBeginPath(context); 107 | CVAddRoundedRectToPath(context, rect, CORNER_OVAL_WIDTH, CORNER_OVAL_HEIGHT); 108 | CGContextClosePath(context); 109 | UIColor *borderColor = [self.delegate selectionBorderColor]; 110 | CGContextSetStrokeColorWithColor(context, [borderColor CGColor]); 111 | CGFloat borderWidth = [self.delegate selectionBorderWidth]; 112 | CGContextSetLineWidth(context, borderWidth); 113 | CGContextDrawPath(context, kCGPathStroke); 114 | } 115 | 116 | if ([self.delegate showTitles]) { 117 | CVTitleStyle *titleStyle = [self.delegate titleStyle]; 118 | CGSize sizeRequiredForTitle = [self.title sizeWithFont:titleStyle.font]; 119 | CGFloat yPoint = self.frame.size.height - sizeRequiredForTitle.height; 120 | CGFloat xPoint = MAX(0.0, (self.frame.size.width - sizeRequiredForTitle.width) / 2.0); 121 | 122 | sizeRequiredForTitle.width = MIN(sizeRequiredForTitle.width, self.frame.size.width); 123 | 124 | // Draw the text background 125 | CGRect textBackgroundRect = CGRectMake(0.0, yPoint, self.frame.size.width, sizeRequiredForTitle.height); 126 | CGContextSetFillColorWithColor(context, [titleStyle.backgroundColor CGColor]); 127 | CGContextFillRect(context, textBackgroundRect); 128 | 129 | // Draw the text 130 | CGPoint titlePoint = CGPointMake(xPoint, yPoint); 131 | CGContextSetFillColorWithColor(context, [titleStyle.foregroundColor CGColor]); 132 | [self.title drawAtPoint:titlePoint forWidth:sizeRequiredForTitle.width withFont:titleStyle.font lineBreakMode:titleStyle.lineBreakMode]; 133 | } 134 | } 135 | 136 | - (CGFloat) deleteSignSideLength { 137 | CGSize deleteSignSize = [[self deleteSignIcon] size]; 138 | CGFloat deleteSignSideLength = deleteSignSize.width; 139 | 140 | return deleteSignSideLength; 141 | } 142 | 143 | - (CGRect) deleteSignRect { 144 | CGSize padding = [self.imageAdorner paddingRequiredForUpperLeftBadgeSize:[[self deleteSignIcon] size]]; 145 | 146 | CGFloat deleteSignOriginX, deleteSignOriginY; 147 | if (padding.width > 0) { 148 | deleteSignOriginX = 0; 149 | } else { 150 | deleteSignOriginX = padding.width; 151 | } 152 | 153 | if (padding.height > 0) { 154 | deleteSignOriginY = 0; 155 | } else { 156 | deleteSignOriginY = padding.height; 157 | } 158 | 159 | return CGRectMake(deleteSignOriginX, deleteSignOriginY, self.deleteSignSideLength, self.deleteSignSideLength); 160 | } 161 | 162 | - (UIImage *) image { 163 | return self.thumbnailImage; 164 | } 165 | 166 | - (void) setImage:(UIImage *)image { 167 | if (nil != image) { 168 | self.thumbnailImage = image; 169 | [self setNeedsDisplay]; 170 | } 171 | } 172 | 173 | #pragma mark Touch events 174 | 175 | - (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { 176 | if (editing_) { 177 | // store the location of the starting touch so we can decide when we've moved far enough to drag 178 | touchLocation_ = [[touches anyObject] locationInView:self]; 179 | // NSLog(@"Cell %f, %f", touchLocation_.x, touchLocation_.y); 180 | if ([delegate_ respondsToSelector:@selector(thumbnailViewCellStartedTracking:)]) 181 | [delegate_ thumbnailViewCellStartedTracking:self]; 182 | } 183 | } 184 | 185 | - (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { 186 | if (!editing_) return; 187 | 188 | // we want to establish a minimum distance that the touch has to move before it counts as dragging, 189 | // so that the slight movement involved in a tap doesn't cause the frame to move. 190 | 191 | CGPoint newTouchLocation = [[touches anyObject] locationInView:self]; 192 | 193 | if (dragging_) { 194 | float deltaX = newTouchLocation.x - touchLocation_.x; 195 | float deltaY = newTouchLocation.y - touchLocation_.y; 196 | [self moveByOffset:CGPointMake(deltaX, deltaY)]; 197 | } 198 | else if (distanceBetweenPoints(touchLocation_, newTouchLocation) > DRAG_THRESHOLD) { 199 | touchLocation_ = newTouchLocation; 200 | dragging_ = YES; 201 | } 202 | } 203 | 204 | - (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { 205 | if (dragging_) { 206 | [self goHome]; 207 | dragging_ = NO; 208 | } else if ([[touches anyObject] tapCount] == 1) { 209 | CGPoint touchLocation = [[touches anyObject] locationInView:self]; 210 | 211 | if (editing_ && CGRectContainsPoint(self.deleteSignRect, touchLocation)) { 212 | if ([delegate_ respondsToSelector:@selector(deleteSignWasTapped:)]) { 213 | [delegate_ deleteSignWasTapped:self]; 214 | } 215 | } else { 216 | if ([delegate_ respondsToSelector:@selector(thumbnailViewCellWasTapped:)]) 217 | [delegate_ thumbnailViewCellWasTapped:self]; 218 | } 219 | 220 | } 221 | 222 | if (editing_ && [delegate_ respondsToSelector:@selector(thumbnailViewCellStoppedTracking:)]) 223 | [delegate_ thumbnailViewCellStoppedTracking:self]; 224 | } 225 | 226 | - (void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event { 227 | if (!editing_) return; 228 | 229 | [self goHome]; 230 | dragging_ = NO; 231 | if ([delegate_ respondsToSelector:@selector(thumbnailViewCellStoppedTracking:)]) 232 | [delegate_ thumbnailViewCellStoppedTracking:self]; 233 | } 234 | 235 | - (void) goHome { 236 | CGFloat distanceFromHome = distanceBetweenPoints([self frame].origin, [self home].origin); // distance is in pixels 237 | CGFloat animationDuration = 0.1 + distanceFromHome * 0.001; 238 | [UIView beginAnimations:nil context:NULL]; 239 | [UIView setAnimationDuration:animationDuration]; 240 | [self setFrame:[self home]]; 241 | [UIView commitAnimations]; 242 | } 243 | 244 | - (void) moveByOffset:(CGPoint)offset { 245 | CGRect frame = [self frame]; 246 | frame.origin.x += offset.x; 247 | frame.origin.y += offset.y; 248 | [self setFrame:frame]; 249 | if ([delegate_ respondsToSelector:@selector(thumbnailViewCellMoved:)]) 250 | [delegate_ thumbnailViewCellMoved:self]; 251 | } 252 | 253 | @end 254 | 255 | -------------------------------------------------------------------------------- /CVLibrary/CVThumbnailViewCell_Private.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CVThumbnailViewCell_Private.h 3 | * CVLibrary 4 | * 5 | * Created by Kerem Karatal on 11/17/09. 6 | * Copyright 2009 Coding Ventures. All rights reserved. 7 | * 8 | */ 9 | #import 10 | 11 | @interface CVThumbnailViewCell() 12 | /*! 13 | @abstract The rectangle that defines the position of the cell. 14 | 15 | @discussion Mainly intended for use by the thumbnail view to control movements, deletions, insertions. 16 | */ 17 | @property (nonatomic, assign) CGRect home; 18 | /*! 19 | @abstract The location of the touch as tracked by touch movements. 20 | 21 | @discussion Mainly intended for use by the thumbnail view to control movements, deletions, insertions. 22 | */ 23 | @property (nonatomic, assign) CGPoint touchLocation; 24 | /*! 25 | @abstract Indicates whether the cell is in edit mode. 26 | 27 | @discussion By default a delete sign is displayed when in edit mode. 28 | */ 29 | @property (nonatomic) BOOL editing; 30 | @property (nonatomic, retain) UIImage *thumbnailImage; 31 | @property (nonatomic, readonly) CVImageAdorner *imageAdorner; 32 | 33 | - (UIImage *) deleteSignIcon; 34 | - (CGFloat) deleteSignSideLength; 35 | - (CGRect) deleteSignRect; 36 | 37 | - (void) goHome; 38 | - (void) moveByOffset:(CGPoint)offset; 39 | @end 40 | -------------------------------------------------------------------------------- /CVLibrary/CVThumbnailViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // CVThumbnailViewController.h 3 | // CVLibrary 4 | // 5 | // Created by Kerem Karatal on 1/22/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CVThumbnailView.h" 11 | #import "CVThumbnailViewCell.h" 12 | 13 | /*! 14 | @abstract The CVThumbnailViewController class creates a controller object that manages a table view. 15 | 16 | @discussion 17 | */ 18 | @interface CVThumbnailViewController : UIViewController { 19 | BOOL firstTimeDisplay_; 20 | CVThumbnailView *thumbnailView_; 21 | } 22 | 23 | /*! 24 | @abstract The instance of thumbnail view that is being controlled by CVThumbnailViewController. 25 | 26 | @discussion 27 | */ 28 | @property (nonatomic, readonly) CVThumbnailView *thumbnailView; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /CVLibrary/CVThumbnailViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // CVThumbnailViewController.m 3 | // CVLibrary 4 | // 5 | // Created by Kerem Karatal on 1/22/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import "CVThumbnailViewController.h" 10 | 11 | @interface CVThumbnailViewController() 12 | - (void) commonInit; 13 | @end 14 | 15 | @implementation CVThumbnailViewController 16 | @synthesize thumbnailView = thumbnailView_; 17 | 18 | - (void) dealloc { 19 | [thumbnailView_ release], thumbnailView_ = nil; 20 | [super dealloc]; 21 | } 22 | 23 | - (id) initWithCoder:(NSCoder *) coder { 24 | if (self = [super initWithCoder:coder]) { 25 | [self commonInit]; 26 | } 27 | return self; 28 | } 29 | 30 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { 31 | if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { 32 | [self commonInit]; 33 | } 34 | return self; 35 | } 36 | 37 | - (void) commonInit { 38 | firstTimeDisplay_ = YES; 39 | } 40 | 41 | // Implement loadView to create a view hierarchy programmatically, without using a nib. 42 | - (void)loadView { 43 | [super loadView]; 44 | 45 | thumbnailView_ = [[CVThumbnailView alloc] initWithFrame:[[self view] bounds]]; 46 | [thumbnailView_ setDataSource:self]; 47 | [thumbnailView_ setDelegate:self]; 48 | [thumbnailView_ setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight]; 49 | [[self view] addSubview:thumbnailView_]; 50 | [thumbnailView_ reloadData]; 51 | } 52 | 53 | - (void)viewWillAppear:(BOOL)animated { 54 | if (firstTimeDisplay_) { 55 | [self.view setAlpha:0]; 56 | [UIView beginAnimations:@"Test" context:nil]; 57 | [UIView setAnimationCurve:UIViewAnimationCurveEaseIn]; 58 | [UIView setAnimationDuration:0.8]; 59 | [self.view setAlpha:1.0]; 60 | [UIView commitAnimations]; 61 | firstTimeDisplay_ = NO; 62 | } 63 | [super viewWillAppear:animated]; 64 | } 65 | 66 | - (void) viewDidAppear:(BOOL) animated { 67 | [super viewDidAppear:animated]; 68 | [self.thumbnailView flashScrollIndicators]; 69 | } 70 | 71 | - (void)didReceiveMemoryWarning { 72 | [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview 73 | // Release anything that's not essential, such as cached data 74 | [thumbnailView_ resetCachedImages]; 75 | } 76 | 77 | #pragma mark CVThumbnailGridViewDelegate methods 78 | 79 | - (CVThumbnailViewCell *)thumbnailView:(CVThumbnailView *)thumbnailView cellAtIndexPath:(NSIndexPath *)indexPath { 80 | return nil; 81 | } 82 | 83 | - (NSInteger) numberOfCellsForThumbnailView:(CVThumbnailView *)thumbnailView { 84 | return 0; 85 | } 86 | 87 | @end 88 | -------------------------------------------------------------------------------- /CVLibrary/CVTitleStyle.h: -------------------------------------------------------------------------------- 1 | // 2 | // CVTextAdorner.h 3 | // CVLibrary 4 | // 5 | // Created by Kerem Karatal on 11/13/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | /*! 13 | @abstract The CVTitleStyle class describes how to render the titles in the thumbnails. 14 | 15 | @discussion 16 | */ 17 | @interface CVTitleStyle : NSObject { 18 | UIFont *font_; 19 | UILineBreakMode lineBreakMode_; 20 | UIColor *backgroundColor_; 21 | UIColor *foregroundColor_; 22 | } 23 | 24 | /*! 25 | @abstract Instance of UIFont class that describes the font to be used. 26 | 27 | @discussion Default is Verdana, 10pt 28 | */ 29 | @property (nonatomic, retain) UIFont *font; 30 | /*! 31 | @abstract Describes what happens when the text can not fit on one line. Assumes one-line always. 32 | 33 | @discussion Same as the UILineBreakMode in UIKit. Default is UILineBreakModeMiddleTruncation 34 | */ 35 | @property (nonatomic) UILineBreakMode lineBreakMode; 36 | /*! 37 | @abstract Text background color to be used. 38 | 39 | @discussion Stretches the full line regardless of the text length. Default is white. 40 | */ 41 | @property (nonatomic, retain) UIColor *backgroundColor; 42 | /*! 43 | @abstract Text foreground color to be used. 44 | 45 | @discussion Default is black. 46 | */ 47 | @property (nonatomic, retain) UIColor *foregroundColor; 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /CVLibrary/CVTitleStyle.m: -------------------------------------------------------------------------------- 1 | // 2 | // CVTextAdorner.m 3 | // CVLibrary 4 | // 5 | // Created by Kerem Karatal on 11/13/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import "CVTitleStyle.h" 10 | 11 | 12 | @implementation CVTitleStyle 13 | @synthesize font = font_; 14 | @synthesize lineBreakMode = lineBreakMode_; 15 | @synthesize backgroundColor = backgroundColor_; 16 | @synthesize foregroundColor = foregroundColor_; 17 | 18 | - (void) dealloc { 19 | [font_ release], font_ = nil; 20 | [backgroundColor_ release], backgroundColor_ = nil; 21 | [foregroundColor_ release], foregroundColor_ = nil; 22 | [super dealloc]; 23 | } 24 | 25 | #define DEFAULT_FONT_FAMILY_NAME @"Verdana" 26 | #define DEFAULT_FONT_SIZE 10 27 | 28 | - (id) init { 29 | self = [super init]; 30 | if (self != nil) { 31 | self.backgroundColor = [UIColor whiteColor]; 32 | self.foregroundColor = [UIColor blackColor]; 33 | lineBreakMode_ = UILineBreakModeMiddleTruncation; 34 | self.font = [UIFont fontWithName:DEFAULT_FONT_FAMILY_NAME size:DEFAULT_FONT_SIZE]; 35 | } 36 | return self; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /CVLibrary/CellHandler.h: -------------------------------------------------------------------------------- 1 | /* 2 | * CellHandler.h 3 | * CVLibraryDemo 4 | * 5 | * Created by Kerem Karatal on 7/12/09. 6 | * Copyright 2009 Coding Ventures. All rights reserved. 7 | * 8 | */ 9 | @protocol CellHandler; 10 | 11 | @protocol CellHandlerDelegate 12 | @required 13 | - (void) cellData:(id) data changedForHandler:(id) handler; 14 | 15 | @end 16 | 17 | 18 | @protocol CellHandler 19 | @required 20 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath usingData:(id) data; 21 | 22 | @property (assign) id delegate; 23 | @property (nonatomic, copy) NSString *label; 24 | @property (nonatomic, copy) NSString *keyPath; 25 | @property (nonatomic, copy) NSString *identifier; 26 | 27 | @optional 28 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 29 | usingNavigationController:(UINavigationController *) navController; 30 | @property (nonatomic, retain) NSArray *options; 31 | @property (nonatomic, copy) NSString *dataConverterClassName; 32 | @property (nonatomic, copy) NSString *keyboardType; 33 | @end 34 | 35 | @protocol DataConverter 36 | @required 37 | - (id) convertFromString:(NSString *) input; 38 | - (NSString *) convertToString:(id) input; 39 | @end 40 | -------------------------------------------------------------------------------- /CVLibrary/EnumCellHandler.h: -------------------------------------------------------------------------------- 1 | // 2 | // EnumCellHandler.h 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 7/13/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CellHandler.h" 11 | #import "EnumOptionsViewController.h" 12 | 13 | @interface EnumCellHandler : NSObject { 14 | id delegate_; 15 | NSString *label_; 16 | NSString *keyPath_; 17 | NSString *identifier_; 18 | NSArray *options_; 19 | NSString *dataConverterClassName_; 20 | NSInteger selectedOptionIndex_; 21 | } 22 | 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /CVLibrary/EnumCellHandler.m: -------------------------------------------------------------------------------- 1 | // 2 | // EnumCellHandler.m 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 7/13/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import "EnumCellHandler.h" 10 | 11 | 12 | @interface EnumCellHandler() 13 | - (UITableViewCell *) tableViewCellWithReuseIdentifier:(NSString *) identifier; 14 | @end 15 | 16 | 17 | @implementation EnumCellHandler 18 | @synthesize delegate = delegate_; 19 | @synthesize label = label_; 20 | @synthesize keyPath = keyPath_; 21 | @synthesize identifier = identifier_; 22 | @synthesize options = options_; 23 | @synthesize dataConverterClassName = dataConverterClassName_; 24 | 25 | - (void) dealloc { 26 | [label_ release], label_ = nil; 27 | [keyPath_ release], keyPath_ = nil; 28 | [identifier_ release], identifier_ = nil; 29 | [options_ release], options_ = nil; 30 | [dataConverterClassName_ release], dataConverterClassName_ = nil; 31 | [super dealloc]; 32 | } 33 | 34 | - (id) init { 35 | self = [super init]; 36 | if (self != nil) { 37 | selectedOptionIndex_ = 0; 38 | } 39 | return self; 40 | } 41 | 42 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath usingData:(id) data { 43 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier_]; 44 | if (cell == nil) { 45 | cell = [self tableViewCellWithReuseIdentifier:identifier_]; 46 | } 47 | 48 | NSString *selectedOptionString = (NSString *) data; 49 | Class converterClass = NSClassFromString(dataConverterClassName_); 50 | if (nil != converterClass) { 51 | id dataConverter = [[converterClass alloc] init]; 52 | selectedOptionString = [dataConverter convertToString:data]; 53 | [dataConverter release]; 54 | } 55 | 56 | for (NSInteger i = 0; i < [options_ count]; i++) { 57 | if ([[options_ objectAtIndex:i] isEqualToString:selectedOptionString]) { 58 | selectedOptionIndex_ = i; 59 | break; 60 | } 61 | } 62 | 63 | cell.textLabel.text = label_; 64 | cell.detailTextLabel.text = [options_ objectAtIndex:selectedOptionIndex_]; 65 | return cell; 66 | } 67 | 68 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 69 | usingNavigationController:(UINavigationController *) navController{ 70 | EnumOptionsViewController *optionsViewController = [[EnumOptionsViewController alloc] initWithStyle:UITableViewStylePlain]; 71 | 72 | optionsViewController.options = options_; 73 | optionsViewController.delegate = self; 74 | optionsViewController.selectedIndex = selectedOptionIndex_; 75 | [navController pushViewController:optionsViewController animated:YES]; 76 | [optionsViewController release]; 77 | } 78 | 79 | 80 | - (UITableViewCell *) tableViewCellWithReuseIdentifier:(NSString *) identifier { 81 | UITableViewCell *cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:identifier] autorelease]; 82 | cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; 83 | return cell; 84 | } 85 | 86 | #pragma mark OptionSelectionChangedDelegate methods 87 | 88 | - (void) selectionChangedToOptionIndex:(NSUInteger) index { 89 | NSString *selection = [options_ objectAtIndex:index]; 90 | 91 | selectedOptionIndex_ = index; 92 | [delegate_ cellData:selection changedForHandler:self]; 93 | } 94 | 95 | @end 96 | -------------------------------------------------------------------------------- /CVLibrary/EnumOptionsViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // EnumOptionsViewController.h 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 7/14/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol OptionSelectionChangedDelegate 12 | @required 13 | - (void) selectionChangedToOptionIndex:(NSUInteger) index; 14 | @end 15 | 16 | 17 | @interface EnumOptionsViewController : UITableViewController { 18 | NSArray *options_; 19 | id delegate_; 20 | UIImage *checkedImage_; 21 | UIImage *uncheckedImage_; 22 | NSUInteger selectedIndex_; 23 | } 24 | 25 | @property (assign) id delegate; 26 | @property (nonatomic, retain) NSArray *options; 27 | @property (nonatomic) NSUInteger selectedIndex; 28 | @end 29 | -------------------------------------------------------------------------------- /CVLibrary/EnumOptionsViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // EnumOptionsViewController.m 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 7/14/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import "EnumOptionsViewController.h" 10 | 11 | @interface EnumOptionsViewController() 12 | - (UITableViewCell *) tableView:(UITableView *) tableView cellWithReuseIdentifier:(NSString *) identifier; 13 | @end 14 | 15 | 16 | @implementation EnumOptionsViewController 17 | @synthesize options = options_; 18 | @synthesize delegate = delegate_; 19 | @synthesize selectedIndex = selectedIndex_; 20 | 21 | - (void)dealloc { 22 | [options_ release], options_ = nil; 23 | [checkedImage_ release], checkedImage_ = nil; 24 | [uncheckedImage_ release], uncheckedImage_ = nil; 25 | [super dealloc]; 26 | } 27 | 28 | - (id)initWithStyle:(UITableViewStyle)style { 29 | // Override initWithStyle: if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad. 30 | if (self = [super initWithStyle:style]) { 31 | checkedImage_ = [[UIImage imageNamed:@"Checkmark_On.png"] retain]; 32 | uncheckedImage_ = [[UIImage imageNamed:@"Checkmark_Off.png"] retain]; 33 | selectedIndex_ = 0; 34 | } 35 | return self; 36 | } 37 | 38 | - (void)viewWillDisappear:(BOOL)animated { 39 | [super viewWillDisappear:animated]; 40 | 41 | [delegate_ selectionChangedToOptionIndex:selectedIndex_]; 42 | } 43 | 44 | 45 | - (void)didReceiveMemoryWarning { 46 | // Releases the view if it doesn't have a superview. 47 | [super didReceiveMemoryWarning]; 48 | 49 | // Release any cached data, images, etc that aren't in use. 50 | } 51 | 52 | - (void)viewDidUnload { 53 | // Release any retained subviews of the main view. 54 | // e.g. self.myOutlet = nil; 55 | } 56 | 57 | 58 | #pragma mark Table view methods 59 | 60 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 61 | return 1; 62 | } 63 | 64 | 65 | // Customize the number of rows in the table view. 66 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 67 | return [options_ count]; 68 | } 69 | 70 | #define CHECKBOX_TAG 0 71 | #define LABEL_TAG 1 72 | 73 | // Customize the appearance of table view cells. 74 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 75 | 76 | static NSString *CellIdentifier = @"CheckboxCell"; 77 | 78 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 79 | if (cell == nil) { 80 | cell = [self tableView:tableView cellWithReuseIdentifier:CellIdentifier]; 81 | } 82 | 83 | // Set up the cell... 84 | UILabel *label = (UILabel *) [cell viewWithTag:LABEL_TAG]; 85 | label.text = [options_ objectAtIndex:indexPath.row]; 86 | 87 | UIImageView *imageView = (UIImageView *) [cell viewWithTag:CHECKBOX_TAG]; 88 | if (indexPath.row == selectedIndex_) { 89 | imageView.image = checkedImage_; 90 | } else { 91 | imageView.image = uncheckedImage_; 92 | } 93 | 94 | 95 | return cell; 96 | } 97 | 98 | #define LEFT_COLUMN_OFFSET 10.0 99 | #define LEFT_COLUMN_WIDTH 160.0 100 | 101 | #define RIGHT_COLUMN_OFFSET 40.0 102 | #define RIGHT_COLUMN_WIDTH 100.0 103 | 104 | #define MAIN_FONT_SIZE 16.0 105 | #define LABEL_HEIGHT 26.0 106 | 107 | - (UITableViewCell *) tableView:(UITableView *) tableView cellWithReuseIdentifier:(NSString *) identifier { 108 | UITableViewCell *cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier] autorelease]; 109 | 110 | UIImageView *imageView = [[UIImageView alloc] initWithImage:checkedImage_]; 111 | imageView.frame = CGRectMake(LEFT_COLUMN_OFFSET, ((tableView.rowHeight - imageView.frame.size.height) / 2.0), imageView.frame.size.width, imageView.frame.size.height); 112 | imageView.tag = CHECKBOX_TAG; 113 | [cell.contentView addSubview:imageView]; 114 | [imageView release]; 115 | 116 | CGRect frame = CGRectMake(RIGHT_COLUMN_OFFSET, (tableView.rowHeight - LABEL_HEIGHT) / 2.0, RIGHT_COLUMN_WIDTH, LABEL_HEIGHT); 117 | UILabel *label = [[UILabel alloc] initWithFrame:frame]; 118 | label.tag = LABEL_TAG; 119 | label.font = [UIFont systemFontOfSize:MAIN_FONT_SIZE]; 120 | label.textAlignment = UITextAlignmentLeft; 121 | [cell.contentView addSubview:label]; 122 | [label release]; 123 | 124 | return cell; 125 | } 126 | 127 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 128 | UIImageView *imageView = nil; 129 | 130 | NSUInteger indexes[2] = { 0, selectedIndex_ }; 131 | NSIndexPath *selectedIndexPath = [[NSIndexPath alloc] initWithIndexes:indexes length:2]; 132 | imageView = (UIImageView *) [[tableView cellForRowAtIndexPath:selectedIndexPath] viewWithTag:CHECKBOX_TAG]; 133 | imageView.image = uncheckedImage_; 134 | [selectedIndexPath release]; 135 | 136 | imageView = (UIImageView *) [[tableView cellForRowAtIndexPath:indexPath] viewWithTag:CHECKBOX_TAG]; 137 | imageView.image = checkedImage_; 138 | 139 | selectedIndex_ = indexPath.row; 140 | [tableView deselectRowAtIndexPath:indexPath animated:YES]; 141 | } 142 | 143 | @end 144 | 145 | -------------------------------------------------------------------------------- /CVLibrary/Frameworks/OCMock.framework/Headers: -------------------------------------------------------------------------------- 1 | Versions/Current/Headers -------------------------------------------------------------------------------- /CVLibrary/Frameworks/OCMock.framework/OCMock: -------------------------------------------------------------------------------- 1 | Versions/Current/OCMock -------------------------------------------------------------------------------- /CVLibrary/Frameworks/OCMock.framework/Resources: -------------------------------------------------------------------------------- 1 | Versions/Current/Resources -------------------------------------------------------------------------------- /CVLibrary/Frameworks/OCMock.framework/Versions/A/Headers/OCMConstraint.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------- 2 | // $Id: $ 3 | // Copyright (c) 2007-2008 by Mulle Kybernetik. See License file for details. 4 | //--------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | 9 | @interface OCMConstraint : NSObject 10 | { 11 | } 12 | 13 | + (id)constraint; 14 | 15 | + (id)constraintWithSelector:(SEL)aSelector onObject:(id)anObject; 16 | + (id)constraintWithSelector:(SEL)aSelector onObject:(id)anObject withValue:(id)aValue; 17 | 18 | + (id)any; 19 | + (id)isNil; 20 | + (id)isNotNil; 21 | + (id)isNotEqual:(id)value; 22 | 23 | - (BOOL)evaluate:(id)value; 24 | 25 | @end 26 | 27 | #define CONSTRAINT(aSelector) [OCMConstraint constraintWithSelector:aSelector onObject:self] 28 | #define CONSTRAINTV(aSelector, aValue) [OCMConstraint constraintWithSelector:aSelector onObject:self withValue:(aValue)] 29 | -------------------------------------------------------------------------------- /CVLibrary/Frameworks/OCMock.framework/Versions/A/Headers/OCMock.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------- 2 | // $Id: OCMock.h 21 2008-01-24 18:59:39Z erik $ 3 | // Copyright (c) 2004-2008 by Mulle Kybernetik. See License file for details. 4 | //--------------------------------------------------------------------------------------- 5 | 6 | #import 7 | #import 8 | #import 9 | -------------------------------------------------------------------------------- /CVLibrary/Frameworks/OCMock.framework/Versions/A/Headers/OCMockObject.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------- 2 | // $Id: OCMockObject.h 21 2008-01-24 18:59:39Z erik $ 3 | // Copyright (c) 2004-2008 by Mulle Kybernetik. See License file for details. 4 | //--------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | @interface OCMockObject : NSProxy 9 | { 10 | BOOL isNice; 11 | NSMutableArray *recorders; 12 | NSMutableSet *expectations; 13 | NSMutableArray *exceptions; 14 | } 15 | 16 | + (id)mockForClass:(Class)aClass; 17 | + (id)mockForProtocol:(Protocol *)aProtocol; 18 | 19 | + (id)niceMockForClass:(Class)aClass; 20 | + (id)niceMockForProtocol:(Protocol *)aProtocol; 21 | 22 | - (id)init; 23 | 24 | - (id)stub; 25 | - (id)expect; 26 | 27 | - (void)verify; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /CVLibrary/Frameworks/OCMock.framework/Versions/A/Headers/OCMockRecorder.h: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------------------------- 2 | // $Id: OCMockRecorder.h 28 2008-06-19 22:37:17Z erik $ 3 | // Copyright (c) 2004-2008 by Mulle Kybernetik. See License file for details. 4 | //--------------------------------------------------------------------------------------- 5 | 6 | #import 7 | 8 | @class OCMConstraint; // reference for backwards compatibility OCMOCK_ANY macro 9 | 10 | 11 | @interface OCMockRecorder : NSProxy 12 | { 13 | id signatureResolver; 14 | id returnValue; 15 | BOOL returnValueIsBoxed; 16 | BOOL returnValueShouldBeThrown; 17 | NSInvocation *recordedInvocation; 18 | } 19 | 20 | - (id)initWithSignatureResolver:(id)anObject; 21 | 22 | - (id)andReturn:(id)anObject; 23 | - (id)andReturnValue:(NSValue *)aValue; 24 | - (id)andThrow:(NSException *)anException; 25 | 26 | - (BOOL)matchesInvocation:(NSInvocation *)anInvocation; 27 | - (void)setUpReturnValue:(NSInvocation *)anInvocation; 28 | - (void)releaseInvocation; 29 | 30 | @end 31 | 32 | #define OCMOCK_ANY [OCMConstraint any] 33 | #define OCMOCK_VALUE(variable) [NSValue value:&variable withObjCType:@encode(typeof(variable))] 34 | -------------------------------------------------------------------------------- /CVLibrary/Frameworks/OCMock.framework/Versions/A/OCMock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keremk/CViPhoneLibrary/a845c169916c0dea05680773b10e85f8020ae700/CVLibrary/Frameworks/OCMock.framework/Versions/A/OCMock -------------------------------------------------------------------------------- /CVLibrary/Frameworks/OCMock.framework/Versions/A/Resources/English.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keremk/CViPhoneLibrary/a845c169916c0dea05680773b10e85f8020ae700/CVLibrary/Frameworks/OCMock.framework/Versions/A/Resources/English.lproj/InfoPlist.strings -------------------------------------------------------------------------------- /CVLibrary/Frameworks/OCMock.framework/Versions/A/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | OCMock 9 | CFBundleIdentifier 10 | com.mulle-kybernetik.OCMock 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | FMWK 15 | CFBundleSignature 16 | ???? 17 | CFBundleVersion 18 | 1.0 19 | 20 | 21 | -------------------------------------------------------------------------------- /CVLibrary/Frameworks/OCMock.framework/Versions/A/Resources/License.txt: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2004-2008 by Mulle Kybernetik. All rights reserved. 3 | 4 | Permission to use, copy, modify and distribute this software and its documentation 5 | is hereby granted, provided that both the copyright notice and this permission 6 | notice appear in all copies of the software, derivative works or modified versions, 7 | and any portions thereof, and that both notices appear in supporting documentation, 8 | and that credit is given to Mulle Kybernetik in all documents and publicity 9 | pertaining to direct or indirect use of this code or its derivatives. 10 | 11 | THIS IS EXPERIMENTAL SOFTWARE AND IT IS KNOWN TO HAVE BUGS, SOME OF WHICH MAY HAVE 12 | SERIOUS CONSEQUENCES. THE COPYRIGHT HOLDER ALLOWS FREE USE OF THIS SOFTWARE IN ITS 13 | "AS IS" CONDITION. THE COPYRIGHT HOLDER DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY 14 | DAMAGES WHATSOEVER RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE 15 | OR OF ANY DERIVATIVE WORK. -------------------------------------------------------------------------------- /CVLibrary/Frameworks/OCMock.framework/Versions/Current: -------------------------------------------------------------------------------- 1 | A -------------------------------------------------------------------------------- /CVLibrary/GTMDefines.h: -------------------------------------------------------------------------------- 1 | // 2 | // GTMDefines.h 3 | // 4 | // Copyright 2008 Google Inc. 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 7 | // use this file except in compliance with the License. You may obtain a copy 8 | // of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15 | // License for the specific language governing permissions and limitations under 16 | // the License. 17 | // 18 | 19 | // ============================================================================ 20 | 21 | #include 22 | #include 23 | 24 | // Not all MAC_OS_X_VERSION_10_X macros defined in past SDKs 25 | #ifndef MAC_OS_X_VERSION_10_5 26 | #define MAC_OS_X_VERSION_10_5 1050 27 | #endif 28 | #ifndef MAC_OS_X_VERSION_10_6 29 | #define MAC_OS_X_VERSION_10_6 1060 30 | #endif 31 | 32 | // ---------------------------------------------------------------------------- 33 | // CPP symbols that can be overridden in a prefix to control how the toolbox 34 | // is compiled. 35 | // ---------------------------------------------------------------------------- 36 | 37 | 38 | // By setting the GTM_CONTAINERS_VALIDATION_FAILED_LOG and 39 | // GTM_CONTAINERS_VALIDATION_FAILED_ASSERT macros you can control what happens 40 | // when a validation fails. If you implement your own validators, you may want 41 | // to control their internals using the same macros for consistency. 42 | #ifndef GTM_CONTAINERS_VALIDATION_FAILED_ASSERT 43 | #define GTM_CONTAINERS_VALIDATION_FAILED_ASSERT 0 44 | #endif 45 | 46 | // Give ourselves a consistent way to do inlines. Apple's macros even use 47 | // a few different actual definitions, so we're based off of the foundation 48 | // one. 49 | #if !defined(GTM_INLINE) 50 | #if defined (__GNUC__) && (__GNUC__ == 4) 51 | #define GTM_INLINE static __inline__ __attribute__((always_inline)) 52 | #else 53 | #define GTM_INLINE static __inline__ 54 | #endif 55 | #endif 56 | 57 | // Give ourselves a consistent way of doing externs that links up nicely 58 | // when mixing objc and objc++ 59 | #if !defined (GTM_EXTERN) 60 | #if defined __cplusplus 61 | #define GTM_EXTERN extern "C" 62 | #else 63 | #define GTM_EXTERN extern 64 | #endif 65 | #endif 66 | 67 | // Give ourselves a consistent way of exporting things if we have visibility 68 | // set to hidden. 69 | #if !defined (GTM_EXPORT) 70 | #define GTM_EXPORT __attribute__((visibility("default"))) 71 | #endif 72 | 73 | // _GTMDevLog & _GTMDevAssert 74 | // 75 | // _GTMDevLog & _GTMDevAssert are meant to be a very lightweight shell for 76 | // developer level errors. This implementation simply macros to NSLog/NSAssert. 77 | // It is not intended to be a general logging/reporting system. 78 | // 79 | // Please see http://code.google.com/p/google-toolbox-for-mac/wiki/DevLogNAssert 80 | // for a little more background on the usage of these macros. 81 | // 82 | // _GTMDevLog log some error/problem in debug builds 83 | // _GTMDevAssert assert if conditon isn't met w/in a method/function 84 | // in all builds. 85 | // 86 | // To replace this system, just provide different macro definitions in your 87 | // prefix header. Remember, any implementation you provide *must* be thread 88 | // safe since this could be called by anything in what ever situtation it has 89 | // been placed in. 90 | // 91 | 92 | // We only define the simple macros if nothing else has defined this. 93 | #ifndef _GTMDevLog 94 | 95 | #ifdef DEBUG 96 | #define _GTMDevLog(...) NSLog(__VA_ARGS__) 97 | #else 98 | #define _GTMDevLog(...) do { } while (0) 99 | #endif 100 | 101 | #endif // _GTMDevLog 102 | 103 | // Declared here so that it can easily be used for logging tracking if 104 | // necessary. See GTMUnitTestDevLog.h for details. 105 | @class NSString; 106 | GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...); 107 | 108 | #ifndef _GTMDevAssert 109 | // we directly invoke the NSAssert handler so we can pass on the varargs 110 | // (NSAssert doesn't have a macro we can use that takes varargs) 111 | #if !defined(NS_BLOCK_ASSERTIONS) 112 | #define _GTMDevAssert(condition, ...) \ 113 | do { \ 114 | if (!(condition)) { \ 115 | [[NSAssertionHandler currentHandler] \ 116 | handleFailureInFunction:[NSString stringWithUTF8String:__PRETTY_FUNCTION__] \ 117 | file:[NSString stringWithUTF8String:__FILE__] \ 118 | lineNumber:__LINE__ \ 119 | description:__VA_ARGS__]; \ 120 | } \ 121 | } while(0) 122 | #else // !defined(NS_BLOCK_ASSERTIONS) 123 | #define _GTMDevAssert(condition, ...) do { } while (0) 124 | #endif // !defined(NS_BLOCK_ASSERTIONS) 125 | 126 | #endif // _GTMDevAssert 127 | 128 | // _GTMCompileAssert 129 | // _GTMCompileAssert is an assert that is meant to fire at compile time if you 130 | // want to check things at compile instead of runtime. For example if you 131 | // want to check that a wchar is 4 bytes instead of 2 you would use 132 | // _GTMCompileAssert(sizeof(wchar_t) == 4, wchar_t_is_4_bytes_on_OS_X) 133 | // Note that the second "arg" is not in quotes, and must be a valid processor 134 | // symbol in it's own right (no spaces, punctuation etc). 135 | 136 | // Wrapping this in an #ifndef allows external groups to define their own 137 | // compile time assert scheme. 138 | #ifndef _GTMCompileAssert 139 | // We got this technique from here: 140 | // http://unixjunkie.blogspot.com/2007/10/better-compile-time-asserts_29.html 141 | 142 | #define _GTMCompileAssertSymbolInner(line, msg) _GTMCOMPILEASSERT ## line ## __ ## msg 143 | #define _GTMCompileAssertSymbol(line, msg) _GTMCompileAssertSymbolInner(line, msg) 144 | #define _GTMCompileAssert(test, msg) \ 145 | typedef char _GTMCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ] 146 | #endif // _GTMCompileAssert 147 | 148 | // Macro to allow fast enumeration when building for 10.5 or later, and 149 | // reliance on NSEnumerator for 10.4. Remember, NSDictionary w/ FastEnumeration 150 | // does keys, so pick the right thing, nothing is done on the FastEnumeration 151 | // side to be sure you're getting what you wanted. 152 | #ifndef GTM_FOREACH_OBJECT 153 | #if TARGET_OS_IPHONE || (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) 154 | #define GTM_FOREACH_OBJECT(element, collection) \ 155 | for (element in collection) 156 | #define GTM_FOREACH_KEY(element, collection) \ 157 | for (element in collection) 158 | #else 159 | #define GTM_FOREACH_OBJECT(element, collection) \ 160 | for (NSEnumerator * _ ## element ## _enum = [collection objectEnumerator]; \ 161 | (element = [_ ## element ## _enum nextObject]) != nil; ) 162 | #define GTM_FOREACH_KEY(element, collection) \ 163 | for (NSEnumerator * _ ## element ## _enum = [collection keyEnumerator]; \ 164 | (element = [_ ## element ## _enum nextObject]) != nil; ) 165 | #endif 166 | #endif 167 | 168 | // ============================================================================ 169 | 170 | // ---------------------------------------------------------------------------- 171 | // CPP symbols defined based on the project settings so the GTM code has 172 | // simple things to test against w/o scattering the knowledge of project 173 | // setting through all the code. 174 | // ---------------------------------------------------------------------------- 175 | 176 | // Provide a single constant CPP symbol that all of GTM uses for ifdefing 177 | // iPhone code. 178 | #if TARGET_OS_IPHONE // iPhone SDK 179 | // For iPhone specific stuff 180 | #define GTM_IPHONE_SDK 1 181 | #if TARGET_IPHONE_SIMULATOR 182 | #define GTM_IPHONE_SIMULATOR 1 183 | #else 184 | #define GTM_IPHONE_DEVICE 1 185 | #endif // TARGET_IPHONE_SIMULATOR 186 | #else 187 | // For MacOS specific stuff 188 | #define GTM_MACOS_SDK 1 189 | #endif 190 | 191 | // Provide a symbol to include/exclude extra code for GC support. (This mainly 192 | // just controls the inclusion of finalize methods). 193 | #ifndef GTM_SUPPORT_GC 194 | #if GTM_IPHONE_SDK 195 | // iPhone never needs GC 196 | #define GTM_SUPPORT_GC 0 197 | #else 198 | // We can't find a symbol to tell if GC is supported/required, so best we 199 | // do on Mac targets is include it if we're on 10.5 or later. 200 | #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 201 | #define GTM_SUPPORT_GC 0 202 | #else 203 | #define GTM_SUPPORT_GC 1 204 | #endif 205 | #endif 206 | #endif 207 | 208 | // To simplify support for 64bit (and Leopard in general), we provide the type 209 | // defines for non Leopard SDKs 210 | #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 211 | // NSInteger/NSUInteger and Max/Mins 212 | #ifndef NSINTEGER_DEFINED 213 | #if __LP64__ || NS_BUILD_32_LIKE_64 214 | typedef long NSInteger; 215 | typedef unsigned long NSUInteger; 216 | #else 217 | typedef int NSInteger; 218 | typedef unsigned int NSUInteger; 219 | #endif 220 | #define NSIntegerMax LONG_MAX 221 | #define NSIntegerMin LONG_MIN 222 | #define NSUIntegerMax ULONG_MAX 223 | #define NSINTEGER_DEFINED 1 224 | #endif // NSINTEGER_DEFINED 225 | // CGFloat 226 | #ifndef CGFLOAT_DEFINED 227 | #if defined(__LP64__) && __LP64__ 228 | // This really is an untested path (64bit on Tiger?) 229 | typedef double CGFloat; 230 | #define CGFLOAT_MIN DBL_MIN 231 | #define CGFLOAT_MAX DBL_MAX 232 | #define CGFLOAT_IS_DOUBLE 1 233 | #else /* !defined(__LP64__) || !__LP64__ */ 234 | typedef float CGFloat; 235 | #define CGFLOAT_MIN FLT_MIN 236 | #define CGFLOAT_MAX FLT_MAX 237 | #define CGFLOAT_IS_DOUBLE 0 238 | #endif /* !defined(__LP64__) || !__LP64__ */ 239 | #define CGFLOAT_DEFINED 1 240 | #endif // CGFLOAT_DEFINED 241 | #endif // MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 242 | -------------------------------------------------------------------------------- /CVLibrary/GTMIPhoneUnitTestDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // GTMIPhoneUnitTestDelegate.h 3 | // 4 | // Copyright 2008 Google Inc. 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 7 | // use this file except in compliance with the License. You may obtain a copy 8 | // of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15 | // License for the specific language governing permissions and limitations under 16 | // the License. 17 | // 18 | 19 | #import 20 | 21 | // Application delegate that runs all test methods in registered classes 22 | // extending SenTestCase. The application is terminated afterwards. 23 | // You can also run the tests directly from your application by invoking 24 | // runTests and clean up, restore data, etc. before the application 25 | // terminates. 26 | @interface GTMIPhoneUnitTestDelegate : NSObject { 27 | @private 28 | NSUInteger totalFailures_; 29 | NSUInteger totalSuccesses_; 30 | } 31 | // Runs through all the registered classes and runs test methods on any 32 | // that are subclasses of SenTestCase. Prints results and run time to 33 | // the default output. 34 | - (void)runTests; 35 | // Fetch the number of successes or failures from the last runTests. 36 | - (NSUInteger)totalSuccesses; 37 | - (NSUInteger)totalFailures; 38 | @end 39 | -------------------------------------------------------------------------------- /CVLibrary/GTMIPhoneUnitTestDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // GTMIPhoneUnitTestDelegate.m 3 | // 4 | // Copyright 2008 Google Inc. 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 7 | // use this file except in compliance with the License. You may obtain a copy 8 | // of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15 | // License for the specific language governing permissions and limitations under 16 | // the License. 17 | // 18 | 19 | #import "GTMIPhoneUnitTestDelegate.h" 20 | 21 | #import "GTMDefines.h" 22 | #if !GTM_IPHONE_SDK 23 | #error GTMIPhoneUnitTestDelegate for iPhone only 24 | #endif 25 | #import 26 | #import 27 | #import 28 | #import "GTMSenTestCase.h" 29 | 30 | // Used for sorting methods below 31 | static int MethodSort(const void *a, const void *b) { 32 | const char *nameA = sel_getName(method_getName(*(Method*)a)); 33 | const char *nameB = sel_getName(method_getName(*(Method*)b)); 34 | return strcmp(nameA, nameB); 35 | } 36 | 37 | // Return YES if class is subclass (1 or more generations) of SenTestCase 38 | static BOOL IsTestFixture(Class aClass) { 39 | BOOL iscase = NO; 40 | Class testCaseClass = [SenTestCase class]; 41 | Class superclass; 42 | for (superclass = aClass; 43 | !iscase && superclass; 44 | superclass = class_getSuperclass(superclass)) { 45 | iscase = superclass == testCaseClass ? YES : NO; 46 | } 47 | return iscase; 48 | } 49 | 50 | @implementation GTMIPhoneUnitTestDelegate 51 | 52 | // Run through all the registered classes and run test methods on any 53 | // that are subclasses of SenTestCase. Terminate the application upon 54 | // test completion. 55 | - (void)applicationDidFinishLaunching:(UIApplication *)application { 56 | [self runTests]; 57 | 58 | if (!getenv("GTM_DISABLE_TERMINATION")) { 59 | // To help using xcodebuild, make the exit status 0/1 to signal the tests 60 | // success/failure. 61 | int exitStatus = (([self totalFailures] == 0U) ? 0 : 1); 62 | exit(exitStatus); 63 | } 64 | } 65 | 66 | // Run through all the registered classes and run test methods on any 67 | // that are subclasses of SenTestCase. Print results and run time to 68 | // the default output. 69 | - (void)runTests { 70 | int count = objc_getClassList(NULL, 0); 71 | NSMutableData *classData 72 | = [NSMutableData dataWithLength:sizeof(Class) * count]; 73 | Class *classes = (Class*)[classData mutableBytes]; 74 | _GTMDevAssert(classes, @"Couldn't allocate class list"); 75 | objc_getClassList(classes, count); 76 | totalFailures_ = 0; 77 | totalSuccesses_ = 0; 78 | NSString *suiteName = [[NSBundle mainBundle] bundlePath]; 79 | NSDate *suiteStartDate = [NSDate date]; 80 | NSString *suiteStartString 81 | = [NSString stringWithFormat:@"Test Suite '%@' started at %@\n", 82 | suiteName, suiteStartDate]; 83 | fputs([suiteStartString UTF8String], stderr); 84 | fflush(stderr); 85 | for (int i = 0; i < count; ++i) { 86 | Class currClass = classes[i]; 87 | if (IsTestFixture(currClass)) { 88 | NSDate *fixtureStartDate = [NSDate date]; 89 | NSString *fixtureName = NSStringFromClass(currClass); 90 | NSString *fixtureStartString 91 | = [NSString stringWithFormat:@"Test Suite '%@' started at %@\n", 92 | fixtureName, fixtureStartDate]; 93 | int fixtureSuccesses = 0; 94 | int fixtureFailures = 0; 95 | fputs([fixtureStartString UTF8String], stderr); 96 | fflush(stderr); 97 | id testcase = [[currClass alloc] init]; 98 | _GTMDevAssert(testcase, @"Unable to instantiate Test Suite: '%@'\n", 99 | fixtureName); 100 | unsigned int methodCount; 101 | Method *methods = class_copyMethodList(currClass, &methodCount); 102 | if (!methods) { 103 | // If the class contains no methods, head on to the next class 104 | NSString *output = [NSString stringWithFormat:@"Test Suite '%@' " 105 | @"finished at %@.\nExecuted 0 tests, with 0 " 106 | @"failures (0 unexpected) in 0 (0) seconds\n", 107 | fixtureName, fixtureStartDate]; 108 | 109 | fputs([output UTF8String], stderr); 110 | continue; 111 | } 112 | // This handles disposing of methods for us even if an 113 | // exception should fly. 114 | [NSData dataWithBytesNoCopy:methods 115 | length:sizeof(Method) * methodCount]; 116 | // Sort our methods so they are called in Alphabetical order just 117 | // because we can. 118 | qsort(methods, methodCount, sizeof(Method), MethodSort); 119 | for (size_t j = 0; j < methodCount; ++j) { 120 | Method currMethod = methods[j]; 121 | SEL sel = method_getName(currMethod); 122 | char *returnType = NULL; 123 | const char *name = sel_getName(sel); 124 | // If it starts with test, takes 2 args (target and sel) and returns 125 | // void run it. 126 | if (strstr(name, "test") == name) { 127 | returnType = method_copyReturnType(currMethod); 128 | if (returnType) { 129 | // This handles disposing of returnType for us even if an 130 | // exception should fly. Length +1 for the terminator, not that 131 | // the length really matters here, as we never reference inside 132 | // the data block. 133 | [NSData dataWithBytesNoCopy:returnType 134 | length:strlen(returnType) + 1]; 135 | } 136 | } 137 | if (returnType // True if name starts with "test" 138 | && strcmp(returnType, @encode(void)) == 0 139 | && method_getNumberOfArguments(currMethod) == 2) { 140 | BOOL failed = NO; 141 | NSDate *caseStartDate = [NSDate date]; 142 | @try { 143 | [testcase performTest:sel]; 144 | } @catch (NSException *exception) { 145 | failed = YES; 146 | } 147 | if (failed) { 148 | fixtureFailures += 1; 149 | } else { 150 | fixtureSuccesses += 1; 151 | } 152 | NSTimeInterval caseEndTime 153 | = [[NSDate date] timeIntervalSinceDate:caseStartDate]; 154 | NSString *caseEndString 155 | = [NSString stringWithFormat:@"Test Case '-[%@ %s]' %@ (%0.3f " 156 | @"seconds).\n", 157 | fixtureName, name, 158 | failed ? @"failed" : @"passed", 159 | caseEndTime]; 160 | fputs([caseEndString UTF8String], stderr); 161 | fflush(stderr); 162 | } 163 | } 164 | [testcase release]; 165 | NSDate *fixtureEndDate = [NSDate date]; 166 | NSTimeInterval fixtureEndTime 167 | = [fixtureEndDate timeIntervalSinceDate:fixtureStartDate]; 168 | NSString *fixtureEndString 169 | = [NSString stringWithFormat:@"Test Suite '%@' finished at %@.\n" 170 | @"Executed %d tests, with %d failures (%d " 171 | @"unexpected) in %0.3f (%0.3f) seconds\n\n", 172 | fixtureName, fixtureEndDate, 173 | fixtureSuccesses + fixtureFailures, 174 | fixtureFailures, fixtureFailures, 175 | fixtureEndTime, fixtureEndTime]; 176 | 177 | fputs([fixtureEndString UTF8String], stderr); 178 | fflush(stderr); 179 | totalSuccesses_ += fixtureSuccesses; 180 | totalFailures_ += fixtureFailures; 181 | } 182 | } 183 | NSDate *suiteEndDate = [NSDate date]; 184 | NSTimeInterval suiteEndTime 185 | = [suiteEndDate timeIntervalSinceDate:suiteStartDate]; 186 | NSString *suiteEndString 187 | = [NSString stringWithFormat:@"Test Suite '%@' finished at %@.\n" 188 | @"Executed %d tests, with %d failures (%d " 189 | @"unexpected) in %0.3f (%0.3f) seconds\n\n", 190 | suiteName, suiteEndDate, 191 | totalSuccesses_ + totalFailures_, 192 | totalFailures_, totalFailures_, 193 | suiteEndTime, suiteEndTime]; 194 | fputs([suiteEndString UTF8String], stderr); 195 | fflush(stderr); 196 | } 197 | 198 | - (NSUInteger)totalSuccesses { 199 | return totalSuccesses_; 200 | } 201 | 202 | - (NSUInteger)totalFailures { 203 | return totalFailures_; 204 | } 205 | 206 | @end 207 | -------------------------------------------------------------------------------- /CVLibrary/GTMIPhoneUnitTestMain.m: -------------------------------------------------------------------------------- 1 | // 2 | // GTMIPhoneUnitTestMain.m 3 | // 4 | // Copyright 2008 Google Inc. 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 7 | // use this file except in compliance with the License. You may obtain a copy 8 | // of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15 | // License for the specific language governing permissions and limitations under 16 | // the License. 17 | // 18 | 19 | #import "GTMDefines.h" 20 | #if !GTM_IPHONE_SDK 21 | #error GTMIPhoneUnitTestMain for iPhone only 22 | #endif 23 | #import 24 | 25 | // Creates an application that runs all tests from classes extending 26 | // SenTestCase, outputs results and test run time, and terminates right 27 | // afterwards. 28 | int main(int argc, char *argv[]) { 29 | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 30 | int retVal = UIApplicationMain(argc, argv, nil, @"GTMIPhoneUnitTestDelegate"); 31 | [pool release]; 32 | return retVal; 33 | } 34 | -------------------------------------------------------------------------------- /CVLibrary/GTMSenTestCase.m: -------------------------------------------------------------------------------- 1 | // 2 | // GTMSenTestCase.m 3 | // 4 | // Copyright 2007-2008 Google Inc. 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not 7 | // use this file except in compliance with the License. You may obtain a copy 8 | // of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15 | // License for the specific language governing permissions and limitations under 16 | // the License. 17 | // 18 | 19 | #import "GTMSenTestCase.h" 20 | #import 21 | 22 | #if !GTM_IPHONE_SDK 23 | #import "GTMGarbageCollection.h" 24 | #endif // !GTM_IPHONE_SDK 25 | 26 | #if GTM_IPHONE_SDK 27 | #import 28 | 29 | @interface NSException (GTMSenTestPrivateAdditions) 30 | + (NSException *)failureInFile:(NSString *)filename 31 | atLine:(int)lineNumber 32 | reason:(NSString *)reason; 33 | @end 34 | 35 | @implementation NSException (GTMSenTestPrivateAdditions) 36 | + (NSException *)failureInFile:(NSString *)filename 37 | atLine:(int)lineNumber 38 | reason:(NSString *)reason { 39 | NSDictionary *userInfo = 40 | [NSDictionary dictionaryWithObjectsAndKeys: 41 | [NSNumber numberWithInteger:lineNumber], SenTestLineNumberKey, 42 | filename, SenTestFilenameKey, 43 | nil]; 44 | 45 | return [self exceptionWithName:SenTestFailureException 46 | reason:reason 47 | userInfo:userInfo]; 48 | } 49 | @end 50 | 51 | @implementation NSException (GTMSenTestAdditions) 52 | 53 | + (NSException *)failureInFile:(NSString *)filename 54 | atLine:(int)lineNumber 55 | withDescription:(NSString *)formatString, ... { 56 | 57 | NSString *testDescription = @""; 58 | if (formatString) { 59 | va_list vl; 60 | va_start(vl, formatString); 61 | testDescription = 62 | [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; 63 | va_end(vl); 64 | } 65 | 66 | NSString *reason = testDescription; 67 | 68 | return [self failureInFile:filename atLine:lineNumber reason:reason]; 69 | } 70 | 71 | + (NSException *)failureInCondition:(NSString *)condition 72 | isTrue:(BOOL)isTrue 73 | inFile:(NSString *)filename 74 | atLine:(int)lineNumber 75 | withDescription:(NSString *)formatString, ... { 76 | 77 | NSString *testDescription = @""; 78 | if (formatString) { 79 | va_list vl; 80 | va_start(vl, formatString); 81 | testDescription = 82 | [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; 83 | va_end(vl); 84 | } 85 | 86 | NSString *reason = [NSString stringWithFormat:@"'%@' should be %s. %@", 87 | condition, isTrue ? "TRUE" : "FALSE", testDescription]; 88 | 89 | return [self failureInFile:filename atLine:lineNumber reason:reason]; 90 | } 91 | 92 | + (NSException *)failureInEqualityBetweenObject:(id)left 93 | andObject:(id)right 94 | inFile:(NSString *)filename 95 | atLine:(int)lineNumber 96 | withDescription:(NSString *)formatString, ... { 97 | 98 | NSString *testDescription = @""; 99 | if (formatString) { 100 | va_list vl; 101 | va_start(vl, formatString); 102 | testDescription = 103 | [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; 104 | va_end(vl); 105 | } 106 | 107 | NSString *reason = 108 | [NSString stringWithFormat:@"'%@' should be equal to '%@'. %@", 109 | [left description], [right description], testDescription]; 110 | 111 | return [self failureInFile:filename atLine:lineNumber reason:reason]; 112 | } 113 | 114 | + (NSException *)failureInEqualityBetweenValue:(NSValue *)left 115 | andValue:(NSValue *)right 116 | withAccuracy:(NSValue *)accuracy 117 | inFile:(NSString *)filename 118 | atLine:(int)lineNumber 119 | withDescription:(NSString *)formatString, ... { 120 | 121 | NSString *testDescription = @""; 122 | if (formatString) { 123 | va_list vl; 124 | va_start(vl, formatString); 125 | testDescription = 126 | [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; 127 | va_end(vl); 128 | } 129 | 130 | NSString *reason; 131 | if (accuracy) { 132 | reason = 133 | [NSString stringWithFormat:@"'%@' should be equal to '%@'. %@", 134 | left, right, testDescription]; 135 | } else { 136 | reason = 137 | [NSString stringWithFormat:@"'%@' should be equal to '%@' +/-'%@'. %@", 138 | left, right, accuracy, testDescription]; 139 | } 140 | 141 | return [self failureInFile:filename atLine:lineNumber reason:reason]; 142 | } 143 | 144 | + (NSException *)failureInRaise:(NSString *)expression 145 | inFile:(NSString *)filename 146 | atLine:(int)lineNumber 147 | withDescription:(NSString *)formatString, ... { 148 | 149 | NSString *testDescription = @""; 150 | if (formatString) { 151 | va_list vl; 152 | va_start(vl, formatString); 153 | testDescription = 154 | [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; 155 | va_end(vl); 156 | } 157 | 158 | NSString *reason = [NSString stringWithFormat:@"'%@' should raise. %@", 159 | expression, testDescription]; 160 | 161 | return [self failureInFile:filename atLine:lineNumber reason:reason]; 162 | } 163 | 164 | + (NSException *)failureInRaise:(NSString *)expression 165 | exception:(NSException *)exception 166 | inFile:(NSString *)filename 167 | atLine:(int)lineNumber 168 | withDescription:(NSString *)formatString, ... { 169 | 170 | NSString *testDescription = @""; 171 | if (formatString) { 172 | va_list vl; 173 | va_start(vl, formatString); 174 | testDescription = 175 | [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; 176 | va_end(vl); 177 | } 178 | 179 | NSString *reason; 180 | if ([[exception name] isEqualToString:SenTestFailureException]) { 181 | // it's our exception, assume it has the right description on it. 182 | reason = [exception reason]; 183 | } else { 184 | // not one of our exception, use the exceptions reason and our description 185 | reason = [NSString stringWithFormat:@"'%@' raised '%@'. %@", 186 | expression, [exception reason], testDescription]; 187 | } 188 | 189 | return [self failureInFile:filename atLine:lineNumber reason:reason]; 190 | } 191 | 192 | @end 193 | 194 | NSString *STComposeString(NSString *formatString, ...) { 195 | NSString *reason = @""; 196 | if (formatString) { 197 | va_list vl; 198 | va_start(vl, formatString); 199 | reason = 200 | [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; 201 | va_end(vl); 202 | } 203 | return reason; 204 | } 205 | 206 | NSString *const SenTestFailureException = @"SenTestFailureException"; 207 | NSString *const SenTestFilenameKey = @"SenTestFilenameKey"; 208 | NSString *const SenTestLineNumberKey = @"SenTestLineNumberKey"; 209 | 210 | @interface SenTestCase (SenTestCasePrivate) 211 | // our method of logging errors 212 | + (void)printException:(NSException *)exception fromTestName:(NSString *)name; 213 | @end 214 | 215 | @implementation SenTestCase 216 | - (void)failWithException:(NSException*)exception { 217 | [exception raise]; 218 | } 219 | 220 | - (void)setUp { 221 | } 222 | 223 | - (void)performTest:(SEL)sel { 224 | currentSelector_ = sel; 225 | @try { 226 | [self invokeTest]; 227 | } @catch (NSException *exception) { 228 | [[self class] printException:exception 229 | fromTestName:NSStringFromSelector(sel)]; 230 | [exception raise]; 231 | } 232 | } 233 | 234 | + (void)printException:(NSException *)exception fromTestName:(NSString *)name { 235 | NSDictionary *userInfo = [exception userInfo]; 236 | NSString *filename = [userInfo objectForKey:SenTestFilenameKey]; 237 | NSNumber *lineNumber = [userInfo objectForKey:SenTestLineNumberKey]; 238 | NSString *className = NSStringFromClass([self class]); 239 | if ([filename length] == 0) { 240 | filename = @"Unknown.m"; 241 | } 242 | fprintf(stderr, "%s:%ld: error: -[%s %s] : %s\n", 243 | [filename UTF8String], 244 | (long)[lineNumber integerValue], 245 | [className UTF8String], 246 | [name UTF8String], 247 | [[exception reason] UTF8String]); 248 | fflush(stderr); 249 | } 250 | 251 | - (void)invokeTest { 252 | NSException *e = nil; 253 | @try { 254 | // Wrap things in autorelease pools because they may 255 | // have an STMacro in their dealloc which may get called 256 | // when the pool is cleaned up 257 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 258 | // We don't log exceptions here, instead we let the person that called 259 | // this log the exception. This ensures they are only logged once but the 260 | // outer layers get the exceptions to report counts, etc. 261 | @try { 262 | [self setUp]; 263 | @try { 264 | [self performSelector:currentSelector_]; 265 | } @catch (NSException *exception) { 266 | e = [exception retain]; 267 | } 268 | [self tearDown]; 269 | } @catch (NSException *exception) { 270 | e = [exception retain]; 271 | } 272 | [pool release]; 273 | } @catch (NSException *exception) { 274 | e = [exception retain]; 275 | } 276 | if (e) { 277 | [e autorelease]; 278 | [e raise]; 279 | } 280 | } 281 | 282 | - (void)tearDown { 283 | } 284 | 285 | - (NSString *)description { 286 | // This matches the description OCUnit would return to you 287 | return [NSString stringWithFormat:@"-[%@ %@]", [self class], 288 | NSStringFromSelector(currentSelector_)]; 289 | } 290 | @end 291 | 292 | #endif // GTM_IPHONE_SDK 293 | 294 | @implementation GTMTestCase : SenTestCase 295 | - (void)invokeTest { 296 | Class devLogClass = NSClassFromString(@"GTMUnitTestDevLog"); 297 | if (devLogClass) { 298 | [devLogClass performSelector:@selector(enableTracking)]; 299 | [devLogClass performSelector:@selector(verifyNoMoreLogsExpected)]; 300 | 301 | } 302 | [super invokeTest]; 303 | if (devLogClass) { 304 | [devLogClass performSelector:@selector(verifyNoMoreLogsExpected)]; 305 | [devLogClass performSelector:@selector(disableTracking)]; 306 | } 307 | } 308 | @end 309 | 310 | // Leak detection 311 | #if !GTM_IPHONE_DEVICE 312 | // Don't want to get leaks on the iPhone Device as the device doesn't 313 | // have 'leaks'. The simulator does though. 314 | 315 | // COV_NF_START 316 | // We don't have leak checking on by default, so this won't be hit. 317 | static void _GTMRunLeaks(void) { 318 | // This is an atexit handler. It runs leaks for us to check if we are 319 | // leaking anything in our tests. 320 | const char* cExclusionsEnv = getenv("GTM_LEAKS_SYMBOLS_TO_IGNORE"); 321 | NSMutableString *exclusions = [NSMutableString string]; 322 | if (cExclusionsEnv) { 323 | NSString *exclusionsEnv = [NSString stringWithUTF8String:cExclusionsEnv]; 324 | NSArray *exclusionsArray = [exclusionsEnv componentsSeparatedByString:@","]; 325 | NSString *exclusion; 326 | NSCharacterSet *wcSet = [NSCharacterSet whitespaceCharacterSet]; 327 | GTM_FOREACH_OBJECT(exclusion, exclusionsArray) { 328 | exclusion = [exclusion stringByTrimmingCharactersInSet:wcSet]; 329 | [exclusions appendFormat:@"-exclude \"%@\" ", exclusion]; 330 | } 331 | } 332 | NSString *string 333 | = [NSString stringWithFormat:@"/usr/bin/leaks %@%d" 334 | @"| /usr/bin/sed -e 's/Leak: /Leaks:0: warning: Leak /'", 335 | exclusions, getpid()]; 336 | int ret = system([string UTF8String]); 337 | if (ret) { 338 | fprintf(stderr, "%s:%d: Error: Unable to run leaks. 'system' returned: %d", 339 | __FILE__, __LINE__, ret); 340 | fflush(stderr); 341 | } 342 | } 343 | // COV_NF_END 344 | 345 | static __attribute__((constructor)) void _GTMInstallLeaks(void) { 346 | BOOL checkLeaks = YES; 347 | #if !GTM_IPHONE_SDK 348 | checkLeaks = GTMIsGarbageCollectionEnabled() ? NO : YES; 349 | #endif // !GTM_IPHONE_SDK 350 | if (checkLeaks) { 351 | checkLeaks = getenv("GTM_ENABLE_LEAKS") ? YES : NO; 352 | if (checkLeaks) { 353 | // COV_NF_START 354 | // We don't have leak checking on by default, so this won't be hit. 355 | fprintf(stderr, "Leak Checking Enabled\n"); 356 | fflush(stderr); 357 | int ret = atexit(&_GTMRunLeaks); 358 | _GTMDevAssert(ret == 0, 359 | @"Unable to install _GTMRunLeaks as an atexit handler (%d)", 360 | errno); 361 | // COV_NF_END 362 | } 363 | } 364 | } 365 | 366 | #endif // !GTM_IPHONE_DEVICE 367 | -------------------------------------------------------------------------------- /CVLibrary/RunIPhoneUnitTest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # RunIPhoneUnitTest.sh 3 | # Copyright 2008 Google Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not 6 | # use this file except in compliance with the License. You may obtain a copy 7 | # of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations under 15 | # the License. 16 | # 17 | # Runs all unittests through the iPhone simulator. We don't handle running them 18 | # on the device. To run on the device just choose "run". 19 | # Controlling environment variables: 20 | # 21 | # GTM_DISABLE_ZOMBIES - 22 | # Set to a non-zero value to turn on zombie checks. You will probably 23 | # want to turn this off if you enable leaks. 24 | # 25 | # GTM_ENABLE_LEAKS - 26 | # Set to a non-zero value to turn on the leaks check. You will probably want 27 | # to disable zombies, otherwise you will get a lot of false positives. 28 | # 29 | # GTM_DISABLE_TERMINATION 30 | # Set to a non-zero value so that the app doesn't terminate when it's finished 31 | # running tests. This is useful when using it with external tools such 32 | # as Instruments. 33 | # 34 | # GTM_LEAKS_SYMBOLS_TO_IGNORE 35 | # List of comma separated symbols that leaks should ignore. Mainly to control 36 | # leaks in frameworks you don't have control over. 37 | # Search this file for GTM_LEAKS_SYMBOLS_TO_IGNORE to see examples. 38 | # Please feel free to add other symbols as you find them but make sure to 39 | # reference Radars or other bug systems so we can track them. 40 | # 41 | # GTM_REMOVE_GCOV_DATA 42 | # Before starting the test, remove any *.gcda files for the current run so 43 | # you won't get errors when the source file has changed and the data can't 44 | # be merged. 45 | # 46 | 47 | ScriptDir=$(dirname $(echo $0 | sed -e "s,^\([^/]\),$(pwd)/\1,")) 48 | ScriptName=$(basename "$0") 49 | ThisScript="${ScriptDir}/${ScriptName}" 50 | 51 | GTMXcodeNote() { 52 | echo ${ThisScript}:${1}: note: GTM ${2} 53 | } 54 | 55 | if [ "$PLATFORM_NAME" == "iphonesimulator" ]; then 56 | # We kill the iPhone simulator because otherwise we run into issues where 57 | # the unittests fail becuase the simulator is currently running, and 58 | # at this time the iPhone SDK won't allow two simulators running at the same 59 | # time. 60 | /usr/bin/killall "iPhone Simulator" 61 | 62 | if [ $GTM_REMOVE_GCOV_DATA ]; then 63 | if [ "${OBJECT_FILE_DIR}-${CURRENT_VARIANT}" != "-" ]; then 64 | if [ -d "${OBJECT_FILE_DIR}-${CURRENT_VARIANT}" ]; then 65 | GTMXcodeNote ${LINENO} "Removing any .gcda files" 66 | (cd "${OBJECT_FILE_DIR}-${CURRENT_VARIANT}" && \ 67 | find . -type f -name "*.gcda" -print0 | xargs -0 rm -f ) 68 | fi 69 | fi 70 | fi 71 | 72 | export DYLD_ROOT_PATH="$SDKROOT" 73 | export DYLD_FRAMEWORK_PATH="$CONFIGURATION_BUILD_DIR" 74 | export IPHONE_SIMULATOR_ROOT="$SDKROOT" 75 | export CFFIXED_USER_HOME="$TEMP_FILES_DIR/iPhone Simulator User Dir" 76 | 77 | # See http://developer.apple.com/technotes/tn2004/tn2124.html for an 78 | # explanation of these environment variables. 79 | 80 | export MallocScribble=YES 81 | export MallocPreScribble=YES 82 | export MallocGuardEdges=YES 83 | export MallocStackLogging=YES 84 | export NSAutoreleaseFreedObjectCheckEnabled=YES 85 | 86 | # Turn on the mostly undocumented OBJC_DEBUG stuff. 87 | export OBJC_DEBUG_FRAGILE_SUPERCLASSES=YES 88 | export OBJC_DEBUG_UNLOAD=YES 89 | # Turned off due to the amount of false positives from NS classes. 90 | # export OBJC_DEBUG_FINALIZERS=YES 91 | export OBJC_DEBUG_NIL_SYNC=YES 92 | 93 | if [ ! $GTM_DISABLE_ZOMBIES ]; then 94 | GTMXcodeNote ${LINENO} "Enabling zombies" 95 | export CFZombieLevel=3 96 | export NSZombieEnabled=YES 97 | fi 98 | 99 | # Cleanup user home and documents directory 100 | if [ -d "$CFFIXED_USER_HOME" ]; then 101 | rm -rf "$CFFIXED_USER_HOME" 102 | fi 103 | mkdir "$CFFIXED_USER_HOME" 104 | mkdir "$CFFIXED_USER_HOME/Documents" 105 | 106 | # 6251475 iPhone simulator leaks @ CFHTTPCookieStore shutdown if 107 | # CFFIXED_USER_HOME empty 108 | GTM_LEAKS_SYMBOLS_TO_IGNORE="CFHTTPCookieStore" 109 | 110 | "$TARGET_BUILD_DIR/$EXECUTABLE_PATH" -RegisterForSystemEvents 111 | else 112 | GTMXcodeNote ${LINENO} "Skipping running of unittests for device build." 113 | fi 114 | exit 0 115 | -------------------------------------------------------------------------------- /CVLibrary/SynthesizeSingleton.h: -------------------------------------------------------------------------------- 1 | // 2 | // SynthesizeSingleton.h 3 | // 4 | // Created by Matt Gallagher on 20/10/08. 5 | // 6 | 7 | #define SYNTHESIZE_SINGLETON_FOR_CLASS(classname) \ 8 | \ 9 | static classname *shared##classname = nil; \ 10 | \ 11 | + (classname *)shared##classname \ 12 | { \ 13 | @synchronized(self) \ 14 | { \ 15 | if (shared##classname == nil) \ 16 | { \ 17 | [[self alloc] init]; \ 18 | } \ 19 | } \ 20 | \ 21 | return shared##classname; \ 22 | } \ 23 | \ 24 | + (id)allocWithZone:(NSZone *)zone \ 25 | { \ 26 | @synchronized(self) \ 27 | { \ 28 | if (shared##classname == nil) \ 29 | { \ 30 | shared##classname = [super allocWithZone:zone]; \ 31 | return shared##classname; \ 32 | } \ 33 | } \ 34 | \ 35 | return nil; \ 36 | } \ 37 | \ 38 | - (id)copyWithZone:(NSZone *)zone \ 39 | { \ 40 | return self; \ 41 | } \ 42 | \ 43 | - (id)retain \ 44 | { \ 45 | return self; \ 46 | } \ 47 | \ 48 | - (NSUInteger)retainCount \ 49 | { \ 50 | return NSUIntegerMax; \ 51 | } \ 52 | \ 53 | - (void)release \ 54 | { \ 55 | } \ 56 | \ 57 | - (id)autorelease \ 58 | { \ 59 | return self; \ 60 | } 61 | -------------------------------------------------------------------------------- /CVLibrary/TextCellHandler.h: -------------------------------------------------------------------------------- 1 | // 2 | // TextCellHandler.h 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 7/12/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CellHandler.h" 11 | 12 | @interface TextCellHandler : NSObject { 13 | id delegate_; 14 | NSString *label_; 15 | NSString *keyPath_; 16 | NSString *identifier_; 17 | NSString *keyboardType_; 18 | } 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /CVLibrary/TextCellHandler.m: -------------------------------------------------------------------------------- 1 | // 2 | // TextCellHandler.m 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 7/12/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import "TextCellHandler.h" 10 | 11 | @interface TextCellHandler() 12 | - (UITableViewCell *) tableView:(UITableView *) tableView cellWithReuseIdentifier:(NSString *) identifier; 13 | - (UIKeyboardType) determineKeyboardType; 14 | @end 15 | 16 | 17 | @implementation TextCellHandler 18 | @synthesize delegate = delegate_; 19 | @synthesize label = label_; 20 | @synthesize keyPath = keyPath_; 21 | @synthesize identifier = identifier_; 22 | @synthesize keyboardType = keyboardType_; 23 | 24 | - (void) dealloc { 25 | [label_ release], label_ = nil; 26 | [keyPath_ release], keyPath_ = nil; 27 | [identifier_ release], identifier_ = nil; 28 | [keyboardType_ release], keyboardType_ = nil; 29 | [super dealloc]; 30 | } 31 | 32 | #define LABEL_TAG 1 33 | #define TEXTFIELD_TAG 2 34 | 35 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath usingData:(id) data { 36 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier_]; 37 | if (cell == nil) { 38 | cell = [self tableView:tableView cellWithReuseIdentifier:identifier_]; 39 | } 40 | 41 | UITextField *textField = (UITextField *) [cell viewWithTag:TEXTFIELD_TAG]; 42 | textField.delegate = self; 43 | textField.keyboardType = [self determineKeyboardType]; 44 | 45 | if (nil == data) { 46 | textField.text = @""; 47 | } else if ([data isKindOfClass:[NSNumber class]]) { 48 | textField.text = [data stringValue]; 49 | } else if ([data isKindOfClass:[NSString class]]) { 50 | textField.text = data; 51 | } 52 | 53 | UILabel *labelField = (UILabel *) [cell viewWithTag:LABEL_TAG]; 54 | labelField.text = label_; 55 | 56 | return cell; 57 | } 58 | 59 | 60 | #define LEFT_COLUMN_OFFSET 10.0 61 | #define LEFT_COLUMN_WIDTH 140.0 62 | 63 | #define RIGHT_COLUMN_OFFSET 150.0 64 | #define RIGHT_COLUMN_WIDTH 100.0 65 | 66 | #define MAIN_FONT_SIZE 16.0 67 | #define LABEL_HEIGHT 26.0 68 | 69 | - (UITableViewCell *) tableView:(UITableView *) tableView cellWithReuseIdentifier:(NSString *) identifier { 70 | UITableViewCell *cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier] autorelease]; 71 | 72 | CGRect frame = CGRectMake(LEFT_COLUMN_OFFSET, (tableView.rowHeight - LABEL_HEIGHT) / 2.0, LEFT_COLUMN_WIDTH, LABEL_HEIGHT); 73 | UILabel *label = [[UILabel alloc] initWithFrame:frame]; 74 | label.tag = LABEL_TAG; 75 | label.font = [UIFont boldSystemFontOfSize:MAIN_FONT_SIZE]; 76 | label.adjustsFontSizeToFitWidth = YES; 77 | label.textAlignment = UITextAlignmentLeft; 78 | [cell.contentView addSubview:label]; 79 | [label release]; 80 | 81 | frame = CGRectMake(RIGHT_COLUMN_OFFSET, (tableView.rowHeight - LABEL_HEIGHT) / 2.0, RIGHT_COLUMN_WIDTH, LABEL_HEIGHT); 82 | UITextField *textField = [[UITextField alloc] initWithFrame:frame]; 83 | textField.borderStyle = UITextBorderStyleNone; 84 | textField.tag = TEXTFIELD_TAG; 85 | textField.font = [UIFont systemFontOfSize:MAIN_FONT_SIZE]; 86 | textField.textAlignment = UITextAlignmentLeft; 87 | textField.textColor = [UIColor blueColor]; 88 | textField.returnKeyType = UIReturnKeyDone; 89 | [cell.contentView addSubview:textField]; 90 | [textField release]; 91 | 92 | return cell; 93 | } 94 | 95 | - (UIKeyboardType) determineKeyboardType { 96 | UIKeyboardType keyboardType = UIKeyboardTypeDefault; 97 | if ([keyboardType_ compare:@"Numeric"] == NSOrderedSame) { 98 | keyboardType = UIKeyboardTypeNumbersAndPunctuation; 99 | } 100 | return keyboardType; 101 | } 102 | 103 | #pragma mark UITextFieldDelegate methods 104 | 105 | - (BOOL)textFieldShouldReturn:(UITextField *)textField { 106 | // the user pressed the "Done" button, so dismiss the keyboard 107 | [textField resignFirstResponder]; 108 | return YES; 109 | } 110 | 111 | - (void)textFieldDidEndEditing:(UITextField *)textField { 112 | // TODO: This is only handling implicit conversions from NSString. For example it fails for unsigned int type. Fix 113 | [delegate_ cellData:textField.text changedForHandler:self]; 114 | } 115 | 116 | 117 | @end 118 | -------------------------------------------------------------------------------- /CVLibrary/UIImage+Adornments.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+Adornments.h 3 | // CVLibrary 4 | // 5 | // Created by Kerem Karatal on 6/4/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CVImageAdorner.h" 11 | 12 | @interface UIImage (CVAdornments) 13 | - (NSUInteger) imageMemorySize; 14 | @end 15 | -------------------------------------------------------------------------------- /CVLibrary/UIImage+Adornments.m: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+Adornments.m 3 | // CVLibrary 4 | // 5 | // Created by Kerem Karatal on 6/4/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import "UIImage+Adornments.h" 10 | #include "CGUtils.h" 11 | 12 | #define SHADOW_BLUR_PIXELS 3 13 | #define BITS_PER_COMPONENT 8 14 | #define NUM_OF_COMPONENTS 4 15 | 16 | @implementation UIImage (CVAdornments) 17 | 18 | - (NSUInteger) imageMemorySize { 19 | if (nil == self) { 20 | return 0; 21 | } 22 | NSUInteger imageMemSize = 0; 23 | CGImageRef cgImage = [self CGImage]; 24 | size_t width = CGImageGetWidth(cgImage); 25 | size_t height = CGImageGetHeight(cgImage); 26 | size_t bitsPerPixel = CGImageGetBitsPerPixel(cgImage); 27 | 28 | imageMemSize = width * height * bitsPerPixel / 8; 29 | return imageMemSize; 30 | } 31 | 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /CVLibrary/UnitTest-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.yourcompany.${PRODUCT_NAME:identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleSignature 16 | ???? 17 | CFBundleVersion 18 | 1.0 19 | 20 | 21 | -------------------------------------------------------------------------------- /CVLibraryDemo/AddItemViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // AddItemViewController.h 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 8/15/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface AddItemViewController : UIViewController { 13 | UITextField *itemName_; 14 | UITextField *itemIndex_; 15 | } 16 | 17 | @property (nonatomic, retain) IBOutlet UITextField *itemName; 18 | @property (nonatomic, retain) IBOutlet UITextField *itemIndex; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /CVLibraryDemo/AddItemViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // AddItemViewController.m 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 8/15/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import "AddItemViewController.h" 10 | 11 | 12 | @implementation AddItemViewController 13 | @synthesize itemName = itemName_; 14 | @synthesize itemIndex = itemIndex_; 15 | 16 | - (void)dealloc { 17 | [itemName_ release], itemName_ = nil; 18 | [itemIndex_ release], itemIndex_ = nil; 19 | [super dealloc]; 20 | } 21 | 22 | - (void)viewDidUnload { 23 | // Release any retained subviews of the main view. 24 | // e.g. self.myOutlet = nil; 25 | 26 | [itemName_ release], itemName_ = nil; 27 | [itemIndex_ release], itemIndex_ = nil; 28 | } 29 | 30 | - (void)didReceiveMemoryWarning { 31 | // Releases the view if it doesn't have a superview. 32 | [super didReceiveMemoryWarning]; 33 | 34 | // Release any cached data, images, etc that aren't in use. 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /CVLibraryDemo/BGTile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keremk/CViPhoneLibrary/a845c169916c0dea05680773b10e85f8020ae700/CVLibraryDemo/BGTile.png -------------------------------------------------------------------------------- /CVLibraryDemo/CVLibraryDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIconFile 12 | 13 | CFBundleIdentifier 14 | com.codingventures.cvlibrarydemo 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | ${PRODUCT_NAME} 19 | CFBundlePackageType 20 | APPL 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | NSMainNibFile 28 | MainWindow 29 | 30 | 31 | -------------------------------------------------------------------------------- /CVLibraryDemo/CVLibraryDemo_Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'CVLibraryDemo' target in the 'CVLibraryDemo' project 3 | // 4 | #import 5 | 6 | #ifndef __IPHONE_3_0 7 | #warning "This project uses features only available in iPhone SDK 3.0 and later." 8 | #endif 9 | 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /CVLibraryDemo/Checkmark_Off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keremk/CViPhoneLibrary/a845c169916c0dea05680773b10e85f8020ae700/CVLibraryDemo/Checkmark_Off.png -------------------------------------------------------------------------------- /CVLibraryDemo/Checkmark_On.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keremk/CViPhoneLibrary/a845c169916c0dea05680773b10e85f8020ae700/CVLibraryDemo/Checkmark_On.png -------------------------------------------------------------------------------- /CVLibraryDemo/Classes/CVLibraryDemoAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // CVLibraryDemoAppDelegate.h 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 6/17/09. 6 | // Copyright Coding Ventures 2009. All rights reserved. 7 | // 8 | 9 | @class RootViewController; 10 | 11 | @interface CVLibraryDemoAppDelegate : NSObject { 12 | UIWindow *window_; 13 | UINavigationController *navigationController_; 14 | RootViewController *rootViewController_; 15 | } 16 | 17 | @property (nonatomic, retain) IBOutlet UIWindow *window; 18 | @property (nonatomic, retain) IBOutlet UINavigationController *navigationController; 19 | @property (nonatomic, retain) IBOutlet RootViewController *rootViewController; 20 | 21 | @end 22 | 23 | -------------------------------------------------------------------------------- /CVLibraryDemo/Classes/CVLibraryDemoAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // CVLibraryDemoAppDelegate.m 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 6/17/09. 6 | // Copyright Coding Ventures 2009. All rights reserved. 7 | // 8 | 9 | #import "CVLibraryDemoAppDelegate.h" 10 | #import "RootViewController.h" 11 | 12 | @implementation CVLibraryDemoAppDelegate 13 | 14 | @synthesize window = window_; 15 | @synthesize navigationController = navigationController_; 16 | @synthesize rootViewController = rootViewController_; 17 | 18 | #pragma mark - 19 | #pragma mark Memory management 20 | 21 | - (void)dealloc { 22 | [navigationController_ release], navigationController_ = nil; 23 | [window_ release], window_ = nil; 24 | [super dealloc]; 25 | } 26 | 27 | #pragma mark - 28 | #pragma mark Application lifecycle 29 | 30 | - (void)applicationDidFinishLaunching:(UIApplication *)application { 31 | // Override point for customization after app launch 32 | 33 | [self.window addSubview:[self.navigationController view]]; 34 | [self.window makeKeyAndVisible]; 35 | } 36 | 37 | 38 | - (void)applicationWillTerminate:(UIApplication *)application { 39 | // Save data if appropriate 40 | } 41 | 42 | 43 | @end 44 | 45 | -------------------------------------------------------------------------------- /CVLibraryDemo/Classes/ColorToStringConverter.h: -------------------------------------------------------------------------------- 1 | // 2 | // ColorToStringConverter.h 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 7/15/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CellHandler.h" 11 | 12 | @interface ColorToStringConverter : NSObject { 13 | } 14 | @end 15 | -------------------------------------------------------------------------------- /CVLibraryDemo/Classes/ColorToStringConverter.m: -------------------------------------------------------------------------------- 1 | // 2 | // ColorToStringConverter.m 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 7/15/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import "ColorToStringConverter.h" 10 | 11 | 12 | @implementation ColorToStringConverter 13 | 14 | - (id) convertFromString:(NSString *) input { 15 | NSString *stringInput = [NSString stringWithFormat:@"%@Color", (NSString *) input]; 16 | return [UIColor performSelector:NSSelectorFromString(stringInput)]; 17 | } 18 | 19 | - (NSString *) convertToString:(id) input { 20 | UIColor *color = (UIColor *) input; 21 | NSString *output = @""; 22 | if ([color isEqual:[UIColor redColor]]) { 23 | output = @"red"; 24 | } else if ([color isEqual:[UIColor orangeColor]]) { 25 | output = @"orange"; 26 | } else if ([color isEqual:[UIColor greenColor]]) { 27 | output = @"green"; 28 | } else if ([color isEqual:[UIColor blueColor]]) { 29 | output = @"blue"; 30 | } else if ([color isEqual:[UIColor yellowColor]]) { 31 | output = @"yellow"; 32 | } else if ([color isEqual:[UIColor blackColor]]) { 33 | output = @"black"; 34 | } else if ([color isEqual:[UIColor whiteColor]]) { 35 | output = @"white"; 36 | } else if ([color isEqual:[UIColor cyanColor]]) { 37 | output = @"cyan"; 38 | } else if ([color isEqual:[UIColor magentaColor]]) { 39 | output = @"magenta"; 40 | } else if ([color isEqual:[UIColor purpleColor]]) { 41 | output = @"purple"; 42 | } else if ([color isEqual:[UIColor brownColor]]) { 43 | output = @"brown"; 44 | } else if ([color isEqual:[UIColor grayColor]]) { 45 | output = @"gray"; 46 | } else if ([color isEqual:[UIColor lightGrayColor]]) { 47 | output = @"lightGray"; 48 | } else if ([color isEqual:[UIColor darkGrayColor]]) { 49 | output = @"darkGray"; 50 | } 51 | 52 | return output; 53 | } 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /CVLibraryDemo/Classes/ConfigOptions.h: -------------------------------------------------------------------------------- 1 | // 2 | // ConfigOptions.h 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 7/26/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface ConfigOptions : NSObject { 13 | CGFloat thumbnailWidth_; 14 | CGFloat thumbnailHeight_; 15 | CGFloat borderWidth_; 16 | CGFloat borderRoundedRadius_; 17 | CGFloat shadowOffsetWidth_; 18 | CGFloat shadowOffsetHeight_; 19 | CGFloat shadowBlur_; 20 | UIColor *borderColor_; 21 | NSInteger numOfSides_; 22 | NSString *shape_; 23 | CGFloat leftMargin_; 24 | CGFloat rightMargin_; 25 | CGFloat columnSpacing_; 26 | CGFloat rotationAngle_; 27 | BOOL showTitles_; 28 | BOOL editMode_; 29 | UIColor *deleteSignBackgroundColor_; 30 | UIColor *deleteSignForegroundColor_; 31 | CGFloat deleteSignSideLength_; 32 | } 33 | 34 | @property (nonatomic, retain) UIColor *borderColor; 35 | @property (nonatomic) CGFloat thumbnailWidth; 36 | @property (nonatomic) CGFloat thumbnailHeight; 37 | @property (nonatomic) CGFloat borderWidth; 38 | @property (nonatomic) CGFloat shadowBlur; 39 | @property (nonatomic) CGFloat shadowOffsetWidth; 40 | @property (nonatomic) CGFloat shadowOffsetHeight; 41 | @property (nonatomic) CGFloat borderRoundedRadius; 42 | @property (nonatomic, copy) NSString *shape; 43 | @property (nonatomic) NSInteger numOfSides; 44 | @property (nonatomic) CGFloat leftMargin; 45 | @property (nonatomic) CGFloat rightMargin; 46 | @property (nonatomic) CGFloat columnSpacing; 47 | @property (nonatomic) CGFloat rotationAngle; 48 | @property (nonatomic) BOOL showTitles; 49 | @property (nonatomic) BOOL editMode; 50 | @property (nonatomic, retain) UIColor *deleteSignBackgroundColor; 51 | @property (nonatomic, retain) UIColor *deleteSignForegroundColor; 52 | @property (nonatomic) CGFloat deleteSignSideLength; 53 | @end 54 | -------------------------------------------------------------------------------- /CVLibraryDemo/Classes/ConfigOptions.m: -------------------------------------------------------------------------------- 1 | // 2 | // ConfigOptions.m 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 7/26/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import "ConfigOptions.h" 10 | 11 | 12 | @implementation ConfigOptions 13 | 14 | @synthesize borderColor = borderColor_; 15 | @synthesize borderRoundedRadius = borderRoundedRadius_; 16 | @synthesize thumbnailWidth = thumbnailWidth_; 17 | @synthesize thumbnailHeight = thumbnailHeight_; 18 | @synthesize borderWidth = borderWidth_; 19 | @synthesize shadowBlur = shadowBlur_; 20 | @synthesize shadowOffsetWidth = shadowOffsetWidth_; 21 | @synthesize shadowOffsetHeight = shadowOffsetHeight_; 22 | @synthesize numOfSides = numOfSides_; 23 | @synthesize shape = shape_; 24 | @synthesize leftMargin = leftMargin_; 25 | @synthesize rightMargin = rightMargin_; 26 | @synthesize columnSpacing = columnSpacing_; 27 | @synthesize rotationAngle = rotationAngle_; 28 | @synthesize showTitles = showTitles_; 29 | @synthesize editMode = editMode_; 30 | @synthesize deleteSignSideLength = deleteSignSideLength_; 31 | @synthesize deleteSignBackgroundColor = deleteSignBackgroundColor_; 32 | @synthesize deleteSignForegroundColor = deleteSignForegroundColor_; 33 | 34 | - (void) dealloc { 35 | [deleteSignBackgroundColor_ release], deleteSignBackgroundColor_ = nil; 36 | [deleteSignForegroundColor_ release], deleteSignForegroundColor_ = nil; 37 | [borderColor_ release], borderColor_ = nil; 38 | [shape_ release], shape_ = nil; 39 | [super dealloc]; 40 | } 41 | 42 | 43 | @end 44 | 45 | -------------------------------------------------------------------------------- /CVLibraryDemo/Classes/DataServices.h: -------------------------------------------------------------------------------- 1 | /* 2 | * DataSources.h 3 | * ActivityApp 4 | * 5 | * Created by Kerem Karatal on 4/14/09. 6 | * Copyright 2009 Coding Ventures. All rights reserved. 7 | * 8 | */ 9 | 10 | @class CVImageAdorner; 11 | 12 | @protocol DemoDataServiceDelegate 13 | @required 14 | - (void) updatedWithItems:(NSArray *) items; 15 | 16 | @optional 17 | - (void) updatedImage:(NSDictionary *) dict; 18 | @end 19 | 20 | @protocol DemoDataService 21 | @required 22 | - (void) beginLoadDemoData; 23 | @property (nonatomic, assign) NSObject *delegate; 24 | 25 | @optional 26 | - (void) beginLoadDemoDataForPage:(NSUInteger) pageNo; 27 | - (void) beginLoadImageForUrl:(NSString *) url; 28 | - (UIImage *) selectedImageForUrl:(NSString *) url; 29 | @end 30 | 31 | -------------------------------------------------------------------------------- /CVLibraryDemo/Classes/DemoGridViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // DemoGridViewController.h 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 6/24/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CVLibrary.h" 11 | #import "DataServices.h" 12 | #import "AddItemViewController.h" 13 | 14 | @interface DemoGridViewController : CVThumbnailViewController { 15 | NSMutableArray *demoItems_; 16 | id dataService_; 17 | BOOL configEnabled_; 18 | AddItemViewController *addItemViewController_; 19 | NSMutableDictionary *activeDownloads_; 20 | } 21 | 22 | @property (nonatomic, retain) id dataService; 23 | @property (nonatomic) BOOL configEnabled; 24 | @end 25 | -------------------------------------------------------------------------------- /CVLibraryDemo/Classes/DemoItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // DemoItem.h 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 6/23/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface DemoItem : NSObject { 13 | NSString *title_; 14 | NSString *imageUrl_; 15 | NSNumber *demoItemId_; 16 | } 17 | 18 | @property (nonatomic, copy) NSString *title; 19 | @property (nonatomic, copy) NSString *imageUrl; 20 | @property (nonatomic, retain) NSNumber *demoItemId; 21 | - (id) initWithDictionary:(NSDictionary *) dict; 22 | 23 | @end 24 | -------------------------------------------------------------------------------- /CVLibraryDemo/Classes/DemoItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // DemoItem.m 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 6/23/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import "DemoItem.h" 10 | 11 | @implementation DemoItem 12 | @synthesize title = title_; 13 | @synthesize imageUrl = imageUrl_; 14 | @synthesize demoItemId = demoItemId_; 15 | 16 | - (void) dealloc { 17 | [title_ release], title_ = nil; 18 | [imageUrl_ release], imageUrl_ = nil; 19 | [demoItemId_ release], demoItemId_ = nil; 20 | [super dealloc]; 21 | } 22 | 23 | - (id) initWithDictionary:(NSDictionary *) dict { 24 | self = [super init]; 25 | if (self != nil) { 26 | self.title = [dict objectForKey:@"title"]; 27 | self.imageUrl = [dict objectForKey:@"image_url"]; 28 | self.demoItemId = [dict objectForKey:@"demo_item_id"]; 29 | } 30 | return self; 31 | } 32 | 33 | @end 34 | -------------------------------------------------------------------------------- /CVLibraryDemo/Classes/FakeDataService.h: -------------------------------------------------------------------------------- 1 | // 2 | // FakeDataService.h 3 | // ActivityApp 4 | // 5 | // Created by Kerem Karatal on 4/14/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "DataServices.h" 11 | #import "DemoItem.h" 12 | 13 | @interface FakeDataService : NSObject { 14 | NSObject *delegate_; 15 | NSOperationQueue *operationQueue_; 16 | } 17 | 18 | - (id) init; 19 | - (UIImage *) createFakeImageForUrl:(NSDictionary *) args; 20 | - (DemoItem *) createDummyDemoItemForId:(NSNumber *) demoItemId title:(NSString *) title url:(NSString *) url; 21 | @end 22 | -------------------------------------------------------------------------------- /CVLibraryDemo/Classes/FakeDataService.m: -------------------------------------------------------------------------------- 1 | // 2 | // FakeDataService.m 3 | // ActivityApp 4 | // 5 | // Created by Kerem Karatal on 4/14/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import "FakeDataService.h" 10 | #import "CVLibrary.h" 11 | #import "DemoItem.h" 12 | 13 | @interface FakeDataService() 14 | - (void) createDummyData; 15 | - (void) asynchCreateFakeImageForUrl:(NSDictionary *) args; 16 | @end 17 | 18 | 19 | @implementation FakeDataService 20 | @synthesize delegate = delegate_; 21 | 22 | - (void) dealloc { 23 | [operationQueue_ release], operationQueue_ = nil; 24 | [super dealloc]; 25 | } 26 | 27 | - (id) init { 28 | self = [super init]; 29 | if (self != nil) { 30 | operationQueue_ = [[NSOperationQueue alloc] init]; 31 | [operationQueue_ setMaxConcurrentOperationCount:1]; 32 | } 33 | return self; 34 | } 35 | 36 | - (void) beginLoadDemoData { 37 | NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(createDummyData) object:nil]; 38 | [operationQueue_ addOperation:operation]; 39 | [operation release]; 40 | } 41 | 42 | - (void) beginLoadImageForUrl:(NSString *) url { 43 | NSDictionary *args = [NSDictionary dictionaryWithObjectsAndKeys:url, @"url", nil]; 44 | NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(asynchCreateFakeImageForUrl:) object:args]; 45 | [operationQueue_ addOperation:operation]; 46 | [operation release]; 47 | } 48 | 49 | - (void) createDummyData { 50 | NSMutableArray *demoItems = [[[NSMutableArray alloc] init] autorelease]; 51 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 52 | for (int i = 0; i < 99; i++) { 53 | NSNumber *demoItemId = [NSNumber numberWithInt:i]; 54 | NSString *title = [NSString stringWithFormat:@"Title_%d", i]; 55 | NSString *url = [NSString stringWithFormat:@"%d", i]; 56 | DemoItem *demoItem = [self createDummyDemoItemForId:demoItemId title:title url:url]; 57 | [demoItems addObject:demoItem]; 58 | } 59 | [pool release]; 60 | [self.delegate performSelectorOnMainThread:@selector(updatedWithItems:) withObject:demoItems waitUntilDone:YES]; 61 | } 62 | 63 | - (DemoItem *) createDummyDemoItemForId:(NSNumber *) demoItemId title:(NSString *) title url:(NSString *) url { 64 | NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:demoItemId, @"demo_item_id", title, @"title", url, @"image_url", nil]; 65 | DemoItem *demoItem = [[[DemoItem alloc] initWithDictionary:dict] autorelease]; 66 | return demoItem; 67 | } 68 | 69 | #define BITS_PER_COMPONENT 8 70 | #define NUM_OF_COMPONENTS 4 71 | 72 | - (void) asynchCreateFakeImageForUrl:(NSDictionary *) args { 73 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 74 | NSString *url = [[[args objectForKey:@"url"] copy] autorelease]; 75 | UIImage *adornedImage = [self createFakeImageForUrl:args]; 76 | NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:adornedImage, @"image", url, @"url", nil]; 77 | [self.delegate performSelectorOnMainThread:@selector(updatedImage:) withObject:dict waitUntilDone:YES]; 78 | [pool drain]; 79 | } 80 | 81 | - (UIImage *) createFakeImageForUrl:(NSDictionary *) args { 82 | NSString *url = [args objectForKey:@"url"]; 83 | CGSize size = {70, 70}; 84 | 85 | // IMPORTANT NOTE: 86 | // DONOT use UIGraphicsBeginImageContext here 87 | // This is done in the background thread and the UI* calls are not threadsafe with the 88 | // main UI thread. So use the pure CoreGraphics APIs instead. 89 | 90 | CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); 91 | 92 | CGContextRef context = CGBitmapContextCreate(NULL, size.width, size.height, 93 | BITS_PER_COMPONENT, 94 | NUM_OF_COMPONENTS * size.width, 95 | colorSpaceRef, 96 | kCGImageAlphaPremultipliedLast); 97 | CGColorSpaceRelease(colorSpaceRef); 98 | CGRect rect = CGRectMake(0.0, 0.0, size.width, size.height); 99 | CGContextClearRect(context, rect); 100 | 101 | CGContextSetRGBFillColor(context, 0.0, 0.0, 1.0, 1.0); 102 | CGContextFillRect(context, rect); 103 | CGContextSetRGBFillColor(context, 0.0, 1.0, 1.0, 1.0); 104 | NSUInteger textLen = [url lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; 105 | char *textChar = malloc(textLen + 1); // +1 for the NULL terminator 106 | [url getCString:textChar maxLength:textLen + 1 encoding:NSUTF8StringEncoding]; 107 | CGContextSetTextMatrix(context, CGAffineTransformIdentity); 108 | CGContextSelectFont(context, "Arial", 40.0, kCGEncodingMacRoman); 109 | CGContextShowTextAtPoint(context, 15.0, 18.0, textChar, textLen); 110 | free(textChar); 111 | CGImageRef cgImage = CGBitmapContextCreateImage(context); 112 | UIImage *image = [UIImage imageWithCGImage:cgImage]; 113 | CGImageRelease(cgImage); 114 | CGContextRelease(context); 115 | 116 | return image; 117 | } 118 | @end 119 | -------------------------------------------------------------------------------- /CVLibraryDemo/Classes/FlickrDataService.h: -------------------------------------------------------------------------------- 1 | // 2 | // FlickrDataService.h 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 7/28/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "DataServices.h" 11 | #import "ObjectiveFlickr.h" 12 | 13 | @interface FlickrDataService : NSObject { 14 | NSObject *delegate_; 15 | OFFlickrAPIContext *context_; 16 | NSOperationQueue *operationQueue_; 17 | } 18 | 19 | - (void) cleanupDiskCache; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /CVLibraryDemo/Classes/FlickrDataService.m: -------------------------------------------------------------------------------- 1 | // 2 | // FlickrDataService.m 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 7/28/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import "FlickrDataService.h" 10 | #import "CVLibrary.h" 11 | #import "DemoItem.h" 12 | #import "SampleAPIKey.h" 13 | 14 | @interface FlickrDataService() 15 | - (void) loadImageForUrl:(NSDictionary *) args; 16 | - (NSString *) filePathFromUrl:(NSString *) url; 17 | - (NSData *) cachedImageDataForUrl:(NSString *) url; 18 | - (BOOL) saveImageData:(NSData *) imageData forUrl:(NSString *) url; 19 | @end 20 | 21 | @implementation FlickrDataService 22 | @synthesize delegate = delegate_; 23 | 24 | - (void) dealloc { 25 | [context_ release], context_ = nil; 26 | [operationQueue_ release], operationQueue_ = nil; 27 | [super dealloc]; 28 | } 29 | 30 | #define CONCURRENT_DOWNLOADS 4 31 | 32 | - (id) init { 33 | self = [super init]; 34 | if (self != nil) { 35 | operationQueue_ = [[NSOperationQueue alloc] init]; 36 | [operationQueue_ setMaxConcurrentOperationCount:CONCURRENT_DOWNLOADS]; 37 | context_ = [[OFFlickrAPIContext alloc] initWithAPIKey:OBJECTIVE_FLICKR_SAMPLE_API_KEY 38 | sharedSecret:OBJECTIVE_FLICKR_SAMPLE_API_SHARED_SECRET]; 39 | } 40 | return self; 41 | } 42 | 43 | #define CACHE_DIR_NAME @"flickrCache" 44 | 45 | - (void) cleanupDiskCache { 46 | // We don't want left over saves from previous sessions... 47 | NSString *cacheDirPath = [NSString stringWithFormat:@"%@%@", NSTemporaryDirectory(), CACHE_DIR_NAME]; 48 | 49 | NSError *error; 50 | if (![[NSFileManager defaultManager] removeItemAtPath:cacheDirPath error:&error]) { 51 | NSLog(@"%@", [error localizedDescription]); 52 | } 53 | } 54 | 55 | - (void) beginLoadDemoData { 56 | OFFlickrAPIRequest *request = [[OFFlickrAPIRequest alloc] initWithAPIContext:context_]; 57 | [request setDelegate:self]; 58 | 59 | [request callAPIMethodWithGET:@"flickr.interestingness.getList" arguments:[NSDictionary dictionaryWithObjectsAndKeys:@"50", @"per_page", nil]]; 60 | } 61 | 62 | - (void) beginLoadDemoDataForPage:(NSUInteger) pageNo { 63 | if (pageNo == 0) 64 | return; 65 | OFFlickrAPIRequest *request = [[OFFlickrAPIRequest alloc] initWithAPIContext:context_]; 66 | [request setDelegate:self]; 67 | 68 | NSString *page = [NSString stringWithFormat:@"%d", pageNo]; 69 | [request callAPIMethodWithGET:@"flickr.interestingness.getList" arguments:[NSDictionary dictionaryWithObjectsAndKeys:@"50", @"per_page", page, @"page", nil]]; 70 | 71 | } 72 | 73 | - (void)flickrAPIRequest:(OFFlickrAPIRequest *)inRequest didCompleteWithResponse:(NSDictionary *)inResponseDictionary { 74 | NSMutableArray *demoItems = [[[NSMutableArray alloc] init] autorelease]; 75 | NSInteger count = [[inResponseDictionary valueForKeyPath:@"photos.photo"] count]; 76 | for (int i = 0; i < count; i++) { 77 | NSDictionary *photoDict = [[inResponseDictionary valueForKeyPath:@"photos.photo"] objectAtIndex:i]; 78 | NSNumber *demoItemId = [NSNumber numberWithInt:i]; 79 | NSString *title = [photoDict valueForKey:@"title"]; 80 | NSURL *url = [context_ photoSourceURLFromDictionary:photoDict size:OFFlickrThumbnailSize]; 81 | NSString *urlString = [url absoluteString]; 82 | NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:demoItemId, @"demo_item_id", title, @"title", urlString, @"image_url", nil]; 83 | DemoItem *demoItem = [[DemoItem alloc] initWithDictionary:dict]; 84 | [demoItems addObject:demoItem]; 85 | [demoItem release]; 86 | } 87 | [self.delegate performSelectorOnMainThread:@selector(updatedWithItems:) withObject:demoItems waitUntilDone:YES]; 88 | 89 | } 90 | 91 | - (void)flickrAPIRequest:(OFFlickrAPIRequest *)inRequest didFailWithError:(NSError *)inError { 92 | 93 | } 94 | 95 | - (void) beginLoadImageForUrl:(NSString *) url { 96 | NSDictionary *args = [NSDictionary dictionaryWithObjectsAndKeys:url, @"url", nil]; 97 | NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(loadImageForUrl:) object:args]; 98 | [operationQueue_ addOperation:operation]; 99 | [operation release]; 100 | } 101 | 102 | - (NSString *) filePathFromUrl:(NSString *) url { 103 | NSString *filePath = [NSString stringWithFormat:@"%@%@/%@", NSTemporaryDirectory(), CACHE_DIR_NAME, [url lastPathComponent] ]; 104 | return filePath; 105 | } 106 | 107 | - (NSData *) cachedImageDataForUrl:(NSString *) url { 108 | NSData *imageData = nil; 109 | NSString *filePath = [self filePathFromUrl:url]; 110 | 111 | if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) { 112 | imageData = [NSData dataWithContentsOfFile:filePath]; 113 | } 114 | 115 | return imageData; 116 | } 117 | 118 | - (BOOL) saveImageData:(NSData *) imageData forUrl:(NSString *) url { 119 | // Check if the temp dir exists 120 | NSString *dirPath = [NSString stringWithFormat:@"%@%@", NSTemporaryDirectory(), CACHE_DIR_NAME]; 121 | BOOL isDirectory; 122 | BOOL pathExists = [[NSFileManager defaultManager] fileExistsAtPath:dirPath isDirectory:&isDirectory]; 123 | 124 | NSError *error; 125 | if (!(pathExists && isDirectory)){ 126 | // Create the directory 127 | if (![[NSFileManager defaultManager] createDirectoryAtPath:dirPath withIntermediateDirectories:NO attributes:nil error:&error]) { 128 | NSLog(@"%@", [error localizedDescription]); 129 | } 130 | } 131 | // Now save the image data 132 | NSString *filePath = [self filePathFromUrl:url]; 133 | 134 | return [imageData writeToFile:filePath atomically:NO]; 135 | } 136 | 137 | - (void) loadImageForUrl:(NSDictionary *) args { 138 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; 139 | 140 | NSString *url = [args objectForKey:@"url"]; 141 | NSData *data = [self cachedImageDataForUrl:url]; 142 | if (nil == data) { 143 | // Image not cached yet, load it from the url location 144 | data = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]]; 145 | 146 | // Save to cache 147 | [self saveImageData:data forUrl:url]; 148 | } 149 | 150 | UIImage *image = [UIImage imageWithData:data]; 151 | 152 | NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:image, @"image", url, @"url", nil]; 153 | [self.delegate performSelectorOnMainThread:@selector(updatedImage:) withObject:dict waitUntilDone:YES]; 154 | [pool release]; 155 | } 156 | 157 | - (UIImage *) selectedImageForUrl:(NSString *) url { 158 | UIImage *image = nil; 159 | NSData *data = [self cachedImageDataForUrl:url]; 160 | if (nil != data) { 161 | image = [UIImage imageWithData:data]; 162 | } 163 | return image; 164 | } 165 | 166 | @end 167 | -------------------------------------------------------------------------------- /CVLibraryDemo/Classes/FlickrDemoViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // FlickrDemoViewController.h 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 8/25/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "CVLibrary.h" 11 | #import "DataServices.h" 12 | 13 | @interface FlickrDemoViewController : CVThumbnailViewController { 14 | NSMutableArray *flickrItems_; 15 | id dataService_; 16 | NSUInteger currentPage_; 17 | NSMutableDictionary *activeDownloads_; 18 | } 19 | 20 | @property (nonatomic, retain) id dataService; 21 | @end 22 | -------------------------------------------------------------------------------- /CVLibraryDemo/Classes/FlickrDemoViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // FlickrDemoViewController.m 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 8/25/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import "FlickrDemoViewController.h" 10 | #import "DemoItem.h" 11 | #import "LoadMoreControl.h" 12 | #import "TestView.h" 13 | 14 | @interface FlickrDemoViewController() 15 | - (void) loadFlickrItems; 16 | - (IBAction) loadMoreRequested:(id) sender; 17 | @end 18 | 19 | @implementation FlickrDemoViewController 20 | @synthesize dataService = dataService_; 21 | 22 | - (void)dealloc { 23 | [flickrItems_ release], flickrItems_ = nil; 24 | [dataService_ setDelegate:nil]; 25 | [dataService_ release], dataService_ = nil; 26 | [activeDownloads_ release], activeDownloads_ = nil; 27 | [super dealloc]; 28 | } 29 | 30 | - (void)viewDidUnload { 31 | // Release any retained subviews of the main view. 32 | // e.g. self.myOutlet = nil; 33 | [dataService_ setDelegate:nil]; 34 | } 35 | 36 | - (void) loadFlickrItems { 37 | // Set up the demo items 38 | if (nil == flickrItems_) { 39 | flickrItems_ = [[NSMutableArray alloc] init]; 40 | [dataService_ setDelegate:self]; 41 | [dataService_ beginLoadDemoData]; 42 | } 43 | } 44 | 45 | - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil { 46 | if (self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]) { 47 | // Custom initialization 48 | flickrItems_ = nil; 49 | currentPage_ = 1; 50 | activeDownloads_ = [[NSMutableDictionary alloc] init]; 51 | } 52 | return self; 53 | } 54 | 55 | // Implement viewDidLoad to do additional setup after loading the view, typically from a nib. 56 | - (void)viewDidLoad { 57 | [super viewDidLoad]; 58 | 59 | LoadMoreControl *loadMoreControl = [[LoadMoreControl alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 100.0)]; 60 | [self.thumbnailView setFooterView:loadMoreControl]; 61 | [self.thumbnailView setNeedsLayout]; 62 | [loadMoreControl addTarget:self action:@selector(loadMoreRequested:) forControlEvents:UIControlEventTouchDown]; 63 | [loadMoreControl release]; 64 | 65 | } 66 | 67 | - (IBAction) loadMoreRequested:(id) sender { 68 | currentPage_++; 69 | [dataService_ beginLoadDemoDataForPage:currentPage_]; 70 | LoadMoreControl *loadMoreControl = (LoadMoreControl *) sender; 71 | [loadMoreControl enableLoadMoreButton]; 72 | } 73 | 74 | - (void)didReceiveMemoryWarning { 75 | // Releases the view if it doesn't have a superview. 76 | [super didReceiveMemoryWarning]; 77 | 78 | // Release any cached data, images, etc that aren't in use. 79 | } 80 | 81 | 82 | #pragma mark CVThumbnailViewDelegate methods 83 | 84 | - (NSInteger) numberOfCellsForThumbnailView:(CVThumbnailView *)thumbnailView { 85 | if (nil == flickrItems_) { 86 | [self loadFlickrItems]; 87 | self.thumbnailView.imageLoadingIcon = [UIImage imageNamed:@"LoadingIcon.png"]; 88 | } 89 | return [flickrItems_ count]; 90 | } 91 | 92 | - (CVThumbnailViewCell *)thumbnailView:(CVThumbnailView *)thumbnailView cellAtIndexPath:(NSIndexPath *)indexPath { 93 | CVThumbnailViewCell *cell = [thumbnailView dequeueReusableCellWithIdentifier:@"Thumbnails"]; 94 | if (nil == cell) { 95 | cell = [[[CVThumbnailViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"Thumbnails"] autorelease]; 96 | } 97 | 98 | DemoItem *demoItem = (DemoItem *) [flickrItems_ objectAtIndex:[indexPath indexForNumOfColumns:[self.thumbnailView numOfColumns]]]; 99 | [cell setImageUrl:demoItem.imageUrl]; 100 | 101 | return cell; 102 | } 103 | 104 | - (void) thumbnailView:(CVThumbnailView *)thumbnailView loadImageForUrl:(NSString *) url forCellAtIndexPath:(NSIndexPath *) indexPath { 105 | [activeDownloads_ setObject:indexPath forKey:url]; 106 | [dataService_ beginLoadImageForUrl:url]; 107 | } 108 | 109 | 110 | #pragma mark DemoDataServiceDelegate methods 111 | - (void) updatedWithItems:(NSArray *) items { 112 | [flickrItems_ addObjectsFromArray:items]; 113 | [self.thumbnailView reloadData]; 114 | } 115 | 116 | - (void) updatedImage:(NSDictionary *) dict { 117 | NSString *url = [dict objectForKey:@"url"]; 118 | UIImage *image = [dict objectForKey:@"image"]; 119 | 120 | NSIndexPath *indexPath = [activeDownloads_ objectForKey:url]; 121 | if (indexPath) { 122 | [self.thumbnailView image:image loadedForUrl:url forCellAtIndexPath:indexPath]; 123 | [activeDownloads_ removeObjectForKey:url]; 124 | } 125 | } 126 | 127 | @end 128 | -------------------------------------------------------------------------------- /CVLibraryDemo/Classes/LoadMoreControl.h: -------------------------------------------------------------------------------- 1 | // 2 | // LoadMoreView.h 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 8/26/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface LoadMoreControl : UIControl { 13 | UIButton *loadMoreButton_; 14 | } 15 | 16 | - (void) enableLoadMoreButton; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /CVLibraryDemo/Classes/LoadMoreControl.m: -------------------------------------------------------------------------------- 1 | // 2 | // LoadMoreView.m 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 8/26/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import "LoadMoreControl.h" 10 | 11 | @interface LoadMoreControl() 12 | - (IBAction) buttonTapped:(id) sender; 13 | @end 14 | 15 | 16 | @implementation LoadMoreControl 17 | 18 | - (void)dealloc { 19 | [loadMoreButton_ removeTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchDown]; 20 | [loadMoreButton_ release], loadMoreButton_ = nil; 21 | [super dealloc]; 22 | } 23 | 24 | - (id)initWithFrame:(CGRect)frame { 25 | if (self = [super initWithFrame:frame]) { 26 | // Initialization code 27 | loadMoreButton_ = [[UIButton buttonWithType:UIButtonTypeCustom] retain]; 28 | [loadMoreButton_ setTitle:@"Load More..." forState:UIControlStateNormal]; 29 | [loadMoreButton_ setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; 30 | [loadMoreButton_ setShowsTouchWhenHighlighted:YES]; 31 | [loadMoreButton_ addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchDown]; 32 | [loadMoreButton_ setContentMode:UIViewContentModeCenter]; 33 | [loadMoreButton_ setContentVerticalAlignment:UIControlContentVerticalAlignmentCenter]; 34 | [loadMoreButton_ setContentHorizontalAlignment:UIControlContentHorizontalAlignmentCenter]; 35 | [self setBackgroundColor:[UIColor orangeColor]]; 36 | [self addSubview:loadMoreButton_]; 37 | } 38 | return self; 39 | } 40 | 41 | - (void) layoutSubviews { 42 | [super layoutSubviews]; 43 | CGSize size = [loadMoreButton_.currentTitle sizeWithFont:loadMoreButton_.titleLabel.font]; 44 | size.width = size.width * 1.2; 45 | size.height = size.height * 1.2; 46 | CGFloat x = (self.bounds.size.width - size.width) / 2; 47 | CGFloat y = (self.bounds.size.height - size.height) / 2; 48 | loadMoreButton_.frame = CGRectMake(x, y, size.width, size.height); 49 | } 50 | 51 | 52 | - (IBAction) buttonTapped:(id) sender { 53 | [loadMoreButton_ setTitle:@"Loading..." forState:UIControlStateNormal]; 54 | loadMoreButton_.enabled = NO; 55 | [self sendActionsForControlEvents:UIControlEventTouchDown]; 56 | } 57 | 58 | - (void) enableLoadMoreButton { 59 | loadMoreButton_.enabled = YES; 60 | [loadMoreButton_ setTitle:@"Load More..." forState:UIControlStateNormal]; 61 | } 62 | 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /CVLibraryDemo/Classes/RootViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // RootViewController.h 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 6/17/09. 6 | // Copyright Coding Ventures 2009. All rights reserved. 7 | // 8 | #import "FakeDataService.h" 9 | #import "FlickrDataService.h" 10 | 11 | @interface RootViewController : UITableViewController { 12 | NSArray *listOfDemos_; 13 | FlickrDataService *flickrDataService_; 14 | FakeDataService *fakeDataService_; 15 | } 16 | 17 | @property (nonatomic, retain) NSArray *listOfDemos; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /CVLibraryDemo/Classes/RootViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // RootViewController.m 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 6/17/09. 6 | // Copyright Coding Ventures 2009. All rights reserved. 7 | // 8 | 9 | #import "RootViewController.h" 10 | #import "DataServices.h" 11 | #import "CVLibrary.h" 12 | #import "DemoGridViewController.h" 13 | #import "FlickrDemoViewController.h" 14 | #import "TestView.h" 15 | 16 | @interface RootViewController() 17 | - (UIView *) testViewWithText:(NSString *) text; 18 | @end 19 | 20 | 21 | @implementation RootViewController 22 | @synthesize listOfDemos = listOfDemos_; 23 | 24 | - (void)dealloc { 25 | [listOfDemos_ release], listOfDemos_ = nil; 26 | [flickrDataService_ release], flickrDataService_ = nil; 27 | [fakeDataService_ release], fakeDataService_ = nil; 28 | [super dealloc]; 29 | } 30 | 31 | - (void)viewDidUnload { 32 | // Release anything that can be recreated in viewDidLoad or on demand. 33 | // e.g. self.myOutlet = nil; 34 | } 35 | 36 | - (id) initWithCoder:(NSCoder *) coder { 37 | if (self = [super initWithCoder:coder]) { 38 | listOfDemos_ = [[NSArray alloc] initWithObjects:@"Generated Image List", @"Flickr Image List", @"Edit Image List", @"Many Images Demo", nil]; 39 | flickrDataService_ = [[FlickrDataService alloc] init]; 40 | [flickrDataService_ cleanupDiskCache]; 41 | fakeDataService_ = [[FakeDataService alloc] init]; 42 | } 43 | return self; 44 | } 45 | 46 | - (void)viewDidLoad { 47 | [super viewDidLoad]; 48 | } 49 | 50 | - (void)didReceiveMemoryWarning { 51 | // Releases the view if it doesn't have a superview. 52 | [super didReceiveMemoryWarning]; 53 | 54 | // Release any cached data, images, etc that aren't in use. 55 | } 56 | 57 | #pragma mark Table view methods 58 | 59 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { 60 | return 1; 61 | } 62 | 63 | 64 | // Customize the number of rows in the table view. 65 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { 66 | return [listOfDemos_ count]; 67 | } 68 | 69 | 70 | // Customize the appearance of table view cells. 71 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { 72 | 73 | static NSString *CellIdentifier = @"Cell"; 74 | 75 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 76 | if (cell == nil) { 77 | cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; 78 | } 79 | 80 | // Configure the cell. 81 | 82 | cell.textLabel.text = [listOfDemos_ objectAtIndex:indexPath.row]; 83 | return cell; 84 | } 85 | 86 | #define GENERATED_IMAGE_LIST_DEMO 0 87 | #define FLICKR_IMAGE_LIST_DEMO 1 88 | #define EDIT_IMAGE_LIST_DEMO 2 89 | #define MANY_IMAGES_DEMO 3 90 | #define THUMBNAIL_WIDTH 70 91 | #define THUMBNAIL_HEIGHT 70 92 | 93 | // Override to support row selection in the table view. 94 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { 95 | CVPolygonBorder *borderStyle = [[CVPolygonBorder alloc] init]; 96 | borderStyle.width = 5.0; 97 | borderStyle.color = [UIColor orangeColor]; 98 | borderStyle.numOfSides = 5; 99 | 100 | CVPolygonBorder *selectedBorderStyle = [[CVPolygonBorder alloc] init]; 101 | selectedBorderStyle.width = 5.0; 102 | selectedBorderStyle.color = [UIColor redColor]; 103 | selectedBorderStyle.numOfSides = 5; 104 | 105 | CVShadowStyle *shadowStyle = [[CVShadowStyle alloc] init]; 106 | shadowStyle.offset = CGSizeMake(-10.0, 10.0); 107 | shadowStyle.blur = 5.0; 108 | 109 | CVImageAdorner *imageAdorner = [[CVImageAdorner alloc] init]; 110 | imageAdorner.borderStyle = borderStyle; 111 | imageAdorner.shadowStyle = shadowStyle; 112 | 113 | CVImageAdorner *selectedImageAdorner = [[CVImageAdorner alloc] init]; 114 | selectedImageAdorner.borderStyle = selectedBorderStyle; 115 | selectedImageAdorner.shadowStyle = shadowStyle; 116 | 117 | CVTitleStyle *titleStyle = [[CVTitleStyle alloc] init]; 118 | titleStyle.font = [UIFont fontWithName:@"Verdana" size:10.0]; 119 | titleStyle.lineBreakMode = UILineBreakModeMiddleTruncation; 120 | titleStyle.backgroundColor = [UIColor whiteColor]; 121 | titleStyle.foregroundColor = [UIColor blackColor]; 122 | 123 | switch (indexPath.row) { 124 | case GENERATED_IMAGE_LIST_DEMO : { 125 | DemoGridViewController *demoGridViewController = [[DemoGridViewController alloc] initWithNibName:nil bundle:nil]; 126 | [demoGridViewController setDataService:fakeDataService_]; 127 | [self.navigationController pushViewController:demoGridViewController animated:YES]; 128 | 129 | CVThumbnailView *gridView = [demoGridViewController thumbnailView]; 130 | [gridView setImageAdorner:imageAdorner]; 131 | [gridView setSelectedImageAdorner:selectedImageAdorner]; 132 | [gridView setRightMargin:20.0]; 133 | [gridView setLeftMargin:20.0]; 134 | [gridView setEditing:YES]; 135 | [gridView setEditModeEnabled:YES]; 136 | [gridView setThumbnailCellSize:CGSizeMake(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT)]; 137 | 138 | [gridView setShowDefaultSelectionEffect:YES]; 139 | [gridView setSelectionBorderColor:[UIColor orangeColor]]; 140 | [gridView setSelectionBorderWidth:6.0]; 141 | [demoGridViewController release]; 142 | 143 | break; 144 | } 145 | case FLICKR_IMAGE_LIST_DEMO : { 146 | DemoGridViewController *demoGridViewController = [[DemoGridViewController alloc] initWithNibName:nil bundle:nil]; 147 | [demoGridViewController setDataService:flickrDataService_]; 148 | [self.navigationController pushViewController:demoGridViewController animated:YES]; 149 | 150 | CVThumbnailView *gridView = [demoGridViewController thumbnailView]; 151 | [gridView setImageAdorner:imageAdorner]; 152 | [gridView setSelectedImageAdorner:selectedImageAdorner]; 153 | [gridView setTitleStyle:titleStyle]; 154 | [gridView setShowDefaultSelectionEffect:NO]; 155 | [gridView setEditModeEnabled:NO]; 156 | [gridView setThumbnailCellSize:CGSizeMake(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT)]; 157 | [gridView setShowTitles:YES]; 158 | [demoGridViewController release]; 159 | 160 | break; 161 | } 162 | case EDIT_IMAGE_LIST_DEMO : { 163 | DemoGridViewController *demoGridViewController = [[DemoGridViewController alloc] initWithNibName:nil bundle:nil]; 164 | [demoGridViewController setConfigEnabled:NO]; 165 | demoGridViewController.navigationItem.rightBarButtonItem = [demoGridViewController editButtonItem]; 166 | [demoGridViewController setDataService:fakeDataService_]; 167 | [self.navigationController pushViewController:demoGridViewController animated:YES]; 168 | 169 | CVThumbnailView *gridView = [demoGridViewController thumbnailView]; 170 | [gridView setImageAdorner:imageAdorner]; 171 | [gridView setSelectedImageAdorner:selectedImageAdorner]; 172 | [gridView setEditModeEnabled:YES]; 173 | [demoGridViewController setConfigEnabled:NO]; 174 | demoGridViewController.navigationItem.rightBarButtonItem = [demoGridViewController editButtonItem]; 175 | [gridView setHeaderView:[self testViewWithText:@"Header View"]]; 176 | [gridView setFooterView:[self testViewWithText:@"Footer View"]]; 177 | [gridView setThumbnailCellSize:CGSizeMake(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT)]; 178 | [demoGridViewController release]; 179 | 180 | break; 181 | } 182 | case MANY_IMAGES_DEMO : { 183 | FlickrDemoViewController *flickrDemoViewController = [[FlickrDemoViewController alloc] initWithNibName:nil bundle:nil]; 184 | [flickrDemoViewController setDataService:flickrDataService_]; 185 | [self.navigationController pushViewController:flickrDemoViewController animated:YES]; 186 | 187 | CVThumbnailView *gridView = [flickrDemoViewController thumbnailView]; 188 | [gridView setImageAdorner:imageAdorner]; 189 | [gridView setSelectedImageAdorner:selectedImageAdorner]; 190 | [gridView setEditModeEnabled:NO]; 191 | [gridView setShowTitles:NO]; 192 | [gridView setThumbnailCellSize:CGSizeMake(THUMBNAIL_WIDTH, THUMBNAIL_HEIGHT)]; 193 | [flickrDemoViewController release]; 194 | break; 195 | } 196 | default: 197 | break; 198 | } 199 | 200 | [borderStyle release], borderStyle = nil; 201 | [selectedBorderStyle release], selectedBorderStyle = nil; 202 | [shadowStyle release], shadowStyle = nil; 203 | [imageAdorner release], imageAdorner = nil; 204 | [selectedImageAdorner release], selectedImageAdorner = nil; 205 | [titleStyle release], titleStyle = nil; 206 | 207 | } 208 | 209 | - (UIView *) testViewWithText:(NSString *) text { 210 | TestView *testView = [[[TestView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 100.0)] autorelease]; 211 | 212 | [testView setBackgroundColor:[UIColor blueColor]]; 213 | [testView setText:text]; 214 | return testView; 215 | } 216 | 217 | @end 218 | 219 | -------------------------------------------------------------------------------- /CVLibraryDemo/Classes/TestView.h: -------------------------------------------------------------------------------- 1 | // 2 | // TestView.h 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 8/24/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | 12 | @interface TestView : UIView { 13 | NSString *text_; 14 | UILabel *label_; 15 | } 16 | 17 | @property (nonatomic, copy) NSString *text; 18 | @end 19 | -------------------------------------------------------------------------------- /CVLibraryDemo/Classes/TestView.m: -------------------------------------------------------------------------------- 1 | // 2 | // TestView.m 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 8/24/09. 6 | // Copyright 2009 Coding Ventures. All rights reserved. 7 | // 8 | 9 | #import "TestView.h" 10 | 11 | 12 | @implementation TestView 13 | @synthesize text = text_; 14 | 15 | - (void)dealloc { 16 | [text_ release], text_ = nil; 17 | [label_ release], label_ = nil; 18 | [super dealloc]; 19 | } 20 | 21 | #define FONT_SIZE 24.0 22 | 23 | - (id)initWithFrame:(CGRect)frame { 24 | if (self = [super initWithFrame:frame]) { 25 | // Initialization code 26 | label_ = [[UILabel alloc] initWithFrame:CGRectZero]; 27 | label_.font = [UIFont systemFontOfSize:FONT_SIZE]; 28 | label_.textColor = [UIColor blackColor]; 29 | [self addSubview:label_]; 30 | } 31 | return self; 32 | } 33 | 34 | - (void) setText:(NSString *) text { 35 | if (text != text_) { 36 | [text_ release]; 37 | text_ = [[NSString alloc] initWithString:text]; 38 | label_.text = text_; 39 | CGSize size = [text_ sizeWithFont:label_.font]; 40 | CGFloat x = (self.bounds.size.width - size.width) / 2; 41 | CGFloat y = (self.bounds.size.height - size.height) / 2; 42 | label_.frame = CGRectMake(x, y, size.width, size.height); 43 | label_.backgroundColor = [self backgroundColor]; 44 | [self setNeedsLayout]; 45 | } 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /CVLibraryDemo/ConfigOptions.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | sectionName 7 | View Configuration 8 | sectionOptions 9 | 10 | 11 | identifier 12 | TextCell 13 | label 14 | Left Margin 15 | keyPath 16 | leftMargin 17 | cellHandlerClass 18 | TextCellHandler 19 | keyboardType 20 | Numeric 21 | 22 | 23 | identifier 24 | TextCell 25 | label 26 | Right Margin 27 | keyPath 28 | rightMargin 29 | cellHandlerClass 30 | TextCellHandler 31 | keyboardType 32 | Numeric 33 | 34 | 35 | identifier 36 | TextCell 37 | label 38 | Column Spacing 39 | keyPath 40 | columnSpacing 41 | cellHandlerClass 42 | TextCellHandler 43 | keyboardType 44 | Numeric 45 | 46 | 47 | identifier 48 | BoolCell 49 | label 50 | Show Titles 51 | keyPath 52 | showTitles 53 | cellHandlerClass 54 | BoolCellHandler 55 | 56 | 57 | identifier 58 | BoolCell 59 | label 60 | Edit Mode 61 | keyPath 62 | editMode 63 | cellHandlerClass 64 | BoolCellHandler 65 | 66 | 67 | 68 | 69 | sectionName 70 | Thumbnail Configuration 71 | sectionOptions 72 | 73 | 74 | identifier 75 | TextCell 76 | label 77 | Width 78 | keyPath 79 | thumbnailWidth 80 | cellHandlerClass 81 | TextCellHandler 82 | keyboardType 83 | Numeric 84 | 85 | 86 | identifier 87 | TextCell 88 | label 89 | Height 90 | keyPath 91 | thumbnailHeight 92 | cellHandlerClass 93 | TextCellHandler 94 | keyboardType 95 | Numeric 96 | 97 | 98 | 99 | 100 | sectionName 101 | Border Configuration 102 | sectionOptions 103 | 104 | 105 | identifier 106 | EnumCell 107 | label 108 | Shape 109 | keyPath 110 | shape 111 | cellHandlerClass 112 | EnumCellHandler 113 | options 114 | RoundedRect,Polygon,Circle 115 | 116 | 117 | identifier 118 | TextCell 119 | label 120 | Width 121 | keyPath 122 | borderWidth 123 | cellHandlerClass 124 | TextCellHandler 125 | 126 | 127 | identifier 128 | EnumCell 129 | label 130 | Color 131 | keyPath 132 | borderColor 133 | cellHandlerClass 134 | EnumCellHandler 135 | options 136 | red,orange,green,blue,yellow,black,white,cyan,magenta,purple,brown,gray,lightGray,darkGray 137 | dataConverter 138 | ColorToStringConverter 139 | 140 | 141 | identifier 142 | TextCell 143 | label 144 | Radius 145 | keyPath 146 | borderRoundedRadius 147 | cellHandlerClass 148 | TextCellHandler 149 | keyboardType 150 | Numeric 151 | 152 | 153 | identifier 154 | TextCell 155 | label 156 | Rotation Angle 157 | keyPath 158 | rotationAngle 159 | cellHandlerClass 160 | TextCellHandler 161 | keyboardType 162 | Numeric 163 | 164 | 165 | identifier 166 | TextCell 167 | label 168 | Sides 169 | keyPath 170 | numOfSides 171 | cellHandlerClass 172 | TextCellHandler 173 | keyboardType 174 | Numeric 175 | 176 | 177 | 178 | 179 | sectionName 180 | Shadow Configuration 181 | sectionOptions 182 | 183 | 184 | identifier 185 | TextCell 186 | label 187 | Offset Width 188 | keyPath 189 | shadowOffsetWidth 190 | cellHandlerClass 191 | TextCellHandler 192 | 193 | 194 | identifier 195 | TextCell 196 | label 197 | Offset Height 198 | keyPath 199 | shadowOffsetHeight 200 | cellHandlerClass 201 | TextCellHandler 202 | 203 | 204 | identifier 205 | TextCell 206 | label 207 | Blur 208 | keyPath 209 | shadowBlur 210 | cellHandlerClass 211 | TextCellHandler 212 | 213 | 214 | 215 | 216 | sectionName 217 | Delete Icon Configuration 218 | sectionOptions 219 | 220 | 221 | identifier 222 | TextCell 223 | label 224 | Side Length 225 | keyPath 226 | deleteSignSideLength 227 | cellHandlerClass 228 | TextCellHandler 229 | keyboardType 230 | Numeric 231 | 232 | 233 | identifier 234 | EnumCell 235 | label 236 | Background Color 237 | keyPath 238 | deleteSignBackgroundColor 239 | cellHandlerClass 240 | EnumCellHandler 241 | options 242 | red,orange,green,blue,yellow,black,white,cyan,magenta,purple,brown,gray,lightGray,darkGray 243 | dataConverter 244 | ColorToStringConverter 245 | 246 | 247 | identifier 248 | EnumCell 249 | label 250 | Foreground Color 251 | keyPath 252 | deleteSignForegroundColor 253 | cellHandlerClass 254 | EnumCellHandler 255 | options 256 | red,orange,green,blue,yellow,black,white,cyan,magenta,purple,brown,gray,lightGray,darkGray 257 | dataConverter 258 | ColorToStringConverter 259 | 260 | 261 | 262 | 263 | 264 | -------------------------------------------------------------------------------- /CVLibraryDemo/LoadingIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keremk/CViPhoneLibrary/a845c169916c0dea05680773b10e85f8020ae700/CVLibraryDemo/LoadingIcon.png -------------------------------------------------------------------------------- /CVLibraryDemo/RootViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 768 5 | 9G55 6 | 677 7 | 949.43 8 | 353.00 9 | 10 | YES 11 | 12 | 13 | 14 | YES 15 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 16 | 17 | 18 | YES 19 | 20 | YES 21 | 22 | 23 | YES 24 | 25 | 26 | 27 | YES 28 | 29 | IBFilesOwner 30 | 31 | 32 | IBFirstResponder 33 | 34 | 35 | 36 | 274 37 | {320, 416} 38 | 39 | 40 | 3 41 | MQA 42 | 43 | NO 44 | YES 45 | NO 46 | 47 | 48 | NO 49 | 50 | NO 51 | 1 52 | 0 53 | YES 54 | 4.400000e+01 55 | 2.200000e+01 56 | 2.200000e+01 57 | 58 | 59 | 60 | 61 | YES 62 | 63 | 64 | tableView 65 | 66 | 67 | 68 | 10 69 | 70 | 71 | 72 | view 73 | 74 | 75 | 76 | 11 77 | 78 | 79 | 80 | dataSource 81 | 82 | 83 | 84 | 12 85 | 86 | 87 | 88 | delegate 89 | 90 | 91 | 92 | 13 93 | 94 | 95 | 96 | 97 | YES 98 | 99 | 0 100 | 101 | YES 102 | 103 | 104 | 105 | 106 | 107 | -1 108 | 109 | 110 | RmlsZSdzIE93bmVyA 111 | 112 | 113 | -2 114 | 115 | 116 | 117 | 118 | 9 119 | 120 | 121 | 122 | 123 | 124 | 125 | YES 126 | 127 | YES 128 | -1.CustomClassName 129 | -2.CustomClassName 130 | 9.IBEditorWindowLastContentRect 131 | 9.IBPluginDependency 132 | 133 | 134 | YES 135 | RootViewController 136 | UIResponder 137 | {{236, 337}, {320, 480}} 138 | com.apple.InterfaceBuilder.IBCocoaTouchPlugin 139 | 140 | 141 | 142 | YES 143 | 144 | YES 145 | 146 | 147 | YES 148 | 149 | 150 | 151 | 152 | YES 153 | 154 | YES 155 | 156 | 157 | YES 158 | 159 | 160 | 161 | 13 162 | 163 | 164 | 165 | YES 166 | 167 | RootViewController 168 | UITableViewController 169 | 170 | IBProjectSource 171 | Classes/RootViewController.h 172 | 173 | 174 | 175 | RootViewController 176 | UITableViewController 177 | 178 | tableView 179 | UITableView 180 | 181 | 182 | IBUserSource 183 | 184 | 185 | 186 | 187 | 188 | 0 189 | CVLibraryDemo.xcodeproj 190 | 3 191 | 192 | 193 | -------------------------------------------------------------------------------- /CVLibraryDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // CVLibraryDemo 4 | // 5 | // Created by Kerem Karatal on 6/17/09. 6 | // Copyright Coding Ventures 2009. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, char *argv[]) { 12 | 13 | NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 14 | int retVal = UIApplicationMain(argc, argv, nil, nil); 15 | [pool release]; 16 | return retVal; 17 | } 18 | --------------------------------------------------------------------------------