Skip to content

Instantly share code, notes, and snippets.

@andrewsardone
Last active December 17, 2015 20:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andrewsardone/5669432 to your computer and use it in GitHub Desktop.
Save andrewsardone/5669432 to your computer and use it in GitHub Desktop.
#import <UIKit/UIKit.h>
@interface AbstractViewController : UIViewController
@property (nonatomic, strong) Class specialButtonClass;
@property (nonatomic, strong) IBOutlet UIButton *specialButton; // laid out in nib
@end
#import "AbstractViewController.h"
#import "UIButton+NLDuplicate.h"
@implementation NLRootViewControllerPad
#pragma mark Object Lifecycle
- (id)init
{
NSString *nibName = NSStringFromClass(AbstractViewController.class);
self = [super initWithNibName:nibName
bundle:nil];
return self;
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
return [self init];
}
#pragma mark UIView lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
[self _updateSpecialButtonWithCustomClass:self.specialButtonClass];
}
- (void)_updateSpecialButtonWithCustomClass:(Class)class
{
self.specialButton = ([self _swapButton:self.specialButton
withDuplicateFromClass:class
inView:self.view]) ?: self.specialButton;
}
- (UIButton *)_swapButton:(UIButton *)button withDuplicateFromClass:(Class)class inView:(UIView *)view
{
if (class == nil) return nil;
UIButton *oldButton = button;
UIButton *newButton = [UIButton nl_duplicateButton:button withClass:class];
[view addSubview:newButton];
[oldButton removeFromSuperview];
return newButton;
}
- (void)setSpecialButtonClass:(Class)specialButtonClass
{
_specialButtonClass = specialButtonClass;
[self _updateSpecialButtonWithCustomClass:_specialButtonClass];
}
@end
#import <UIKit/UIKit.h>
#import "AbstractViewController.h"
@interface AbstractViewController : AbstractViewController
@end
#import "ConcreteViewController.h"
@interface AwesomeButton : UIButton
@end
@implementation AwesomeButton
- (void)drawRect:(CGRect)rect
{
[super drawRect:rect];
// custom drawing
}
@end
@implementation ConcreteViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.specialButtonClass = AwesomeButton.class;
}
@end
#import <UIKit/UIKit.h>
@interface UIButton (NLDuplicate)
/**
* Duplicates the given `UIButton` object (copying its various properties) into
* a newly initialized button allocated from the given `Class`.
*
* @discussion This is really a v0.1 utility method, only duplicating the specific
* properties I needed at the time. It has some baseline test coverage
* (see UIButton+NLDuplicateTests.m), and might be a good candidate
* for generalizing across other UIViews or general NSObjects.
*
* I primary use case is for replacing an Interface Builder initialized
* object with one created from a custom class that you want to switch
* out at runtime. You would duplicate an object created in the
* Interface Builder file, replacing its various outlets and position
* in the view hierarchy with the duplicate object.
*/
+ (instancetype)nl_duplicateButton:(UIButton *)button withClass:(Class)class;
@end
#import "UIButton+NLDuplicate.h"
@implementation UIButton (NLDuplicate)
+ (instancetype)nl_duplicateButton:(UIButton *)button withClass:(Class)class
{
class = class ?: self;
UIButton *newButton = [class buttonWithType:button.buttonType];
newButton.frame = button.frame;
for (id target in [button allTargets]) {
for (NSString *selectorName in [button actionsForTarget:target forControlEvent:UIControlEventTouchUpInside]) {
[newButton addTarget:target
action:NSSelectorFromString(selectorName)
forControlEvents:UIControlEventTouchUpInside];
}
}
id<NSFastEnumeration> controlStates = @[
@(UIControlStateNormal),
@(UIControlStateSelected),
@(UIControlStateHighlighted),
@(UIControlStateHighlighted|UIControlStateSelected)
];
for (NSNumber *controlState in controlStates) {
UIControlState cs = controlState.unsignedIntegerValue;
[newButton setImage:[button imageForState:cs]
forState:cs];
}
return newButton;
}
@end
#import <SenTestingKit/SenTestingKit.h>
#import "UIButton+NLDuplicate.h"
@interface SpecialButton : UIButton
@end
@implementation SpecialButton
@end
@interface TestTargetAction : NSObject
@end
@implementation TestTargetAction
- (void)someAction:(id)sender {}
@end
@interface TestImage : UIImage
@end
@implementation TestImage
+ (instancetype)testImage
{
CGSize size = (CGSize) {1, 1};
UIGraphicsBeginImageContext(size);
[UIColor.redColor setFill];
[[UIBezierPath bezierPathWithRect:CGRectMake(0, 0, size.width, size.height)] fill];
UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return (id)img;
}
@end
#pragma mark -
@interface UIButton_NLDuplicateTests : SenTestCase
@end
@implementation UIButton_NLDuplicateTests
- (void)testDefaultDuplicateClassToUIBUtton
{
UIButton *newButton = [UIButton nl_duplicateButton:[UIButton buttonWithType:UIButtonTypeCustom] withClass:nil];
STAssertTrue([newButton isKindOfClass:UIButton.class], nil);
}
- (void)testEqualButtonTypes
{
UIButton *oldButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
UIButton *newButton = [UIButton nl_duplicateButton:oldButton withClass:nil];
STAssertEquals(newButton.buttonType, oldButton.buttonType, nil);
}
- (void)testInitializesDuplicateButtonWithGivenClass
{
UIButton *oldButton = [UIButton buttonWithType:UIButtonTypeCustom];
UIButton *newButton = [UIButton nl_duplicateButton:oldButton withClass:SpecialButton.class];
STAssertTrue([newButton isKindOfClass:SpecialButton.class], nil);
}
- (void)testEqualFrames
{
UIButton *oldButton = [UIButton buttonWithType:UIButtonTypeCustom];
oldButton.frame = CGRectMake(20, 20, 100, 100);
UIButton *newButton = [UIButton nl_duplicateButton:oldButton withClass:SpecialButton.class];
STAssertTrue(CGRectEqualToRect(newButton.frame, oldButton.frame), nil);
}
- (void)testDuplicateTargetActionForUIControlEventTouchUpInside
{
id buttonActionTarget = [TestTargetAction new];
UIButton *oldButton = [UIButton buttonWithType:UIButtonTypeCustom];
[oldButton addTarget:buttonActionTarget action:@selector(someAction:) forControlEvents:UIControlEventTouchUpInside];
UIButton *newButton = [UIButton nl_duplicateButton:oldButton withClass:SpecialButton.class];
STAssertEqualObjects([newButton actionsForTarget:buttonActionTarget forControlEvent:UIControlEventTouchUpInside],
[oldButton actionsForTarget:buttonActionTarget forControlEvent:UIControlEventTouchUpInside],
nil);
}
- (void)testImageForUIControlStateNormal
{
UIButton *oldButton = [UIButton buttonWithType:UIButtonTypeCustom];
[oldButton setImage:[TestImage testImage] forState:UIControlStateNormal];
UIButton *newButton = [UIButton nl_duplicateButton:oldButton withClass:SpecialButton.class];
STAssertEqualObjects([newButton imageForState:UIControlStateNormal],
[oldButton imageForState:UIControlStateNormal],
nil);
}
- (void)testImageForUIControlStateSelected
{
UIButton *oldButton = [UIButton buttonWithType:UIButtonTypeCustom];
[oldButton setImage:[TestImage testImage] forState:UIControlStateSelected];
UIButton *newButton = [UIButton nl_duplicateButton:oldButton withClass:SpecialButton.class];
STAssertEqualObjects([newButton imageForState:UIControlStateSelected],
[oldButton imageForState:UIControlStateSelected],
nil);
}
- (void)testImageForUIControlStateHighlighted
{
UIButton *oldButton = [UIButton buttonWithType:UIButtonTypeCustom];
[oldButton setImage:[TestImage testImage] forState:UIControlStateHighlighted];
UIButton *newButton = [UIButton nl_duplicateButton:oldButton withClass:SpecialButton.class];
STAssertEqualObjects([newButton imageForState:UIControlStateHighlighted],
[oldButton imageForState:UIControlStateHighlighted],
nil);
}
- (void)testImageForUIControlStateHighlightedAndSelected
{
UIButton *oldButton = [UIButton buttonWithType:UIButtonTypeCustom];
[oldButton setImage:[TestImage testImage] forState:UIControlStateHighlighted|UIControlStateSelected];
UIButton *newButton = [UIButton nl_duplicateButton:oldButton withClass:SpecialButton.class];
STAssertEqualObjects([newButton imageForState:UIControlStateHighlighted|UIControlStateSelected],
[oldButton imageForState:UIControlStateHighlighted|UIControlStateSelected],
nil);
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment