diff --git a/Demo/1.png b/Demo/1.png new file mode 100644 index 0000000..b9d80af Binary files /dev/null and b/Demo/1.png differ diff --git a/Demo/1@2x.png b/Demo/1@2x.png new file mode 100644 index 0000000..ac64791 Binary files /dev/null and b/Demo/1@2x.png differ diff --git a/Demo/2.png b/Demo/2.png new file mode 100644 index 0000000..3eef3f9 Binary files /dev/null and b/Demo/2.png differ diff --git a/Demo/2@2x.png b/Demo/2@2x.png new file mode 100644 index 0000000..df7f82d Binary files /dev/null and b/Demo/2@2x.png differ diff --git a/Demo/3.png b/Demo/3.png new file mode 100644 index 0000000..2f65526 Binary files /dev/null and b/Demo/3.png differ diff --git a/Demo/3@2x.png b/Demo/3@2x.png new file mode 100644 index 0000000..951a709 Binary files /dev/null and b/Demo/3@2x.png differ diff --git a/Demo/Classes/ViewController.m b/Demo/Classes/ViewController.m index e712d4b..62a6630 100644 --- a/Demo/Classes/ViewController.m +++ b/Demo/Classes/ViewController.m @@ -19,7 +19,7 @@ - (void)viewDidLoad { SVSegmentedControl *navSC = [[SVSegmentedControl alloc] initWithSectionTitles:[NSArray arrayWithObjects:@"Section 1", @"Section 2", nil]]; navSC.changeHandler = ^(NSUInteger newIndex) { - NSLog(@"segmentedControl did select index %i (via block handler)", newIndex); + NSLog(@"segmentedControl did select index %@ (via block handler)", @(newIndex)); }; [self.view addSubview:navSC]; @@ -36,7 +36,7 @@ - (void)viewDidLoad { [self.view addSubview:redSC]; - redSC.center = CGPointMake(160, 170); + redSC.center = CGPointMake(160, 150); // 3rd CONTROL @@ -50,7 +50,7 @@ - (void)viewDidLoad { [self.view addSubview:grayRC]; - grayRC.center = CGPointMake(160, 270); + grayRC.center = CGPointMake(160, 230); // 4th CONTROL @@ -68,14 +68,48 @@ - (void)viewDidLoad { [self.view addSubview:yellowRC]; - yellowRC.center = CGPointMake(160, 370); + yellowRC.center = CGPointMake(160, 310); + + + // 4th CONTROL + SVSegmentedControl *imageRC = [[SVSegmentedControl alloc] initWithSectionTitles:[NSArray arrayWithObjects:@"Some", @"Section", @"Images", nil]]; + [imageRC setSectionImages:@[[UIImage imageNamed:@"1"], [UIImage imageNamed:@"2"], [UIImage imageNamed:@"3"], [UIImage imageNamed:@"3"]]]; + [imageRC addTarget:self action:@selector(segmentedControlChangedValue:) forControlEvents:UIControlEventValueChanged]; + imageRC.crossFadeLabelsOnDrag = YES; + imageRC.titleEdgeInsets = UIEdgeInsetsMake(0, 5, 0, 5); + imageRC.height = 40; + [imageRC setSelectedSegmentIndex:0 animated:NO]; + imageRC.thumb.tintColor = [UIColor colorWithRed:0.100 green:0.400 blue:0.800 alpha:1.000]; + imageRC.thumb.textColor = [UIColor whiteColor]; + imageRC.thumb.textColor = [UIColor whiteColor]; + imageRC.thumb.textShadowColor = [UIColor colorWithWhite:0 alpha:0.5]; + imageRC.thumb.textShadowOffset = CGSizeMake(0, 1); + + [self.view addSubview:imageRC]; + + imageRC.center = CGPointMake(160, 390); + + + // 5th CONTROL + SVSegmentedControl *iOS7FlatRC = [[SVSegmentedControl alloc] initWithSectionTitles:[NSArray arrayWithObjects:@"Can", @"Look", @"Flat", nil] stylePreset:SVSegmentedControlStylePresetFlat]; + [iOS7FlatRC addTarget:self action:@selector(segmentedControlChangedValue:) forControlEvents:UIControlEventValueChanged]; + iOS7FlatRC.titleEdgeInsets = UIEdgeInsetsMake(0, 5, 0, 5); + iOS7FlatRC.height = 40; + [iOS7FlatRC setSelectedSegmentIndex:1 animated:NO]; + iOS7FlatRC.thumb.tintColor = [UIColor colorWithRed:1.000 green:0.300 blue:0.100 alpha:1.000]; + iOS7FlatRC.thumb.textColor = [UIColor whiteColor]; + iOS7FlatRC.thumb.textColor = [UIColor whiteColor]; + + [self.view addSubview:iOS7FlatRC]; + + iOS7FlatRC.center = CGPointMake(160, 470); } #pragma mark - UIControlEventValueChanged - (void)segmentedControlChangedValue:(SVSegmentedControl*)segmentedControl { - NSLog(@"segmentedControl %i did select index %i (via UIControl method)", segmentedControl.tag, segmentedControl.selectedSegmentIndex); + NSLog(@"segmentedControl %@ did select index %@ (via UIControl method)", @(segmentedControl.tag), @(segmentedControl.selectedSegmentIndex)); } diff --git a/Demo/SVSegmentedControl.xcodeproj/project.pbxproj b/Demo/SVSegmentedControl.xcodeproj/project.pbxproj index ea02dbc..87a5ab6 100755 --- a/Demo/SVSegmentedControl.xcodeproj/project.pbxproj +++ b/Demo/SVSegmentedControl.xcodeproj/project.pbxproj @@ -21,6 +21,12 @@ 2899E5220DE3E06400AC0155 /* ViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2899E5210DE3E06400AC0155 /* ViewController.xib */; }; 28AD733F0D9D9553002E5188 /* MainWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = 28AD733E0D9D9553002E5188 /* MainWindow.xib */; }; 28D7ACF80DDB3853001CB0EB /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 28D7ACF70DDB3853001CB0EB /* ViewController.m */; }; + 651F8D091940E22A00345FC0 /* 1.png in Resources */ = {isa = PBXBuildFile; fileRef = 651F8D031940E22A00345FC0 /* 1.png */; }; + 651F8D0A1940E22A00345FC0 /* 1@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 651F8D041940E22A00345FC0 /* 1@2x.png */; }; + 651F8D0B1940E22A00345FC0 /* 2.png in Resources */ = {isa = PBXBuildFile; fileRef = 651F8D051940E22A00345FC0 /* 2.png */; }; + 651F8D0C1940E22A00345FC0 /* 2@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 651F8D061940E22A00345FC0 /* 2@2x.png */; }; + 651F8D0D1940E22A00345FC0 /* 3.png in Resources */ = {isa = PBXBuildFile; fileRef = 651F8D071940E22A00345FC0 /* 3.png */; }; + 651F8D0E1940E22A00345FC0 /* 3@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 651F8D081940E22A00345FC0 /* 3@2x.png */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -44,6 +50,12 @@ 28D7ACF70DDB3853001CB0EB /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 32CA4F630368D1EE00C91783 /* SVSegmentedControl_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SVSegmentedControl_Prefix.pch; sourceTree = ""; }; + 651F8D031940E22A00345FC0 /* 1.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = 1.png; sourceTree = ""; }; + 651F8D041940E22A00345FC0 /* 1@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "1@2x.png"; sourceTree = ""; }; + 651F8D051940E22A00345FC0 /* 2.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = 2.png; sourceTree = ""; }; + 651F8D061940E22A00345FC0 /* 2@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "2@2x.png"; sourceTree = ""; }; + 651F8D071940E22A00345FC0 /* 3.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = 3.png; sourceTree = ""; }; + 651F8D081940E22A00345FC0 /* 3@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "3@2x.png"; sourceTree = ""; }; 8D1107310486CEB800E47090 /* SVSegmentedControl-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "SVSegmentedControl-Info.plist"; plistStructureDefinitionIdentifier = "com.apple.xcode.plist.structure-definition.iphone.info-plist"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -124,6 +136,12 @@ children = ( 28AD733E0D9D9553002E5188 /* MainWindow.xib */, 8D1107310486CEB800E47090 /* SVSegmentedControl-Info.plist */, + 651F8D031940E22A00345FC0 /* 1.png */, + 651F8D041940E22A00345FC0 /* 1@2x.png */, + 651F8D051940E22A00345FC0 /* 2.png */, + 651F8D061940E22A00345FC0 /* 2@2x.png */, + 651F8D071940E22A00345FC0 /* 3.png */, + 651F8D081940E22A00345FC0 /* 3@2x.png */, ); name = Resources; sourceTree = ""; @@ -165,7 +183,7 @@ 29B97313FDCFA39411CA2CEA /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0450; + LastUpgradeCheck = 0510; }; buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "SVSegmentedControl" */; compatibilityVersion = "Xcode 3.2"; @@ -191,9 +209,15 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 651F8D0A1940E22A00345FC0 /* 1@2x.png in Resources */, + 651F8D0E1940E22A00345FC0 /* 3@2x.png in Resources */, + 651F8D0B1940E22A00345FC0 /* 2.png in Resources */, + 651F8D0D1940E22A00345FC0 /* 3.png in Resources */, + 651F8D091940E22A00345FC0 /* 1.png in Resources */, 28AD733F0D9D9553002E5188 /* MainWindow.xib in Resources */, 2899E5220DE3E06400AC0155 /* ViewController.xib in Resources */, 224B1756145F25D600BD2DDB /* README.md in Resources */, + 651F8D0C1940E22A00345FC0 /* 2@2x.png in Resources */, 22A748901630AD4E004893A8 /* Default-568h@2x.png in Resources */, 223F841D16BC891900C3C219 /* SVSegmentedControl.podspec in Resources */, ); @@ -229,7 +253,6 @@ GCC_PREFIX_HEADER = SVSegmentedControl_Prefix.pch; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; INFOPLIST_FILE = "SVSegmentedControl-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 4.0; PRODUCT_NAME = SVSC; RUN_CLANG_STATIC_ANALYZER = YES; }; @@ -254,11 +277,12 @@ C01FCF4F08A954540054247B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_BIT)"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; GCC_C_LANGUAGE_STANDARD = c99; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 5.0; + ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; }; name = Debug; @@ -266,11 +290,11 @@ C01FCF5008A954540054247B /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ARCHS = "$(ARCHS_STANDARD_32_BIT)"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; GCC_C_LANGUAGE_STANDARD = c99; GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 5.0; OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; SDKROOT = iphoneos; }; diff --git a/SVSegmentedControl.podspec b/SVSegmentedControl.podspec index 70e18d7..c0b2c22 100644 --- a/SVSegmentedControl.podspec +++ b/SVSegmentedControl.podspec @@ -10,6 +10,5 @@ Pod::Spec.new do |s| s.description = 'SVSegmentedControl is a customizable UIControl class that mimics UISegmentedControl but that looks like an UISwitch.' s.frameworks = 'QuartzCore' s.source_files = 'SVSegmentedControl/*.{h,m}' - s.preserve_paths = 'Demo' s.requires_arc = true end diff --git a/SVSegmentedControl/SVSegmentedControl.h b/SVSegmentedControl/SVSegmentedControl.h index 2c6bf10..efd01e1 100644 --- a/SVSegmentedControl/SVSegmentedControl.h +++ b/SVSegmentedControl/SVSegmentedControl.h @@ -12,38 +12,187 @@ #import "SVSegmentedThumb.h" #import -@protocol SVSegmentedControlDelegate; +/** + * Default initial styles to enable or disable multiple visual attributes at once. + */ +typedef NS_ENUM(NSInteger, SVSegmentedControlStylePreset) +{ + /** + * The default style used when nothing is set. Shadows and gradient are intact. + */ + SVSegmentedControlStylePresetDefault, + /** + * A flat style to match iOS 7+ style layouts. All shadows and gradient disabled. + */ + SVSegmentedControlStylePresetFlat, +}; @interface SVSegmentedControl : UIControl +/** + * A block called whenever there is a change to the switch. + * If you haven't fallen in love with blocks yet, you can still use the classic UIControl method: + * @code +[mySegmentedControl addTarget:self action:@selector(segmentedControlChangedValue:) forControlEvents:UIControlEventValueChanged]; + * @endcode + * + * Providing an action method ending with a semicolon, the sender object is therefore made accessible: + * @code +- (void)segmentedControlChangedValue:(SVSegmentedControl*)segmentedControl { + NSLog(@"segmentedControl did select index %i", segmentedControl.selectedIndex); +} + * @endcode + */ @property (nonatomic, copy) void (^changeHandler)(NSUInteger newIndex); // you can also use addTarget:action:forControlEvents: + +/** + * The titles for each section of the switch. These are laid out in the order specicied in the array. + */ @property (nonatomic, copy) NSArray *sectionTitles; + +/** + * Images placed to the left of section titles. Images are assigned to titles with corresponding indices. + * If not enough images are provided, the remaining section(s) will be displayed normally. + * If too many images are provided, the remaining images are not displayed. + */ @property (nonatomic, copy) NSArray *sectionImages; +/** + * The thumb that moves to the selected section. Access this property to change its appearance. + */ @property (nonatomic, strong, readonly) SVSegmentedThumb *thumb; -@property (nonatomic, readonly) NSUInteger selectedSegmentIndex; // default is 0 + +/** + * The currently selected index of the switch. The default value is 0. + */ +@property (nonatomic, readwrite) NSUInteger selectedSegmentIndex; // default is 0 + +/** + * Whether or not to animated the change to initial selection. The default value is NO. + * @remarks This doesn't seem to be used anywhere. + */ @property (nonatomic, readwrite) BOOL animateToInitialSelection; // default is NO + +/** + * Whether or not to change the title on the thumb, or slide the thumb over the titles. + * If set to NO (the default) the thumb moves while the old and new titles stay lined up with the titles + * in the background, sliding on and off the thumb. + * If set to YES, as the thumb moves, the title stays in place and fades between the two sections. + */ @property (nonatomic, readwrite) BOOL crossFadeLabelsOnDrag; // default is NO +/** + * Whether the switch must be changed with a pan gesture to make the control difficult to accidentally change. + * The default is NO. + */ @property (nonatomic, readwrite) BOOL mustSlideToChange; // default is NO - To make the control difficult to accidentally change, force the user to slide it -@property (nonatomic, readwrite) CGFloat minimumOverlapToChange; // default is 0.66 - Only snap to a new segment if the thumb overlaps it by this fraction -@property (nonatomic, readwrite) UIEdgeInsets touchTargetMargins; // default is UIEdgeInsetsMake(0, 0, 0, 0) - Enlarge touch target of control - -@property (nonatomic, strong) UIColor *backgroundTintColor; // default is [UIColor colorWithWhite:0.1 alpha:1] -@property (nonatomic, strong) UIImage *backgroundImage; // default is nil -@property (nonatomic, readwrite) CGFloat height; // default is 32.0 -@property (nonatomic, readwrite) UIEdgeInsets thumbEdgeInset; // default is UIEdgeInsetsMake(2, 2, 3, 2) -@property (nonatomic, readwrite) UIEdgeInsets titleEdgeInsets; // default is UIEdgeInsetsMake(0, 10, 0, 10) -@property (nonatomic, readwrite) CGFloat cornerRadius; // default is 4.0 +/** + * The fractional amount by which the thumb must overlap a destination segment to cause a snap to that segment. + * The default value is 0.66. + */ +@property (nonatomic, readwrite) CGFloat minimumOverlapToChange; // default is 0.66 - Only snap to a new segment if the thumb overlaps it by this fraction -@property (nonatomic, strong) UIFont *font; // default is [UIFont boldSystemFontOfSize:15] -@property (nonatomic, strong) UIColor *textColor; // default is [UIColor grayColor]; -@property (nonatomic, strong) UIColor *textShadowColor; // default is [UIColor blackColor] -@property (nonatomic, readwrite) CGSize textShadowOffset; // default is CGSizeMake(0, -1) -@property (nonatomic, strong) UIColor *innerShadowColor; // default is [UIColor colorWithWhite:0 alpha:0.8] +/** + * Margins to add to the touchable area of the control. + * The default is UIEdgeInsetsMake(0, 0, 0, 0). + */ +@property (nonatomic, readwrite) UIEdgeInsets touchTargetMargins; // default is UIEdgeInsetsMake(0, 0, 0, 0) - Enlarge touch target of control +/** + * The color used in the background of the switch. + * The default is [UIColor colorWithWhite:0.1 alpha:1]. + */ +@property (nonatomic, strong) UIColor *backgroundTintColor UI_APPEARANCE_SELECTOR; // default is [UIColor colorWithWhite:0.1 alpha:1] + +/** + * The image used in the background of the switch. + * The default is nil. + */ +@property (nonatomic, strong) UIImage *backgroundImage UI_APPEARANCE_SELECTOR; // default is nil + +@property (nonatomic, strong) UIColor *strokeColor UI_APPEARANCE_SELECTOR; // default is nil; + +/** + * The height of the control. The default is 32.0. + */ +@property (nonatomic, readwrite) CGFloat height UI_APPEARANCE_SELECTOR; // default is 32.0 + +/** + * The insets of the thumb from the boundries of the segment and the control. + * The default is UIEdgeInsetsMake(2, 2, 3, 2). + */ +@property (nonatomic, readwrite) UIEdgeInsets thumbEdgeInset UI_APPEARANCE_SELECTOR; // default is UIEdgeInsetsMake(2, 2, 3, 2) + +/** + * The insets from the edge of the control to the edges of the segment titles. + * The default is UIEdgeInsetsMake(0, 10, 0, 10). + */ +@property (nonatomic, readwrite) UIEdgeInsets titleEdgeInsets UI_APPEARANCE_SELECTOR; // default is UIEdgeInsetsMake(0, 10, 0, 10) + +/** + * The corner radius of the control. The default is 4.0. + */ +@property (nonatomic, readwrite) CGFloat cornerRadius UI_APPEARANCE_SELECTOR; // default is 4.0 + +/** + * The font used for the titles. The default is [UIFont boldSystemFontOfSize:15]. + */ +@property (nonatomic, strong) UIFont *font UI_APPEARANCE_SELECTOR; // default is [UIFont boldSystemFontOfSize:15] + +/** + * The color of the text in the segments that are not selected. The default is [UIColor grayColor]. + */ +@property (nonatomic, strong) UIColor *textColor UI_APPEARANCE_SELECTOR; // default is [UIColor grayColor]; + +/** + * The color of the shadow for the text in the segments that are not selected. The default is [UIColor blackColor]. + */ +@property (nonatomic, strong) UIColor *textShadowColor UI_APPEARANCE_SELECTOR; // default is [UIColor blackColor] + +/** + * The offset of the shadow from the text in the segments that are not selected. The default is CGSizeMake(0, -1). + */ +@property (nonatomic, readwrite) CGSize textShadowOffset UI_APPEARANCE_SELECTOR; // default is CGSizeMake(0, -1) + +/** + * The color of the shadow inside the control along the edges. The default is [UIColor colorWithWhite:0 alpha:0.8]. + */ +@property (nonatomic, strong) UIColor *innerShadowColor UI_APPEARANCE_SELECTOR; // default is [UIColor colorWithWhite:0 alpha:0.8] + +/** + * The preset style. Setting this will change all documented shadow colors, the thumb gradient, + * and remove the bottom gloss. The default value is SVSegmentedControlStylePresetDefault. + */ +@property (nonatomic, readwrite) SVSegmentedControlStylePreset stylePreset; + +/** + * Make a new segmented control with the specified section titles. + * Initialized with SVSegmentedControlStylePresetDefault. + * + * @param titlesArray an array of section titles for the new control + * + * @return a new segemented control with the specified titles + */ - (SVSegmentedControl*)initWithSectionTitles:(NSArray*)titlesArray; + +/** + * Make a new segmented control with the specified section titles and style. + * + * + * @param titlesArray an array of section titles for the new control + * @param style the initial styling preset for the control + * + * @return a new segmented control with the specified titles and style preset + */ +- (SVSegmentedControl*)initWithSectionTitles:(NSArray*)titlesArray stylePreset:(SVSegmentedControlStylePreset)stylePreset; + +/** + * Selects a specified index in the control. Either animates or jumps straight to the new state. + * + * @param index the new index to select + * @param animated whether to animate the change in selection + */ - (void)setSelectedSegmentIndex:(NSUInteger)index animated:(BOOL)animated; // deprecated @@ -54,9 +203,3 @@ @end - -@protocol SVSegmentedControlDelegate - -- (void)segmentedControl:(SVSegmentedControl*)segmentedControl didSelectIndex:(NSUInteger)index; - -@end diff --git a/SVSegmentedControl/SVSegmentedControl.m b/SVSegmentedControl/SVSegmentedControl.m index 4437107..8b2f5e3 100644 --- a/SVSegmentedControl/SVSegmentedControl.m +++ b/SVSegmentedControl/SVSegmentedControl.m @@ -42,7 +42,6 @@ - (void)setupAccessibility; @property (nonatomic, strong) NSMutableArray *thumbRects; @property (nonatomic, strong) NSMutableArray *accessibilityElements; -@property (nonatomic, readwrite) NSUInteger selectedSegmentIndex; @property (nonatomic, readwrite) NSUInteger snapToIndex; @property (nonatomic, readwrite) BOOL trackingThumb; @property (nonatomic, readwrite) BOOL moved; @@ -68,7 +67,7 @@ - (id)initWithSectionTitles:(NSArray*)array { self.accessibilityElements = [NSMutableArray arrayWithCapacity:self.sectionTitles.count]; self.backgroundColor = [UIColor clearColor]; - self.backgroundTintColor = [UIColor colorWithWhite:0 alpha:0.5]; + _backgroundTintColor = [UIColor colorWithWhite:0 alpha:0.5]; self.clipsToBounds = YES; self.userInteractionEnabled = YES; self.animateToInitialSelection = NO; @@ -76,19 +75,19 @@ - (id)initWithSectionTitles:(NSArray*)array { self.mustSlideToChange = NO; self.minimumOverlapToChange = 0.66; - self.font = [UIFont boldSystemFontOfSize:15]; - self.textColor = [UIColor grayColor]; - self.textShadowColor = [UIColor blackColor]; - self.textShadowOffset = CGSizeMake(0, -1); + _font = [UIFont boldSystemFontOfSize:15]; + _textColor = [UIColor grayColor]; + _textShadowColor = [UIColor blackColor]; + _textShadowOffset = CGSizeMake(0, -1); - self.titleEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 10); - self.thumbEdgeInset = UIEdgeInsetsMake(2, 2, 3, 2); - self.height = 32.0; - self.cornerRadius = 4.0; + _titleEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 10); + _thumbEdgeInset = UIEdgeInsetsMake(2, 2, 3, 2); + _height = 32.0; + _cornerRadius = 4.0; self.selectedSegmentIndex = 0; - self.innerShadowColor = [UIColor colorWithWhite:0 alpha:0.8]; + _innerShadowColor = [UIColor colorWithWhite:0 alpha:0.8]; [self setupAccessibility]; } @@ -96,6 +95,31 @@ - (id)initWithSectionTitles:(NSArray*)array { return self; } +- (SVSegmentedControl*)initWithSectionTitles:(NSArray*)titlesArray stylePreset:(SVSegmentedControlStylePreset)stylePreset +{ + if (self = [self initWithSectionTitles:titlesArray]) { + _stylePreset = stylePreset; + + switch (_stylePreset) { + case SVSegmentedControlStylePresetFlat: + // remove shadows and gradient + self.textShadowColor = [UIColor clearColor]; + self.innerShadowColor = [UIColor clearColor]; + self.thumb.textShadowColor = [UIColor clearColor]; + self.thumb.shouldCastShadow = FALSE; + self.thumb.gradientIntensity = 0.0f; + break; + + case SVSegmentedControlStylePresetDefault: + default: + // do nothing + break; + } + } + + return self; +} + - (SVSegmentedThumb *)thumb { if(_thumb == nil) @@ -112,8 +136,8 @@ - (void)sizeToFit - (void)updateSectionRects { - int c = [self.sectionTitles count]; - int i = 0; + NSUInteger c = [self.sectionTitles count]; + NSUInteger i = 0; [self calculateSegmentWidth]; @@ -133,14 +157,14 @@ - (void)updateSectionRects { i = 0; self.thumbRects = [NSMutableArray new]; - for(NSString *titleString in self.sectionTitles) { + for(NSString *titleString __unused in self.sectionTitles) { CGRect thumbRect = CGRectMake(self.segmentWidth*i, 0, self.segmentWidth, self.bounds.size.height); thumbRect.size.width+=10; // 5px drop shadow on each side thumbRect.origin.x-=5; thumbRect.size.height-=1; // for segmented bottom gloss [self.thumbRects addObject:[NSValue valueWithCGRect:thumbRect]]; i++; - } + } self.thumb.frame = [[self.thumbRects objectAtIndex:self.selectedSegmentIndex] CGRectValue]; self.thumb.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:self.thumb.bounds cornerRadius:2].CGPath; @@ -319,7 +343,7 @@ - (void)endTrackingWithTouch:(UITouch *)touch withEvent:(UIEvent *)event { if(!self.mustSlideToChange && !self.moved && self.trackingThumb && [self.sectionTitles count] == 2) [self toggle]; else if(!self.activated && posX > pMinX && posX < pMaxX) { - int potentialSnapToIndex = MIN(floor(cPos.x/self.segmentWidth), self.sectionTitles.count-1); + NSInteger potentialSnapToIndex = MIN(floor(cPos.x/self.segmentWidth), self.sectionTitles.count-1); if (self.mustSlideToChange) { CGRect potentialSegmentRect = CGRectMake(self.segmentWidth * potentialSnapToIndex, 0, self.segmentWidth, self.bounds.size.height); @@ -365,7 +389,7 @@ - (void)snap:(BOOL)animated { self.thumb.secondImageView.alpha = 0; } - int index; + NSUInteger index; if(self.snapToIndex != -1) index = self.snapToIndex; @@ -513,6 +537,32 @@ - (void)setThumbSecondValuesForIndex:(NSUInteger)index { image:[self sectionImage:[self imageForSectionIndex:index] withTintColor:self.thumb.textColor]]; } +- (void)setStylePreset:(SVSegmentedControlStylePreset)stylePreset +{ + _stylePreset = stylePreset; + + switch (_stylePreset) { + case SVSegmentedControlStylePresetFlat: + // remove shadows and gradient + self.textShadowColor = [UIColor clearColor]; + self.innerShadowColor = [UIColor clearColor]; + self.thumb.textShadowColor = [UIColor clearColor]; + self.thumb.shouldCastShadow = FALSE; + self.thumb.gradientIntensity = 0.0f; + break; + + case SVSegmentedControlStylePresetDefault: + default: + // do nothing + self.textShadowColor = [UIColor blackColor]; + self.innerShadowColor = [UIColor colorWithWhite:0 alpha:0.8]; + self.thumb.textShadowColor = [UIColor blackColor]; + self.thumb.shouldCastShadow = !!_backgroundImage; + self.thumb.gradientIntensity = 0.15f; + break; + } +} + #pragma mark - Deprecated methods - (void)setSelectedIndex:(NSUInteger)index { @@ -534,21 +584,22 @@ - (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); - if(self.backgroundImage) + if(self.backgroundImage) { [self.backgroundImage drawInRect:rect]; - - else { - // bottom gloss - CGRect insetRect = CGRectMake(0, 0, rect.size.width, rect.size.height-1); - CGContextSetFillColorWithColor(context, [UIColor colorWithWhite:1 alpha:0.1].CGColor); - - UIBezierPath *bottomGlossPath = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:self.cornerRadius]; - [bottomGlossPath appendPath:[UIBezierPath bezierPathWithRoundedRect:insetRect cornerRadius:self.cornerRadius]]; - bottomGlossPath.usesEvenOddFillRule = YES; - [bottomGlossPath fill]; + } else { + CGRect insetRect = CGRectMake(0, 0, rect.size.width, rect.size.height-1);; + if (_stylePreset == SVSegmentedControlStylePresetDefault) { + // bottom gloss + CGContextSetFillColorWithColor(context, [UIColor colorWithWhite:1 alpha:0.1].CGColor); + + UIBezierPath *bottomGlossPath = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:self.cornerRadius]; + [bottomGlossPath appendPath:[UIBezierPath bezierPathWithRoundedRect:insetRect cornerRadius:self.cornerRadius]]; + bottomGlossPath.usesEvenOddFillRule = YES; + [bottomGlossPath fill]; + } - CGPathRef roundedRect = [UIBezierPath bezierPathWithRoundedRect:insetRect cornerRadius:self.cornerRadius].CGPath; - CGContextAddPath(context, roundedRect); + UIBezierPath *roundedRectPath = [UIBezierPath bezierPathWithRoundedRect:insetRect cornerRadius:self.cornerRadius]; + CGContextAddPath(context, roundedRectPath.CGPath); CGContextClip(context); // background tint @@ -565,7 +616,7 @@ - (void)drawRect:(CGRect)rect { CGFloat tintColorRGBA[4]; [tintColorToApply getRed:&tintColorRGBA[0] green:&tintColorRGBA[1] blue:&tintColorRGBA[2] alpha:&tintColorRGBA[3]]; - float darkeningDelta = 0.2; + float darkeningDelta = (_stylePreset == SVSegmentedControlStylePresetDefault) ? 0.2 : 0; // no gradient on flat UIColor *darkerTintColor = [UIColor colorWithRed:(tintColorRGBA[0] - darkeningDelta) green:(tintColorRGBA[1] - darkeningDelta) blue:(tintColorRGBA[2] - darkeningDelta) alpha:(tintColorRGBA[3] + darkeningDelta*0.2)]; CGFloat darkerTintColorRGBA[4]; [darkerTintColor getRed:&darkerTintColorRGBA[0] green:&darkerTintColorRGBA[1] blue:&darkerTintColorRGBA[2] alpha:&darkerTintColorRGBA[3]]; @@ -574,15 +625,24 @@ - (void)drawRect:(CGRect)rect { CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace, components, NULL, 2); CGContextDrawLinearGradient(context, gradient, CGPointMake(0,0), CGPointMake(0,CGRectGetHeight(rect)-1), 0); CGGradientRelease(gradient); - CGColorSpaceRelease(colorSpace); - // inner shadow - NSArray *paths = [NSArray arrayWithObject:[UIBezierPath bezierPathWithRoundedRect:insetRect cornerRadius:self.cornerRadius]]; - UIImage *mask = [self maskWithPaths:paths bounds:CGRectInset(insetRect, -10, -10)]; - UIImage *invertedImage = [self invertedImageWithMask:mask color:self.innerShadowColor]; + if (self.strokeColor) { + [self.strokeColor setStroke]; + roundedRectPath.lineWidth = 1.f; + [roundedRectPath stroke]; + } - CGContextSetShadowWithColor(context, CGSizeMake(0, 1), 2, self.innerShadowColor.CGColor); - [invertedImage drawAtPoint:CGPointMake(-10, -10)]; + CGColorSpaceRelease(colorSpace); + + if (_stylePreset == SVSegmentedControlStylePresetDefault) { + // inner shadow + NSArray *paths = [NSArray arrayWithObject:[UIBezierPath bezierPathWithRoundedRect:insetRect cornerRadius:self.cornerRadius]]; + UIImage *mask = [self maskWithPaths:paths bounds:CGRectInset(insetRect, -10, -10)]; + UIImage *invertedImage = [self invertedImageWithMask:mask color:self.innerShadowColor]; + + CGContextSetShadowWithColor(context, CGSizeMake(0, 1), 2, self.innerShadowColor.CGColor); + [invertedImage drawAtPoint:CGPointMake(-10, -10)]; + } } @@ -613,7 +673,7 @@ - (void)drawRect:(CGRect)rect { [[self sectionImage:image withTintColor:self.textColor] drawAtPoint:CGPointMake(titlePosX, round((rect.size.height-image.size.height)/2))]; #if __IPHONE_OS_VERSION_MIN_REQUIRED < 60000 - [titleString drawAtPoint:CGPointMake(titlePosX+imageWidth, posY) forWidth:self.segmentWidth withFont:self.font lineBreakMode:UILineBreakModeTailTruncation]; + [titleString drawAtPoint:CGPointMake(titlePosX+imageWidth, posY) forWidth:self.segmentWidth withFont:self.font lineBreakMode:NSLineBreakByTruncatingTail]; #else [titleString drawAtPoint:CGPointMake(titlePosX+imageWidth, posY) forWidth:self.segmentWidth withFont:self.font lineBreakMode:NSLineBreakByClipping]; #endif @@ -653,7 +713,7 @@ - (UIImage *)maskWithPaths:(NSArray *)paths bounds:(CGRect)bounds // Create the bitmap with just an alpha channel. // When created, it has value 0 at every pixel. - CGContextRef gc = CGBitmapContextCreate(NULL, scaledSize.width, scaledSize.height, 8, scaledSize.width, NULL, kCGImageAlphaOnly); + CGContextRef gc = CGBitmapContextCreate(NULL, scaledSize.width, scaledSize.height, 8, scaledSize.width, NULL, kCGBitmapAlphaInfoMask); // Adjust the current transform matrix for the screen scale. CGContextScaleCTM(gc, scale, scale); @@ -709,4 +769,21 @@ - (void)drawInnerGlowWithPaths:(NSArray *)paths bounds:(CGRect)bounds color:(UIC } CGContextRestoreGState(gc); } +#pragma mark - Setters + +- (void)setFont:(UIFont *)font { + _font = font; + [self setNeedsDisplay]; +} + +- (void)setCornerRadius:(CGFloat)cornerRadius { + _cornerRadius = cornerRadius; + [self setNeedsLayout]; +} + +- (void)setHeight:(CGFloat)height { + _height = height; + [self setNeedsLayout]; +} + @end diff --git a/SVSegmentedControl/SVSegmentedThumb.h b/SVSegmentedControl/SVSegmentedThumb.h index 8f4877a..2e37b50 100644 --- a/SVSegmentedControl/SVSegmentedThumb.h +++ b/SVSegmentedControl/SVSegmentedThumb.h @@ -14,14 +14,47 @@ @interface SVSegmentedThumb : UIView -@property (nonatomic, strong) UIImage *backgroundImage; // default is nil; -@property (nonatomic, strong) UIImage *highlightedBackgroundImage; // default is nil; - -@property (nonatomic, strong) UIColor *tintColor; // default is [UIColor grayColor] -@property (nonatomic, strong) UIColor *textColor; // default is [UIColor whiteColor] -@property (nonatomic, strong) UIColor *textShadowColor; // default is [UIColor blackColor] -@property (nonatomic, readwrite) CGSize textShadowOffset; // default is CGSizeMake(0, -1) -@property (nonatomic, readwrite) BOOL shouldCastShadow; // default is YES (NO when backgroundImage is set) -@property (nonatomic, assign) CGFloat gradientIntensity; // default is 0.15 +/** + * The image to use in the background of the thumb. The default is nil. + * Setting this property, sets the valye of shouldCastShadow to NO. + */ +@property (nonatomic, strong) UIImage *backgroundImage UI_APPEARANCE_SELECTOR; // default is nil; + +/** + * The image used in the background when highlighted. The default is nil. + */ +@property (nonatomic, strong) UIImage *highlightedBackgroundImage UI_APPEARANCE_SELECTOR; // default is nil; + +/** + * The color of the thumb used as a base for the gradient. The default is [UIColor grayColor]. + */ +@property (nonatomic, strong) UIColor *tintColor UI_APPEARANCE_SELECTOR; // default is [UIColor grayColor] + +/** + * The color the title text in the thumb. The default is [UIColor whiteColor]. + */ +@property (nonatomic, strong) UIColor *textColor UI_APPEARANCE_SELECTOR; // default is [UIColor whiteColor] + +/** + * The color of the shadow for the title text in the thumb. The default is [UIColor blackColor]. + */ +@property (nonatomic, strong) UIColor *textShadowColor UI_APPEARANCE_SELECTOR; // default is [UIColor blackColor] + +/** + * The offset of the shadow from the title text in the thumb. The default is CGSizeMake(0, -1). + */ +@property (nonatomic, readwrite) CGSize textShadowOffset UI_APPEARANCE_SELECTOR; // default is CGSizeMake(0, -1) + +/** + * Whether or not the thumb should cast a shadow on the rest of the control. The default is YES. + * This value is set to NO if the backgroundImage property is set to something other than nil. + */ +@property (nonatomic, readwrite) BOOL shouldCastShadow UI_APPEARANCE_SELECTOR; // default is YES (NO when backgroundImage is set) + +/** + * How much darker to make the second color in the gradient. Valid values range from 0 (no gradient) + * to 1 (gradient from tintColor to black). The default is 0.15. + */ +@property (nonatomic, assign) CGFloat gradientIntensity UI_APPEARANCE_SELECTOR; // default is 0.15 @end diff --git a/SVSegmentedControl/SVSegmentedThumb.m b/SVSegmentedControl/SVSegmentedThumb.m index ca494d1..6edff93 100644 --- a/SVSegmentedControl/SVSegmentedThumb.m +++ b/SVSegmentedControl/SVSegmentedThumb.m @@ -35,7 +35,6 @@ - (void)deactivate; @implementation SVSegmentedThumb -@synthesize segmentedControl, backgroundImage, highlightedBackgroundImage, font, tintColor, textColor, textShadowColor, textShadowOffset, shouldCastShadow, selected; @synthesize firstLabel = _firstLabel; @synthesize secondLabel = _secondLabel; @synthesize firstImageView = _firstImageView; @@ -50,13 +49,12 @@ - (id)initWithFrame:(CGRect)frame { self.userInteractionEnabled = NO; self.clipsToBounds = YES; self.backgroundColor = [UIColor clearColor]; - self.textColor = [UIColor whiteColor]; - self.textShadowColor = [UIColor blackColor]; - self.textShadowOffset = CGSizeMake(0, -1); - self.tintColor = [UIColor grayColor]; - self.shouldCastShadow = YES; - self.backgroundColor = [UIColor clearColor]; - self.gradientIntensity = 0.15; + _textColor = [UIColor whiteColor]; + _textShadowColor = [UIColor blackColor]; + _textShadowOffset = CGSizeMake(0, -1); + _tintColor = [UIColor grayColor]; + _shouldCastShadow = YES; + _gradientIntensity = 0.15; } return self; @@ -109,6 +107,7 @@ - (UIImageView *)imageView { _firstImageView = [[UIImageView alloc] initWithFrame:CGRectZero]; _firstImageView.layer.shadowOpacity = 1; _firstImageView.layer.shadowRadius = 0; + _firstImageView.layer.shadowOffset = CGSizeMake(0,-1); [self addSubview:_firstImageView]; } return _firstImageView; @@ -119,6 +118,7 @@ - (UIImageView *)secondImageView { _secondImageView = [[UIImageView alloc] initWithFrame:CGRectZero]; _secondImageView.layer.shadowOpacity = 1; _secondImageView.layer.shadowRadius = 0; + _secondImageView.layer.shadowOffset = CGSizeMake(0,-1); [self addSubview:_secondImageView]; } return _secondImageView; @@ -299,11 +299,11 @@ - (void)arrangeLabel:(UILabel*)label imageView:(UIImageView*)imageView { - (void)setBackgroundImage:(UIImage *)newImage { - if(backgroundImage) - backgroundImage = nil; + if(_backgroundImage) + _backgroundImage = nil; if(newImage) { - backgroundImage = newImage; + _backgroundImage = newImage; self.shouldCastShadow = NO; } else { self.shouldCastShadow = YES; @@ -312,11 +312,11 @@ - (void)setBackgroundImage:(UIImage *)newImage { - (void)setTintColor:(UIColor *)newColor { - if(tintColor) - tintColor = nil; + if(_tintColor) + _tintColor = nil; if(newColor) - tintColor = newColor; + _tintColor = newColor; [self setNeedsDisplay]; } @@ -327,13 +327,13 @@ - (void)setFont:(UIFont *)newFont { } - (void)setTextColor:(UIColor *)newColor { - textColor = newColor; + _textColor = newColor; self.label.textColor = newColor; self.secondLabel.textColor = newColor; } - (void)setTextShadowColor:(UIColor *)newColor { - textShadowColor = newColor; + _textShadowColor = newColor; self.label.shadowColor = newColor; self.secondLabel.shadowColor = newColor; self.imageView.layer.shadowColor = newColor.CGColor; @@ -341,7 +341,7 @@ - (void)setTextShadowColor:(UIColor *)newColor { } - (void)setTextShadowOffset:(CGSize)newOffset { - textShadowOffset = newOffset; + _textShadowOffset = newOffset; self.label.shadowOffset = newOffset; self.secondLabel.shadowOffset = newOffset; self.imageView.layer.shadowOffset = newOffset; @@ -365,9 +365,9 @@ - (void)setFrame:(CGRect)newFrame { - (void)setSelected:(BOOL)s { - selected = s; + _selected = s; - if(selected && !self.segmentedControl.crossFadeLabelsOnDrag && !self.highlightedBackgroundImage) + if(_selected && !self.segmentedControl.crossFadeLabelsOnDrag && !self.highlightedBackgroundImage) self.alpha = 0.8; else self.alpha = 1;