Skip to content

Commit

Permalink
Add animation for button selection.
Browse files Browse the repository at this point in the history
  • Loading branch information
DavydLiu committed Jul 25, 2016
1 parent ffb3ad5 commit 131b631
Show file tree
Hide file tree
Showing 11 changed files with 122 additions and 70 deletions.
4 changes: 2 additions & 2 deletions DLRadioButton.podspec.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "DLRadioButton",
"version": "1.4.8",
"version": "1.4.9",
"summary": "A highly customizable Radio Button for iOS",
"description": "A highly customizable Radio Button for iOS.\n\n* Buttons are drew by UIBezierPath, customize it however you want.\n* You can also use pictures to indicate buttons' selection state.\n",
"homepage": "https://github.com/DavydLiu/DLRadioButton",
Expand All @@ -17,7 +17,7 @@
},
"source": {
"git": "https://github.com/DavydLiu/DLRadioButton.git",
"tag": "1.4.8"
"tag": "1.4.9"
},
"source_files": "DLRadioButton/**/*.{h,m}",
"exclude_files": "Classes/Exclude",
Expand Down
53 changes: 28 additions & 25 deletions DLRadioButton/DLRadioButton.h
Original file line number Diff line number Diff line change
@@ -1,95 +1,98 @@
#import <UIKit/UIKit.h>

/**
* A hightly customizable Radio Button for iOS.
* A hightly customizable Radio Button for iOS.
*/
IB_DESIGNABLE
NS_ASSUME_NONNULL_BEGIN
@interface DLRadioButton : UIButton

#pragma mark - Access buttons

/**
* Finds out selected button in same group.
*
* @return Selected button.
* @brief Finds out selected button in same group.
* @return Selected button.
*/
- (nullable DLRadioButton *)selectedButton;

/**
* Finds out selected buttons in same group, use it only if multiple selection is enabled.
*
* @return Selected buttons.
* @brief Finds out selected buttons in same group, use it only if multiple selection is enabled.
* @return Selected buttons.
*/
- (nonnull NSArray<DLRadioButton *> *)selectedButtons;
- (NSArray<DLRadioButton *> *)selectedButtons;

/**
* Container for holding other buttons in same group.
* @brief Container for holding other buttons in same group.
*/
@property (nonnull, nonatomic) IBOutletCollection(DLRadioButton) NSArray<DLRadioButton *> *otherButtons;
@property (nonatomic) IBOutletCollection(DLRadioButton) NSArray<DLRadioButton *> *otherButtons;

/**
* Clears selection for other buttons in in same group.
* @brief Clears selection for other buttons in in same group.
*/
- (void)deselectOtherButtons;

#pragma mark - Customization

NS_ASSUME_NONNULL_BEGIN
/**
* Size of icon, default is kDefaulIconSize.
* @brief Size of icon, default is kDefaulIconSize.
*/
@property (nonatomic) IBInspectable CGFloat iconSize;

/**
* Color of icon, default is title color for current UIControlState.
* @brief Color of icon, default is title color for current UIControlState.
*/
@property (nonatomic) IBInspectable UIColor *iconColor;

/**
* Stroke width of icon, default is iconSize / 9.
* @brief Stroke width of icon, default is iconSize / 9.
*/
@property (nonatomic) IBInspectable CGFloat iconStrokeWidth;

/**
* Size of selection indicator, default is iconSize * 0.5.
* @brief Size of selection indicator, default is iconSize * 0.5.
*/
@property (nonatomic) IBInspectable CGFloat indicatorSize;

/**
* Color of selection indicator, default is title color for current UIControlState.
* @brief Color of selection indicator, default is title color for current UIControlState.
*/
@property (nonatomic) IBInspectable UIColor *indicatorColor;

/**
* Margin width between icon and title, default is kDefaultMarginWidth.
* @brief Margin width between icon and title, default is kDefaultMarginWidth.
*/
@property (nonatomic) IBInspectable CGFloat marginWidth;

/**
* Whether icon on the right side, default is NO.
* @warning Please also set contentHorizontalAlignment to UIControlContentHorizontalAlignmentRight.
* @brief Whether icon on the right side, default is NO.
* @warning Please also set contentHorizontalAlignment to UIControlContentHorizontalAlignmentRight.
*/
@property (nonatomic, getter = isIconOnRight) IBInspectable BOOL iconOnRight;

/**
* Whether use square icon, default is NO.
* @brief Whether use square icon, default is NO.
*/
@property (nonatomic, getter = isIconSquare) IBInspectable BOOL iconSquare;

/**
* Image for radio button icon (optional).
* @brief Image for radio button icon (optional).
*/
@property (nonatomic) IBInspectable UIImage *icon;

/**
* Image for radio button icon when selected (optional).
* @brief Image for radio button icon when selected (optional).
*/
@property (nonatomic) IBInspectable UIImage *iconSelected;

/**
* Whether enable multiple selection, default is NO.
* @brief Whether enable multiple selection, default is NO.
*/
@property (nonatomic, getter = isMultipleSelectionEnabled) BOOL multipleSelectionEnabled;
NS_ASSUME_NONNULL_END

/**
* @brief Duration of radio button icon animation in seconds. Set it to 0.0 to turn off animation, default is 0.3.
*/
@property (nonatomic) CFTimeInterval animationDuration;

@end
NS_ASSUME_NONNULL_END
42 changes: 36 additions & 6 deletions DLRadioButton/DLRadioButton.m
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
#import "DLRadioButton.h"

static const CGFloat kDefaulIconSize = 15.0;
static const CGFloat kDefaultIconSize = 15.0;
static const CGFloat kDefaultMarginWidth = 5.0;
static const CFTimeInterval kDefaultAnimationDuration = 0.3;
static NSString * const kGeneratedIconName = @"Generated Icon";

static BOOL _groupModifing = NO;

@implementation DLRadioButton
Expand Down Expand Up @@ -43,6 +45,17 @@ - (void)setMultipleSelectionEnabled:(BOOL)multipleSelectionEnabled {
_multipleSelectionEnabled = multipleSelectionEnabled;
}

- (void)setAnimationDuration:(CFTimeInterval)animationDuration {
if (![DLRadioButton isGroupModifing]) {
[DLRadioButton groupModifing:YES];
for (DLRadioButton *radioButton in self.otherButtons) {
radioButton.animationDuration = animationDuration;
}
[DLRadioButton groupModifing:NO];
}
_animationDuration = animationDuration;
}

#pragma mark - Helpers

- (void)drawButton {
Expand All @@ -52,7 +65,7 @@ - (void)drawButton {
if (!self.iconSelected || [self.iconSelected.accessibilityIdentifier isEqualToString:kGeneratedIconName]) {
self.iconSelected = [self drawIconWithSelection:YES];
}
CGFloat marginWidth = self.marginWidth ? self.marginWidth : kDefaultMarginWidth;
CGFloat marginWidth = self.marginWidth;
BOOL isRightToLeftLayout = NO;
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 9.0) {
isRightToLeftLayout = [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:self.semanticContentAttribute] == UIUserInterfaceLayoutDirectionRightToLeft;
Expand All @@ -77,7 +90,7 @@ - (UIImage *)drawIconWithSelection:(BOOL)selected {
UIColor *defaulColor = selected ? [self titleColorForState:UIControlStateSelected | UIControlStateHighlighted] : [self titleColorForState:UIControlStateNormal];
UIColor *iconColor = self.iconColor ? self.iconColor : defaulColor;
UIColor *indicatorColor = self.indicatorColor ? self.indicatorColor : defaulColor;
CGFloat iconSize = self.iconSize ? self.iconSize : kDefaulIconSize;
CGFloat iconSize = self.iconSize;
CGFloat iconStrokeWidth = self.iconStrokeWidth ? self.iconStrokeWidth : iconSize / 9;
CGFloat indicatorSize = self.indicatorSize ? self.indicatorSize : iconSize * 0.5;

Expand Down Expand Up @@ -121,14 +134,19 @@ - (UIImage *)drawIconWithSelection:(BOOL)selected {
return image;
}

- (void)touchDown {
- (void)touchUpInside {
[self setSelected:YES];
}

- (void)initRadioButton {
[super addTarget:self action:@selector(touchDown) forControlEvents:UIControlEventTouchUpInside];
_iconSize = kDefaultIconSize;
_marginWidth = kDefaultMarginWidth;
_animationDuration = kDefaultAnimationDuration;
[super addTarget:self action:@selector(touchUpInside) forControlEvents:UIControlEventTouchUpInside];
}

#pragma mark - NSObject

- (void)prepareForInterfaceBuilder {
[self initRadioButton];
[self drawButton];
Expand Down Expand Up @@ -199,6 +217,18 @@ - (UIColor *)titleColorForState:(UIControlState)state {
#pragma mark - UIControl

- (void)setSelected:(BOOL)selected {
if ((self.isMultipleSelectionEnabled ||
(selected != self.isSelected &&
[self.icon.accessibilityIdentifier isEqualToString:kGeneratedIconName] &&
[self.iconSelected.accessibilityIdentifier isEqualToString:kGeneratedIconName])) &&
self.animationDuration > 0.0) {
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"contents"];
animation.duration = self.animationDuration;
animation.fromValue = self.isSelected ? (id)self.iconSelected.CGImage : (id)self.icon.CGImage;
animation.toValue = self.isSelected ? (id)self.icon.CGImage : (id)self.iconSelected.CGImage;
[self.imageView.layer addAnimation:animation forKey:@"icon"];
}

if (self.isMultipleSelectionEnabled) {
[super setSelected:!self.isSelected];
} else {
Expand Down Expand Up @@ -232,4 +262,4 @@ - (void)drawRect:(CGRect)rect {
[self drawButton];
}

@end
@end
2 changes: 2 additions & 0 deletions DLRadioButtonTests/DLRadioButtonTests.m
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ - (void)testButtonsGroupModifingPerformance {
}
[self measureBlock:^{
self.firstButton.otherButtons = otherButtons;
self.firstButton.multipleSelectionEnabled = YES;
self.firstButton.animationDuration = 0.0;
}];
}

Expand Down
Binary file modified Example/DLRadioButton.framework/DLRadioButton
Binary file not shown.
53 changes: 28 additions & 25 deletions Example/DLRadioButton.framework/Headers/DLRadioButton.h
Original file line number Diff line number Diff line change
@@ -1,95 +1,98 @@
#import <UIKit/UIKit.h>

/**
* A hightly customizable Radio Button for iOS.
* A hightly customizable Radio Button for iOS.
*/
IB_DESIGNABLE
NS_ASSUME_NONNULL_BEGIN
@interface DLRadioButton : UIButton

#pragma mark - Access buttons

/**
* Finds out selected button in same group.
*
* @return Selected button.
* @brief Finds out selected button in same group.
* @return Selected button.
*/
- (nullable DLRadioButton *)selectedButton;

/**
* Finds out selected buttons in same group, use it only if multiple selection is enabled.
*
* @return Selected buttons.
* @brief Finds out selected buttons in same group, use it only if multiple selection is enabled.
* @return Selected buttons.
*/
- (nonnull NSArray<DLRadioButton *> *)selectedButtons;
- (NSArray<DLRadioButton *> *)selectedButtons;

/**
* Container for holding other buttons in same group.
* @brief Container for holding other buttons in same group.
*/
@property (nonnull, nonatomic) IBOutletCollection(DLRadioButton) NSArray<DLRadioButton *> *otherButtons;
@property (nonatomic) IBOutletCollection(DLRadioButton) NSArray<DLRadioButton *> *otherButtons;

/**
* Clears selection for other buttons in in same group.
* @brief Clears selection for other buttons in in same group.
*/
- (void)deselectOtherButtons;

#pragma mark - Customization

NS_ASSUME_NONNULL_BEGIN
/**
* Size of icon, default is kDefaulIconSize.
* @brief Size of icon, default is kDefaulIconSize.
*/
@property (nonatomic) IBInspectable CGFloat iconSize;

/**
* Color of icon, default is title color for current UIControlState.
* @brief Color of icon, default is title color for current UIControlState.
*/
@property (nonatomic) IBInspectable UIColor *iconColor;

/**
* Stroke width of icon, default is iconSize / 9.
* @brief Stroke width of icon, default is iconSize / 9.
*/
@property (nonatomic) IBInspectable CGFloat iconStrokeWidth;

/**
* Size of selection indicator, default is iconSize * 0.5.
* @brief Size of selection indicator, default is iconSize * 0.5.
*/
@property (nonatomic) IBInspectable CGFloat indicatorSize;

/**
* Color of selection indicator, default is title color for current UIControlState.
* @brief Color of selection indicator, default is title color for current UIControlState.
*/
@property (nonatomic) IBInspectable UIColor *indicatorColor;

/**
* Margin width between icon and title, default is kDefaultMarginWidth.
* @brief Margin width between icon and title, default is kDefaultMarginWidth.
*/
@property (nonatomic) IBInspectable CGFloat marginWidth;

/**
* Whether icon on the right side, default is NO.
* @warning Please also set contentHorizontalAlignment to UIControlContentHorizontalAlignmentRight.
* @brief Whether icon on the right side, default is NO.
* @warning Please also set contentHorizontalAlignment to UIControlContentHorizontalAlignmentRight.
*/
@property (nonatomic, getter = isIconOnRight) IBInspectable BOOL iconOnRight;

/**
* Whether use square icon, default is NO.
* @brief Whether use square icon, default is NO.
*/
@property (nonatomic, getter = isIconSquare) IBInspectable BOOL iconSquare;

/**
* Image for radio button icon (optional).
* @brief Image for radio button icon (optional).
*/
@property (nonatomic) IBInspectable UIImage *icon;

/**
* Image for radio button icon when selected (optional).
* @brief Image for radio button icon when selected (optional).
*/
@property (nonatomic) IBInspectable UIImage *iconSelected;

/**
* Whether enable multiple selection, default is NO.
* @brief Whether enable multiple selection, default is NO.
*/
@property (nonatomic, getter = isMultipleSelectionEnabled) BOOL multipleSelectionEnabled;
NS_ASSUME_NONNULL_END

/**
* @brief Duration of radio button icon animation in seconds. Set it to 0.0 to turn off animation, default is 0.3.
*/
@property (nonatomic) CFTimeInterval animationDuration;

@end
NS_ASSUME_NONNULL_END
Binary file modified Example/DLRadioButton.framework/Info.plist
Binary file not shown.
Loading

0 comments on commit 131b631

Please sign in to comment.