Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds class methods and strongly typed return values. #8

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 37 additions & 5 deletions CodeGenTestApp/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="4514" systemVersion="13B42" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" initialViewController="INF-sx-8CY">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="5053" systemVersion="13C64" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" initialViewController="INF-sx-8CY">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="3747"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="3733"/>
</dependencies>
<scenes>
<!--Master View Controller-->
Expand Down Expand Up @@ -71,7 +71,7 @@
<!--Detail View Controller - Detail-->
<scene sceneID="Cn3-H9-jdl">
<objects>
<viewController title="Detail" id="Ah7-4n-0Wa" customClass="CGTADetailViewController" sceneMemberID="viewController">
<viewController storyboardIdentifier="Detail View Controller" title="Detail" id="Ah7-4n-0Wa" customClass="CGTADetailViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="EK1-eN-tCq"/>
<viewControllerLayoutGuide type="bottom" id="wQM-DP-Tgk"/>
Expand All @@ -80,18 +80,50 @@
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cK7-Bz-0X1">
<imageView contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="cK7-Bz-0X1">
<rect key="frame" x="0.0" y="1" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<gestureRecognizers/>
<connections>
<outletCollection property="gestureRecognizers" destination="7Ox-9Z-jyy" appends="YES" id="tVL-VO-d0n"/>
</connections>
</imageView>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Country Name" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="D8d-Ff-nf5">
<rect key="frame" x="58" y="493" width="205" height="39"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="32"/>
<color key="textColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Tap to view name" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XA9-HU-Mx0">
<rect key="frame" x="93" y="84" width="134" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstAttribute="centerX" secondItem="D8d-Ff-nf5" secondAttribute="centerX" id="6Ei-aD-kBS"/>
<constraint firstItem="XA9-HU-Mx0" firstAttribute="top" secondItem="EK1-eN-tCq" secondAttribute="bottom" constant="20" id="IoW-zC-bSQ"/>
<constraint firstAttribute="centerX" secondItem="XA9-HU-Mx0" secondAttribute="centerX" id="Y6M-Ux-1qK"/>
<constraint firstItem="wQM-DP-Tgk" firstAttribute="top" secondItem="D8d-Ff-nf5" secondAttribute="top" constant="75" id="pjb-U5-Ai8"/>
</constraints>
</view>
<navigationItem key="navigationItem" title="Detail" id="cra-N8-TIN"/>
<connections>
<outlet property="countryNameLabel" destination="D8d-Ff-nf5" id="h5l-XS-5ac"/>
<outlet property="countryNameTopConstraint" destination="pjb-U5-Ai8" id="UrE-La-Oyg"/>
<outlet property="imageView" destination="cK7-Bz-0X1" id="dbA-f5-cQY"/>
<outlet property="tapLabel" destination="XA9-HU-Mx0" id="INt-97-ZiP"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="lrm-kH-fPn" sceneMemberID="firstResponder"/>
<tapGestureRecognizer id="7Ox-9Z-jyy">
<connections>
<action selector="imageTapped:" destination="Ah7-4n-0Wa" id="MLH-Hq-MSG"/>
</connections>
</tapGestureRecognizer>
</objects>
<point key="canvasLocation" x="1428" y="64"/>
</scene>
Expand Down Expand Up @@ -126,4 +158,4 @@
<simulatedOrientationMetrics key="orientation"/>
<simulatedScreenMetrics key="destination" type="retina4"/>
</simulatedMetricsContainer>
</document>
</document>
1 change: 1 addition & 0 deletions CodeGenTestApp/CGTADetailViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@
@interface CGTADetailViewController : UIViewController

@property (nonatomic, strong) UIImage *image;
@property (nonatomic, copy) NSString *countryName;

@end
28 changes: 28 additions & 0 deletions CodeGenTestApp/CGTADetailViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,15 @@

#import "CGTADetailViewController.h"
#import "CGTATestAppColorList.h"
#import "CGTAMainStoryboardIdentifiers.h"


@interface CGTADetailViewController ()

@property (nonatomic, strong) IBOutlet UIImageView *imageView;
@property (weak, nonatomic) IBOutlet UILabel *tapLabel;
@property (weak, nonatomic) IBOutlet UILabel *countryNameLabel;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *countryNameTopConstraint;

@end

Expand All @@ -26,6 +30,12 @@ - (void)setImage:(UIImage *)image;
[self updateView];
}

- (void)setCountryName:(NSString *)countryName;
{
_countryName = countryName;
[self updateView];
}

- (void)viewDidLoad;
{
[self updateView];
Expand All @@ -37,11 +47,29 @@ - (void)viewDidLoad;
layer.colors = @[(id)[UIColor whiteColor].CGColor, (id)[CGTATestAppColorList tealColor].CGColor];
layer.frame = self.view.layer.bounds;
[self.view.layer insertSublayer:layer atIndex:0];

// hide the label at first
self.countryNameTopConstraint.constant = 0;
}

- (void)updateView;
{
self.imageView.image = self.image;
self.countryNameLabel.text = self.countryName;
}

- (IBAction)imageTapped:(UITapGestureRecognizer *)sender;
{
if (sender.state == UIGestureRecognizerStateEnded) {
// the label was positioned perfectly via the storyboard, so now we can restore
// the perfect positioning easily, by refering to the constant that was generated for us!
self.countryNameTopConstraint.constant = self.countryNameTopConstraint.constant == 0 ? [self countryNameTopConstraintOriginalConstant] : 0;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Holy equality comparisons between floating-point numbers and integer literals, Batman!

(Seriously, while this does work in this case, it's probably a bad idea to do this kind of thing in example code that will be copied ad nauseam.)

[UIView animateWithDuration:0.2
animations:^{
self.tapLabel.alpha = self.countryNameTopConstraint.constant == 0 ? 1 : 0;
[self.view layoutIfNeeded];
}];
}
}

@end
8 changes: 8 additions & 0 deletions CodeGenTestApp/CGTAFlagCollectionViewCell.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#import <UIKit/UIKit.h>

@interface CGTAFlagCollectionViewCell : UICollectionViewCell

@property (nonatomic, weak) IBOutlet UIImageView *imageView;
@property (nonatomic, copy) NSString *countryName;

@end
4 changes: 4 additions & 0 deletions CodeGenTestApp/CGTAFlagCollectionViewCell.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#import "CGTAFlagCollectionViewCell.h"

@implementation CGTAFlagCollectionViewCell
@end
1 change: 1 addition & 0 deletions CodeGenTestApp/CGTAImagesCatalog+RuntimeHackery.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

@interface CGTAImagesCatalog (RuntimeHackery)

+ (NSArray *)allImageNames;
+ (NSArray *)allImages;

@end
22 changes: 21 additions & 1 deletion CodeGenTestApp/CGTAImagesCatalog+RuntimeHackery.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,33 @@

@implementation CGTAImagesCatalog (RuntimeHackery)

+ (NSArray *)allImageNames;
{
NSMutableArray *imageNames = [NSMutableArray array];
unsigned int count;
Method *methods = class_copyMethodList(object_getClass(self), &count);
for (unsigned int index = 0; index < count; index++) {
SEL methodName = method_getName(methods[index]);
if (sel_isEqual(methodName, _cmd) || sel_isEqual(methodName, @selector(allImages))) {
continue;
}
NSString *imageName = NSStringFromSelector(method_getName(methods[index]));
// remove the "Image" suffix
imageName = [imageName substringToIndex:[imageName length] - [@"Image" length]];
[imageNames addObject:[imageName uppercaseString]];
}
free(methods);
return imageNames;
}

+ (NSArray *)allImages;
{
NSMutableArray *images = [NSMutableArray array];
unsigned int count;
Method *methods = class_copyMethodList(object_getClass(self), &count);
for (unsigned int index = 0; index < count; index++) {
if (sel_isEqual(method_getName(methods[index]), _cmd)) {
SEL methodName = method_getName(methods[index]);
if (sel_isEqual(methodName, _cmd) || sel_isEqual(methodName, @selector(allImageNames))) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that you're doing this twice in subtly different ways, maybe it's worth having a list somewhere of the runtime hackery methods to compare against instead?

continue;
}
id image = method_invoke(self, methods[index]);
Expand Down
59 changes: 34 additions & 25 deletions CodeGenTestApp/CGTAMasterViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,13 @@
#import "CGTADetailViewController.h"
#import "CGTAImagesCatalog+RuntimeHackery.h"
#import "CGTAMainStoryboardIdentifiers.h"


@interface CGTAFlagCollectionViewCell : UICollectionViewCell

@property (nonatomic, weak) IBOutlet UIImageView *imageView;

@end

#import "CGTAFlagCollectionViewCell.h"

@interface CGTAMasterViewController ()

@property (nonatomic, weak) IBOutlet UISlider *cellSizeSlider;
@property (nonatomic, strong) NSArray *flagImages;
@property (nonatomic, strong) NSArray *flagImageNames;

@end

Expand All @@ -41,9 +35,11 @@ - (void)awakeFromNib;

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender;
{
if ([segue.identifier isEqualToString:CGTAMainStoryboardTapOnFlagIdentifier]) {
if ([segue.identifier isEqualToString:[self tapOnFlagSegueIdentifier]]) {
CGTADetailViewController *detailViewController = segue.destinationViewController;
detailViewController.image = ((CGTAFlagCollectionViewCell *)sender).imageView.image;
CGTAFlagCollectionViewCell *cellSender = sender;
detailViewController.image = cellSender.imageView.image;
detailViewController.countryName = cellSender.countryName;
}
}

Expand All @@ -58,17 +54,26 @@ - (IBAction)sliderValueChanged:(UISlider *)sender;

- (NSArray *)flagImages;
{
NSArray *allFlagImages = nil;

// Initial version: full of strings that you have to type correctly!
// Misspell any of these and your app will crash on trying to add `nil` to an array.
allFlagImages = @[[UIImage imageNamed:@"USA"], [UIImage imageNamed:@"Canada"], [UIImage imageNamed:@"UK"], [UIImage imageNamed:@"Australia"]];

// New version: get the properly compiler-checked spelling from the image catalog.
allFlagImages = @[[CGTAImagesCatalog usaImage], [CGTAImagesCatalog canadaImage], [CGTAImagesCatalog ukImage], [CGTAImagesCatalog australiaImage]];
if (!_flagImages) {
// Initial version: full of strings that you have to type correctly!
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of "Initial version", how about "What you might have done without this tool"

// Misspell any of these and your app will crash on trying to add `nil` to an array.
_flagImages = @[[UIImage imageNamed:@"USA"], [UIImage imageNamed:@"Canada"], [UIImage imageNamed:@"UK"], [UIImage imageNamed:@"Australia"]];

// New version: get the properly compiler-checked spelling from the image catalog.
_flagImages = @[[CGTAImagesCatalog usaImage], [CGTAImagesCatalog canadaImage], [CGTAImagesCatalog ukImage], [CGTAImagesCatalog australiaImage]];

// But really, why not use a little runtime hackery because we can?
_flagImages = [CGTAImagesCatalog allImages];
}
return _flagImages;
}

// But really, why not use a little runtime hackery because we can?
return [CGTAImagesCatalog allImages];
- (NSArray *)flagImageNames;
{
if (!_flagImageNames) {
_flagImageNames = [CGTAImagesCatalog allImageNames];
}
return _flagImageNames;
}

#pragma mark - UICollectionViewDataSource
Expand All @@ -85,13 +90,17 @@ - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSe

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath;
{
CGTAFlagCollectionViewCell *cell = (CGTAFlagCollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:CGTAMainStoryboardImageCellIdentifier forIndexPath:indexPath];
CGTAFlagCollectionViewCell *cell = nil;

// Initial version: we must type in the identifier, and have no guarantees as to which class it returns
cell = (CGTAFlagCollectionViewCell *)[collectionView dequeueReusableCellWithReuseIdentifier:@"Image Cell" forIndexPath:indexPath];

// New version: class extension which returns the exact type we are expecting
cell = [self dequeueImageCellForIndexPath:indexPath ofCollectionView:collectionView];

cell.imageView.image = self.flagImages[indexPath.item];
cell.countryName = self.flagImageNames[indexPath.item];
return cell;
}

@end


@implementation CGTAFlagCollectionViewCell
@end
16 changes: 16 additions & 0 deletions CodeGenTestApp/CodeGenTestApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
A881854118A9B622002803FC /* CGTAImagesCatalog+RuntimeHackery.m in Sources */ = {isa = PBXBuildFile; fileRef = A838793518A05B6D00B386D6 /* CGTAImagesCatalog+RuntimeHackery.m */; };
A881854218A9B622002803FC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = A83878E518A0367C00B386D6 /* main.m */; };
A881854418A9B663002803FC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A83878EB18A0367C00B386D6 /* Main.storyboard */; };
AA24EC5A18EB4F8E00DB0F94 /* MoreExamples.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA24EC5918EB4F8E00DB0F94 /* MoreExamples.storyboard */; };
AA509EE318F85CA5000F4136 /* CGTAFlagCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = AA509EE218F85CA5000F4136 /* CGTAFlagCollectionViewCell.m */; };
AAA9F41518ED1A0C00BA7A27 /* CGTAMoreExamplesStoryboardIdentifiers.m in Sources */ = {isa = PBXBuildFile; fileRef = AAA9F41418ED1A0C00BA7A27 /* CGTAMoreExamplesStoryboardIdentifiers.m */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -95,6 +98,11 @@
A881852518A9B512002803FC /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.0.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; };
A881852718A9B520002803FC /* codegenutils.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = codegenutils.xcodeproj; path = ../codegenutils.xcodeproj; sourceTree = "<group>"; };
A89D8FE617CFFDCE0077F2B5 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
AA24EC5918EB4F8E00DB0F94 /* MoreExamples.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = MoreExamples.storyboard; sourceTree = "<group>"; };
AA509EE118F85CA5000F4136 /* CGTAFlagCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CGTAFlagCollectionViewCell.h; sourceTree = "<group>"; };
AA509EE218F85CA5000F4136 /* CGTAFlagCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CGTAFlagCollectionViewCell.m; sourceTree = "<group>"; };
AAA9F41318ED1A0C00BA7A27 /* CGTAMoreExamplesStoryboardIdentifiers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CGTAMoreExamplesStoryboardIdentifiers.h; sourceTree = "<group>"; };
AAA9F41418ED1A0C00BA7A27 /* CGTAMoreExamplesStoryboardIdentifiers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CGTAMoreExamplesStoryboardIdentifiers.m; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand All @@ -117,11 +125,14 @@
A83878E818A0367C00B386D6 /* CGTAAppDelegate.h */,
A83878E918A0367C00B386D6 /* CGTAAppDelegate.m */,
A83878EB18A0367C00B386D6 /* Main.storyboard */,
AA24EC5918EB4F8E00DB0F94 /* MoreExamples.storyboard */,
A83878F418A0367C00B386D6 /* Images.xcassets */,
A83878EE18A0367C00B386D6 /* CGTAMasterViewController.h */,
A83878EF18A0367C00B386D6 /* CGTAMasterViewController.m */,
A83878F118A0367C00B386D6 /* CGTADetailViewController.h */,
A83878F218A0367C00B386D6 /* CGTADetailViewController.m */,
AA509EE118F85CA5000F4136 /* CGTAFlagCollectionViewCell.h */,
AA509EE218F85CA5000F4136 /* CGTAFlagCollectionViewCell.m */,
A838791718A0456500B386D6 /* Derived Sources */,
A83878E018A0367C00B386D6 /* Supporting Files */,
);
Expand All @@ -146,6 +157,8 @@
A838791518A0455E00B386D6 /* CGTAImagesCatalog.m */,
A838793118A0557E00B386D6 /* CGTAMainStoryboardIdentifiers.h */,
A838793218A0557E00B386D6 /* CGTAMainStoryboardIdentifiers.m */,
AAA9F41318ED1A0C00BA7A27 /* CGTAMoreExamplesStoryboardIdentifiers.h */,
AAA9F41418ED1A0C00BA7A27 /* CGTAMoreExamplesStoryboardIdentifiers.m */,
A838791818A04AB300B386D6 /* CGTATestAppColorList.h */,
A838791918A04AB300B386D6 /* CGTATestAppColorList.m */,
A838793418A05B6D00B386D6 /* CGTAImagesCatalog+RuntimeHackery.h */,
Expand Down Expand Up @@ -280,6 +293,7 @@
buildActionMask = 2147483647;
files = (
A881854418A9B663002803FC /* Main.storyboard in Resources */,
AA24EC5A18EB4F8E00DB0F94 /* MoreExamples.storyboard in Resources */,
A881853A18A9B614002803FC /* Images.xcassets in Resources */,
A83878E418A0367C00B386D6 /* InfoPlist.strings in Resources */,
);
Expand Down Expand Up @@ -309,10 +323,12 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
AAA9F41518ED1A0C00BA7A27 /* CGTAMoreExamplesStoryboardIdentifiers.m in Sources */,
A881853918A9B609002803FC /* CGTAAppDelegate.m in Sources */,
A881853C18A9B622002803FC /* CGTAMasterViewController.m in Sources */,
A881853F18A9B622002803FC /* CGTAMainStoryboardIdentifiers.m in Sources */,
A881854118A9B622002803FC /* CGTAImagesCatalog+RuntimeHackery.m in Sources */,
AA509EE318F85CA5000F4136 /* CGTAFlagCollectionViewCell.m in Sources */,
A881853E18A9B622002803FC /* CGTAImagesCatalog.m in Sources */,
A881854018A9B622002803FC /* CGTATestAppColorList.m in Sources */,
A881854218A9B622002803FC /* main.m in Sources */,
Expand Down
Loading