├── .gitignore ├── LICENSE.md ├── NYSegmentedControl.podspec ├── NYSegmentedControl ├── NYSegment.h ├── NYSegment.m ├── NYSegmentIndicator.h ├── NYSegmentIndicator.m ├── NYSegmentTextRenderView.h ├── NYSegmentTextRenderView.m ├── NYSegmentedControl.h └── NYSegmentedControl.m ├── NYSegmentedControlDemo ├── NYSegmentedControlDemo.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── NYSegmentedControlDemo │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── DemoViewController.h │ ├── DemoViewController.m │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ └── LaunchImage.launchimage │ │ │ └── Contents.json │ ├── Launch Screen.xib │ ├── NYSegmentedControlDemo-Info.plist │ ├── NYSegmentedControlDemo-Prefix.pch │ ├── en.lproj │ │ └── InfoPlist.strings │ └── main.m └── NYSegmentedControlDemoTests │ ├── NYSegmentedControlDemoTests-Info.plist │ ├── NYSegmentedControlDemoTests.m │ └── en.lproj │ └── InfoPlist.strings ├── README.md ├── example-animated.gif └── image.png /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *.DS_Store 3 | *.pbxuser 4 | !default.pbxuser 5 | *.mode1v3 6 | !default.mode1v3 7 | *.mode2v3 8 | !default.mode2v3 9 | *.perspectivev3 10 | !default.perspectivev3 11 | xcuserdata 12 | *.xccheckout 13 | profile 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | .idea/ 19 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Nealon Young 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NYSegmentedControl.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "NYSegmentedControl" 3 | s.version = "1.1.0" 4 | s.summary = "Animated, customizable replacement for UISegmentedControl" 5 | s.description = "NYSegmentedControl is a customizable, animated replacement for UISegmentedControl inspired by controls found in Instagram, Foursquare, and other apps." 6 | s.homepage = "https://github.com/nealyoung/NYSegmentedControl" 7 | s.screenshots = "https://github.com/nealyoung/NYSegmentedControl/raw/master/image.png" 8 | s.license = { :type => "MIT", :file => "LICENSE.md" } 9 | s.author = { "Neal Young" => "hi@nealyoung.me" } 10 | s.social_media_url = "http://nealyoung.me" 11 | s.platform = :ios, "7.0" 12 | s.source = { :git => "https://github.com/nealyoung/NYSegmentedControl.git", :tag => "#{s.version}" } 13 | s.source_files = "NYSegmentedControl/*.{h,m}" 14 | s.requires_arc = true 15 | end 16 | -------------------------------------------------------------------------------- /NYSegmentedControl/NYSegment.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class NYSegment; 4 | @class NYSegmentTextRenderView; 5 | 6 | @interface NYSegment : UIView 7 | 8 | @property (nonatomic) NYSegmentTextRenderView *titleLabel; 9 | @property (nonatomic) BOOL selected; 10 | 11 | - (instancetype)initWithTitle:(NSString *)title; 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /NYSegmentedControl/NYSegment.m: -------------------------------------------------------------------------------- 1 | #import "NYSegment.h" 2 | #import "NYSegmentTextRenderView.h" 3 | 4 | static CGFloat const kMinimumSegmentWidth = 64.0f; 5 | 6 | @implementation NYSegment 7 | 8 | - (instancetype)initWithTitle:(NSString *)title { 9 | self = [self initWithFrame:CGRectZero]; 10 | if (self) { 11 | self.titleLabel.text = title; 12 | } 13 | return self; 14 | } 15 | 16 | - (id)initWithFrame:(CGRect)frame { 17 | self = [super initWithFrame:frame]; 18 | if (self) { 19 | self.isAccessibilityElement = YES; 20 | self.accessibilityTraits = UIAccessibilityTraitButton; 21 | 22 | self.userInteractionEnabled = NO; 23 | self.titleLabel = [[NYSegmentTextRenderView alloc] initWithFrame:self.frame]; 24 | self.titleLabel.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; 25 | self.titleLabel.font = [UIFont systemFontOfSize:13.0f]; 26 | self.titleLabel.backgroundColor = [UIColor clearColor]; 27 | [self addSubview:self.titleLabel]; 28 | } 29 | return self; 30 | } 31 | 32 | - (CGSize)sizeThatFits:(CGSize)size { 33 | CGSize sizeThatFits = [self.titleLabel sizeThatFits:size]; 34 | return CGSizeMake(MAX(sizeThatFits.width * 1.4f, kMinimumSegmentWidth), sizeThatFits.height); 35 | } 36 | 37 | - (void)setSelected:(BOOL)selected { 38 | _selected = selected; 39 | if (selected) { 40 | self.accessibilityTraits = self.accessibilityTraits | UIAccessibilityTraitSelected; 41 | } else { 42 | self.accessibilityTraits = self.accessibilityTraits & ~UIAccessibilityTraitSelected; 43 | } 44 | } 45 | 46 | - (NSString *)accessibilityLabel { 47 | return self.titleLabel.text; 48 | } 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /NYSegmentedControl/NYSegmentIndicator.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface NYSegmentIndicator : UIView 4 | 5 | @property (nonatomic) CGFloat cornerRadius; 6 | 7 | @property (nonatomic) CGFloat borderWidth; 8 | @property (nonatomic) UIColor *borderColor; 9 | 10 | @property (nonatomic) BOOL drawsGradientBackground; 11 | @property (nonatomic) UIColor *gradientTopColor; 12 | @property (nonatomic) UIColor *gradientBottomColor; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /NYSegmentedControl/NYSegmentIndicator.m: -------------------------------------------------------------------------------- 1 | #import "NYSegmentIndicator.h" 2 | 3 | @implementation NYSegmentIndicator 4 | 5 | + (Class)layerClass { 6 | return [CAGradientLayer class]; 7 | } 8 | 9 | - (id)initWithFrame:(CGRect)frame { 10 | self = [super initWithFrame:frame]; 11 | if (self) { 12 | self.layer.masksToBounds = YES; 13 | self.drawsGradientBackground = NO; 14 | self.opaque = NO; 15 | self.cornerRadius = 4.0f; 16 | self.gradientTopColor = [UIColor colorWithRed:0.27f green:0.54f blue:0.21f alpha:1.0f]; 17 | self.gradientBottomColor = [UIColor colorWithRed:0.22f green:0.43f blue:0.16f alpha:1.0f]; 18 | self.borderColor = [UIColor lightGrayColor]; 19 | self.borderWidth = 1.0f; 20 | } 21 | return self; 22 | } 23 | 24 | - (void)drawRect:(CGRect)rect { 25 | if (self.drawsGradientBackground) { 26 | CAGradientLayer *gradientLayer = (CAGradientLayer *)self.layer; 27 | gradientLayer.colors = @[(__bridge id)[self.gradientTopColor CGColor], 28 | (__bridge id)[self.gradientBottomColor CGColor]]; 29 | } else { 30 | self.layer.backgroundColor = [self.backgroundColor CGColor]; 31 | } 32 | } 33 | 34 | #pragma mark - Getters and Setters 35 | 36 | - (void)setBorderColor:(UIColor *)borderColor { 37 | self.layer.borderColor = [borderColor CGColor]; 38 | } 39 | 40 | - (UIColor *)borderColor { 41 | return [UIColor colorWithCGColor:self.layer.borderColor]; 42 | } 43 | 44 | - (void)setBorderWidth:(CGFloat)borderWidth { 45 | self.layer.borderWidth = borderWidth; 46 | } 47 | 48 | - (CGFloat)borderWidth { 49 | return self.layer.borderWidth; 50 | } 51 | 52 | - (void)setCornerRadius:(CGFloat)cornerRadius { 53 | self.layer.cornerRadius = cornerRadius; 54 | [self setNeedsDisplay]; 55 | } 56 | 57 | - (CGFloat)cornerRadius { 58 | return self.layer.cornerRadius; 59 | } 60 | 61 | - (void)setDrawsGradientBackground:(BOOL)drawsGradientBackground { 62 | _drawsGradientBackground = drawsGradientBackground; 63 | [self setNeedsDisplay]; 64 | } 65 | 66 | - (void)setGradientTopColor:(UIColor *)gradientTopColor { 67 | _gradientTopColor = gradientTopColor; 68 | [self setNeedsDisplay]; 69 | } 70 | 71 | - (void)setGradientBottomColor:(UIColor *)gradientBottomColor { 72 | _gradientBottomColor = gradientBottomColor; 73 | [self setNeedsDisplay]; 74 | } 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /NYSegmentedControl/NYSegmentTextRenderView.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | NS_ASSUME_NONNULL_BEGIN 4 | 5 | @interface NYSegmentTextRenderView : UIView 6 | 7 | @property (nonatomic, strong, nullable) NSString *text; 8 | 9 | @property (nonatomic, strong) UIFont *font; 10 | @property (nonatomic, strong) UIColor *textColor; 11 | 12 | @property (nonatomic, strong) UIColor *selectedTextColor; 13 | @property (nonatomic, strong) UIFont *selectedFont; 14 | 15 | @property (nonatomic, assign) CGRect selectedTextDrawingRect; 16 | @property (nonatomic, assign) CGFloat maskCornerRadius; 17 | 18 | @end 19 | 20 | NS_ASSUME_NONNULL_END 21 | -------------------------------------------------------------------------------- /NYSegmentedControl/NYSegmentTextRenderView.m: -------------------------------------------------------------------------------- 1 | #import "NYSegmentTextRenderView.h" 2 | 3 | @interface NYSegmentTextRenderView () 4 | 5 | @property (nonatomic, strong, nonnull) NSDictionary *unselectedTextAttributes; 6 | @property (nonatomic, strong, nonnull) NSDictionary *selectedTextAttributes; 7 | 8 | @end 9 | 10 | @implementation NYSegmentTextRenderView 11 | 12 | - (instancetype)initWithFrame:(CGRect)frame { 13 | self = [super initWithFrame:frame]; 14 | 15 | if (self) { 16 | _font = [UIFont systemFontOfSize:14.0f]; 17 | _textColor = [UIColor darkGrayColor]; 18 | 19 | _selectedTextColor = _textColor; 20 | _selectedFont = _font; 21 | 22 | [self setupSelectedAttributes]; 23 | [self setupUnselectedAttributes]; 24 | } 25 | 26 | return self; 27 | } 28 | 29 | - (void)drawRect:(CGRect)rect { 30 | CGContextRef context = UIGraphicsGetCurrentContext(); 31 | CGContextSaveGState(context); 32 | 33 | if (!CGRectIsEmpty(self.selectedTextDrawingRect)) { 34 | CGRect beforeMaskFrame = CGRectMake(0, 0, CGRectGetMinX(self.selectedTextDrawingRect), CGRectGetHeight(self.frame)); 35 | CGRect afterMaskFrame = CGRectMake(CGRectGetMaxX(self.selectedTextDrawingRect), 36 | 0, 37 | CGRectGetWidth(self.frame) - CGRectGetMaxX(self.selectedTextDrawingRect), 38 | CGRectGetHeight(self.frame)); 39 | CGRect unselectedTextDrawingRects[2] = {beforeMaskFrame, afterMaskFrame}; 40 | CGContextClipToRects(context, unselectedTextDrawingRects, 2); 41 | } 42 | 43 | CGFloat unselectedTextRectHeight = [self.text sizeWithAttributes:self.unselectedTextAttributes].height; 44 | CGRect unselectedTextDrawingRect = CGRectMake(rect.origin.x, 45 | rect.origin.y + (rect.size.height - unselectedTextRectHeight) / 2.0f, 46 | rect.size.width, 47 | unselectedTextRectHeight); 48 | 49 | [self.text drawInRect:unselectedTextDrawingRect withAttributes:self.unselectedTextAttributes]; 50 | 51 | CGContextRestoreGState(context); 52 | 53 | if (!CGRectIsEmpty(self.selectedTextDrawingRect)) { 54 | CGFloat selectedTextRectHeight = [self.text sizeWithAttributes:self.selectedTextAttributes].height; 55 | CGRect selectedTextDrawingRect = CGRectMake(rect.origin.x, 56 | rect.origin.y + (rect.size.height - selectedTextRectHeight) / 2.0f, 57 | rect.size.width, 58 | selectedTextRectHeight); 59 | CGContextClipToRect(context, self.selectedTextDrawingRect); 60 | 61 | [self.text drawInRect:selectedTextDrawingRect withAttributes:self.selectedTextAttributes]; 62 | } 63 | } 64 | 65 | - (CGSize)sizeThatFits:(CGSize)size { 66 | return [self.text sizeWithAttributes:self.unselectedTextAttributes]; 67 | } 68 | 69 | #pragma mark - Private 70 | 71 | - (void)setupSelectedAttributes { 72 | NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; 73 | paragraphStyle.alignment = NSTextAlignmentCenter; 74 | 75 | NSDictionary *selectedAttributes = @{ NSFontAttributeName: self.selectedFont, 76 | NSForegroundColorAttributeName: self.selectedTextColor, 77 | NSParagraphStyleAttributeName: paragraphStyle }; 78 | 79 | self.selectedTextAttributes = selectedAttributes; 80 | } 81 | 82 | - (void)setupUnselectedAttributes { 83 | NSMutableParagraphStyle *paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; 84 | paragraphStyle.alignment = NSTextAlignmentCenter; 85 | 86 | NSDictionary *unselectedAttributes = @{ NSFontAttributeName: self.font, 87 | NSForegroundColorAttributeName: self.textColor, 88 | NSParagraphStyleAttributeName: paragraphStyle }; 89 | 90 | self.unselectedTextAttributes = unselectedAttributes; 91 | } 92 | 93 | #pragma mark - Setters 94 | 95 | - (void)setSelectedTextDrawingRect:(CGRect)selectedTextDrawingRect { 96 | _selectedTextDrawingRect = selectedTextDrawingRect; 97 | 98 | [self setNeedsDisplay]; 99 | } 100 | 101 | - (void)setMaskCornerRadius:(CGFloat)maskCornerRadius { 102 | _maskCornerRadius = maskCornerRadius; 103 | 104 | [self setNeedsDisplay]; 105 | } 106 | 107 | - (void)setTextColor:(UIColor *)textColor { 108 | _textColor = textColor; 109 | 110 | [self setupUnselectedAttributes]; 111 | } 112 | 113 | - (void)setSelectedTextColor:(UIColor *)selectedTextColor { 114 | _selectedTextColor = selectedTextColor; 115 | 116 | [self setupSelectedAttributes]; 117 | } 118 | 119 | - (void)setSelectedFont:(UIFont *)selectedFont { 120 | _selectedFont = selectedFont; 121 | 122 | [self setupSelectedAttributes]; 123 | } 124 | 125 | - (void)setFont:(UIFont *)font { 126 | _font = font; 127 | 128 | [self setupUnselectedAttributes]; 129 | } 130 | 131 | @end 132 | -------------------------------------------------------------------------------- /NYSegmentedControl/NYSegmentedControl.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | NS_ASSUME_NONNULL_BEGIN 4 | 5 | @class NYSegmentedControl; 6 | 7 | @protocol NYSegmentedControlDataSource 8 | 9 | - (NSUInteger)numberOfSegments:(NYSegmentedControl *)control; 10 | - (NSString *)segmentedControl:(NYSegmentedControl *)control titleForSegmentAtIndex:(NSUInteger)index; 11 | 12 | @end 13 | 14 | @interface NYSegmentedControl : UIControl 15 | 16 | /** 17 | The segmented control's data source object. 18 | */ 19 | @property (nonatomic, weak, nullable) IBOutlet id dataSource; 20 | 21 | /** 22 | If YES, selectedTitleFont and selectedTitleTextColor are used for the selected segment's title label. The default value is YES. 23 | */ 24 | @property (nonatomic) BOOL stylesTitleForSelectedSegment; 25 | 26 | /** 27 | The font used for the segment titles. 28 | */ 29 | @property (nonatomic) UIFont *titleFont UI_APPEARANCE_SELECTOR; 30 | 31 | /** 32 | The color of the segment titles. 33 | */ 34 | @property (nonatomic) UIColor *titleTextColor UI_APPEARANCE_SELECTOR; 35 | 36 | /** 37 | The font used for the selected segment's title. 38 | */ 39 | @property (nonatomic) UIFont *selectedTitleFont UI_APPEARANCE_SELECTOR; 40 | 41 | /** 42 | The color of the selected segment's title. 43 | */ 44 | @property (nonatomic) UIColor *selectedTitleTextColor UI_APPEARANCE_SELECTOR; 45 | 46 | /** 47 | The radius of the control's corners. 48 | */ 49 | @property (nonatomic) CGFloat cornerRadius UI_APPEARANCE_SELECTOR; 50 | 51 | /** 52 | The color of the control's border. 53 | */ 54 | @property (nonatomic) UIColor *borderColor UI_APPEARANCE_SELECTOR; 55 | 56 | /** 57 | The width of the control's border 58 | */ 59 | @property (nonatomic) CGFloat borderWidth UI_APPEARANCE_SELECTOR; 60 | 61 | /** 62 | If YES, the control's background will be drawn with a background gradient specified by the gradientTopColor and gradientBottomColor properties. The default value is YES. 63 | 64 | @see gradientTopColor 65 | @see gradientBottomColor 66 | */ 67 | @property (nonatomic) BOOL drawsGradientBackground; 68 | 69 | /** 70 | The start (top) color of the control's background gradient. 71 | 72 | @see drawsGradientBackground 73 | */ 74 | @property (nonatomic) UIColor *gradientTopColor UI_APPEARANCE_SELECTOR; 75 | 76 | /** 77 | The end (bottom) color of the control's background gradient. 78 | 79 | @see drawsGradientBackground 80 | */ 81 | @property (nonatomic) UIColor *gradientBottomColor UI_APPEARANCE_SELECTOR; 82 | 83 | /** 84 | The duration of the segment change animation. 85 | */ 86 | @property (nonatomic) CGFloat segmentIndicatorAnimationDuration UI_APPEARANCE_SELECTOR; 87 | 88 | /** 89 | The amount the selected segment indicator should be inset from the outer edge of the control. 90 | */ 91 | @property (nonatomic) CGFloat segmentIndicatorInset UI_APPEARANCE_SELECTOR; 92 | 93 | /** 94 | If YES, the selected segment indicator will be drawn with a background gradient specified by the selectedSegmentIndicatorGradientTopColor and selectedSegmentIndicatorGradientBottomColor properties. If set to NO, the indicator will be filled with the color specified by the selectedSegmentIndicatorBackgroundColor property. The default value is YES. 95 | 96 | @see segmentIndicatorGradientTopColor 97 | @see segmentIndicatorGradientBottomColor 98 | */ 99 | @property (nonatomic) BOOL drawsSegmentIndicatorGradientBackground; 100 | 101 | /** 102 | The color of the selected segment indicator. 103 | */ 104 | @property (nonatomic) UIColor *segmentIndicatorBackgroundColor UI_APPEARANCE_SELECTOR; 105 | 106 | /** 107 | The start color of the gradient filling the selected segment indicator. 108 | 109 | @see drawsSegmentIndicatorGradientBackground 110 | */ 111 | @property (nonatomic) UIColor *segmentIndicatorGradientTopColor UI_APPEARANCE_SELECTOR; 112 | 113 | /** 114 | The end color of the gradient filling the selected segment indicator. 115 | 116 | @see drawsSegmentIndicatorGradientBackground 117 | */ 118 | @property (nonatomic) UIColor *segmentIndicatorGradientBottomColor UI_APPEARANCE_SELECTOR; 119 | 120 | /** 121 | The color of the selected segment indicator's border. 122 | */ 123 | @property (nonatomic) UIColor *segmentIndicatorBorderColor UI_APPEARANCE_SELECTOR; 124 | 125 | /** 126 | The width of the selected segment indicator's border. 127 | */ 128 | @property (nonatomic) CGFloat segmentIndicatorBorderWidth UI_APPEARANCE_SELECTOR; 129 | 130 | /** 131 | The number of segments in the control (read-only). 132 | */ 133 | @property (nonatomic, readonly) NSUInteger numberOfSegments; 134 | 135 | /** 136 | The index of the currently selected segment. 137 | */ 138 | @property (nonatomic) NSUInteger selectedSegmentIndex; 139 | 140 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_7_0 141 | 142 | /** 143 | If set to YES, UIView spring animations will be used to animate the position of the segment indicator. 144 | */ 145 | @property (nonatomic) BOOL usesSpringAnimations UI_APPEARANCE_SELECTOR; 146 | 147 | /** 148 | The animation duration used if spring animations are enabled. 149 | 150 | @see usesSpringAnimations 151 | */ 152 | @property (nonatomic) CGFloat springAnimationDuration UI_APPEARANCE_SELECTOR; 153 | 154 | /** 155 | The damping ratio for the spring animation used if spring animations are enabled. 156 | 157 | @see usesSpringAnimations 158 | */ 159 | @property (nonatomic) CGFloat springAnimationDampingRatio UI_APPEARANCE_SELECTOR; 160 | 161 | /** 162 | The initial spring velocity used if spring animations are enabled. 163 | 164 | @see usesSpringAnimations 165 | */ 166 | @property (nonatomic) CGFloat springAnimationVelocity UI_APPEARANCE_SELECTOR; 167 | 168 | #endif 169 | 170 | /** 171 | Initializes and returns a control with segments having the specified titles. 172 | 173 | @param items An array of NSString objects representing the titles of segments in the control. 174 | 175 | @return An initialized NYSegmentedControl object, or nil if it could not be created. 176 | */ 177 | - (instancetype)initWithItems:(NSArray *)items; 178 | 179 | /** 180 | Inserts a segment at the specified index. 181 | 182 | @param title A string to use as the segment's title. 183 | @param index The index of the segment. It must be a number between 0 and the number of segments (numberOfSegments); values exceeding this upper range are pinned to it. 184 | */ 185 | - (void)insertSegmentWithTitle:(NSString *)title atIndex:(NSUInteger)index; 186 | 187 | /** 188 | Removes the segment at the specified index. 189 | 190 | @param index The index of the segment. It must be a number between 0 and the number of segments (numberOfSegments) minus 1; values exceeding this upper range are pinned to it. 191 | */ 192 | - (void)removeSegmentAtIndex:(NSUInteger)index; 193 | 194 | /** 195 | Removes all segments from the control. 196 | */ 197 | - (void)removeAllSegments; 198 | 199 | /** 200 | Sets the title for a segment in the control. 201 | 202 | @param title A string to display in the segment as its title. 203 | @param index The index of the segment. It must be a number between 0 and the number of segments (numberOfSegments) minus 1; values exceeding this upper range are pinned to it. 204 | */ 205 | - (void)setTitle:(NSString *)title forSegmentAtIndex:(NSUInteger)index; 206 | 207 | /** 208 | Gets the title of a segment in the control. 209 | 210 | @param index The index of the segment. It must be a number between 0 and the number of segments (numberOfSegments) minus 1; values exceeding this upper range are pinned to it. 211 | 212 | @return The title of the specified segment. 213 | */ 214 | - (NSString *)titleForSegmentAtIndex:(NSUInteger)index; 215 | 216 | /** 217 | Sets the selected segment index. 218 | 219 | @param selectedSegmentIndex The index of the segment to select. It must be a number between 0 and the number of segments (numberOfSegments) minus 1; values exceeding this upper range are pinned to it. 220 | @param animated Specify YES if the selected segment indicator should animate its position change. 221 | */ 222 | - (void)setSelectedSegmentIndex:(NSUInteger)selectedSegmentIndex animated:(BOOL)animated; 223 | 224 | /** 225 | Reloads the control's items from its data source, if defined. 226 | 227 | @see dataSource 228 | */ 229 | - (void)reloadData; 230 | 231 | @end 232 | 233 | NS_ASSUME_NONNULL_END 234 | -------------------------------------------------------------------------------- /NYSegmentedControl/NYSegmentedControl.m: -------------------------------------------------------------------------------- 1 | #import "NYSegmentedControl.h" 2 | #import "NYSegment.h" 3 | #import "NYSegmentIndicator.h" 4 | #import "NYSegmentTextRenderView.h" 5 | 6 | @interface NYSegmentedControl () 7 | 8 | @property (nonatomic) NSArray *segments; 9 | @property (nonatomic) NYSegmentIndicator *selectedSegmentIndicator; 10 | @property (nonatomic, getter=isAnimating) BOOL animating; 11 | 12 | - (void)moveSelectedSegmentIndicatorToSegmentAtIndex:(NSUInteger)index animated:(BOOL)animated; 13 | - (CGRect)indicatorFrameForSegment:(NYSegment *)segment; 14 | 15 | - (void)panGestureRecognized:(UIPanGestureRecognizer *)panGestureRecognizer; 16 | - (void)tapGestureRecognized:(UITapGestureRecognizer *)tapGestureRecognizer; 17 | 18 | @end 19 | 20 | @implementation NYSegmentedControl 21 | 22 | + (Class)layerClass { 23 | return [CAGradientLayer class]; 24 | } 25 | 26 | - (instancetype)initWithCoder:(NSCoder *)aDecoder { 27 | self = [super initWithCoder:aDecoder]; 28 | 29 | if (self) { 30 | [self initialize]; 31 | } 32 | 33 | return self; 34 | } 35 | 36 | - (instancetype)initWithFrame:(CGRect)frame { 37 | self = [super initWithFrame:frame]; 38 | 39 | if (self) { 40 | [self initialize]; 41 | } 42 | 43 | return self; 44 | } 45 | 46 | - (instancetype)initWithItems:(NSArray *)items { 47 | self = [self initWithFrame:CGRectZero]; 48 | 49 | if (self) { 50 | NSMutableArray *mutableSegments = [NSMutableArray array]; 51 | 52 | for (NSString *segmentTitle in items) { 53 | NYSegment *segment = [[NYSegment alloc] initWithTitle:segmentTitle]; 54 | segment.titleLabel.maskCornerRadius = self.cornerRadius; 55 | [self addSubview:segment]; 56 | [mutableSegments addObject:segment]; 57 | } 58 | 59 | self.segments = [NSArray arrayWithArray:mutableSegments]; 60 | self.selectedSegmentIndex = 0; 61 | } 62 | 63 | return self; 64 | } 65 | 66 | - (void)initialize { 67 | // We need to directly access the ivars for UIAppearance properties in the initializer 68 | _titleFont = [UIFont systemFontOfSize:13.0f]; 69 | _titleTextColor = [UIColor blackColor]; 70 | _selectedTitleFont = [UIFont boldSystemFontOfSize:13.0f]; 71 | _selectedTitleTextColor = [UIColor blackColor]; 72 | _stylesTitleForSelectedSegment = YES; 73 | _segmentIndicatorInset = 0.0f; 74 | _segmentIndicatorAnimationDuration = 0.15f; 75 | _gradientTopColor = [UIColor colorWithRed:0.21f green:0.21f blue:0.21f alpha:1.0f]; 76 | _gradientBottomColor = [UIColor colorWithRed:0.16f green:0.16f blue:0.16f alpha:1.0f]; 77 | _usesSpringAnimations = NO; 78 | _springAnimationDuration = 0.25f; 79 | _springAnimationDampingRatio = 0.7f; 80 | _springAnimationVelocity = 0.2f; 81 | 82 | self.layer.borderColor = [[UIColor lightGrayColor] CGColor]; 83 | self.layer.masksToBounds = YES; 84 | self.layer.cornerRadius = 4.0f; 85 | self.layer.borderWidth = 1.0f; 86 | 87 | self.drawsGradientBackground = NO; 88 | self.opaque = NO; 89 | 90 | self.selectedSegmentIndicator = [[NYSegmentIndicator alloc] initWithFrame:CGRectZero]; 91 | self.drawsSegmentIndicatorGradientBackground = YES; 92 | [self addSubview:self.selectedSegmentIndicator]; 93 | 94 | UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGestureRecognized:)]; 95 | panGestureRecognizer.maximumNumberOfTouches = 1; 96 | [self.selectedSegmentIndicator addGestureRecognizer:panGestureRecognizer]; 97 | 98 | UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestureRecognized:)]; 99 | tapGestureRecognizer.numberOfTapsRequired = 1; 100 | [self addGestureRecognizer:tapGestureRecognizer]; 101 | } 102 | 103 | - (void)reloadData { 104 | if (self.dataSource) { 105 | for (NYSegment *segment in self.segments) { 106 | [segment removeFromSuperview]; 107 | } 108 | self.segments = [self buildSegmentsFromDataSource]; 109 | } 110 | } 111 | 112 | - (NSArray *)buildSegmentsFromDataSource { 113 | if (self.dataSource) { 114 | NSUInteger numberOfSegments = [self.dataSource numberOfSegments:self]; 115 | NSMutableArray *segmentsArray = [NSMutableArray arrayWithCapacity:numberOfSegments]; 116 | 117 | for (int i = 0; i < numberOfSegments; i++) { 118 | NSString *title = [self.dataSource segmentedControl:self titleForSegmentAtIndex:i]; 119 | NYSegment *segment = [[NYSegment alloc] initWithTitle:title]; 120 | [self addSubview:segment]; 121 | [segmentsArray addObject:segment]; 122 | } 123 | 124 | return [segmentsArray copy]; 125 | } else { 126 | return nil; 127 | } 128 | } 129 | 130 | - (NSArray *)segments { 131 | if (!_segments) { 132 | _segments = [self buildSegmentsFromDataSource]; 133 | } 134 | 135 | return _segments; 136 | } 137 | 138 | - (CGSize)sizeThatFits:(CGSize)size { 139 | CGFloat maxSegmentWidth = 0.0f; 140 | 141 | for (NYSegment *segment in self.segments) { 142 | CGFloat segmentWidth = [segment sizeThatFits:size].width; 143 | if (segmentWidth > maxSegmentWidth) { 144 | maxSegmentWidth = segmentWidth; 145 | } 146 | } 147 | 148 | return CGSizeMake(maxSegmentWidth * self.segments.count, 32.0f); 149 | } 150 | 151 | - (CGSize)intrinsicContentSize { 152 | return [self sizeThatFits:self.bounds.size]; 153 | } 154 | 155 | - (void)layoutSubviews { 156 | [super layoutSubviews]; 157 | 158 | CGFloat segmentWidth = CGRectGetWidth(self.frame) / self.segments.count; 159 | CGFloat segmentHeight = CGRectGetHeight(self.frame); 160 | for (int i = 0; i < self.segments.count; i++) { 161 | NYSegment *segment = self.segments[i]; 162 | segment.frame = CGRectMake(segmentWidth * i, 0.0f, segmentWidth, segmentHeight); 163 | 164 | if (self.stylesTitleForSelectedSegment) { 165 | if (self.selectedSegmentIndex == i) { 166 | segment.titleLabel.selectedTextDrawingRect = segment.titleLabel.bounds; 167 | } 168 | 169 | segment.titleLabel.font = self.titleFont; 170 | segment.titleLabel.selectedFont = self.selectedTitleFont; 171 | 172 | segment.titleLabel.textColor = self.titleTextColor; 173 | segment.titleLabel.selectedTextColor = self.selectedTitleTextColor; 174 | } else { 175 | segment.titleLabel.font = self.titleFont; 176 | segment.titleLabel.textColor = self.titleTextColor; 177 | } 178 | } 179 | 180 | self.selectedSegmentIndicator.frame = [self indicatorFrameForSegment:self.segments[self.selectedSegmentIndex]]; 181 | } 182 | 183 | - (void)drawRect:(CGRect)rect { 184 | if (self.drawsGradientBackground) { 185 | CAGradientLayer *gradientLayer = (CAGradientLayer *)self.layer; 186 | gradientLayer.colors = @[(__bridge id)[self.gradientTopColor CGColor], 187 | (__bridge id)[self.gradientBottomColor CGColor]]; 188 | } else { 189 | self.layer.backgroundColor = [self.backgroundColor CGColor]; 190 | } 191 | } 192 | 193 | - (void)insertSegmentWithTitle:(NSString *)title atIndex:(NSUInteger)index { 194 | if (index >= self.segments.count) { 195 | index = self.segments.count; 196 | } 197 | 198 | NYSegment *newSegment = [[NYSegment alloc] initWithTitle:title]; 199 | newSegment.titleLabel.maskCornerRadius = self.cornerRadius; 200 | [self addSubview:newSegment]; 201 | 202 | NSMutableArray *mutableSegments = [NSMutableArray arrayWithArray:self.segments]; 203 | [mutableSegments insertObject:newSegment atIndex:index]; 204 | self.segments = [NSArray arrayWithArray:mutableSegments]; 205 | 206 | [self setNeedsLayout]; 207 | } 208 | 209 | - (void)removeSegmentAtIndex:(NSUInteger)index { 210 | if (index >= self.numberOfSegments) { 211 | index = self.numberOfSegments - 1; 212 | } 213 | 214 | NYSegment *segment = self.segments[index]; 215 | [segment removeFromSuperview]; 216 | 217 | NSMutableArray *mutableSegments = [NSMutableArray arrayWithArray:self.segments]; 218 | [mutableSegments removeObjectAtIndex:index]; 219 | self.segments = [NSArray arrayWithArray:mutableSegments]; 220 | 221 | [self setNeedsLayout]; 222 | } 223 | 224 | - (void)removeAllSegments { 225 | for (NYSegment *segment in self.segments) { 226 | [segment removeFromSuperview]; 227 | } 228 | 229 | self.segments = [NSArray array]; 230 | [self setNeedsLayout]; 231 | } 232 | 233 | - (void)setTitle:(NSString *)title forSegmentAtIndex:(NSUInteger)index { 234 | NYSegment *segment = self.segments[index]; 235 | segment.titleLabel.text = title; 236 | } 237 | 238 | - (NSString *)titleForSegmentAtIndex:(NSUInteger)index { 239 | NYSegment *segment = self.segments[index]; 240 | return segment.titleLabel.text; 241 | } 242 | 243 | - (void)moveSelectedSegmentIndicatorToSegmentAtIndex:(NSUInteger)index animated:(BOOL)animated { 244 | NYSegment *selectedSegment = self.segments[index]; 245 | 246 | // If we're moving the indicator back to the originally selected segment, don't change the segment's font style 247 | if (index != self.selectedSegmentIndex && self.stylesTitleForSelectedSegment) { 248 | NYSegment *previousSegment = self.segments[self.selectedSegmentIndex]; 249 | 250 | [UIView transitionWithView:previousSegment.titleLabel 251 | duration:self.segmentIndicatorAnimationDuration 252 | options:UIViewAnimationOptionTransitionCrossDissolve 253 | animations:^{ 254 | previousSegment.titleLabel.selectedTextDrawingRect = CGRectZero; 255 | } 256 | completion:nil]; 257 | 258 | [UIView transitionWithView:selectedSegment.titleLabel 259 | duration:self.segmentIndicatorAnimationDuration 260 | options:UIViewAnimationOptionTransitionCrossDissolve 261 | animations:^{ 262 | } 263 | completion:nil]; 264 | } 265 | 266 | if (animated) { 267 | void (^animationsBlock)(void) = ^{ 268 | self.selectedSegmentIndicator.frame = [self indicatorFrameForSegment:selectedSegment]; 269 | 270 | if (self.stylesTitleForSelectedSegment) { 271 | [self.segments enumerateObjectsUsingBlock:^(NYSegment *segment, NSUInteger index, BOOL *stop) { 272 | segment.titleLabel.selectedTextDrawingRect = CGRectZero; 273 | }]; 274 | 275 | selectedSegment.titleLabel.selectedTextDrawingRect = selectedSegment.titleLabel.bounds; 276 | } 277 | }; 278 | 279 | if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_6_1 || !self.usesSpringAnimations) { 280 | [UIView animateWithDuration:self.segmentIndicatorAnimationDuration 281 | animations:animationsBlock 282 | completion:nil]; 283 | } else { 284 | [UIView animateWithDuration:self.springAnimationDuration 285 | delay:0.f 286 | usingSpringWithDamping:self.springAnimationDampingRatio 287 | initialSpringVelocity:self.springAnimationVelocity 288 | options:kNilOptions 289 | animations:animationsBlock 290 | completion:nil]; 291 | } 292 | } else { 293 | self.selectedSegmentIndicator.frame = [self indicatorFrameForSegment:selectedSegment]; 294 | 295 | if (self.stylesTitleForSelectedSegment) { 296 | selectedSegment.titleLabel.selectedTextDrawingRect = selectedSegment.titleLabel.bounds; 297 | } 298 | } 299 | } 300 | 301 | #pragma mark - Touch Tracking 302 | 303 | - (void)panGestureRecognized:(UIPanGestureRecognizer *)panGestureRecognizer { 304 | CGPoint translation = [panGestureRecognizer translationInView:panGestureRecognizer.view.superview]; 305 | 306 | if (self.stylesTitleForSelectedSegment) { 307 | // Style the segment the center of the indicator is covering 308 | [self.segments enumerateObjectsUsingBlock:^(NYSegment *segment, NSUInteger index, BOOL *stop) { 309 | // We get the intersection of the the selected segment indicator's frame and the segment's frame, so the text render view can show text with the selected style in the area covered by the selected segment indicator 310 | CGRect intersectionRect = CGRectIntersection(segment.frame, self.selectedSegmentIndicator.frame); 311 | segment.titleLabel.selectedTextDrawingRect = [segment convertRect:intersectionRect fromView:segment.superview]; 312 | }]; 313 | } 314 | 315 | // Find the difference in horizontal position between the current and previous touches 316 | CGFloat xDiff = translation.x; 317 | 318 | // Check that the indicator doesn't exit the bounds of the control 319 | CGRect newSegmentIndicatorFrame = self.selectedSegmentIndicator.frame; 320 | newSegmentIndicatorFrame.origin.x += xDiff; 321 | 322 | CGFloat selectedSegmentIndicatorCenterX; 323 | 324 | if (CGRectContainsRect(CGRectInset(self.bounds, self.segmentIndicatorInset, 0), newSegmentIndicatorFrame)) { 325 | selectedSegmentIndicatorCenterX = self.selectedSegmentIndicator.center.x + xDiff; 326 | } else { 327 | if (self.selectedSegmentIndicator.center.x < CGRectGetMidX(self.bounds)) { 328 | selectedSegmentIndicatorCenterX = self.segmentIndicatorInset + CGRectGetMidX(self.selectedSegmentIndicator.bounds); 329 | } else { 330 | selectedSegmentIndicatorCenterX = CGRectGetMaxX(self.bounds) - CGRectGetMidX(self.selectedSegmentIndicator.bounds) - self.segmentIndicatorInset; 331 | } 332 | } 333 | 334 | self.selectedSegmentIndicator.center = CGPointMake(selectedSegmentIndicatorCenterX, self.selectedSegmentIndicator.center.y); 335 | 336 | [panGestureRecognizer setTranslation:CGPointMake(0, 0) inView:panGestureRecognizer.view.superview]; 337 | 338 | if (panGestureRecognizer.state == UIGestureRecognizerStateEnded) { 339 | [self.segments enumerateObjectsUsingBlock:^(NYSegment *segment, NSUInteger index, BOOL *stop) { 340 | if (CGRectContainsPoint(segment.frame, self.selectedSegmentIndicator.center)) { 341 | [self moveSelectedSegmentIndicatorToSegmentAtIndex:index animated:YES]; 342 | 343 | if (index != self.selectedSegmentIndex) { 344 | _selectedSegmentIndex = index; 345 | [self sendActionsForControlEvents:UIControlEventValueChanged]; 346 | } 347 | } 348 | }]; 349 | } 350 | } 351 | 352 | - (void)tapGestureRecognized:(UITapGestureRecognizer *)tapGestureRecognizer { 353 | CGPoint location = [tapGestureRecognizer locationInView:self]; 354 | [self.segments enumerateObjectsUsingBlock:^(NYSegment *segment, NSUInteger index, BOOL *stop) { 355 | if (CGRectContainsPoint(segment.frame, location)) { 356 | if (index != self.selectedSegmentIndex) { 357 | [self setSelectedSegmentIndex:index animated:YES]; 358 | [self sendActionsForControlEvents:UIControlEventValueChanged]; 359 | } 360 | } 361 | }]; 362 | } 363 | 364 | #pragma mark - Helpers 365 | 366 | - (CGRect)indicatorFrameForSegment:(NYSegment *)segment { 367 | CGRect indicatorFrameRect = CGRectMake(CGRectGetMinX(segment.frame) + self.segmentIndicatorInset, 368 | CGRectGetMinY(segment.frame) + self.segmentIndicatorInset, 369 | CGRectGetWidth(segment.frame) - (2.0f * self.segmentIndicatorInset), 370 | CGRectGetHeight(segment.frame) - (2.0f * self.segmentIndicatorInset)); 371 | 372 | return CGRectIntegral(indicatorFrameRect); 373 | } 374 | 375 | #pragma mark - Getters and Setters 376 | 377 | - (NSUInteger)numberOfSegments { 378 | return self.segments.count; 379 | } 380 | 381 | - (void)setBackgroundColor:(UIColor *)backgroundColor { 382 | self.layer.backgroundColor = [backgroundColor CGColor]; 383 | } 384 | 385 | - (UIColor *)backgroundColor { 386 | return [UIColor colorWithCGColor:self.layer.backgroundColor]; 387 | } 388 | 389 | - (void)setBorderColor:(UIColor *)borderColor { 390 | self.layer.borderColor = [borderColor CGColor]; 391 | } 392 | 393 | - (UIColor *)borderColor { 394 | return [UIColor colorWithCGColor:self.layer.borderColor]; 395 | } 396 | 397 | - (void)setBorderWidth:(CGFloat)borderWidth { 398 | self.layer.borderWidth = borderWidth; 399 | } 400 | 401 | - (CGFloat)borderWidth { 402 | return self.layer.borderWidth; 403 | } 404 | 405 | - (void)setCornerRadius:(CGFloat)cornerRadius { 406 | for (NYSegment *segment in self.segments) { 407 | segment.titleLabel.maskCornerRadius = cornerRadius; 408 | } 409 | 410 | self.layer.cornerRadius = cornerRadius; 411 | self.selectedSegmentIndicator.cornerRadius = cornerRadius * ((self.frame.size.height - self.segmentIndicatorInset * 2) / self.frame.size.height); 412 | [self setNeedsDisplay]; 413 | } 414 | 415 | - (CGFloat)cornerRadius { 416 | return self.layer.cornerRadius; 417 | } 418 | 419 | - (void)setDrawsSegmentIndicatorGradientBackground:(BOOL)drawsSegmentIndicatorGradientBackground { 420 | self.selectedSegmentIndicator.drawsGradientBackground = drawsSegmentIndicatorGradientBackground; 421 | } 422 | 423 | - (BOOL)drawsSegmentIndicatorGradientBackground { 424 | return self.selectedSegmentIndicator.drawsGradientBackground; 425 | } 426 | 427 | - (void)setBounds:(CGRect)bounds { 428 | [super setBounds:bounds]; 429 | self.selectedSegmentIndicator.cornerRadius = self.cornerRadius * ((self.frame.size.height - self.segmentIndicatorInset * 2) / self.frame.size.height); 430 | } 431 | 432 | - (void)setFrame:(CGRect)frame { 433 | [super setFrame:frame]; 434 | self.selectedSegmentIndicator.cornerRadius = self.cornerRadius * ((self.frame.size.height - self.segmentIndicatorInset * 2) / self.frame.size.height); 435 | } 436 | 437 | - (void)setSegmentIndicatorBackgroundColor:(UIColor *)segmentIndicatorBackgroundColor { 438 | self.selectedSegmentIndicator.backgroundColor = segmentIndicatorBackgroundColor; 439 | } 440 | 441 | - (UIColor *)segmentIndicatorBackgroundColor { 442 | return self.selectedSegmentIndicator.backgroundColor; 443 | } 444 | 445 | - (void)setSegmentIndicatorInset:(CGFloat)segmentIndicatorInset { 446 | _segmentIndicatorInset = segmentIndicatorInset; 447 | self.selectedSegmentIndicator.cornerRadius = self.cornerRadius * ((self.frame.size.height - self.segmentIndicatorInset * 2) / self.frame.size.height); 448 | [self setNeedsLayout]; 449 | } 450 | 451 | - (void)setSegmentIndicatorGradientTopColor:(UIColor *)segmentIndicatorGradientTopColor { 452 | self.selectedSegmentIndicator.gradientTopColor = segmentIndicatorGradientTopColor; 453 | } 454 | 455 | - (UIColor *)segmentIndicatorGradientTopColor { 456 | return self.selectedSegmentIndicator.gradientTopColor; 457 | } 458 | 459 | - (void)setSegmentIndicatorGradientBottomColor:(UIColor *)segmentIndicatorGradientBottomColor { 460 | self.selectedSegmentIndicator.gradientBottomColor = segmentIndicatorGradientBottomColor; 461 | } 462 | 463 | - (UIColor *)segmentIndicatorGradientBottomColor { 464 | return self.selectedSegmentIndicator.gradientBottomColor; 465 | } 466 | 467 | - (void)setSegmentIndicatorBorderColor:(UIColor *)segmentIndicatorBorderColor { 468 | self.selectedSegmentIndicator.borderColor = segmentIndicatorBorderColor; 469 | } 470 | 471 | - (UIColor *)segmentIndicatorBorderColor { 472 | return self.selectedSegmentIndicator.borderColor; 473 | } 474 | 475 | - (void)setSegmentIndicatorBorderWidth:(CGFloat)segmentIndicatorBorderWidth { 476 | self.selectedSegmentIndicator.borderWidth = segmentIndicatorBorderWidth; 477 | } 478 | 479 | - (CGFloat)segmentIndicatorBorderWidth { 480 | return self.selectedSegmentIndicator.borderWidth; 481 | } 482 | 483 | - (void)setTitleFont:(UIFont *)titleFont { 484 | _titleFont = titleFont; 485 | [self setNeedsLayout]; 486 | } 487 | 488 | - (void)setTitleTextColor:(UIColor *)titleTextColor { 489 | _titleTextColor = titleTextColor; 490 | [self setNeedsLayout]; 491 | } 492 | 493 | - (void)setSelectedTitleFont:(UIFont *)selectedTitleFont { 494 | _selectedTitleFont = selectedTitleFont; 495 | [self setNeedsLayout]; 496 | } 497 | 498 | - (void)setSelectedTitleTextColor:(UIColor *)selectedTitleTextColor { 499 | _selectedTitleTextColor = selectedTitleTextColor; 500 | [self setNeedsLayout]; 501 | } 502 | 503 | - (void)setSelectedSegmentIndex:(NSUInteger)selectedSegmentIndex { 504 | if (selectedSegmentIndex >= self.numberOfSegments) { 505 | selectedSegmentIndex = self.numberOfSegments - 1; 506 | } 507 | 508 | [self moveSelectedSegmentIndicatorToSegmentAtIndex:selectedSegmentIndex animated:NO]; 509 | 510 | self.segments[_selectedSegmentIndex].selected = NO; 511 | self.segments[selectedSegmentIndex].selected = YES; 512 | 513 | _selectedSegmentIndex = selectedSegmentIndex; 514 | } 515 | 516 | - (void)setSelectedSegmentIndex:(NSUInteger)selectedSegmentIndex animated:(BOOL)animated { 517 | if (selectedSegmentIndex >= self.numberOfSegments) { 518 | selectedSegmentIndex = self.numberOfSegments - 1; 519 | } 520 | 521 | [self moveSelectedSegmentIndicatorToSegmentAtIndex:selectedSegmentIndex animated:animated]; 522 | 523 | self.segments[_selectedSegmentIndex].selected = NO; 524 | self.segments[selectedSegmentIndex].selected = YES; 525 | 526 | _selectedSegmentIndex = selectedSegmentIndex; 527 | } 528 | 529 | @end 530 | -------------------------------------------------------------------------------- /NYSegmentedControlDemo/NYSegmentedControlDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0E0A20AE1AF19FC3003855D5 /* Launch Screen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0E0A20AD1AF19FC3003855D5 /* Launch Screen.xib */; }; 11 | 0E3CBE1118DECF82001EC237 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E3CBE1018DECF82001EC237 /* Foundation.framework */; }; 12 | 0E3CBE1518DECF82001EC237 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E3CBE1418DECF82001EC237 /* UIKit.framework */; }; 13 | 0E3CBE1B18DECF82001EC237 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0E3CBE1918DECF82001EC237 /* InfoPlist.strings */; }; 14 | 0E3CBE1D18DECF82001EC237 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CBE1C18DECF82001EC237 /* main.m */; }; 15 | 0E3CBE2918DECF82001EC237 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0E3CBE2818DECF82001EC237 /* Images.xcassets */; }; 16 | 0E3CBE3018DECF82001EC237 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E3CBE2F18DECF82001EC237 /* XCTest.framework */; }; 17 | 0E3CBE3118DECF82001EC237 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E3CBE1018DECF82001EC237 /* Foundation.framework */; }; 18 | 0E3CBE3218DECF82001EC237 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E3CBE1418DECF82001EC237 /* UIKit.framework */; }; 19 | 0E3CBE3A18DECF82001EC237 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0E3CBE3818DECF82001EC237 /* InfoPlist.strings */; }; 20 | 0E3CBE3C18DECF82001EC237 /* NYSegmentedControlDemoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CBE3B18DECF82001EC237 /* NYSegmentedControlDemoTests.m */; }; 21 | 0E3CBE4918DECFAC001EC237 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CBE4618DECFAC001EC237 /* AppDelegate.m */; }; 22 | 0E3CBE4A18DECFAC001EC237 /* DemoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E3CBE4818DECFAC001EC237 /* DemoViewController.m */; }; 23 | 659F21897B4838586BF193C7 /* NYSegment.m in Sources */ = {isa = PBXBuildFile; fileRef = 659F26883AD0F08EA44F6774 /* NYSegment.m */; }; 24 | 659F244CE6F6022E8146667F /* NYSegmentedControl.m in Sources */ = {isa = PBXBuildFile; fileRef = 659F2880C9CF8CACC71FACF6 /* NYSegmentedControl.m */; }; 25 | 659F2A3F8905B6E980263458 /* NYSegmentTextRenderView.m in Sources */ = {isa = PBXBuildFile; fileRef = 659F2E2AD1CACF14B488DE0F /* NYSegmentTextRenderView.m */; }; 26 | 659F2CAA7E8B80BD4171B737 /* NYSegmentIndicator.m in Sources */ = {isa = PBXBuildFile; fileRef = 659F28C9E7C2C16F3C90C066 /* NYSegmentIndicator.m */; }; 27 | /* End PBXBuildFile section */ 28 | 29 | /* Begin PBXContainerItemProxy section */ 30 | 0E3CBE3318DECF82001EC237 /* PBXContainerItemProxy */ = { 31 | isa = PBXContainerItemProxy; 32 | containerPortal = 0E3CBE0518DECF82001EC237 /* Project object */; 33 | proxyType = 1; 34 | remoteGlobalIDString = 0E3CBE0C18DECF82001EC237; 35 | remoteInfo = NYSegmentedControlDemo; 36 | }; 37 | /* End PBXContainerItemProxy section */ 38 | 39 | /* Begin PBXFileReference section */ 40 | 0E0A20AD1AF19FC3003855D5 /* Launch Screen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = "Launch Screen.xib"; sourceTree = ""; }; 41 | 0E3CBE0D18DECF82001EC237 /* NYSegmentedControlDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NYSegmentedControlDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | 0E3CBE1018DECF82001EC237 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 43 | 0E3CBE1218DECF82001EC237 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 44 | 0E3CBE1418DECF82001EC237 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 45 | 0E3CBE1818DECF82001EC237 /* NYSegmentedControlDemo-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "NYSegmentedControlDemo-Info.plist"; sourceTree = ""; }; 46 | 0E3CBE1A18DECF82001EC237 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 47 | 0E3CBE1C18DECF82001EC237 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 48 | 0E3CBE1E18DECF82001EC237 /* NYSegmentedControlDemo-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NYSegmentedControlDemo-Prefix.pch"; sourceTree = ""; }; 49 | 0E3CBE2818DECF82001EC237 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 50 | 0E3CBE2E18DECF82001EC237 /* NYSegmentedControlDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = NYSegmentedControlDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | 0E3CBE2F18DECF82001EC237 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 52 | 0E3CBE3718DECF82001EC237 /* NYSegmentedControlDemoTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "NYSegmentedControlDemoTests-Info.plist"; sourceTree = ""; }; 53 | 0E3CBE3918DECF82001EC237 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 54 | 0E3CBE3B18DECF82001EC237 /* NYSegmentedControlDemoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NYSegmentedControlDemoTests.m; sourceTree = ""; }; 55 | 0E3CBE4518DECFAC001EC237 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 56 | 0E3CBE4618DECFAC001EC237 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 57 | 0E3CBE4718DECFAC001EC237 /* DemoViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DemoViewController.h; sourceTree = ""; }; 58 | 0E3CBE4818DECFAC001EC237 /* DemoViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DemoViewController.m; sourceTree = ""; }; 59 | 659F243FA0BCD42CEFAAE8E2 /* NYSegmentedControl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NYSegmentedControl.h; sourceTree = ""; }; 60 | 659F255C114426B98D821354 /* NYSegmentTextRenderView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NYSegmentTextRenderView.h; sourceTree = ""; }; 61 | 659F26883AD0F08EA44F6774 /* NYSegment.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NYSegment.m; sourceTree = ""; }; 62 | 659F2880C9CF8CACC71FACF6 /* NYSegmentedControl.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NYSegmentedControl.m; sourceTree = ""; }; 63 | 659F28C9E7C2C16F3C90C066 /* NYSegmentIndicator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NYSegmentIndicator.m; sourceTree = ""; }; 64 | 659F29086DDA90B516DACA22 /* NYSegmentIndicator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NYSegmentIndicator.h; sourceTree = ""; }; 65 | 659F2E2AD1CACF14B488DE0F /* NYSegmentTextRenderView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NYSegmentTextRenderView.m; sourceTree = ""; }; 66 | 659F2E554A1A4D16DA1FA816 /* NYSegment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NYSegment.h; sourceTree = ""; }; 67 | /* End PBXFileReference section */ 68 | 69 | /* Begin PBXFrameworksBuildPhase section */ 70 | 0E3CBE0A18DECF82001EC237 /* Frameworks */ = { 71 | isa = PBXFrameworksBuildPhase; 72 | buildActionMask = 2147483647; 73 | files = ( 74 | 0E3CBE1518DECF82001EC237 /* UIKit.framework in Frameworks */, 75 | 0E3CBE1118DECF82001EC237 /* Foundation.framework in Frameworks */, 76 | ); 77 | runOnlyForDeploymentPostprocessing = 0; 78 | }; 79 | 0E3CBE2B18DECF82001EC237 /* Frameworks */ = { 80 | isa = PBXFrameworksBuildPhase; 81 | buildActionMask = 2147483647; 82 | files = ( 83 | 0E3CBE3018DECF82001EC237 /* XCTest.framework in Frameworks */, 84 | 0E3CBE3218DECF82001EC237 /* UIKit.framework in Frameworks */, 85 | 0E3CBE3118DECF82001EC237 /* Foundation.framework in Frameworks */, 86 | ); 87 | runOnlyForDeploymentPostprocessing = 0; 88 | }; 89 | /* End PBXFrameworksBuildPhase section */ 90 | 91 | /* Begin PBXGroup section */ 92 | 0E3CBE0418DECF82001EC237 = { 93 | isa = PBXGroup; 94 | children = ( 95 | 0E3CBE1618DECF82001EC237 /* NYSegmentedControlDemo */, 96 | 0E3CBE3518DECF82001EC237 /* NYSegmentedControlDemoTests */, 97 | 0E3CBE0F18DECF82001EC237 /* Frameworks */, 98 | 0E3CBE0E18DECF82001EC237 /* Products */, 99 | ); 100 | sourceTree = ""; 101 | }; 102 | 0E3CBE0E18DECF82001EC237 /* Products */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 0E3CBE0D18DECF82001EC237 /* NYSegmentedControlDemo.app */, 106 | 0E3CBE2E18DECF82001EC237 /* NYSegmentedControlDemoTests.xctest */, 107 | ); 108 | name = Products; 109 | sourceTree = ""; 110 | }; 111 | 0E3CBE0F18DECF82001EC237 /* Frameworks */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | 0E3CBE1018DECF82001EC237 /* Foundation.framework */, 115 | 0E3CBE1218DECF82001EC237 /* CoreGraphics.framework */, 116 | 0E3CBE1418DECF82001EC237 /* UIKit.framework */, 117 | 0E3CBE2F18DECF82001EC237 /* XCTest.framework */, 118 | ); 119 | name = Frameworks; 120 | sourceTree = ""; 121 | }; 122 | 0E3CBE1618DECF82001EC237 /* NYSegmentedControlDemo */ = { 123 | isa = PBXGroup; 124 | children = ( 125 | C53E593E1932DA7100087BCD /* NYSegmentedControl */, 126 | 0E3CBE4518DECFAC001EC237 /* AppDelegate.h */, 127 | 0E3CBE4618DECFAC001EC237 /* AppDelegate.m */, 128 | 0E3CBE4718DECFAC001EC237 /* DemoViewController.h */, 129 | 0E3CBE4818DECFAC001EC237 /* DemoViewController.m */, 130 | 0E3CBE2818DECF82001EC237 /* Images.xcassets */, 131 | 0E3CBE1718DECF82001EC237 /* Supporting Files */, 132 | ); 133 | path = NYSegmentedControlDemo; 134 | sourceTree = ""; 135 | }; 136 | 0E3CBE1718DECF82001EC237 /* Supporting Files */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | 0E0A20AD1AF19FC3003855D5 /* Launch Screen.xib */, 140 | 0E3CBE1818DECF82001EC237 /* NYSegmentedControlDemo-Info.plist */, 141 | 0E3CBE1918DECF82001EC237 /* InfoPlist.strings */, 142 | 0E3CBE1C18DECF82001EC237 /* main.m */, 143 | 0E3CBE1E18DECF82001EC237 /* NYSegmentedControlDemo-Prefix.pch */, 144 | ); 145 | name = "Supporting Files"; 146 | sourceTree = ""; 147 | }; 148 | 0E3CBE3518DECF82001EC237 /* NYSegmentedControlDemoTests */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | 0E3CBE3B18DECF82001EC237 /* NYSegmentedControlDemoTests.m */, 152 | 0E3CBE3618DECF82001EC237 /* Supporting Files */, 153 | ); 154 | path = NYSegmentedControlDemoTests; 155 | sourceTree = ""; 156 | }; 157 | 0E3CBE3618DECF82001EC237 /* Supporting Files */ = { 158 | isa = PBXGroup; 159 | children = ( 160 | 0E3CBE3718DECF82001EC237 /* NYSegmentedControlDemoTests-Info.plist */, 161 | 0E3CBE3818DECF82001EC237 /* InfoPlist.strings */, 162 | ); 163 | name = "Supporting Files"; 164 | sourceTree = ""; 165 | }; 166 | C53E593E1932DA7100087BCD /* NYSegmentedControl */ = { 167 | isa = PBXGroup; 168 | children = ( 169 | 659F243FA0BCD42CEFAAE8E2 /* NYSegmentedControl.h */, 170 | 659F2880C9CF8CACC71FACF6 /* NYSegmentedControl.m */, 171 | 659F29086DDA90B516DACA22 /* NYSegmentIndicator.h */, 172 | 659F28C9E7C2C16F3C90C066 /* NYSegmentIndicator.m */, 173 | 659F255C114426B98D821354 /* NYSegmentTextRenderView.h */, 174 | 659F2E2AD1CACF14B488DE0F /* NYSegmentTextRenderView.m */, 175 | 659F2E554A1A4D16DA1FA816 /* NYSegment.h */, 176 | 659F26883AD0F08EA44F6774 /* NYSegment.m */, 177 | ); 178 | name = NYSegmentedControl; 179 | path = ../../NYSegmentedControl; 180 | sourceTree = ""; 181 | }; 182 | /* End PBXGroup section */ 183 | 184 | /* Begin PBXNativeTarget section */ 185 | 0E3CBE0C18DECF82001EC237 /* NYSegmentedControlDemo */ = { 186 | isa = PBXNativeTarget; 187 | buildConfigurationList = 0E3CBE3F18DECF82001EC237 /* Build configuration list for PBXNativeTarget "NYSegmentedControlDemo" */; 188 | buildPhases = ( 189 | 0E3CBE0918DECF82001EC237 /* Sources */, 190 | 0E3CBE0A18DECF82001EC237 /* Frameworks */, 191 | 0E3CBE0B18DECF82001EC237 /* Resources */, 192 | ); 193 | buildRules = ( 194 | ); 195 | dependencies = ( 196 | ); 197 | name = NYSegmentedControlDemo; 198 | productName = NYSegmentedControlDemo; 199 | productReference = 0E3CBE0D18DECF82001EC237 /* NYSegmentedControlDemo.app */; 200 | productType = "com.apple.product-type.application"; 201 | }; 202 | 0E3CBE2D18DECF82001EC237 /* NYSegmentedControlDemoTests */ = { 203 | isa = PBXNativeTarget; 204 | buildConfigurationList = 0E3CBE4218DECF82001EC237 /* Build configuration list for PBXNativeTarget "NYSegmentedControlDemoTests" */; 205 | buildPhases = ( 206 | 0E3CBE2A18DECF82001EC237 /* Sources */, 207 | 0E3CBE2B18DECF82001EC237 /* Frameworks */, 208 | 0E3CBE2C18DECF82001EC237 /* Resources */, 209 | ); 210 | buildRules = ( 211 | ); 212 | dependencies = ( 213 | 0E3CBE3418DECF82001EC237 /* PBXTargetDependency */, 214 | ); 215 | name = NYSegmentedControlDemoTests; 216 | productName = NYSegmentedControlDemoTests; 217 | productReference = 0E3CBE2E18DECF82001EC237 /* NYSegmentedControlDemoTests.xctest */; 218 | productType = "com.apple.product-type.bundle.unit-test"; 219 | }; 220 | /* End PBXNativeTarget section */ 221 | 222 | /* Begin PBXProject section */ 223 | 0E3CBE0518DECF82001EC237 /* Project object */ = { 224 | isa = PBXProject; 225 | attributes = { 226 | CLASSPREFIX = NY; 227 | LastUpgradeCheck = 0510; 228 | ORGANIZATIONNAME = "Neal Young"; 229 | TargetAttributes = { 230 | 0E3CBE2D18DECF82001EC237 = { 231 | TestTargetID = 0E3CBE0C18DECF82001EC237; 232 | }; 233 | }; 234 | }; 235 | buildConfigurationList = 0E3CBE0818DECF82001EC237 /* Build configuration list for PBXProject "NYSegmentedControlDemo" */; 236 | compatibilityVersion = "Xcode 3.2"; 237 | developmentRegion = English; 238 | hasScannedForEncodings = 0; 239 | knownRegions = ( 240 | en, 241 | Base, 242 | ); 243 | mainGroup = 0E3CBE0418DECF82001EC237; 244 | productRefGroup = 0E3CBE0E18DECF82001EC237 /* Products */; 245 | projectDirPath = ""; 246 | projectRoot = ""; 247 | targets = ( 248 | 0E3CBE0C18DECF82001EC237 /* NYSegmentedControlDemo */, 249 | 0E3CBE2D18DECF82001EC237 /* NYSegmentedControlDemoTests */, 250 | ); 251 | }; 252 | /* End PBXProject section */ 253 | 254 | /* Begin PBXResourcesBuildPhase section */ 255 | 0E3CBE0B18DECF82001EC237 /* Resources */ = { 256 | isa = PBXResourcesBuildPhase; 257 | buildActionMask = 2147483647; 258 | files = ( 259 | 0E3CBE2918DECF82001EC237 /* Images.xcassets in Resources */, 260 | 0E3CBE1B18DECF82001EC237 /* InfoPlist.strings in Resources */, 261 | 0E0A20AE1AF19FC3003855D5 /* Launch Screen.xib in Resources */, 262 | ); 263 | runOnlyForDeploymentPostprocessing = 0; 264 | }; 265 | 0E3CBE2C18DECF82001EC237 /* Resources */ = { 266 | isa = PBXResourcesBuildPhase; 267 | buildActionMask = 2147483647; 268 | files = ( 269 | 0E3CBE3A18DECF82001EC237 /* InfoPlist.strings in Resources */, 270 | ); 271 | runOnlyForDeploymentPostprocessing = 0; 272 | }; 273 | /* End PBXResourcesBuildPhase section */ 274 | 275 | /* Begin PBXSourcesBuildPhase section */ 276 | 0E3CBE0918DECF82001EC237 /* Sources */ = { 277 | isa = PBXSourcesBuildPhase; 278 | buildActionMask = 2147483647; 279 | files = ( 280 | 0E3CBE4A18DECFAC001EC237 /* DemoViewController.m in Sources */, 281 | 0E3CBE4918DECFAC001EC237 /* AppDelegate.m in Sources */, 282 | 0E3CBE1D18DECF82001EC237 /* main.m in Sources */, 283 | 659F2CAA7E8B80BD4171B737 /* NYSegmentIndicator.m in Sources */, 284 | 659F2A3F8905B6E980263458 /* NYSegmentTextRenderView.m in Sources */, 285 | 659F21897B4838586BF193C7 /* NYSegment.m in Sources */, 286 | 659F244CE6F6022E8146667F /* NYSegmentedControl.m in Sources */, 287 | ); 288 | runOnlyForDeploymentPostprocessing = 0; 289 | }; 290 | 0E3CBE2A18DECF82001EC237 /* Sources */ = { 291 | isa = PBXSourcesBuildPhase; 292 | buildActionMask = 2147483647; 293 | files = ( 294 | 0E3CBE3C18DECF82001EC237 /* NYSegmentedControlDemoTests.m in Sources */, 295 | ); 296 | runOnlyForDeploymentPostprocessing = 0; 297 | }; 298 | /* End PBXSourcesBuildPhase section */ 299 | 300 | /* Begin PBXTargetDependency section */ 301 | 0E3CBE3418DECF82001EC237 /* PBXTargetDependency */ = { 302 | isa = PBXTargetDependency; 303 | target = 0E3CBE0C18DECF82001EC237 /* NYSegmentedControlDemo */; 304 | targetProxy = 0E3CBE3318DECF82001EC237 /* PBXContainerItemProxy */; 305 | }; 306 | /* End PBXTargetDependency section */ 307 | 308 | /* Begin PBXVariantGroup section */ 309 | 0E3CBE1918DECF82001EC237 /* InfoPlist.strings */ = { 310 | isa = PBXVariantGroup; 311 | children = ( 312 | 0E3CBE1A18DECF82001EC237 /* en */, 313 | ); 314 | name = InfoPlist.strings; 315 | sourceTree = ""; 316 | }; 317 | 0E3CBE3818DECF82001EC237 /* InfoPlist.strings */ = { 318 | isa = PBXVariantGroup; 319 | children = ( 320 | 0E3CBE3918DECF82001EC237 /* en */, 321 | ); 322 | name = InfoPlist.strings; 323 | sourceTree = ""; 324 | }; 325 | /* End PBXVariantGroup section */ 326 | 327 | /* Begin XCBuildConfiguration section */ 328 | 0E3CBE3D18DECF82001EC237 /* Debug */ = { 329 | isa = XCBuildConfiguration; 330 | buildSettings = { 331 | ALWAYS_SEARCH_USER_PATHS = NO; 332 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 333 | CLANG_CXX_LIBRARY = "libc++"; 334 | CLANG_ENABLE_MODULES = YES; 335 | CLANG_ENABLE_OBJC_ARC = YES; 336 | CLANG_WARN_BOOL_CONVERSION = YES; 337 | CLANG_WARN_CONSTANT_CONVERSION = YES; 338 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 339 | CLANG_WARN_EMPTY_BODY = YES; 340 | CLANG_WARN_ENUM_CONVERSION = YES; 341 | CLANG_WARN_INT_CONVERSION = YES; 342 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 343 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 344 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 345 | COPY_PHASE_STRIP = NO; 346 | GCC_C_LANGUAGE_STANDARD = gnu99; 347 | GCC_DYNAMIC_NO_PIC = NO; 348 | GCC_OPTIMIZATION_LEVEL = 0; 349 | GCC_PREPROCESSOR_DEFINITIONS = ( 350 | "DEBUG=1", 351 | "$(inherited)", 352 | ); 353 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 354 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 355 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 356 | GCC_WARN_UNDECLARED_SELECTOR = YES; 357 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 358 | GCC_WARN_UNUSED_FUNCTION = YES; 359 | GCC_WARN_UNUSED_VARIABLE = YES; 360 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 361 | ONLY_ACTIVE_ARCH = YES; 362 | SDKROOT = iphoneos; 363 | }; 364 | name = Debug; 365 | }; 366 | 0E3CBE3E18DECF82001EC237 /* Release */ = { 367 | isa = XCBuildConfiguration; 368 | buildSettings = { 369 | ALWAYS_SEARCH_USER_PATHS = NO; 370 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 371 | CLANG_CXX_LIBRARY = "libc++"; 372 | CLANG_ENABLE_MODULES = YES; 373 | CLANG_ENABLE_OBJC_ARC = YES; 374 | CLANG_WARN_BOOL_CONVERSION = YES; 375 | CLANG_WARN_CONSTANT_CONVERSION = YES; 376 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 377 | CLANG_WARN_EMPTY_BODY = YES; 378 | CLANG_WARN_ENUM_CONVERSION = YES; 379 | CLANG_WARN_INT_CONVERSION = YES; 380 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 381 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 382 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 383 | COPY_PHASE_STRIP = YES; 384 | ENABLE_NS_ASSERTIONS = NO; 385 | GCC_C_LANGUAGE_STANDARD = gnu99; 386 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 387 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 388 | GCC_WARN_UNDECLARED_SELECTOR = YES; 389 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 390 | GCC_WARN_UNUSED_FUNCTION = YES; 391 | GCC_WARN_UNUSED_VARIABLE = YES; 392 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 393 | SDKROOT = iphoneos; 394 | VALIDATE_PRODUCT = YES; 395 | }; 396 | name = Release; 397 | }; 398 | 0E3CBE4018DECF82001EC237 /* Debug */ = { 399 | isa = XCBuildConfiguration; 400 | buildSettings = { 401 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 402 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 403 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 404 | GCC_PREFIX_HEADER = "NYSegmentedControlDemo/NYSegmentedControlDemo-Prefix.pch"; 405 | INFOPLIST_FILE = "NYSegmentedControlDemo/NYSegmentedControlDemo-Info.plist"; 406 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 407 | PRODUCT_NAME = "$(TARGET_NAME)"; 408 | WRAPPER_EXTENSION = app; 409 | }; 410 | name = Debug; 411 | }; 412 | 0E3CBE4118DECF82001EC237 /* Release */ = { 413 | isa = XCBuildConfiguration; 414 | buildSettings = { 415 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 416 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; 417 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 418 | GCC_PREFIX_HEADER = "NYSegmentedControlDemo/NYSegmentedControlDemo-Prefix.pch"; 419 | INFOPLIST_FILE = "NYSegmentedControlDemo/NYSegmentedControlDemo-Info.plist"; 420 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 421 | PRODUCT_NAME = "$(TARGET_NAME)"; 422 | WRAPPER_EXTENSION = app; 423 | }; 424 | name = Release; 425 | }; 426 | 0E3CBE4318DECF82001EC237 /* Debug */ = { 427 | isa = XCBuildConfiguration; 428 | buildSettings = { 429 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/NYSegmentedControlDemo.app/NYSegmentedControlDemo"; 430 | FRAMEWORK_SEARCH_PATHS = ( 431 | "$(SDKROOT)/Developer/Library/Frameworks", 432 | "$(inherited)", 433 | "$(DEVELOPER_FRAMEWORKS_DIR)", 434 | ); 435 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 436 | GCC_PREFIX_HEADER = "NYSegmentedControlDemo/NYSegmentedControlDemo-Prefix.pch"; 437 | GCC_PREPROCESSOR_DEFINITIONS = ( 438 | "DEBUG=1", 439 | "$(inherited)", 440 | ); 441 | INFOPLIST_FILE = "NYSegmentedControlDemoTests/NYSegmentedControlDemoTests-Info.plist"; 442 | PRODUCT_NAME = "$(TARGET_NAME)"; 443 | TEST_HOST = "$(BUNDLE_LOADER)"; 444 | WRAPPER_EXTENSION = xctest; 445 | }; 446 | name = Debug; 447 | }; 448 | 0E3CBE4418DECF82001EC237 /* Release */ = { 449 | isa = XCBuildConfiguration; 450 | buildSettings = { 451 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/NYSegmentedControlDemo.app/NYSegmentedControlDemo"; 452 | FRAMEWORK_SEARCH_PATHS = ( 453 | "$(SDKROOT)/Developer/Library/Frameworks", 454 | "$(inherited)", 455 | "$(DEVELOPER_FRAMEWORKS_DIR)", 456 | ); 457 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 458 | GCC_PREFIX_HEADER = "NYSegmentedControlDemo/NYSegmentedControlDemo-Prefix.pch"; 459 | INFOPLIST_FILE = "NYSegmentedControlDemoTests/NYSegmentedControlDemoTests-Info.plist"; 460 | PRODUCT_NAME = "$(TARGET_NAME)"; 461 | TEST_HOST = "$(BUNDLE_LOADER)"; 462 | WRAPPER_EXTENSION = xctest; 463 | }; 464 | name = Release; 465 | }; 466 | /* End XCBuildConfiguration section */ 467 | 468 | /* Begin XCConfigurationList section */ 469 | 0E3CBE0818DECF82001EC237 /* Build configuration list for PBXProject "NYSegmentedControlDemo" */ = { 470 | isa = XCConfigurationList; 471 | buildConfigurations = ( 472 | 0E3CBE3D18DECF82001EC237 /* Debug */, 473 | 0E3CBE3E18DECF82001EC237 /* Release */, 474 | ); 475 | defaultConfigurationIsVisible = 0; 476 | defaultConfigurationName = Release; 477 | }; 478 | 0E3CBE3F18DECF82001EC237 /* Build configuration list for PBXNativeTarget "NYSegmentedControlDemo" */ = { 479 | isa = XCConfigurationList; 480 | buildConfigurations = ( 481 | 0E3CBE4018DECF82001EC237 /* Debug */, 482 | 0E3CBE4118DECF82001EC237 /* Release */, 483 | ); 484 | defaultConfigurationIsVisible = 0; 485 | defaultConfigurationName = Release; 486 | }; 487 | 0E3CBE4218DECF82001EC237 /* Build configuration list for PBXNativeTarget "NYSegmentedControlDemoTests" */ = { 488 | isa = XCConfigurationList; 489 | buildConfigurations = ( 490 | 0E3CBE4318DECF82001EC237 /* Debug */, 491 | 0E3CBE4418DECF82001EC237 /* Release */, 492 | ); 493 | defaultConfigurationIsVisible = 0; 494 | defaultConfigurationName = Release; 495 | }; 496 | /* End XCConfigurationList section */ 497 | }; 498 | rootObject = 0E3CBE0518DECF82001EC237 /* Project object */; 499 | } 500 | -------------------------------------------------------------------------------- /NYSegmentedControlDemo/NYSegmentedControlDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /NYSegmentedControlDemo/NYSegmentedControlDemo/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // NYSegmentedControlDemo 4 | // 5 | // Created by Nealon Young on 3/22/14. 6 | // Copyright (c) 2014 Neal Young. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /NYSegmentedControlDemo/NYSegmentedControlDemo/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // NYSegmentedControlDemo 4 | // 5 | // Created by Nealon Young on 3/22/14. 6 | // Copyright (c) 2014 Neal Young. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "DemoViewController.h" 11 | #import "NYSegmentedControl.h" 12 | 13 | @implementation AppDelegate 14 | 15 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 16 | DemoViewController *viewController = [[DemoViewController alloc] initWithNibName:nil bundle:nil]; 17 | 18 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; 19 | self.window.rootViewController = viewController; 20 | [self.window makeKeyAndVisible]; 21 | 22 | return YES; 23 | } 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /NYSegmentedControlDemo/NYSegmentedControlDemo/DemoViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // DemoViewController.h 3 | // NYSegmentedControlDemo 4 | // 5 | // Created by Nealon Young on 3/22/14. 6 | // Copyright (c) 2014 Neal Young. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface DemoViewController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /NYSegmentedControlDemo/NYSegmentedControlDemo/DemoViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // DemoViewController.m 3 | // NYSegmentedControlDemo 4 | // 5 | // Created by Nealon Young on 3/22/14. 6 | // Copyright (c) 2014 Neal Young. All rights reserved. 7 | // 8 | 9 | #import "DemoViewController.h" 10 | #import "NYSegmentedControl.h" 11 | 12 | @interface DemoViewController () 13 | 14 | @property (nonatomic, strong) UIStackView *stackView; 15 | 16 | @end 17 | 18 | @implementation DemoViewController 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | 23 | self.stackView = [[UIStackView alloc] init]; 24 | self.stackView.axis = UILayoutConstraintAxisVertical; 25 | self.stackView.distribution = UIStackViewDistributionFillEqually; 26 | [self.view addSubview:self.stackView]; 27 | self.stackView.translatesAutoresizingMaskIntoConstraints = NO; 28 | [self.stackView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor].active = YES; 29 | [self.stackView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor].active = YES; 30 | [self.stackView.topAnchor constraintEqualToAnchor:self.view.topAnchor].active = YES; 31 | [self.stackView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor].active = YES; 32 | 33 | [self addSegmentedControlExample:[self graySegmentedControl] withBackgroundColor:[UIColor colorWithWhite:0.96f alpha:1.0f]]; 34 | [self addSegmentedControlExample:[self blueSegmentedControl] withBackgroundColor:[UIColor colorWithRed:0.36f green:0.64f blue:0.88f alpha:1.0f]]; 35 | [self addSegmentedControlExample:[self flatGraySegmentedControl] withBackgroundColor:[UIColor colorWithRed:0.12f green:0.12f blue:0.15f alpha:1.0f]]; 36 | [self addSegmentedControlExample:[self purpleSegmentedControl] withBackgroundColor:[UIColor colorWithWhite:0.24f alpha:1.0f]]; 37 | [self addSegmentedControlExample:[self switchSegmentedControl] withBackgroundColor:[UIColor colorWithWhite:0.18f alpha:1.0f]]; 38 | } 39 | 40 | - (void)addSegmentedControlExample:(NYSegmentedControl *)segmentedControl withBackgroundColor:(UIColor *)backgroundColor { 41 | UIView *backgroundView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, CGRectGetWidth([UIScreen mainScreen].bounds), 44.0f)]; 42 | backgroundView.backgroundColor = backgroundColor; 43 | [self.stackView addArrangedSubview:backgroundView]; 44 | 45 | [backgroundView addSubview:segmentedControl]; 46 | segmentedControl.translatesAutoresizingMaskIntoConstraints = NO; 47 | [segmentedControl.centerXAnchor constraintEqualToAnchor:backgroundView.centerXAnchor].active = YES; 48 | [segmentedControl.centerYAnchor constraintEqualToAnchor:backgroundView.centerYAnchor].active = YES; 49 | } 50 | 51 | - (NYSegmentedControl *)graySegmentedControl { 52 | NYSegmentedControl *graySegmentedControl = [[NYSegmentedControl alloc] initWithItems:@[@"Popular", @"Following", @"News"]]; 53 | graySegmentedControl.backgroundColor = [UIColor colorWithWhite:0.9f alpha:1.0f]; 54 | graySegmentedControl.segmentIndicatorBackgroundColor = [UIColor whiteColor]; 55 | graySegmentedControl.segmentIndicatorInset = 0.0f; 56 | graySegmentedControl.titleTextColor = [UIColor lightGrayColor]; 57 | graySegmentedControl.selectedTitleTextColor = [UIColor darkGrayColor]; 58 | 59 | return graySegmentedControl; 60 | } 61 | 62 | - (NYSegmentedControl *)blueSegmentedControl { 63 | NYSegmentedControl *blueSegmentedControl = [[NYSegmentedControl alloc] initWithItems:@[@"Nearby", @"Worldwide"]]; 64 | blueSegmentedControl.titleTextColor = [UIColor colorWithRed:0.38f green:0.68f blue:0.93f alpha:1.0f]; 65 | blueSegmentedControl.selectedTitleTextColor = [UIColor whiteColor]; 66 | blueSegmentedControl.segmentIndicatorBackgroundColor = [UIColor colorWithRed:0.38f green:0.68f blue:0.93f alpha:1.0f]; 67 | blueSegmentedControl.backgroundColor = [UIColor colorWithRed:0.31f green:0.53f blue:0.72f alpha:1.0f]; 68 | blueSegmentedControl.borderWidth = 0.0f; 69 | blueSegmentedControl.segmentIndicatorBorderWidth = 0.0f; 70 | blueSegmentedControl.segmentIndicatorInset = 2.0f; 71 | blueSegmentedControl.segmentIndicatorBorderColor = self.view.backgroundColor; 72 | blueSegmentedControl.cornerRadius = blueSegmentedControl.intrinsicContentSize.height / 2.0f; 73 | blueSegmentedControl.usesSpringAnimations = YES; 74 | 75 | return blueSegmentedControl; 76 | } 77 | 78 | - (NYSegmentedControl *)flatGraySegmentedControl { 79 | NYSegmentedControl *flatGraySegmentedControl = [[NYSegmentedControl alloc] initWithItems:@[@"ENGLISH", @"中文文学"]]; 80 | flatGraySegmentedControl.backgroundColor = [UIColor colorWithRed:0.09f green:0.09f blue:0.12f alpha:1.0f]; 81 | flatGraySegmentedControl.selectedTitleFont = [UIFont systemFontOfSize:12.0f weight:UIFontWeightSemibold]; 82 | flatGraySegmentedControl.titleFont = [UIFont systemFontOfSize:12.0f weight:UIFontWeightSemibold]; 83 | flatGraySegmentedControl.borderColor = [UIColor colorWithRed:0.18f green:0.18f blue:0.22f alpha:1.0f]; 84 | flatGraySegmentedControl.borderWidth = 2.0f; 85 | flatGraySegmentedControl.segmentIndicatorBorderColor = [UIColor clearColor]; 86 | flatGraySegmentedControl.segmentIndicatorBackgroundColor = [UIColor colorWithRed:0.18f green:0.18f blue:0.22f alpha:1.0f]; 87 | flatGraySegmentedControl.segmentIndicatorInset = 5.0f; 88 | flatGraySegmentedControl.titleTextColor = [UIColor colorWithRed:0.30f green:0.31f blue:0.36f alpha:1.0f]; 89 | flatGraySegmentedControl.selectedTitleTextColor = [UIColor whiteColor]; 90 | flatGraySegmentedControl.cornerRadius = 22.0f; 91 | [flatGraySegmentedControl.widthAnchor constraintEqualToConstant:240.0f].active = YES; 92 | [flatGraySegmentedControl.heightAnchor constraintEqualToConstant:44.0f].active = YES; 93 | 94 | return flatGraySegmentedControl; 95 | } 96 | 97 | - (NYSegmentedControl *)purpleSegmentedControl { 98 | NYSegmentedControl *purpleSegmentedControl = [[NYSegmentedControl alloc] initWithItems:@[@"Lists", @"Followers"]]; 99 | purpleSegmentedControl.borderWidth = 2.0f; 100 | purpleSegmentedControl.borderColor = [UIColor colorWithWhite:0.15f alpha:1.0f]; 101 | purpleSegmentedControl.titleFont = [UIFont fontWithName:@"AvenirNext-Medium" size:14.0f]; 102 | purpleSegmentedControl.titleTextColor = [UIColor colorWithWhite:0.34f alpha:1.0f]; 103 | purpleSegmentedControl.selectedTitleFont = [UIFont fontWithName:@"AvenirNext-DemiBold" size:14.0f]; 104 | purpleSegmentedControl.selectedTitleTextColor = [UIColor colorWithWhite:0.9f alpha:1.0f]; 105 | purpleSegmentedControl.borderWidth = 0.0f; 106 | purpleSegmentedControl.drawsGradientBackground = YES; 107 | purpleSegmentedControl.gradientTopColor = [UIColor colorWithWhite:0.17f alpha:1.0f]; 108 | purpleSegmentedControl.gradientBottomColor = [UIColor colorWithWhite:0.05f alpha:1.0f]; 109 | purpleSegmentedControl.segmentIndicatorInset = 4.0f; 110 | purpleSegmentedControl.segmentIndicatorBackgroundColor = [UIColor clearColor]; 111 | purpleSegmentedControl.segmentIndicatorBorderWidth = 0.0f; 112 | purpleSegmentedControl.drawsSegmentIndicatorGradientBackground = YES; 113 | purpleSegmentedControl.segmentIndicatorGradientTopColor = [UIColor colorWithRed:0.65f green:0.25f blue:0.95f alpha:1.0f]; 114 | purpleSegmentedControl.segmentIndicatorGradientBottomColor = [UIColor colorWithRed:0.4f green:0.15f blue:0.8f alpha:1.0f]; 115 | [purpleSegmentedControl.widthAnchor constraintEqualToConstant:200.0f].active = YES; 116 | [purpleSegmentedControl.heightAnchor constraintEqualToConstant:40.0f].active = YES; 117 | 118 | return purpleSegmentedControl; 119 | } 120 | 121 | - (NYSegmentedControl *)switchSegmentedControl { 122 | NYSegmentedControl *switchSegmentedControl = [[NYSegmentedControl alloc] initWithItems:@[@"On", @"Off"]]; 123 | switchSegmentedControl.borderWidth = 2.0f; 124 | switchSegmentedControl.borderColor = [UIColor colorWithWhite:0.15f alpha:1.0f]; 125 | switchSegmentedControl.titleFont = [UIFont fontWithName:@"AvenirNext-Medium" size:16.0f]; 126 | switchSegmentedControl.titleTextColor = [UIColor colorWithWhite:0.3f alpha:1.0f]; 127 | switchSegmentedControl.selectedTitleFont = [UIFont fontWithName:@"AvenirNext-DemiBold" size:18.0f]; 128 | switchSegmentedControl.selectedTitleTextColor = [UIColor colorWithWhite:0.2f alpha:1.0f]; 129 | switchSegmentedControl.drawsGradientBackground = YES; 130 | switchSegmentedControl.gradientTopColor = [UIColor colorWithWhite:0.17f alpha:1.0f]; 131 | switchSegmentedControl.gradientBottomColor = [UIColor colorWithWhite:0.05f alpha:1.0f]; 132 | switchSegmentedControl.segmentIndicatorAnimationDuration = 0.2f; 133 | switchSegmentedControl.segmentIndicatorInset = 6.0f; 134 | switchSegmentedControl.drawsSegmentIndicatorGradientBackground = YES; 135 | switchSegmentedControl.segmentIndicatorGradientTopColor = [UIColor colorWithRed:0.75f green:0.9f blue:0.4f alpha:1.0f]; 136 | switchSegmentedControl.segmentIndicatorGradientBottomColor = [UIColor colorWithRed:0.47f green:0.72f blue:0.29f alpha:1.0f]; 137 | switchSegmentedControl.segmentIndicatorBorderWidth = 0.0f; 138 | switchSegmentedControl.cornerRadius = 35.0f; 139 | switchSegmentedControl.translatesAutoresizingMaskIntoConstraints = NO; 140 | [switchSegmentedControl.widthAnchor constraintEqualToConstant:140.0f].active = YES; 141 | [switchSegmentedControl.heightAnchor constraintEqualToConstant:70.0f].active = YES; 142 | 143 | return switchSegmentedControl; 144 | } 145 | 146 | - (BOOL)prefersStatusBarHidden { 147 | return YES; 148 | } 149 | 150 | @end 151 | -------------------------------------------------------------------------------- /NYSegmentedControlDemo/NYSegmentedControlDemo/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /NYSegmentedControlDemo/NYSegmentedControlDemo/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "7.0", 8 | "scale" : "2x" 9 | }, 10 | { 11 | "orientation" : "portrait", 12 | "idiom" : "iphone", 13 | "subtype" : "retina4", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "7.0", 16 | "scale" : "2x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /NYSegmentedControlDemo/NYSegmentedControlDemo/Launch Screen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 23 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /NYSegmentedControlDemo/NYSegmentedControlDemo/NYSegmentedControlDemo-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.nealonyoung.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | Launch Screen 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIStatusBarStyle 34 | UIStatusBarStyleBlackTranslucent 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | 39 | UIViewControllerBasedStatusBarAppearance 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /NYSegmentedControlDemo/NYSegmentedControlDemo/NYSegmentedControlDemo-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #import 8 | 9 | #ifndef __IPHONE_5_0 10 | #warning "This project uses features only available in iOS SDK 5.0 and later." 11 | #endif 12 | 13 | #ifdef __OBJC__ 14 | #import 15 | #import 16 | #endif 17 | -------------------------------------------------------------------------------- /NYSegmentedControlDemo/NYSegmentedControlDemo/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /NYSegmentedControlDemo/NYSegmentedControlDemo/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // NYSegmentedControlDemo 4 | // 5 | // Created by Nealon Young on 3/23/14. 6 | // Copyright (c) 2014 Neal Young. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char * argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /NYSegmentedControlDemo/NYSegmentedControlDemoTests/NYSegmentedControlDemoTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | com.nealonyoung.${PRODUCT_NAME:rfc1034identifier} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /NYSegmentedControlDemo/NYSegmentedControlDemoTests/NYSegmentedControlDemoTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // NYSegmentedControlDemoTests.m 3 | // NYSegmentedControlDemoTests 4 | // 5 | // Created by Nealon Young on 3/23/14. 6 | // Copyright (c) 2014 Neal Young. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NYSegmentedControlDemoTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation NYSegmentedControlDemoTests 16 | 17 | - (void)setUp 18 | { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown 24 | { 25 | // Put teardown code here. This method is called after the invocation of each test method in the class. 26 | [super tearDown]; 27 | } 28 | 29 | - (void)testExample 30 | { 31 | XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); 32 | } 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /NYSegmentedControlDemo/NYSegmentedControlDemoTests/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NYSegmentedControl 2 | 3 | NYSegmentedControl is a customizable, animated replacement for UISegmentedControl inspired by controls found in Instagram, Foursquare, and other apps. 4 | 5 | ![Screenshot](https://github.com/nealyoung/NYSegmentedControl/raw/master/image.png) 6 | 7 | ### Features 8 | * Create segmented controls with animated selection indicator 9 | * Customize colors, gradients, fonts, etc. either directly or globally with UIAppearance 10 | * Configure distinct text styles for the selected segment 11 | * VoiceOver/accessibility support 12 | 13 | ![Animated Example](https://github.com/nealyoung/NYSegmentedControl/raw/master/example-animated.gif) 14 | 15 | ### Installation 16 | #### Manual 17 | Add the files to your project manually by dragging the NYSegmentedControl directory into your Xcode project. 18 | 19 | #### CocoaPods 20 | Add `pod 'NYSegmentedControl'` to your Podfile, and run `pod install`. 21 | 22 | ### Usage 23 | Use is largely identical to UISegmentedControl. An example project is included in the NYSegmentedControlDemo directory. 24 | 25 | ```objc 26 | // Import the class and create an NYSegmentedControl instance 27 | #import "NYSegmentedControl.h" 28 | 29 | // ... 30 | 31 | NYSegmentedControl *segmentedControl = [[NYSegmentedControl alloc] initWithItems:@[@"Segment 1", @"Segment 2"]]; 32 | 33 | // Add desired targets/actions 34 | [segmentedControl addTarget:self action:@selector(segmentSelected) forControlEvents:UIControlEventValueChanged]; 35 | 36 | // Customize and size the control 37 | segmentedControl.borderWidth = 1.0f; 38 | segmentedControl.borderColor = [UIColor colorWithWhite:0.15f alpha:1.0f]; 39 | segmentedControl.drawsGradientBackground = YES; 40 | segmentedControl.segmentIndicatorInset = 2.0f; 41 | segmentedControl.drawsSegmentIndicatorGradientBackground = YES; 42 | segmentedControl.segmentIndicatorGradientTopColor = [UIColor colorWithRed:0.30 green:0.50 blue:0.88f alpha:1.0f]; 43 | segmentedControl.segmentIndicatorGradientBottomColor = [UIColor colorWithRed:0.20 green:0.35 blue:0.75f alpha:1.0f]; 44 | segmentedControl.segmentIndicatorAnimationDuration = 0.3f; 45 | segmentedControl.segmentIndicatorBorderWidth = 0.0f; 46 | [segmentedControl sizeToFit]; 47 | 48 | // Add the control to your view 49 | self.navigationItem.titleView = self.segmentedControl; 50 | ``` 51 | 52 | ### License 53 | This project is released under the MIT License. 54 | -------------------------------------------------------------------------------- /example-animated.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nealyoung/NYSegmentedControl/842222068b17464fe1a78c7f8abd2155c5de1c39/example-animated.gif -------------------------------------------------------------------------------- /image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nealyoung/NYSegmentedControl/842222068b17464fe1a78c7f8abd2155c5de1c39/image.png --------------------------------------------------------------------------------