diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d522f94 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control? +# +# Pods/ + diff --git a/AYVibrantButton/AYVibrantButton.h b/AYVibrantButton/AYVibrantButton.h new file mode 100644 index 0000000..a27a00a --- /dev/null +++ b/AYVibrantButton/AYVibrantButton.h @@ -0,0 +1,83 @@ +// +// AYVibrantButton.h +// AYVibrantButton +// +// http://github.com/a1anyip/AYVibrantButton +// +// The MIT License (MIT) +// +// Copyright (c) 2014 Alan Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +@import UIKit; + +typedef enum { + + AYVibrantButtonStyleInvert, + AYVibrantButtonStyleTranslucent + +} AYVibrantButtonStyle; + +@interface AYVibrantButton : UIButton + +@property (nonatomic, assign) BOOL animated; +@property (nonatomic, assign) CGFloat animationInterval; +@property (nonatomic, assign) CGFloat translucencyAlpha; +@property (nonatomic, assign) CGFloat cornerRadius; +@property (nonatomic, assign) CGFloat borderWidth; +@property (nonatomic, copy) NSString *text; +@property (nonatomic, retain) UIFont *font; + +#ifdef __IPHONE_8_0 +// the vibrancy effect to be applied on the button +@property (nonatomic, retain) UIVibrancyEffect *vibrancyEffect; +#endif + +// the background color when vibrancy effect is nil, or not supported. +@property (nonatomic, retain) UIColor *backgroundColor; + +// this is the only method to initialize a vibrant button +- (instancetype)initWithFrame:(CGRect)frame style:(AYVibrantButtonStyle)style; + +@end + +typedef enum { + + AYVibrantButtonOverlayStyleNormal, + AYVibrantButtonOverlayStyleInvert + +} AYVibrantButtonOverlayStyle; + +@interface AYVibrantButtonOverlay : UIView + +// numeric configurations +@property (nonatomic, assign) CGFloat cornerRadius; +@property (nonatomic, assign) CGFloat borderWidth; + +// display text +@property (nonatomic, copy) NSString *text; +@property (nonatomic, retain) UIFont *font; + +// background color +@property (nonatomic, retain) UIColor *backgroundColor; + +- (instancetype)initWithStyle:(AYVibrantButtonOverlayStyle)style; + +@end \ No newline at end of file diff --git a/AYVibrantButton/AYVibrantButton.m b/AYVibrantButton/AYVibrantButton.m new file mode 100644 index 0000000..f5c1884 --- /dev/null +++ b/AYVibrantButton/AYVibrantButton.m @@ -0,0 +1,334 @@ +// +// AYVibrantButton.m +// AYVibrantButton +// +// http://github.com/a1anyip/AYVibrantButton +// +// The MIT License (MIT) +// +// Copyright (c) 2014 Alan Yip +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#import "AYVibrantButton.h" + +#define kAYVibrantButtonDefaultAnimationInterval 0.15 +#define kAYVibrantButtonDefaultTranslucencyAlpha 0.6 +#define kAYVibrantButtonDefaultCornerRadius 4.0 +#define kAYVibrantButtonDefaultBorderWidth 0.6 +#define kAYVibrantButtonDefaultFontSize 14.0 +#define kAYVibrantButtonDefaultBackgroundColor [UIColor whiteColor] + +@interface AYVibrantButton () { + + __strong UIColor *_backgroundColor; +} + +@property (nonatomic, assign) AYVibrantButtonStyle style; + +#ifdef __IPHONE_8_0 +@property (nonatomic, strong) UIVisualEffectView *visualEffectView; +#endif + +@property (nonatomic, strong) AYVibrantButtonOverlay *normalOverlay; +@property (nonatomic, strong) AYVibrantButtonOverlay *highlightedOverlay; + +- (void)createOverlays; + +@end + +@implementation AYVibrantButton + +- (instancetype)init { + NSLog(@"AYVibrantButton must be initialized with initWithFrame:style:"); + return nil; +} + +- (instancetype)initWithFrame:(CGRect)frame { + NSLog(@"AYVibrantButton must be initialized with initWithFrame:style:"); + return nil; +} + +- (instancetype)initWithFrame:(CGRect)frame style:(AYVibrantButtonStyle)style { + if (self = [super initWithFrame:frame]) { + + self.style = style; + self.opaque = NO; + self.userInteractionEnabled = YES; + + // default values + _animated = YES; + _animationInterval = kAYVibrantButtonDefaultAnimationInterval; + _cornerRadius = kAYVibrantButtonDefaultCornerRadius; + _borderWidth = kAYVibrantButtonDefaultBorderWidth; + _translucencyAlpha = kAYVibrantButtonDefaultTranslucencyAlpha; + + // create overlay views + [self createOverlays]; + +#ifdef __IPHONE_8_0 + // add the default vibrancy effect + self.vibrancyEffect = [UIVibrancyEffect effectForBlurEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleLight]]; +#endif + + [self addTarget:self action:@selector(touchDown) forControlEvents:UIControlEventTouchDown | UIControlEventTouchDragInside]; + [self addTarget:self action:@selector(touchUp) forControlEvents:UIControlEventTouchUpInside | UIControlEventTouchUpOutside | UIControlEventTouchDragOutside | UIControlEventTouchCancel]; + } + return self; +} + +- (void)layoutSubviews { +#ifdef __IPHONE_8_0 + self.visualEffectView.frame = self.bounds; +#endif + self.normalOverlay.frame = self.bounds; + self.highlightedOverlay.frame = self.bounds; +} + +- (void)createOverlays { + + self.normalOverlay = [[AYVibrantButtonOverlay alloc] initWithStyle:AYVibrantButtonOverlayStyleNormal]; + + if (self.style == AYVibrantButtonStyleInvert) { + self.highlightedOverlay = [[AYVibrantButtonOverlay alloc] initWithStyle:AYVibrantButtonOverlayStyleInvert]; + self.highlightedOverlay.alpha = 0.0; + } else if (self.style == AYVibrantButtonStyleTranslucent) { + self.normalOverlay.alpha = self.translucencyAlpha; + } + +#ifndef __IPHONE_8_0 + // for iOS 8, these two overlay views will be added as subviews in setVibrancyEffect: + [self addSubview:self.normalOverlay]; + [self addSubview:self.highlightedOverlay]; +#endif + +} + +#pragma mark - Control Event Handlers + +- (void)touchDown { + + void(^update)(void) = ^(void) { + if (self.style == AYVibrantButtonStyleInvert) { + self.normalOverlay.alpha = 0.0; + self.highlightedOverlay.alpha = 1.0; + } else if (self.style == AYVibrantButtonStyleTranslucent) { + self.normalOverlay.alpha = 1.0; + } + }; + + if (self.animated) { + [UIView animateWithDuration:self.animationInterval animations:update]; + } else { + update(); + } +} + +- (void)touchUp { + + void(^update)(void) = ^(void) { + if (self.style == AYVibrantButtonStyleInvert) { + self.normalOverlay.alpha = 1.0; + self.highlightedOverlay.alpha = 0.0; + } else if (self.style == AYVibrantButtonStyleTranslucent) { + self.normalOverlay.alpha = self.translucencyAlpha; + } + }; + + if (self.animated) { + [UIView animateWithDuration:self.animationInterval animations:update]; + } else { + update(); + } +} + +#pragma mark - Override Getters + +- (UIColor *)backgroundColor { + return _backgroundColor == nil ? kAYVibrantButtonDefaultBackgroundColor : _backgroundColor; +} + +#pragma mark - Override Setters + +- (void)setCornerRadius:(CGFloat)cornerRadius { + self.normalOverlay.cornerRadius = cornerRadius; + self.highlightedOverlay.cornerRadius = cornerRadius; +} + +- (void)setBorderWidth:(CGFloat)borderWidth { + self.normalOverlay.borderWidth = borderWidth; + self.highlightedOverlay.borderWidth = borderWidth; +} + +- (void)setText:(NSString *)text { + self.normalOverlay.text = text; + self.highlightedOverlay.text = text; +} + +- (void)setFont:(UIFont *)font { + self.normalOverlay.font = font; + self.highlightedOverlay.font = font; +} + +#ifdef __IPHONE_8_0 +- (void)setVibrancyEffect:(UIVibrancyEffect *)vibrancyEffect { + + [self.normalOverlay removeFromSuperview]; + [self.highlightedOverlay removeFromSuperview]; + [self.visualEffectView removeFromSuperview]; + + if (vibrancyEffect != nil) { + self.visualEffectView = [[UIVisualEffectView alloc] initWithEffect:vibrancyEffect]; + self.visualEffectView.userInteractionEnabled = NO; + [self.visualEffectView.contentView addSubview:self.normalOverlay]; + [self.visualEffectView.contentView addSubview:self.highlightedOverlay]; + [self addSubview:self.visualEffectView]; + } else { + [self addSubview:self.normalOverlay]; + [self addSubview:self.highlightedOverlay]; + } +} +#endif + +- (void)setBackgroundColor:(UIColor *)backgroundColor { + self.normalOverlay.backgroundColor = backgroundColor; + self.highlightedOverlay.backgroundColor = backgroundColor; +} + +@end + +@interface AYVibrantButtonOverlay () { + + __strong UIFont *_font; + __strong UIColor *_backgroundColor; +} + +@property (nonatomic, assign) AYVibrantButtonOverlayStyle style; +@property (nonatomic, assign) CGFloat textHeight; + +- (void)_updateTextHeight; + +@end + +@implementation AYVibrantButtonOverlay + +- (instancetype)initWithStyle:(AYVibrantButtonOverlayStyle)style { + if (self = [self init]) { + self.style = style; + } + return self; +} + +- (instancetype)init { + if (self = [super init]) { + + _cornerRadius = kAYVibrantButtonDefaultCornerRadius; + _borderWidth = kAYVibrantButtonDefaultBorderWidth; + + self.opaque = NO; + self.userInteractionEnabled = NO; + } + return self; +} + +- (void)drawRect:(CGRect)rect { + + [super drawRect:rect]; + + if (self.bounds.size.width == 0 || self.bounds.size.height == 0) return; + + CGContextRef context = UIGraphicsGetCurrentContext(); + CGContextClearRect(context, self.bounds); + + // draw background and border + UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(self.bounds, self.borderWidth, self.borderWidth) cornerRadius:self.cornerRadius]; + path.lineWidth = self.borderWidth; + [self.backgroundColor setStroke]; + [path stroke]; + + if (self.style == AYVibrantButtonOverlayStyleInvert) { + // fill the rounded rectangle area + [self.backgroundColor setFill]; + [path fill]; + } + + // draw text + if (self.text != nil) { + + NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; + style.lineBreakMode = NSLineBreakByTruncatingTail; + style.alignment = NSTextAlignmentCenter; + + if (self.style == AYVibrantButtonOverlayStyleInvert) { + // this will make the drawInRect method below clear the text area + CGContextSetBlendMode(context, kCGBlendModeClear); + } + + [self.text drawInRect:CGRectMake(0.0, (self.bounds.size.height - self.textHeight) / 2, self.bounds.size.width, self.textHeight) withAttributes:@{ NSFontAttributeName:self.font, NSForegroundColorAttributeName:self.backgroundColor, NSParagraphStyleAttributeName:style }]; + } +} + +#pragma mark - Override Getters + +- (UIFont *)font { + if (_font == nil) _font = [UIFont systemFontOfSize:kAYVibrantButtonDefaultFontSize]; + return _font; +} + +- (UIColor *)backgroundColor { + return _backgroundColor == nil ? kAYVibrantButtonDefaultBackgroundColor : _backgroundColor; +} + +#pragma mark - Override Setters + +- (void)setCornerRadius:(CGFloat)cornerRadius { + _cornerRadius = cornerRadius; + [self setNeedsDisplay]; +} + +- (void)setBorderWidth:(CGFloat)borderWidth { + _borderWidth = borderWidth; + [self setNeedsDisplay]; +} + +- (void)setText:(NSString *)text { + _text = text; + [self _updateTextHeight]; + [self setNeedsDisplay]; +} + +- (void)setFont:(UIFont *)font { + _font = font; + [self _updateTextHeight]; + [self setNeedsDisplay]; +} + +- (void)setBackgroundColor:(UIColor *)backgroundColor { + _backgroundColor = backgroundColor; + [self setNeedsDisplay]; +} + +#pragma mark - Private Methods + +- (void)_updateTextHeight { + CGRect bounds = [self.text boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{ NSFontAttributeName:self.font } context:nil]; + self.textHeight = bounds.size.height; +} + +@end diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..dd92343 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Alan Yip + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..fa7f9d5 --- /dev/null +++ b/README.md @@ -0,0 +1,99 @@ +AYVibrantButton +=============== + +AYVibrantButton is a stylish button with iOS 8 vibrancy effect. It is a subclass of `UIButton` that has a simple yet elegant appearance and built-in support for `UIVisualEffectView` and `UIVibrancyEffect` classes introduced in iOS 8. Yet, it can be used on iOS 7 without the vibrancy effect. + +## Configurations + +Vibrant buttons can be configured with one of the two supported button styles, **invert** and **translucent** (see examples below). Some basic properties like **text**, **font**, **corner radius**, **border width** and **background color** (for no vibrancy effect) can all be changed easily. + +The default vibrancy effect is for blur effect `UIBlurEffectStyleLight`. It could be set to any `UIVibrancyEffect` instance. For today extensions, it should be set to `[UIVibrancyEffect notificationCenterVibrancyEffect]`. + +## Note + +Though vibrant buttons can be placed anywhere, it is recommended that vibrant buttons with vibrancy effects should be placed in the `contentView` of `UIVisualEffectView` (except in today view). + +`UIVisualEffectView` can be created as follows. + +```objective-c +UIVisualEffectView *effectView = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleExtraLight]]; +effectView.frame = self.view.bounds; +[self.view addSubview:effectView]; +``` + +## Examples + +### Invert style with vibrancy effect + +![Invert Dark](https://github.com/a1anyip/AYVibrantButton/blob/master/Readme/invert2-dark.gif?raw=true) +![Invert Extra Light](https://github.com/a1anyip/AYVibrantButton/blob/master/Readme/invert2-extralight.gif?raw=true) + +![Invert Dark](https://github.com/a1anyip/AYVibrantButton/blob/master/Readme/invert-dark.gif?raw=true) +![Invert Extra Light](https://github.com/a1anyip/AYVibrantButton/blob/master/Readme/invert-extralight.gif?raw=true) + +````objective-c +AYVibrantButton *invertButton = [[AYVibrantButton alloc] initWithFrame:CGRectZero style:AYVibrantButtonStyleInvert]; +invertButton.vibrancyEffect = [UIVibrancyEffect effectForBlurEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleExtraLight]]; +invertButton.text = @"Invert"; +invertButton.font = [UIFont systemFontOfSize:18.0]; +[effectView.contentView addSubview:invertButton]; +``` + +### Translucent style with vibrancy effect + +![Translucent Dark](https://github.com/a1anyip/AYVibrantButton/blob/master/Readme/translucent-dark.gif?raw=true) +![Translucent Extra Light](https://github.com/a1anyip/AYVibrantButton/blob/master/Readme/translucent-extralight.gif?raw=true) + +````objective-c +AYVibrantButton *translucentButton = [[AYVibrantButton alloc] initWithFrame:CGRectZero style:AYVibrantButtonStyleTranslucent]; +translucentButton.vibrancyEffect = [UIVibrancyEffect effectForBlurEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleExtraLight]]; +translucentButton.text = @"Translucent"; +translucentButton.font = [UIFont systemFontOfSize:18.0]; +[effectView.contentView addSubview:translucentButton]; +``` + +### Translucent style without vibrancy effect + +![Translucent Dark](https://github.com/a1anyip/AYVibrantButton/blob/master/Readme/anycolor-dark.gif?raw=true) +![Translucent Extra Light](https://github.com/a1anyip/AYVibrantButton/blob/master/Readme/anycolor-extralight.gif?raw=true) + +````objective-c +AYVibrantButton *button = [[AYVibrantButton alloc] initWithFrame:CGRectZero style:AYVibrantButtonStyleTranslucent]; +button.vibrancyEffect = [UIVibrancyEffect effectForBlurEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleExtraLight]]; +button.text = @"Any Color"; +button.font = [UIFont systemFontOfSize:18.0]; +button.backgroundColor = [UIColor blackColor]; +[effectView.contentView addSubview:button]; +``` + +## Creator + + +**Alan Yip** +* [http://alanyip.me](http://alanyip.me) +* [@tweakcc](https://twitter.com/tweakcc) + +## License +``` +The MIT License (MIT) + +Copyright (c) 2014 Alan Yip + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +``` \ No newline at end of file diff --git a/Readme/anycolor-dark.gif b/Readme/anycolor-dark.gif new file mode 100644 index 0000000..3c963da Binary files /dev/null and b/Readme/anycolor-dark.gif differ diff --git a/Readme/anycolor-extralight.gif b/Readme/anycolor-extralight.gif new file mode 100644 index 0000000..b6c1cb1 Binary files /dev/null and b/Readme/anycolor-extralight.gif differ diff --git a/Readme/invert-dark.gif b/Readme/invert-dark.gif new file mode 100644 index 0000000..589b488 Binary files /dev/null and b/Readme/invert-dark.gif differ diff --git a/Readme/invert-extralight.gif b/Readme/invert-extralight.gif new file mode 100644 index 0000000..c280adf Binary files /dev/null and b/Readme/invert-extralight.gif differ diff --git a/Readme/invert2-dark.gif b/Readme/invert2-dark.gif new file mode 100644 index 0000000..27ae7c4 Binary files /dev/null and b/Readme/invert2-dark.gif differ diff --git a/Readme/invert2-extralight.gif b/Readme/invert2-extralight.gif new file mode 100644 index 0000000..b08b8a3 Binary files /dev/null and b/Readme/invert2-extralight.gif differ diff --git a/Readme/translucent-dark.gif b/Readme/translucent-dark.gif new file mode 100644 index 0000000..9167b1d Binary files /dev/null and b/Readme/translucent-dark.gif differ diff --git a/Readme/translucent-extralight.gif b/Readme/translucent-extralight.gif new file mode 100644 index 0000000..56a988b Binary files /dev/null and b/Readme/translucent-extralight.gif differ