SlideShare une entreprise Scribd logo
1  sur  61
Télécharger pour lire hors ligne
iOS UI Component Design
Configuration and Callbacks Using the
“Parameter Object”Pattern
Brian Gesiak
April 9th, 2014
Research Student, The University of Tokyo
@modocache
Today
• Problem: How do we allow users to customize UI
elements?
• Appearance, animations, and behavior should be
customizable
• Composition over inheritance
• Solution: Configuration objects
!
• Problem: How do we define an API for callbacks?
• Public delegate and block callbacks are hard to
deprecate or change
• Solution: Parameter objects
Customization API Example
JVFloatLabeledTextField
Customization API Example
JVFloatLabeledTextField
Customization API Example
JVFloatLabeledTextField Appearance API
Customization API Example
JVFloatLabeledTextField Appearance API
@interface JVFloatLabeledTextField : UITextField
!
@property (nonatomic, strong)
NSNumber *floatingLabelYPadding UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIFont *floatingLabelFont UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIColor *floatingLabelTextColor UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIColor *floatingLabelActiveTextColor UI_APPEARANCE_SELECTOR;
@property (nonatomic, assign)
NSInteger animateEvenIfNotFirstResponder UI_APPEARANCE_SELECTOR;
!
@end
Customization API Example
JVFloatLabeledTextField Appearance API
@interface JVFloatLabeledTextField : UITextField
!
@property (nonatomic, strong)
NSNumber *floatingLabelYPadding UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIFont *floatingLabelFont UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIColor *floatingLabelTextColor UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIColor *floatingLabelActiveTextColor UI_APPEARANCE_SELECTOR;
@property (nonatomic, assign)
NSInteger animateEvenIfNotFirstResponder UI_APPEARANCE_SELECTOR;
!
@end
Customization API Example
JVFloatLabeledTextField Appearance API
@interface JVFloatLabeledTextField : UITextField
!
@property (nonatomic, strong)
NSNumber *floatingLabelYPadding UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIFont *floatingLabelFont UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIColor *floatingLabelTextColor UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIColor *floatingLabelActiveTextColor UI_APPEARANCE_SELECTOR;
@property (nonatomic, assign)
NSInteger animateEvenIfNotFirstResponder UI_APPEARANCE_SELECTOR;
!
@end
Customization API Example
JVFloatLabeledTextField Appearance API
@interface JVFloatLabeledTextField : UITextField
!
@property (nonatomic, strong)
NSNumber *floatingLabelYPadding UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIFont *floatingLabelFont UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIColor *floatingLabelTextColor UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIColor *floatingLabelActiveTextColor UI_APPEARANCE_SELECTOR;
@property (nonatomic, assign)
NSInteger animateEvenIfNotFirstResponder UI_APPEARANCE_SELECTOR;
!
@end
But What About Categories?
Favoring Composition Over Inheritance
But What About Categories?
• JVFloatLabeledTextField is a subclass of UITextField
Favoring Composition Over Inheritance
But What About Categories?
• JVFloatLabeledTextField is a subclass of UITextField
• We need to use the class for its functionality
Favoring Composition Over Inheritance
But What About Categories?
• JVFloatLabeledTextField is a subclass of UITextField
• We need to use the class for its functionality
• We’re forced to choose it or other useful subclasses
Favoring Composition Over Inheritance
But What About Categories?
• JVFloatLabeledTextField is a subclass of UITextField
• We need to use the class for its functionality
• We’re forced to choose it or other useful subclasses
• We need to subclass it to add functionality
Favoring Composition Over Inheritance
But What About Categories?
• JVFloatLabeledTextField is a subclass of UITextField
• We need to use the class for its functionality
• We’re forced to choose it or other useful subclasses
• We need to subclass it to add functionality
• It forces itself upon our inheritance hierarchy
Favoring Composition Over Inheritance
But What About Categories?
• JVFloatLabeledTextField is a subclass of UITextField
• We need to use the class for its functionality
• We’re forced to choose it or other useful subclasses
• We need to subclass it to add functionality
• It forces itself upon our inheritance hierarchy
Favoring Composition Over Inheritance
• If it were a category, we’d be able to use it with any
UITextField
@interface JVFloatLabeledTextField : UITextField
!
@property (nonatomic, strong)
NSNumber *floatingLabelYPadding UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIFont *floatingLabelFont UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIColor *floatingLabelTextColor UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIColor *floatingLabelActiveTextColor UI_APPEARANCE_SELECTOR;
@property (nonatomic, assign)
NSInteger animateEvenIfNotFirstResponder UI_APPEARANCE_SELECTOR;
!
@end
But What About Categories?
Favoring Composition Over Inheritance
@interface JVFloatLabeledTextField : UITextField
!
@property (nonatomic, strong)
NSNumber *floatingLabelYPadding UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIFont *floatingLabelFont UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIColor *floatingLabelTextColor UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIColor *floatingLabelActiveTextColor UI_APPEARANCE_SELECTOR;
@property (nonatomic, assign)
NSInteger animateEvenIfNotFirstResponder UI_APPEARANCE_SELECTOR;
!
@end
But What About Categories?
Favoring Composition Over Inheritance
@interface JVFloatLabeledTextField : UITextField
!
@property (nonatomic, strong)
NSNumber *floatingLabelYPadding UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIFont *floatingLabelFont UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIColor *floatingLabelTextColor UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIColor *floatingLabelActiveTextColor UI_APPEARANCE_SELECTOR;
@property (nonatomic, assign)
NSInteger animateEvenIfNotFirstResponder UI_APPEARANCE_SELECTOR;
!
@end
But What About Categories?
Favoring Composition Over Inheritance
@interface JVFloatLabeledTextField : UITextField
!
@property (nonatomic, strong)
NSNumber *floatingLabelYPadding UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIFont *floatingLabelFont UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIColor *floatingLabelTextColor UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIColor *floatingLabelActiveTextColor UI_APPEARANCE_SELECTOR;
@property (nonatomic, assign)
NSInteger animateEvenIfNotFirstResponder UI_APPEARANCE_SELECTOR;
!
@end
But What About Categories?
Favoring Composition Over Inheritance
@interface UITextField (JVFloatLabeledTextField)
@interface JVFloatLabeledTextField : UITextField
!
@property (nonatomic, strong)
NSNumber *floatingLabelYPadding UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIFont *floatingLabelFont UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIColor *floatingLabelTextColor UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIColor *floatingLabelActiveTextColor UI_APPEARANCE_SELECTOR;
@property (nonatomic, assign)
NSInteger animateEvenIfNotFirstResponder UI_APPEARANCE_SELECTOR;
!
@end
But What About Categories?
Favoring Composition Over Inheritance
@interface UITextField (JVFloatLabeledTextField)
@interface JVFloatLabeledTextField : UITextField
!
@property (nonatomic, strong)
NSNumber *floatingLabelYPadding UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIFont *floatingLabelFont UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIColor *floatingLabelTextColor UI_APPEARANCE_SELECTOR;
@property (nonatomic, strong)
UIColor *floatingLabelActiveTextColor UI_APPEARANCE_SELECTOR;
@property (nonatomic, assign)
NSInteger animateEvenIfNotFirstResponder UI_APPEARANCE_SELECTOR;
!
@end
But What About Categories?
Favoring Composition Over Inheritance
objc_setAssociatedObject
@interface UITextField (JVFloatLabeledTextField)
But What About Categories?
Favoring Composition Over Inheritance
But What About Categories?
Favoring Composition Over Inheritance
static void *JVFloatingLabelYPaddingKey =
&JVFloatingLabelYPaddingKey;
!
- (void)setFloatingLabelYPadding:(NSNumber *)floatingLabelYPadding {
objc_setAssociatedObject(self,
JVFloatingLabelYPaddingKey,
floatingLabelYPadding,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
!
- (NSNumber *)floatingLabelYPadding {
return objc_getAssociatedObject(self,
JVFloatingLabelYPaddingKey);
}
!
/// Add custom setters and getters for all properties
But What About Categories?
Favoring Composition Over Inheritance
static void *JVFloatingLabelYPaddingKey =
&JVFloatingLabelYPaddingKey;
!
- (void)setFloatingLabelYPadding:(NSNumber *)floatingLabelYPadding {
objc_setAssociatedObject(self,
JVFloatingLabelYPaddingKey,
floatingLabelYPadding,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
!
- (NSNumber *)floatingLabelYPadding {
return objc_getAssociatedObject(self,
JVFloatingLabelYPaddingKey);
}
!
/// Add custom setters and getters for all properties
But What About Categories?
Favoring Composition Over Inheritance
static void *JVFloatingLabelYPaddingKey =
&JVFloatingLabelYPaddingKey;
!
- (void)setFloatingLabelYPadding:(NSNumber *)floatingLabelYPadding {
objc_setAssociatedObject(self,
JVFloatingLabelYPaddingKey,
floatingLabelYPadding,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
!
- (NSNumber *)floatingLabelYPadding {
return objc_getAssociatedObject(self,
JVFloatingLabelYPaddingKey);
}
!
/// Add custom setters and getters for all properties
But What About Categories?
Favoring Composition Over Inheritance
static void *JVFloatingLabelYPaddingKey =
&JVFloatingLabelYPaddingKey;
!
- (void)setFloatingLabelYPadding:(NSNumber *)floatingLabelYPadding {
objc_setAssociatedObject(self,
JVFloatingLabelYPaddingKey,
floatingLabelYPadding,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
!
- (NSNumber *)floatingLabelYPadding {
return objc_getAssociatedObject(self,
JVFloatingLabelYPaddingKey);
}
!
/// Add custom setters and getters for all properties
But What About Categories?
Favoring Composition Over Inheritance
static void *JVFloatingLabelYPaddingKey =
&JVFloatingLabelYPaddingKey;
!
- (void)setFloatingLabelYPadding:(NSNumber *)floatingLabelYPadding {
objc_setAssociatedObject(self,
JVFloatingLabelYPaddingKey,
floatingLabelYPadding,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
!
- (NSNumber *)floatingLabelYPadding {
return objc_getAssociatedObject(self,
JVFloatingLabelYPaddingKey);
}
!
/// Add custom setters and getters for all properties
But What About Categories?
Favoring Composition Over Inheritance
static void *JVFloatingLabelYPaddingKey =
&JVFloatingLabelYPaddingKey;
!
- (void)setFloatingLabelYPadding:(NSNumber *)floatingLabelYPadding {
objc_setAssociatedObject(self,
JVFloatingLabelYPaddingKey,
floatingLabelYPadding,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
!
- (NSNumber *)floatingLabelYPadding {
return objc_getAssociatedObject(self,
JVFloatingLabelYPaddingKey);
}
!
/// Add custom setters and getters for all properties
But What About Categories?
Favoring Composition Over Inheritance
static void *JVFloatingLabelYPaddingKey =
&JVFloatingLabelYPaddingKey;
!
- (void)setFloatingLabelYPadding:(NSNumber *)floatingLabelYPadding {
objc_setAssociatedObject(self,
JVFloatingLabelYPaddingKey,
floatingLabelYPadding,
OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
!
- (NSNumber *)floatingLabelYPadding {
return objc_getAssociatedObject(self,
JVFloatingLabelYPaddingKey);
}
!
/// Add custom setters and getters for all properties
Doesn’t scale
Configuration Objects
Encapsulate Configuration
Configuration Objects
Encapsulate Configuration
Configuration Objects
Encapsulate Configuration
Configuration Objects
Encapsulate Configuration
Configuration Objects
Encapsulate Configuration
Configuration Object Example
MDCSwipeToChoose
Configuration Object Example
MDCSwipeToChoose
Configuration Object Example
MDCSwipeToChoose
MDCSwipeOptions *options = [MDCSwipeOptions new];
options.delegate = self;
options.onPan = ^(MDCPanState *state){
switch (state.direction) {
case MDCSwipeDirectionLeft:
self.webView.alpha = 0.5f - state.thresholdRatio;
break;
case MDCSwipeDirectionRight:
self.webView.alpha = 0.5f + state.thresholdRatio;
break;
case MDCSwipeDirectionNone:
self.webView.alpha = 0.5f;
break;
}
};
!
[self.webView mdc_swipeToChooseSetup:options];
Configuration Object Example
MDCSwipeToChoose
MDCSwipeOptions *options = [MDCSwipeOptions new];
options.delegate = self;
options.onPan = ^(MDCPanState *state){
switch (state.direction) {
case MDCSwipeDirectionLeft:
self.webView.alpha = 0.5f - state.thresholdRatio;
break;
case MDCSwipeDirectionRight:
self.webView.alpha = 0.5f + state.thresholdRatio;
break;
case MDCSwipeDirectionNone:
self.webView.alpha = 0.5f;
break;
}
};
!
[self.webView mdc_swipeToChooseSetup:options];
Configuration Object Example
MDCSwipeToChoose
MDCSwipeOptions *options = [MDCSwipeOptions new];
options.delegate = self;
options.onPan = ^(MDCPanState *state){
switch (state.direction) {
case MDCSwipeDirectionLeft:
self.webView.alpha = 0.5f - state.thresholdRatio;
break;
case MDCSwipeDirectionRight:
self.webView.alpha = 0.5f + state.thresholdRatio;
break;
case MDCSwipeDirectionNone:
self.webView.alpha = 0.5f;
break;
}
};
!
[self.webView mdc_swipeToChooseSetup:options];
Configuration Object Example
MDCSwipeToChoose
MDCSwipeOptions *options = [MDCSwipeOptions new];
options.delegate = self;
options.onPan = ^(MDCPanState *state){
switch (state.direction) {
case MDCSwipeDirectionLeft:
self.webView.alpha = 0.5f - state.thresholdRatio;
break;
case MDCSwipeDirectionRight:
self.webView.alpha = 0.5f + state.thresholdRatio;
break;
case MDCSwipeDirectionNone:
self.webView.alpha = 0.5f;
break;
}
};
!
[self.webView mdc_swipeToChooseSetup:options];
Configuration Object Example
MDCSwipeToChoose
MDCSwipeOptions *options = [MDCSwipeOptions new];
options.delegate = self;
options.onPan = ^(MDCPanState *state){
switch (state.direction) {
case MDCSwipeDirectionLeft:
self.webView.alpha = 0.5f - state.thresholdRatio;
break;
case MDCSwipeDirectionRight:
self.webView.alpha = 0.5f + state.thresholdRatio;
break;
case MDCSwipeDirectionNone:
self.webView.alpha = 0.5f;
break;
}
};
!
[self.webView mdc_swipeToChooseSetup:options];
Configuration Object Example
MDCSwipeToChoose
MDCSwipeOptions *options = [MDCSwipeOptions new];
options.delegate = self;
options.onPan = ^(MDCPanState *state){
switch (state.direction) {
case MDCSwipeDirectionLeft:
self.webView.alpha = 0.5f - state.thresholdRatio;
break;
case MDCSwipeDirectionRight:
self.webView.alpha = 0.5f + state.thresholdRatio;
break;
case MDCSwipeDirectionNone:
self.webView.alpha = 0.5f;
break;
}
};
!
[self.webView mdc_swipeToChooseSetup:options];
Configuration Object Example
MDCSwipeToChoose
MDCSwipeOptions *options = [MDCSwipeOptions new];
options.delegate = self;
options.onPan = ^(MDCPanState *state){
switch (state.direction) {
case MDCSwipeDirectionLeft:
self.webView.alpha = 0.5f - state.thresholdRatio;
break;
case MDCSwipeDirectionRight:
self.webView.alpha = 0.5f + state.thresholdRatio;
break;
case MDCSwipeDirectionNone:
self.webView.alpha = 0.5f;
break;
}
};
!
[self.webView mdc_swipeToChooseSetup:options];
Configuration Object Example
MDCSwipeToChoose
MDCSwipeOptions *options = [MDCSwipeOptions new];
options.delegate = self;
options.onPan = ^(MDCPanState *state){
switch (state.direction) {
case MDCSwipeDirectionLeft:
self.webView.alpha = 0.5f - state.thresholdRatio;
break;
case MDCSwipeDirectionRight:
self.webView.alpha = 0.5f + state.thresholdRatio;
break;
case MDCSwipeDirectionNone:
self.webView.alpha = 0.5f;
break;
}
};
!
[self.webView mdc_swipeToChooseSetup:options];
Configuration Object Example
MDCSwipeToChoose
MDCSwipeOptions *options = [MDCSwipeOptions new];
options.delegate = self;
options.onPan = ^(MDCPanState *state){
switch (state.direction) {
case MDCSwipeDirectionLeft:
self.webView.alpha = 0.5f - state.thresholdRatio;
break;
case MDCSwipeDirectionRight:
self.webView.alpha = 0.5f + state.thresholdRatio;
break;
case MDCSwipeDirectionNone:
self.webView.alpha = 0.5f;
break;
}
};
!
[self.webView mdc_swipeToChooseSetup:options];
Configuration Object Example
MDCSwipeToChoose
MDCSwipeOptions *options = [MDCSwipeOptions new];
options.delegate = self;
options.onPan = ^(MDCPanState *state){
switch (state.direction) {
case MDCSwipeDirectionLeft:
self.webView.alpha = 0.5f - state.thresholdRatio;
break;
case MDCSwipeDirectionRight:
self.webView.alpha = 0.5f + state.thresholdRatio;
break;
case MDCSwipeDirectionNone:
self.webView.alpha = 0.5f;
break;
}
};
!
[self.webView mdc_swipeToChooseSetup:options];
Parameter Objects for Blocks
Extensible Block Signatures
options.onPan = ^(UIView *view,
MDCSwipeDirection direction,
CGFloat thresholdRatio){
if (direction == MDCSwipeDirectionLeft) {
NSLog(@"Panning to the left...");
}
};
Parameter Objects for Blocks
Extensible Block Signatures
options.onPan = ^(UIView *view,
MDCSwipeDirection direction,
CGFloat thresholdRatio){
if (direction == MDCSwipeDirectionLeft) {
NSLog(@"Panning to the left...");
}
};
Parameter Objects for Blocks
Extensible Block Signatures
options.onPan = ^(UIView *view,
MDCSwipeDirection direction,
CGFloat thresholdRatio){
if (direction == MDCSwipeDirectionLeft) {
NSLog(@"Panning to the left...");
}
};
Parameter Objects for Blocks
Extensible Block Signatures
options.onPan = ^(UIView *view,
MDCSwipeDirection direction,
CGFloat thresholdRatio){
if (direction == MDCSwipeDirectionLeft) {
NSLog(@"Panning to the left...");
}
};
Parameter Objects for Blocks
Extensible Block Signatures
options.onPan = ^(UIView *view,
MDCSwipeDirection direction,
CGFloat thresholdRatio){
if (direction == MDCSwipeDirectionLeft) {
NSLog(@"Panning to the left...");
}
};
Parameter Objects for Blocks
Extensible Block Signatures
options.onPan = ^(MDCPanState *state){
MDCSwipeDirection direction = state.direction;
options.onPan = ^(UIView *view,
MDCSwipeDirection direction,
CGFloat thresholdRatio){
if (direction == MDCSwipeDirectionLeft) {
NSLog(@"Panning to the left...");
}
};
Parameter Objects for Blocks
Extensible Block Signatures
options.onPan = ^(MDCPanState *state){
MDCSwipeDirection direction = state.direction;
Extensible Block Arguments
Update the API without Breaking Old Versions
@interface MDCPanState : NSObject
!
@property (nonatomic, strong)
UIView *view;
@property (nonatomic, assign)
MDCSwipeDirection direction;
@property (nonatomic, assign)
CGFloat thresholdRatio;
!
@end
Extensible Block Arguments
Update the API without Breaking Old Versions
@interface MDCPanState : NSObject
!
@property (nonatomic, strong)
UIView *view;
@property (nonatomic, assign)
MDCSwipeDirection direction;
@property (nonatomic, assign)
CGFloat thresholdRatio;
!
@end
DEPRECATED_ATTRIBUTE;
options.onPan = ^(UIView *view,
MDCSwipeDirection direction,
CGFloat thresholdRatio){
if (direction == MDCSwipeDirectionLeft) {
NSLog(@"Panning to the left...");
}
};
options.onPan = ^(MDCPanState *state){
MDCSwipeDirection direction = state.direction;
Extensible Block Arguments
Slowly Phase Out Deprecated Parameters
options.onPan = ^(UIView *view,
MDCSwipeDirection direction,
CGFloat thresholdRatio){
if (direction == MDCSwipeDirectionLeft) {
NSLog(@"Panning to the left...");
}
};
options.onPan = ^(MDCPanState *state){
MDCSwipeDirection direction = state.direction;
Extensible Block Arguments
Slowly Phase Out Deprecated Parameters
options.onPan = ^(UIView *view,
MDCSwipeDirection direction,
CGFloat thresholdRatio){
if (direction == MDCSwipeDirectionLeft) {
NSLog(@"Panning to the left...");
}
};
options.onPan = ^(MDCPanState *state){
MDCSwipeDirection direction = state.direction;
Extensible Block Arguments
Slowly Phase Out Deprecated Parameters
Takeaways
• Favor composition over inheritance when building UI
components
• Build extensible, future-proof customization APIs using
parameter objects
• Parameter objects are especially useful as block
arguments
Additional Resources
• Follow me on Twitter and GitHub at @modocache
• Today’s slides
• http://modocache.io/ios-ui-component-api-design
• JVFloatLabeledTextField
• https://github.com/jverdi/JVFloatLabeledTextField
• MDCSwipeToChoose
• https://github.com/modocache/MDCSwipeToChoose
• The Parameter Object Design Pattern
• http://c2.com/cgi/wiki?ParameterObject

Contenu connexe

Tendances

Real-World Scala Design Patterns
Real-World Scala Design PatternsReal-World Scala Design Patterns
Real-World Scala Design Patterns
NLJUG
 

Tendances (20)

Exploring Kotlin
Exploring KotlinExploring Kotlin
Exploring Kotlin
 
Real-World Scala Design Patterns
Real-World Scala Design PatternsReal-World Scala Design Patterns
Real-World Scala Design Patterns
 
Scala’s implicits
Scala’s implicitsScala’s implicits
Scala’s implicits
 
Seven Versions of One Web Application
Seven Versions of One Web ApplicationSeven Versions of One Web Application
Seven Versions of One Web Application
 
Painless Javascript Unit Testing
Painless Javascript Unit TestingPainless Javascript Unit Testing
Painless Javascript Unit Testing
 
IoC&Laravel
IoC&LaravelIoC&Laravel
IoC&Laravel
 
slingmodels
slingmodelsslingmodels
slingmodels
 
Design Patterns For 70% Of Programmers In The World
Design Patterns For 70% Of Programmers In The WorldDesign Patterns For 70% Of Programmers In The World
Design Patterns For 70% Of Programmers In The World
 
Introduction to Sightly
Introduction to SightlyIntroduction to Sightly
Introduction to Sightly
 
Spring AOP
Spring AOPSpring AOP
Spring AOP
 
Javascript Best Practices
Javascript Best PracticesJavascript Best Practices
Javascript Best Practices
 
React advance
React advanceReact advance
React advance
 
Doc abap
Doc abapDoc abap
Doc abap
 
Effective Java
Effective JavaEffective Java
Effective Java
 
Introduction to Core Java Programming
Introduction to Core Java ProgrammingIntroduction to Core Java Programming
Introduction to Core Java Programming
 
ParisJS #10 : RequireJS
ParisJS #10 : RequireJSParisJS #10 : RequireJS
ParisJS #10 : RequireJS
 
Effective Java, Third Edition - Keepin' it Effective
Effective Java, Third Edition - Keepin' it EffectiveEffective Java, Third Edition - Keepin' it Effective
Effective Java, Third Edition - Keepin' it Effective
 
Angular Intermediate
Angular IntermediateAngular Intermediate
Angular Intermediate
 
Advance JS and oop
Advance JS and oopAdvance JS and oop
Advance JS and oop
 
2CPP03 - Object Orientation Fundamentals
2CPP03 - Object Orientation Fundamentals2CPP03 - Object Orientation Fundamentals
2CPP03 - Object Orientation Fundamentals
 

En vedette (7)

iOS UI Component API Design
iOS UI Component API DesigniOS UI Component API Design
iOS UI Component API Design
 
Apple Templates Considered Harmful
Apple Templates Considered HarmfulApple Templates Considered Harmful
Apple Templates Considered Harmful
 
RSpec 3.0: Under the Covers
RSpec 3.0: Under the CoversRSpec 3.0: Under the Covers
RSpec 3.0: Under the Covers
 
Intel® Xeon® Phi Coprocessor High Performance Programming
Intel® Xeon® Phi Coprocessor High Performance ProgrammingIntel® Xeon® Phi Coprocessor High Performance Programming
Intel® Xeon® Phi Coprocessor High Performance Programming
 
iOS Behavior-Driven Development
iOS Behavior-Driven DevelopmentiOS Behavior-Driven Development
iOS Behavior-Driven Development
 
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられるアップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
 
iOSビヘイビア駆動開発
iOSビヘイビア駆動開発iOSビヘイビア駆動開発
iOSビヘイビア駆動開発
 

Similaire à iOS UI Component API Design

Codemotion 2013 - Designing complex applications using html5 and knockoutjs
Codemotion 2013 - Designing complex applications using html5 and knockoutjsCodemotion 2013 - Designing complex applications using html5 and knockoutjs
Codemotion 2013 - Designing complex applications using html5 and knockoutjs
Fabio Franzini
 
[E-Dev-Day-US-2015][8/9] he EFL API in Review (Tom Hacohen)
[E-Dev-Day-US-2015][8/9] he EFL API in Review (Tom Hacohen)[E-Dev-Day-US-2015][8/9] he EFL API in Review (Tom Hacohen)
[E-Dev-Day-US-2015][8/9] he EFL API in Review (Tom Hacohen)
EnlightenmentProject
 
Writing Testable Code
Writing Testable CodeWriting Testable Code
Writing Testable Code
jameshalsall
 

Similaire à iOS UI Component API Design (20)

Codemotion 2013 - Designing complex applications using html5 and knockoutjs
Codemotion 2013 - Designing complex applications using html5 and knockoutjsCodemotion 2013 - Designing complex applications using html5 and knockoutjs
Codemotion 2013 - Designing complex applications using html5 and knockoutjs
 
Declarative UI on iOS without SwiftUI (中文)
Declarative UI on iOS without SwiftUI (中文)Declarative UI on iOS without SwiftUI (中文)
Declarative UI on iOS without SwiftUI (中文)
 
KKBOX WWDC17 UIKit - QQ
KKBOX WWDC17 UIKit - QQKKBOX WWDC17 UIKit - QQ
KKBOX WWDC17 UIKit - QQ
 
KAAccessControl
KAAccessControlKAAccessControl
KAAccessControl
 
Introduction to Kotlin for Android developers
Introduction to Kotlin for Android developersIntroduction to Kotlin for Android developers
Introduction to Kotlin for Android developers
 
Functional programming with Java 8
Functional programming with Java 8Functional programming with Java 8
Functional programming with Java 8
 
Owner - Java properties reinvented.
Owner - Java properties reinvented.Owner - Java properties reinvented.
Owner - Java properties reinvented.
 
A tour through Swift attributes
A tour through Swift attributesA tour through Swift attributes
A tour through Swift attributes
 
Striking a Balance With UI Tests - ConnectTech
Striking a Balance With UI Tests - ConnectTechStriking a Balance With UI Tests - ConnectTech
Striking a Balance With UI Tests - ConnectTech
 
[E-Dev-Day-US-2015][8/9] he EFL API in Review (Tom Hacohen)
[E-Dev-Day-US-2015][8/9] he EFL API in Review (Tom Hacohen)[E-Dev-Day-US-2015][8/9] he EFL API in Review (Tom Hacohen)
[E-Dev-Day-US-2015][8/9] he EFL API in Review (Tom Hacohen)
 
Form part1
Form part1Form part1
Form part1
 
Beyond Fluffy Bunny. How I leveraged WebObjects in my lean startup.
Beyond Fluffy Bunny. How I leveraged WebObjects in my lean startup.Beyond Fluffy Bunny. How I leveraged WebObjects in my lean startup.
Beyond Fluffy Bunny. How I leveraged WebObjects in my lean startup.
 
Iterator - a powerful but underappreciated design pattern
Iterator - a powerful but underappreciated design patternIterator - a powerful but underappreciated design pattern
Iterator - a powerful but underappreciated design pattern
 
Eclipse e4
Eclipse e4Eclipse e4
Eclipse e4
 
Scala - By Luu Thanh Thuy CWI team from eXo Platform SEA
Scala - By Luu Thanh Thuy CWI team from eXo Platform SEAScala - By Luu Thanh Thuy CWI team from eXo Platform SEA
Scala - By Luu Thanh Thuy CWI team from eXo Platform SEA
 
Writing Testable Code
Writing Testable CodeWriting Testable Code
Writing Testable Code
 
Kevin Whinnery: Write Better JavaScript
Kevin Whinnery: Write Better JavaScriptKevin Whinnery: Write Better JavaScript
Kevin Whinnery: Write Better JavaScript
 
JPA For Beginner's
JPA For Beginner'sJPA For Beginner's
JPA For Beginner's
 
Lezione 03 Introduzione a react
Lezione 03   Introduzione a reactLezione 03   Introduzione a react
Lezione 03 Introduzione a react
 
Object-oriented Design: Polymorphism via Inheritance (vs. Delegation)
Object-oriented Design: Polymorphism via Inheritance (vs. Delegation)Object-oriented Design: Polymorphism via Inheritance (vs. Delegation)
Object-oriented Design: Polymorphism via Inheritance (vs. Delegation)
 

Dernier

Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
WSO2
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(☎️+971_581248768%)**%*]'#abortion pills for sale in dubai@
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 

Dernier (20)

Corporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptxCorporate and higher education May webinar.pptx
Corporate and higher education May webinar.pptx
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024Manulife - Insurer Transformation Award 2024
Manulife - Insurer Transformation Award 2024
 
Artificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : UncertaintyArtificial Intelligence Chap.5 : Uncertainty
Artificial Intelligence Chap.5 : Uncertainty
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
Emergent Methods: Multi-lingual narrative tracking in the news - real-time ex...
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu SubbuApidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
Apidays Singapore 2024 - Modernizing Securities Finance by Madhu Subbu
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?A Year of the Servo Reboot: Where Are We Now?
A Year of the Servo Reboot: Where Are We Now?
 
Navi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Navi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot ModelNavi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Navi Mumbai Call Girls 🥰 8617370543 Service Offer VIP Hot Model
 
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
Apidays Singapore 2024 - Scalable LLM APIs for AI and Generative AI Applicati...
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 

iOS UI Component API Design

  • 1. iOS UI Component Design Configuration and Callbacks Using the “Parameter Object”Pattern Brian Gesiak April 9th, 2014 Research Student, The University of Tokyo @modocache
  • 2. Today • Problem: How do we allow users to customize UI elements? • Appearance, animations, and behavior should be customizable • Composition over inheritance • Solution: Configuration objects ! • Problem: How do we define an API for callbacks? • Public delegate and block callbacks are hard to deprecate or change • Solution: Parameter objects
  • 6. Customization API Example JVFloatLabeledTextField Appearance API @interface JVFloatLabeledTextField : UITextField ! @property (nonatomic, strong) NSNumber *floatingLabelYPadding UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIFont *floatingLabelFont UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIColor *floatingLabelTextColor UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIColor *floatingLabelActiveTextColor UI_APPEARANCE_SELECTOR; @property (nonatomic, assign) NSInteger animateEvenIfNotFirstResponder UI_APPEARANCE_SELECTOR; ! @end
  • 7. Customization API Example JVFloatLabeledTextField Appearance API @interface JVFloatLabeledTextField : UITextField ! @property (nonatomic, strong) NSNumber *floatingLabelYPadding UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIFont *floatingLabelFont UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIColor *floatingLabelTextColor UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIColor *floatingLabelActiveTextColor UI_APPEARANCE_SELECTOR; @property (nonatomic, assign) NSInteger animateEvenIfNotFirstResponder UI_APPEARANCE_SELECTOR; ! @end
  • 8. Customization API Example JVFloatLabeledTextField Appearance API @interface JVFloatLabeledTextField : UITextField ! @property (nonatomic, strong) NSNumber *floatingLabelYPadding UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIFont *floatingLabelFont UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIColor *floatingLabelTextColor UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIColor *floatingLabelActiveTextColor UI_APPEARANCE_SELECTOR; @property (nonatomic, assign) NSInteger animateEvenIfNotFirstResponder UI_APPEARANCE_SELECTOR; ! @end
  • 9. Customization API Example JVFloatLabeledTextField Appearance API @interface JVFloatLabeledTextField : UITextField ! @property (nonatomic, strong) NSNumber *floatingLabelYPadding UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIFont *floatingLabelFont UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIColor *floatingLabelTextColor UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIColor *floatingLabelActiveTextColor UI_APPEARANCE_SELECTOR; @property (nonatomic, assign) NSInteger animateEvenIfNotFirstResponder UI_APPEARANCE_SELECTOR; ! @end
  • 10. But What About Categories? Favoring Composition Over Inheritance
  • 11. But What About Categories? • JVFloatLabeledTextField is a subclass of UITextField Favoring Composition Over Inheritance
  • 12. But What About Categories? • JVFloatLabeledTextField is a subclass of UITextField • We need to use the class for its functionality Favoring Composition Over Inheritance
  • 13. But What About Categories? • JVFloatLabeledTextField is a subclass of UITextField • We need to use the class for its functionality • We’re forced to choose it or other useful subclasses Favoring Composition Over Inheritance
  • 14. But What About Categories? • JVFloatLabeledTextField is a subclass of UITextField • We need to use the class for its functionality • We’re forced to choose it or other useful subclasses • We need to subclass it to add functionality Favoring Composition Over Inheritance
  • 15. But What About Categories? • JVFloatLabeledTextField is a subclass of UITextField • We need to use the class for its functionality • We’re forced to choose it or other useful subclasses • We need to subclass it to add functionality • It forces itself upon our inheritance hierarchy Favoring Composition Over Inheritance
  • 16. But What About Categories? • JVFloatLabeledTextField is a subclass of UITextField • We need to use the class for its functionality • We’re forced to choose it or other useful subclasses • We need to subclass it to add functionality • It forces itself upon our inheritance hierarchy Favoring Composition Over Inheritance • If it were a category, we’d be able to use it with any UITextField
  • 17. @interface JVFloatLabeledTextField : UITextField ! @property (nonatomic, strong) NSNumber *floatingLabelYPadding UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIFont *floatingLabelFont UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIColor *floatingLabelTextColor UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIColor *floatingLabelActiveTextColor UI_APPEARANCE_SELECTOR; @property (nonatomic, assign) NSInteger animateEvenIfNotFirstResponder UI_APPEARANCE_SELECTOR; ! @end But What About Categories? Favoring Composition Over Inheritance
  • 18. @interface JVFloatLabeledTextField : UITextField ! @property (nonatomic, strong) NSNumber *floatingLabelYPadding UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIFont *floatingLabelFont UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIColor *floatingLabelTextColor UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIColor *floatingLabelActiveTextColor UI_APPEARANCE_SELECTOR; @property (nonatomic, assign) NSInteger animateEvenIfNotFirstResponder UI_APPEARANCE_SELECTOR; ! @end But What About Categories? Favoring Composition Over Inheritance
  • 19. @interface JVFloatLabeledTextField : UITextField ! @property (nonatomic, strong) NSNumber *floatingLabelYPadding UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIFont *floatingLabelFont UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIColor *floatingLabelTextColor UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIColor *floatingLabelActiveTextColor UI_APPEARANCE_SELECTOR; @property (nonatomic, assign) NSInteger animateEvenIfNotFirstResponder UI_APPEARANCE_SELECTOR; ! @end But What About Categories? Favoring Composition Over Inheritance
  • 20. @interface JVFloatLabeledTextField : UITextField ! @property (nonatomic, strong) NSNumber *floatingLabelYPadding UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIFont *floatingLabelFont UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIColor *floatingLabelTextColor UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIColor *floatingLabelActiveTextColor UI_APPEARANCE_SELECTOR; @property (nonatomic, assign) NSInteger animateEvenIfNotFirstResponder UI_APPEARANCE_SELECTOR; ! @end But What About Categories? Favoring Composition Over Inheritance @interface UITextField (JVFloatLabeledTextField)
  • 21. @interface JVFloatLabeledTextField : UITextField ! @property (nonatomic, strong) NSNumber *floatingLabelYPadding UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIFont *floatingLabelFont UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIColor *floatingLabelTextColor UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIColor *floatingLabelActiveTextColor UI_APPEARANCE_SELECTOR; @property (nonatomic, assign) NSInteger animateEvenIfNotFirstResponder UI_APPEARANCE_SELECTOR; ! @end But What About Categories? Favoring Composition Over Inheritance @interface UITextField (JVFloatLabeledTextField)
  • 22. @interface JVFloatLabeledTextField : UITextField ! @property (nonatomic, strong) NSNumber *floatingLabelYPadding UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIFont *floatingLabelFont UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIColor *floatingLabelTextColor UI_APPEARANCE_SELECTOR; @property (nonatomic, strong) UIColor *floatingLabelActiveTextColor UI_APPEARANCE_SELECTOR; @property (nonatomic, assign) NSInteger animateEvenIfNotFirstResponder UI_APPEARANCE_SELECTOR; ! @end But What About Categories? Favoring Composition Over Inheritance objc_setAssociatedObject @interface UITextField (JVFloatLabeledTextField)
  • 23. But What About Categories? Favoring Composition Over Inheritance
  • 24. But What About Categories? Favoring Composition Over Inheritance static void *JVFloatingLabelYPaddingKey = &JVFloatingLabelYPaddingKey; ! - (void)setFloatingLabelYPadding:(NSNumber *)floatingLabelYPadding { objc_setAssociatedObject(self, JVFloatingLabelYPaddingKey, floatingLabelYPadding, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } ! - (NSNumber *)floatingLabelYPadding { return objc_getAssociatedObject(self, JVFloatingLabelYPaddingKey); } ! /// Add custom setters and getters for all properties
  • 25. But What About Categories? Favoring Composition Over Inheritance static void *JVFloatingLabelYPaddingKey = &JVFloatingLabelYPaddingKey; ! - (void)setFloatingLabelYPadding:(NSNumber *)floatingLabelYPadding { objc_setAssociatedObject(self, JVFloatingLabelYPaddingKey, floatingLabelYPadding, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } ! - (NSNumber *)floatingLabelYPadding { return objc_getAssociatedObject(self, JVFloatingLabelYPaddingKey); } ! /// Add custom setters and getters for all properties
  • 26. But What About Categories? Favoring Composition Over Inheritance static void *JVFloatingLabelYPaddingKey = &JVFloatingLabelYPaddingKey; ! - (void)setFloatingLabelYPadding:(NSNumber *)floatingLabelYPadding { objc_setAssociatedObject(self, JVFloatingLabelYPaddingKey, floatingLabelYPadding, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } ! - (NSNumber *)floatingLabelYPadding { return objc_getAssociatedObject(self, JVFloatingLabelYPaddingKey); } ! /// Add custom setters and getters for all properties
  • 27. But What About Categories? Favoring Composition Over Inheritance static void *JVFloatingLabelYPaddingKey = &JVFloatingLabelYPaddingKey; ! - (void)setFloatingLabelYPadding:(NSNumber *)floatingLabelYPadding { objc_setAssociatedObject(self, JVFloatingLabelYPaddingKey, floatingLabelYPadding, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } ! - (NSNumber *)floatingLabelYPadding { return objc_getAssociatedObject(self, JVFloatingLabelYPaddingKey); } ! /// Add custom setters and getters for all properties
  • 28. But What About Categories? Favoring Composition Over Inheritance static void *JVFloatingLabelYPaddingKey = &JVFloatingLabelYPaddingKey; ! - (void)setFloatingLabelYPadding:(NSNumber *)floatingLabelYPadding { objc_setAssociatedObject(self, JVFloatingLabelYPaddingKey, floatingLabelYPadding, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } ! - (NSNumber *)floatingLabelYPadding { return objc_getAssociatedObject(self, JVFloatingLabelYPaddingKey); } ! /// Add custom setters and getters for all properties
  • 29. But What About Categories? Favoring Composition Over Inheritance static void *JVFloatingLabelYPaddingKey = &JVFloatingLabelYPaddingKey; ! - (void)setFloatingLabelYPadding:(NSNumber *)floatingLabelYPadding { objc_setAssociatedObject(self, JVFloatingLabelYPaddingKey, floatingLabelYPadding, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } ! - (NSNumber *)floatingLabelYPadding { return objc_getAssociatedObject(self, JVFloatingLabelYPaddingKey); } ! /// Add custom setters and getters for all properties
  • 30. But What About Categories? Favoring Composition Over Inheritance static void *JVFloatingLabelYPaddingKey = &JVFloatingLabelYPaddingKey; ! - (void)setFloatingLabelYPadding:(NSNumber *)floatingLabelYPadding { objc_setAssociatedObject(self, JVFloatingLabelYPaddingKey, floatingLabelYPadding, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } ! - (NSNumber *)floatingLabelYPadding { return objc_getAssociatedObject(self, JVFloatingLabelYPaddingKey); } ! /// Add custom setters and getters for all properties Doesn’t scale
  • 38. Configuration Object Example MDCSwipeToChoose MDCSwipeOptions *options = [MDCSwipeOptions new]; options.delegate = self; options.onPan = ^(MDCPanState *state){ switch (state.direction) { case MDCSwipeDirectionLeft: self.webView.alpha = 0.5f - state.thresholdRatio; break; case MDCSwipeDirectionRight: self.webView.alpha = 0.5f + state.thresholdRatio; break; case MDCSwipeDirectionNone: self.webView.alpha = 0.5f; break; } }; ! [self.webView mdc_swipeToChooseSetup:options];
  • 39. Configuration Object Example MDCSwipeToChoose MDCSwipeOptions *options = [MDCSwipeOptions new]; options.delegate = self; options.onPan = ^(MDCPanState *state){ switch (state.direction) { case MDCSwipeDirectionLeft: self.webView.alpha = 0.5f - state.thresholdRatio; break; case MDCSwipeDirectionRight: self.webView.alpha = 0.5f + state.thresholdRatio; break; case MDCSwipeDirectionNone: self.webView.alpha = 0.5f; break; } }; ! [self.webView mdc_swipeToChooseSetup:options];
  • 40. Configuration Object Example MDCSwipeToChoose MDCSwipeOptions *options = [MDCSwipeOptions new]; options.delegate = self; options.onPan = ^(MDCPanState *state){ switch (state.direction) { case MDCSwipeDirectionLeft: self.webView.alpha = 0.5f - state.thresholdRatio; break; case MDCSwipeDirectionRight: self.webView.alpha = 0.5f + state.thresholdRatio; break; case MDCSwipeDirectionNone: self.webView.alpha = 0.5f; break; } }; ! [self.webView mdc_swipeToChooseSetup:options];
  • 41. Configuration Object Example MDCSwipeToChoose MDCSwipeOptions *options = [MDCSwipeOptions new]; options.delegate = self; options.onPan = ^(MDCPanState *state){ switch (state.direction) { case MDCSwipeDirectionLeft: self.webView.alpha = 0.5f - state.thresholdRatio; break; case MDCSwipeDirectionRight: self.webView.alpha = 0.5f + state.thresholdRatio; break; case MDCSwipeDirectionNone: self.webView.alpha = 0.5f; break; } }; ! [self.webView mdc_swipeToChooseSetup:options];
  • 42. Configuration Object Example MDCSwipeToChoose MDCSwipeOptions *options = [MDCSwipeOptions new]; options.delegate = self; options.onPan = ^(MDCPanState *state){ switch (state.direction) { case MDCSwipeDirectionLeft: self.webView.alpha = 0.5f - state.thresholdRatio; break; case MDCSwipeDirectionRight: self.webView.alpha = 0.5f + state.thresholdRatio; break; case MDCSwipeDirectionNone: self.webView.alpha = 0.5f; break; } }; ! [self.webView mdc_swipeToChooseSetup:options];
  • 43. Configuration Object Example MDCSwipeToChoose MDCSwipeOptions *options = [MDCSwipeOptions new]; options.delegate = self; options.onPan = ^(MDCPanState *state){ switch (state.direction) { case MDCSwipeDirectionLeft: self.webView.alpha = 0.5f - state.thresholdRatio; break; case MDCSwipeDirectionRight: self.webView.alpha = 0.5f + state.thresholdRatio; break; case MDCSwipeDirectionNone: self.webView.alpha = 0.5f; break; } }; ! [self.webView mdc_swipeToChooseSetup:options];
  • 44. Configuration Object Example MDCSwipeToChoose MDCSwipeOptions *options = [MDCSwipeOptions new]; options.delegate = self; options.onPan = ^(MDCPanState *state){ switch (state.direction) { case MDCSwipeDirectionLeft: self.webView.alpha = 0.5f - state.thresholdRatio; break; case MDCSwipeDirectionRight: self.webView.alpha = 0.5f + state.thresholdRatio; break; case MDCSwipeDirectionNone: self.webView.alpha = 0.5f; break; } }; ! [self.webView mdc_swipeToChooseSetup:options];
  • 45. Configuration Object Example MDCSwipeToChoose MDCSwipeOptions *options = [MDCSwipeOptions new]; options.delegate = self; options.onPan = ^(MDCPanState *state){ switch (state.direction) { case MDCSwipeDirectionLeft: self.webView.alpha = 0.5f - state.thresholdRatio; break; case MDCSwipeDirectionRight: self.webView.alpha = 0.5f + state.thresholdRatio; break; case MDCSwipeDirectionNone: self.webView.alpha = 0.5f; break; } }; ! [self.webView mdc_swipeToChooseSetup:options];
  • 46. Configuration Object Example MDCSwipeToChoose MDCSwipeOptions *options = [MDCSwipeOptions new]; options.delegate = self; options.onPan = ^(MDCPanState *state){ switch (state.direction) { case MDCSwipeDirectionLeft: self.webView.alpha = 0.5f - state.thresholdRatio; break; case MDCSwipeDirectionRight: self.webView.alpha = 0.5f + state.thresholdRatio; break; case MDCSwipeDirectionNone: self.webView.alpha = 0.5f; break; } }; ! [self.webView mdc_swipeToChooseSetup:options];
  • 47. Configuration Object Example MDCSwipeToChoose MDCSwipeOptions *options = [MDCSwipeOptions new]; options.delegate = self; options.onPan = ^(MDCPanState *state){ switch (state.direction) { case MDCSwipeDirectionLeft: self.webView.alpha = 0.5f - state.thresholdRatio; break; case MDCSwipeDirectionRight: self.webView.alpha = 0.5f + state.thresholdRatio; break; case MDCSwipeDirectionNone: self.webView.alpha = 0.5f; break; } }; ! [self.webView mdc_swipeToChooseSetup:options];
  • 48. Parameter Objects for Blocks Extensible Block Signatures
  • 49. options.onPan = ^(UIView *view, MDCSwipeDirection direction, CGFloat thresholdRatio){ if (direction == MDCSwipeDirectionLeft) { NSLog(@"Panning to the left..."); } }; Parameter Objects for Blocks Extensible Block Signatures
  • 50. options.onPan = ^(UIView *view, MDCSwipeDirection direction, CGFloat thresholdRatio){ if (direction == MDCSwipeDirectionLeft) { NSLog(@"Panning to the left..."); } }; Parameter Objects for Blocks Extensible Block Signatures
  • 51. options.onPan = ^(UIView *view, MDCSwipeDirection direction, CGFloat thresholdRatio){ if (direction == MDCSwipeDirectionLeft) { NSLog(@"Panning to the left..."); } }; Parameter Objects for Blocks Extensible Block Signatures
  • 52. options.onPan = ^(UIView *view, MDCSwipeDirection direction, CGFloat thresholdRatio){ if (direction == MDCSwipeDirectionLeft) { NSLog(@"Panning to the left..."); } }; Parameter Objects for Blocks Extensible Block Signatures
  • 53. options.onPan = ^(UIView *view, MDCSwipeDirection direction, CGFloat thresholdRatio){ if (direction == MDCSwipeDirectionLeft) { NSLog(@"Panning to the left..."); } }; Parameter Objects for Blocks Extensible Block Signatures options.onPan = ^(MDCPanState *state){ MDCSwipeDirection direction = state.direction;
  • 54. options.onPan = ^(UIView *view, MDCSwipeDirection direction, CGFloat thresholdRatio){ if (direction == MDCSwipeDirectionLeft) { NSLog(@"Panning to the left..."); } }; Parameter Objects for Blocks Extensible Block Signatures options.onPan = ^(MDCPanState *state){ MDCSwipeDirection direction = state.direction;
  • 55. Extensible Block Arguments Update the API without Breaking Old Versions @interface MDCPanState : NSObject ! @property (nonatomic, strong) UIView *view; @property (nonatomic, assign) MDCSwipeDirection direction; @property (nonatomic, assign) CGFloat thresholdRatio; ! @end
  • 56. Extensible Block Arguments Update the API without Breaking Old Versions @interface MDCPanState : NSObject ! @property (nonatomic, strong) UIView *view; @property (nonatomic, assign) MDCSwipeDirection direction; @property (nonatomic, assign) CGFloat thresholdRatio; ! @end DEPRECATED_ATTRIBUTE;
  • 57. options.onPan = ^(UIView *view, MDCSwipeDirection direction, CGFloat thresholdRatio){ if (direction == MDCSwipeDirectionLeft) { NSLog(@"Panning to the left..."); } }; options.onPan = ^(MDCPanState *state){ MDCSwipeDirection direction = state.direction; Extensible Block Arguments Slowly Phase Out Deprecated Parameters
  • 58. options.onPan = ^(UIView *view, MDCSwipeDirection direction, CGFloat thresholdRatio){ if (direction == MDCSwipeDirectionLeft) { NSLog(@"Panning to the left..."); } }; options.onPan = ^(MDCPanState *state){ MDCSwipeDirection direction = state.direction; Extensible Block Arguments Slowly Phase Out Deprecated Parameters
  • 59. options.onPan = ^(UIView *view, MDCSwipeDirection direction, CGFloat thresholdRatio){ if (direction == MDCSwipeDirectionLeft) { NSLog(@"Panning to the left..."); } }; options.onPan = ^(MDCPanState *state){ MDCSwipeDirection direction = state.direction; Extensible Block Arguments Slowly Phase Out Deprecated Parameters
  • 60. Takeaways • Favor composition over inheritance when building UI components • Build extensible, future-proof customization APIs using parameter objects • Parameter objects are especially useful as block arguments
  • 61. Additional Resources • Follow me on Twitter and GitHub at @modocache • Today’s slides • http://modocache.io/ios-ui-component-api-design • JVFloatLabeledTextField • https://github.com/jverdi/JVFloatLabeledTextField • MDCSwipeToChoose • https://github.com/modocache/MDCSwipeToChoose • The Parameter Object Design Pattern • http://c2.com/cgi/wiki?ParameterObject