├── LICENSE ├── README.md ├── RadioButton.h └── RadioButton.m /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2014 Sergey Nikitenko. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Radio Button for iOS 2 | Pretty simple class that extends standard UIButton functionality. 3 | Default and selected states can be configured for every button. 4 | 5 | ![Demo](https://raw.github.com/onegray/RadioButton-ios/data/demo.gif) 6 | 7 | #### Super easy to use 8 | It does not need any central manager. Just link the buttons right in Interface Builder: 9 | 10 | ![Demo](https://raw.github.com/onegray/RadioButton-ios/data/linking.gif) 11 | 12 | 13 | Alternatively group the buttons using single line of code: 14 | 15 | radio1.groupButtons = @[radio1, radio2, radio3]; 16 | 17 | 18 | Select any button, and all other button in the same group become deselected automatically: 19 | 20 | radio2.selected = YES; // radio1 and radio3 become deselected 21 | 22 | 23 | Any button from the group knows which one is selected: 24 | 25 | RadioButton* r1 = radio1.selectedButton; 26 | RadioButton* r2 = radio2.selectedButton; 27 | RadioButton* r3 = radio3.selectedButton; 28 | NSAssert (r1==r2 && r2==r3, @"Must be equal"); 29 | 30 | And a helpful method to select button by tag: 31 | 32 | [radio1 setSelectedWithTag:kTagRadio3]; 33 | 34 | #### CocoaPods 35 | 36 | pod 'RadioButton' 37 | 38 | #### Resources 39 | [sample.zip](https://github.com/onegray/RadioButton-ios/archive/sample.zip) - Sample app 40 | [radio-res-ios.zip](https://raw.github.com/onegray/RadioButton-ios/data/radio-res-ios.zip) - Radio Button images used in the sample 41 | 42 | #### License 43 | RadioButton is available under the MIT license - fork, modify and use however you want. 44 | -------------------------------------------------------------------------------- /RadioButton.h: -------------------------------------------------------------------------------- 1 | // 2 | // RadioButton.h 3 | // 4 | // Created by Sergey Nikitenko on 3/5/13. 5 | // Copyright 2013 Sergey Nikitenko. All rights reserved. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | // 25 | 26 | #import 27 | 28 | @interface RadioButton : UIButton 29 | 30 | // Outlet collection of links to other buttons in the group. 31 | @property (nonatomic, strong) IBOutletCollection(RadioButton) NSArray* groupButtons; 32 | 33 | // Currently selected radio button in the group. 34 | // If there are multiple buttons selected then it returns the first one. 35 | @property (nonatomic, readonly) RadioButton* selectedButton; 36 | 37 | // If selected==YES, then it selects the button and deselects other buttons in the group. 38 | // If selected==NO, then it deselects the button and if there are only two buttons in the group, then it selects second. 39 | -(void) setSelected:(BOOL)selected; 40 | 41 | // Find first radio with given tag and makes it selected. 42 | // All of other buttons in the group become deselected. 43 | -(void) setSelectedWithTag:(NSInteger)tag; 44 | 45 | -(void) deselectAllButtons; 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /RadioButton.m: -------------------------------------------------------------------------------- 1 | // 2 | // RadioButton.m 3 | // 4 | // Created by Sergey Nikitenko on 3/5/13. 5 | // Copyright 2013 Sergey Nikitenko. All rights reserved. 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | // 25 | 26 | #import "RadioButton.h" 27 | 28 | @interface RadioButton() 29 | { 30 | NSMutableArray* _sharedLinks; 31 | } 32 | @end 33 | 34 | @implementation RadioButton 35 | 36 | - (id)initWithFrame:(CGRect)frame 37 | { 38 | self = [super initWithFrame:frame]; 39 | if (self) { 40 | if(![[self allTargets] containsObject:self]) { 41 | [super addTarget:self action:@selector(onTouchUpInside) forControlEvents:UIControlEventTouchUpInside]; 42 | } 43 | } 44 | return self; 45 | } 46 | 47 | -(void) awakeFromNib 48 | { 49 | if(![[self allTargets] containsObject:self]) { 50 | [super addTarget:self action:@selector(onTouchUpInside) forControlEvents:UIControlEventTouchUpInside]; 51 | } 52 | } 53 | 54 | -(void) addTarget:(id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents 55 | { 56 | // 'self' should be the first target 57 | if(![[self allTargets] containsObject:self]) { 58 | [super addTarget:self action:@selector(onTouchUpInside) forControlEvents:UIControlEventTouchUpInside]; 59 | } 60 | [super addTarget:target action:action forControlEvents:controlEvents]; 61 | } 62 | 63 | -(void) onTouchUpInside 64 | { 65 | [self setSelected:YES distinct:YES sendControlEvent:YES]; 66 | } 67 | 68 | -(void) setGroupButtons:(NSArray *)buttons 69 | { 70 | if(!_sharedLinks) { 71 | for(RadioButton* rb in buttons) { 72 | if(rb->_sharedLinks) { 73 | _sharedLinks = rb->_sharedLinks; 74 | break; 75 | } 76 | } 77 | if(!_sharedLinks) { 78 | _sharedLinks = [[NSMutableArray alloc] initWithCapacity:[buttons count]+1]; 79 | } 80 | } 81 | 82 | BOOL (^btnExistsInList)(NSArray*, RadioButton*) = ^(NSArray* list, RadioButton* rb){ 83 | for(NSValue* v in list) { 84 | if([v nonretainedObjectValue]==rb) { 85 | return YES; 86 | } 87 | } 88 | return NO; 89 | }; 90 | 91 | if(!btnExistsInList(_sharedLinks, self)) { 92 | [_sharedLinks addObject:[NSValue valueWithNonretainedObject:self]]; 93 | } 94 | 95 | for(RadioButton* rb in buttons) { 96 | if(rb->_sharedLinks!=_sharedLinks) { 97 | if(!rb->_sharedLinks) { 98 | rb->_sharedLinks = _sharedLinks; 99 | } else { 100 | for(NSValue* v in rb->_sharedLinks) { 101 | RadioButton* vrb = [v nonretainedObjectValue]; 102 | if(!btnExistsInList(_sharedLinks, vrb)) { 103 | [_sharedLinks addObject:v]; 104 | vrb->_sharedLinks = _sharedLinks; 105 | } 106 | } 107 | } 108 | } 109 | if(!btnExistsInList(_sharedLinks, rb)) { 110 | [_sharedLinks addObject:[NSValue valueWithNonretainedObject:rb]]; 111 | } 112 | } 113 | } 114 | 115 | -(NSArray*) groupButtons 116 | { 117 | if([_sharedLinks count]) { 118 | NSMutableArray* buttons = [[NSMutableArray alloc] initWithCapacity:[_sharedLinks count]]; 119 | for(NSValue* v in _sharedLinks) { 120 | [buttons addObject:[v nonretainedObjectValue]]; 121 | } 122 | return buttons; 123 | } 124 | return nil; 125 | } 126 | 127 | -(RadioButton*) selectedButton 128 | { 129 | if([self isSelected]) { 130 | return self; 131 | } else { 132 | for(NSValue* v in _sharedLinks) { 133 | RadioButton* rb = [v nonretainedObjectValue]; 134 | if([rb isSelected]) { 135 | return rb; 136 | } 137 | } 138 | } 139 | return nil; 140 | } 141 | 142 | -(void) setSelected:(BOOL)selected 143 | { 144 | [self setSelected:selected distinct:YES sendControlEvent:NO]; 145 | } 146 | 147 | -(void) setButtonSelected:(BOOL)selected sendControlEvent:(BOOL)sendControlEvent 148 | { 149 | BOOL valueChanged = (self.selected != selected); 150 | [super setSelected:selected]; 151 | if(valueChanged && sendControlEvent) { 152 | [self sendActionsForControlEvents:UIControlEventValueChanged]; 153 | } 154 | } 155 | 156 | -(void) setSelected:(BOOL)selected distinct:(BOOL)distinct sendControlEvent:(BOOL)sendControlEvent 157 | { 158 | [self setButtonSelected:selected sendControlEvent:sendControlEvent]; 159 | 160 | if( distinct && (selected || [_sharedLinks count]==2) ) 161 | { 162 | selected = !selected; 163 | for(NSValue* v in _sharedLinks) { 164 | RadioButton* rb = [v nonretainedObjectValue]; 165 | if(rb!=self) { 166 | [rb setButtonSelected:selected sendControlEvent:sendControlEvent]; 167 | } 168 | } 169 | } 170 | } 171 | 172 | -(void) deselectAllButtons 173 | { 174 | for(NSValue* v in _sharedLinks) { 175 | RadioButton* rb = [v nonretainedObjectValue]; 176 | [rb setButtonSelected:NO sendControlEvent:NO]; 177 | } 178 | } 179 | 180 | -(void) setSelectedWithTag:(NSInteger)tag 181 | { 182 | if(self.tag == tag) { 183 | [self setSelected:YES distinct:YES sendControlEvent:NO]; 184 | } else { 185 | for(NSValue* v in _sharedLinks) { 186 | RadioButton* rb = [v nonretainedObjectValue]; 187 | if(rb.tag == tag) { 188 | [rb setSelected:YES distinct:YES sendControlEvent:NO]; 189 | break; 190 | } 191 | } 192 | } 193 | } 194 | 195 | - (void)dealloc 196 | { 197 | for(NSValue* v in _sharedLinks) { 198 | if([v nonretainedObjectValue]==self) { 199 | [_sharedLinks removeObjectIdenticalTo:v]; 200 | break; 201 | } 202 | } 203 | } 204 | 205 | 206 | @end 207 | --------------------------------------------------------------------------------