Skip to content

Commit 923ccd3

Browse files
committed
Added a new group feature for radio button functionality
1 parent e880db8 commit 923ccd3

File tree

5 files changed

+210
-1
lines changed

5 files changed

+210
-1
lines changed

Classes/BEMCheckBox.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#import <UIKit/UIKit.h>
1010

11+
@class BEMCheckBoxGroup;
1112
@protocol BEMCheckBoxDelegate;
1213

1314
/** The different type of boxes available.
@@ -108,6 +109,10 @@ typedef NS_ENUM(NSInteger, BEMAnimationType) {
108109
*/
109110
@property (strong, nonatomic) IBInspectable UIColor *tintColor;
110111

112+
/** The group this box is associated with.
113+
*/
114+
@property (weak, nonatomic, nullable, readonly) BEMCheckBoxGroup *group;
115+
111116
/** The type of box.
112117
* @see BEMBoxType.
113118
*/

Classes/BEMCheckBox.m

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#import "BEMCheckBox.h"
1010
#import "BEMAnimationManager.h"
1111
#import "BEMPathManager.h"
12+
#import "BEMCheckBoxGroup.h"
1213

1314
@interface BEMCheckBox ()
1415

@@ -32,6 +33,18 @@ @interface BEMCheckBox ()
3233
*/
3334
@property (strong, nonatomic) BEMPathManager *pathManager;
3435

36+
/** The group this box is associated with.
37+
*/
38+
@property (weak, nonatomic, nullable) BEMCheckBoxGroup *group;
39+
40+
@end
41+
42+
/** Defines private methods that we can call to update our group's status.
43+
*/
44+
@interface BEMCheckBoxGroup ()
45+
46+
- (void)_checkBoxSelectionChanged:(BEMCheckBox *)checkBox;
47+
3548
@end
3649

3750
@implementation BEMCheckBox
@@ -101,7 +114,7 @@ - (void)reload {
101114
}
102115

103116
#pragma mark Setters
104-
- (void)setOn:(BOOL)on animated:(BOOL)animated {
117+
- (void)_setOn:(BOOL)on animated:(BOOL)animated notifyGroup:(BOOL)notifyGroup {
105118
_on = on;
106119

107120
[self drawEntireCheckBox];
@@ -118,6 +131,15 @@ - (void)setOn:(BOOL)on animated:(BOOL)animated {
118131
[self.checkMarkLayer removeFromSuperlayer];
119132
}
120133
}
134+
135+
// Notify our group if we have one that something has changed
136+
if(notifyGroup){
137+
[self.group _checkBoxSelectionChanged:self];
138+
}
139+
}
140+
141+
- (void)setOn:(BOOL)on animated:(BOOL)animated {
142+
[self _setOn:on animated:animated notifyGroup:YES];
121143
}
122144

123145
- (void)setOn:(BOOL)on {
@@ -167,6 +189,11 @@ - (void)setOnCheckColor:(UIColor *)onCheckColor {
167189

168190
#pragma mark Gesture Recognizer
169191
- (void)handleTapCheckBox:(UITapGestureRecognizer *)recognizer {
192+
// If we have a group that requires a selection, and we're already selected, don't allow a deselection
193+
if(self.group && self.group.mustHaveSelection && self.on){
194+
return;
195+
}
196+
170197
[self setOn:!self.on animated:YES];
171198
if ([self.delegate respondsToSelector:@selector(didTapCheckBox:)]) {
172199
[self.delegate didTapCheckBox:self];

Classes/BEMCheckBoxGroup.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//
2+
// BEMCheckBoxGroup.h
3+
// CheckBox
4+
//
5+
// Created by Cory Imdieke on 10/17/16.
6+
// Copyright © 2016 Boris Emorine. All rights reserved.
7+
//
8+
9+
#import <Foundation/Foundation.h>
10+
11+
@class BEMCheckBox;
12+
13+
@interface BEMCheckBoxGroup : NSObject
14+
15+
/** An array of check boxes in this group.
16+
*/
17+
@property (nonatomic, strong, nonnull, readonly) NSArray<BEMCheckBox *> *checkBoxes;
18+
19+
/** The currently selected check box. Only can be nil if mustHaveSelection is NO. Setting this value will cause the other check boxes to deselect automatically.
20+
*/
21+
@property (nonatomic, strong, nullable) BEMCheckBox *selectedCheckBox;
22+
23+
/** If YES, don't allow the user to unselect all options, must have single selection at all times. Default to NO.
24+
*/
25+
@property (nonatomic) BOOL mustHaveSelection;
26+
27+
/** Creates a new group with the list of check boxes.
28+
*/
29+
+ (nonnull instancetype)groupWithCheckBoxes:(nullable NSArray<BEMCheckBox *> *)checkBoxes;
30+
31+
/** Adds a check box to this group. Check boxes can only belong to a single group, adding to a group removes it from its current group.
32+
*/
33+
- (void)addCheckBoxToGroup:(nonnull BEMCheckBox *)checkBox;
34+
35+
/** Removes a check box from this group.
36+
*/
37+
- (void)removeCheckBoxFromGroup:(nonnull BEMCheckBox *)checkBox;
38+
39+
@end

Classes/BEMCheckBoxGroup.m

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
//
2+
// BEMCheckBoxGroup.m
3+
// CheckBox
4+
//
5+
// Created by Cory Imdieke on 10/17/16.
6+
// Copyright © 2016 Boris Emorine. All rights reserved.
7+
//
8+
9+
#import "BEMCheckBoxGroup.h"
10+
#import "BEMCheckBox.h"
11+
12+
@interface BEMCheckBoxGroup ()
13+
14+
@property (nonatomic, strong, nonnull) NSArray<BEMCheckBox *> *checkBoxes;
15+
16+
@end
17+
18+
/** Defines private methods that we can call on the check box.
19+
*/
20+
@interface BEMCheckBox ()
21+
22+
@property (weak, nonatomic, nullable) BEMCheckBoxGroup *group;
23+
24+
- (void)_setOn:(BOOL)on animated:(BOOL)animated notifyGroup:(BOOL)notifyGroup;
25+
26+
@end
27+
28+
@implementation BEMCheckBoxGroup
29+
30+
- (instancetype)init {
31+
self = [super init];
32+
if (self) {
33+
_mustHaveSelection = NO;
34+
_checkBoxes = @[];
35+
}
36+
return self;
37+
}
38+
39+
+ (nonnull instancetype)groupWithCheckBoxes:(nullable NSArray<BEMCheckBox *> *)checkBoxes {
40+
BEMCheckBoxGroup *group = [[BEMCheckBoxGroup alloc] init];
41+
for (BEMCheckBox *checkbox in checkBoxes) {
42+
[group addCheckBoxToGroup:checkbox];
43+
}
44+
45+
return group;
46+
}
47+
48+
- (void)addCheckBoxToGroup:(nonnull BEMCheckBox *)checkBox {
49+
if([checkBox group]){
50+
// Already has a group, remove first
51+
[[checkBox group] removeCheckBoxFromGroup:checkBox];
52+
}
53+
54+
[checkBox _setOn:NO animated:NO notifyGroup:NO];
55+
[checkBox setGroup:self];
56+
self.checkBoxes = [self.checkBoxes arrayByAddingObject:checkBox];
57+
}
58+
59+
- (void)removeCheckBoxFromGroup:(nonnull BEMCheckBox *)checkBox {
60+
if(![self.checkBoxes containsObject:checkBox]){
61+
// Not in this group
62+
return;
63+
}
64+
65+
[checkBox setGroup:nil];
66+
NSMutableArray *mutableBoxes = [self.checkBoxes mutableCopy];
67+
[mutableBoxes removeObject:checkBox];
68+
self.checkBoxes = [NSArray arrayWithArray:mutableBoxes];
69+
}
70+
71+
- (BEMCheckBox *)selectedCheckBox {
72+
BEMCheckBox *checkbox = nil;
73+
for (BEMCheckBox *b in self.checkBoxes) {
74+
if([b on]){
75+
checkbox = b;
76+
break;
77+
}
78+
}
79+
80+
return checkbox;
81+
}
82+
83+
- (void)setSelectedCheckBox:(BEMCheckBox *)selectedCheckBox {
84+
if(selectedCheckBox){
85+
for (BEMCheckBox *b in self.checkBoxes) {
86+
BOOL shouldBeOn = (b == selectedCheckBox);
87+
if([b on] != shouldBeOn){
88+
[b _setOn:shouldBeOn animated:YES notifyGroup:NO];
89+
}
90+
}
91+
} else {
92+
// Selection is nil
93+
if(self.mustHaveSelection && [self.checkBoxes count] > 0){
94+
// We must have a selected checkbox, so re-call this method with the first checkbox
95+
[self setSelectedCheckBox:[self.checkBoxes firstObject]];
96+
} else {
97+
for (BEMCheckBox *b in self.checkBoxes) {
98+
BOOL shouldBeOn = NO;
99+
if([b on] != shouldBeOn){
100+
[b _setOn:shouldBeOn animated:YES notifyGroup:NO];
101+
}
102+
}
103+
}
104+
}
105+
}
106+
107+
- (void)setMustHaveSelection:(BOOL)mustHaveSelection{
108+
_mustHaveSelection = mustHaveSelection;
109+
110+
// If it must have a selection and we currently don't, select the first box
111+
if(mustHaveSelection && !self.selectedCheckBox){
112+
[self setSelectedCheckBox:[self.checkBoxes firstObject]];
113+
}
114+
}
115+
116+
#pragma mark Private methods called by BEMCheckBox
117+
118+
- (void)_checkBoxSelectionChanged:(BEMCheckBox *)checkBox {
119+
if([checkBox on]){
120+
// Change selected checkbox to this one
121+
[self setSelectedCheckBox:checkBox];
122+
} else if(checkBox == self.selectedCheckBox) {
123+
// Selected checkbox was this one, clear it
124+
[self setSelectedCheckBox:nil];
125+
}
126+
}
127+
128+
@end

Sample Project/CheckBox.xcodeproj/project.pbxproj

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
5643F13B1CDE724A0020E238 /* BEMAnimationManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C3E837A91BAE3493004576D6 /* BEMAnimationManager.m */; };
1616
5643F13C1CDE724A0020E238 /* BEMPathManager.h in Headers */ = {isa = PBXBuildFile; fileRef = C3E837AB1BAE35D8004576D6 /* BEMPathManager.h */; settings = {ATTRIBUTES = (Public, ); }; };
1717
5643F13D1CDE724A0020E238 /* BEMPathManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C3E837AC1BAE35D8004576D6 /* BEMPathManager.m */; };
18+
984F9E0F1DB59228002F746B /* BEMCheckBoxGroup.h in Headers */ = {isa = PBXBuildFile; fileRef = 984F9E0D1DB59228002F746B /* BEMCheckBoxGroup.h */; };
19+
984F9E101DB59228002F746B /* BEMCheckBoxGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 984F9E0E1DB59228002F746B /* BEMCheckBoxGroup.m */; };
20+
984F9E111DB59228002F746B /* BEMCheckBoxGroup.m in Sources */ = {isa = PBXBuildFile; fileRef = 984F9E0E1DB59228002F746B /* BEMCheckBoxGroup.m */; };
1821
C32537131B9231780059F394 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C32537121B9231780059F394 /* main.m */; };
1922
C32537161B9231780059F394 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = C32537151B9231780059F394 /* AppDelegate.m */; };
2023
C325371C1B9231780059F394 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C325371A1B9231780059F394 /* Main.storyboard */; };
@@ -70,6 +73,8 @@
7073
5643F12B1CDE722C0020E238 /* BEMCheckBox.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = BEMCheckBox.framework; sourceTree = BUILT_PRODUCTS_DIR; };
7174
5643F12D1CDE722C0020E238 /* CheckBox.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CheckBox.h; sourceTree = "<group>"; };
7275
5643F12F1CDE722C0020E238 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
76+
984F9E0D1DB59228002F746B /* BEMCheckBoxGroup.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = BEMCheckBoxGroup.h; path = ../Classes/BEMCheckBoxGroup.h; sourceTree = "<group>"; };
77+
984F9E0E1DB59228002F746B /* BEMCheckBoxGroup.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = BEMCheckBoxGroup.m; path = ../Classes/BEMCheckBoxGroup.m; sourceTree = "<group>"; };
7378
C325370D1B9231780059F394 /* CheckBox.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CheckBox.app; sourceTree = BUILT_PRODUCTS_DIR; };
7479
C32537111B9231780059F394 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
7580
C32537121B9231780059F394 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
@@ -219,6 +224,8 @@
219224
C3E837A91BAE3493004576D6 /* BEMAnimationManager.m */,
220225
C3E837AB1BAE35D8004576D6 /* BEMPathManager.h */,
221226
C3E837AC1BAE35D8004576D6 /* BEMPathManager.m */,
227+
984F9E0D1DB59228002F746B /* BEMCheckBoxGroup.h */,
228+
984F9E0E1DB59228002F746B /* BEMCheckBoxGroup.m */,
222229
);
223230
name = Classes;
224231
sourceTree = "<group>";
@@ -239,6 +246,7 @@
239246
isa = PBXHeadersBuildPhase;
240247
buildActionMask = 2147483647;
241248
files = (
249+
984F9E0F1DB59228002F746B /* BEMCheckBoxGroup.h in Headers */,
242250
5643F1381CDE724A0020E238 /* BEMCheckBox.h in Headers */,
243251
5643F13C1CDE724A0020E238 /* BEMPathManager.h in Headers */,
244252
5643F13A1CDE724A0020E238 /* BEMAnimationManager.h in Headers */,
@@ -408,6 +416,7 @@
408416
buildActionMask = 2147483647;
409417
files = (
410418
5643F1391CDE724A0020E238 /* BEMCheckBox.m in Sources */,
419+
984F9E111DB59228002F746B /* BEMCheckBoxGroup.m in Sources */,
411420
5643F13D1CDE724A0020E238 /* BEMPathManager.m in Sources */,
412421
5643F13B1CDE724A0020E238 /* BEMAnimationManager.m in Sources */,
413422
);
@@ -420,6 +429,7 @@
420429
C39F1AB71BAFEAA400E8A023 /* BEMMainViewController.m in Sources */,
421430
C3DFB9BD1BBD0E2800D2F8B4 /* BEMAnimationsTableViewController.m in Sources */,
422431
C32537161B9231780059F394 /* AppDelegate.m in Sources */,
432+
984F9E101DB59228002F746B /* BEMCheckBoxGroup.m in Sources */,
423433
C32537131B9231780059F394 /* main.m in Sources */,
424434
C3E594901BC220C7005EA38B /* BEMCurrentSetupTableViewController.m in Sources */,
425435
);

0 commit comments

Comments
 (0)