Refactor WBNoticeView for simplicity and to support easier implementation of additional notice types.

* Reorganizes codebase to rely on properties instead of methods.
* Adds `ForSubclassEyesOnly` category to expose protected interfaces needed by subclasses to implement notice types.
* Removes the `noticeType` notion in favor of using subclassing to create notice types.
* Removes seemingly unused `defaultManager` static method.
* Removes `_raiseIfObjectIsNil:named:` method in favor of `NSParameterAssert`
* Adds Appledoc documentation to all public interfaces.
* Adds a Boolean argument to the dismiss callback block to disambiguate interactive vs. non-interactive dismissal.
* Replaces the dimissed block property with a setter method to enable code completion in Xcode.
* Adds Default-568h@2x.png image to suppress Xcode warning.
This commit is contained in:
Blake Watters
2012-11-12 23:10:13 -05:00
parent 793fcb725a
commit 6286f99219
13 changed files with 416 additions and 612 deletions

BIN
Default-568h@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -7,6 +7,7 @@
objects = {
/* Begin PBXBuildFile section */
250628781651F7DC00D3443E /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 250628771651F7DB00D3443E /* Default-568h@2x.png */; };
592BDDE915645BEE00B78820 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 592BDDE115645BEE00B78820 /* Default.png */; };
592BDDEA15645BEE00B78820 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 592BDDE215645BEE00B78820 /* Default@2x.png */; };
592BDDEB15645BEE00B78820 /* NoticeView.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 592BDDE415645BEE00B78820 /* NoticeView.bundle */; };
@@ -31,6 +32,8 @@
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
250628731651E78600D3443E /* WBNoticeView+ForSubclassEyesOnly.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "WBNoticeView+ForSubclassEyesOnly.h"; sourceTree = "<group>"; };
250628771651F7DB00D3443E /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "../../Default-568h@2x.png"; sourceTree = "<group>"; };
592BDDE115645BEE00B78820 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = "<group>"; };
592BDDE215645BEE00B78820 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = "<group>"; };
592BDDE415645BEE00B78820 /* NoticeView.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = NoticeView.bundle; sourceTree = "<group>"; };
@@ -55,7 +58,6 @@
59A782581562F8750001F08D /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; };
746CBCFE1570A1D100B844B1 /* WBErrorNoticeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WBErrorNoticeView.h; sourceTree = "<group>"; };
746CBCFF1570A1D100B844B1 /* WBErrorNoticeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WBErrorNoticeView.m; sourceTree = "<group>"; };
746CBD011570A65D00B844B1 /* WBNoticeView_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WBNoticeView_Private.h; sourceTree = "<group>"; };
746CBD061570A8FA00B844B1 /* WBSuccessNoticeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WBSuccessNoticeView.h; sourceTree = "<group>"; };
746CBD071570A8FA00B844B1 /* WBSuccessNoticeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WBSuccessNoticeView.m; sourceTree = "<group>"; };
748A6A57157D0B74003C7655 /* WBGrayGradientView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WBGrayGradientView.h; sourceTree = "<group>"; };
@@ -86,6 +88,7 @@
592BDDE015645BEE00B78820 /* Images */ = {
isa = PBXGroup;
children = (
250628771651F7DB00D3443E /* Default-568h@2x.png */,
595FE868156F021E004C505B /* navigationBar.png */,
592BDDE115645BEE00B78820 /* Default.png */,
592BDDE215645BEE00B78820 /* Default@2x.png */,
@@ -166,7 +169,6 @@
children = (
592BDDE715645BEE00B78820 /* WBNoticeView.h */,
592BDDE815645BEE00B78820 /* WBNoticeView.m */,
746CBD011570A65D00B844B1 /* WBNoticeView_Private.h */,
592BDDE415645BEE00B78820 /* NoticeView.bundle */,
592BDDE515645BEE00B78820 /* UILabel+WBExtensions.h */,
592BDDE615645BEE00B78820 /* UILabel+WBExtensions.m */,
@@ -176,6 +178,7 @@
748A6A58157D0B74003C7655 /* WBGrayGradientView.m */,
7492FA43157C8CD700FB834C /* WBRedGradientView.h */,
7492FA44157C8CD700FB834C /* WBRedGradientView.m */,
250628731651E78600D3443E /* WBNoticeView+ForSubclassEyesOnly.h */,
);
name = Private;
sourceTree = "<group>";
@@ -238,6 +241,7 @@
592BDDEA15645BEE00B78820 /* Default@2x.png in Resources */,
592BDDEB15645BEE00B78820 /* NoticeView.bundle in Resources */,
595FE869156F021E004C505B /* navigationBar.png in Resources */,
250628781651F7DC00D3443E /* Default-568h@2x.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@@ -8,10 +8,23 @@
#import "WBNoticeView.h"
/**
The `WBErrorNoticeView` class is a `WBNoticeView` subclass suitable for displaying an error to a user. The notice is presented on a red gradient background with an error icon on the left hand side of the notice. It supports the display of a title and a message.
*/
@interface WBErrorNoticeView : WBNoticeView
///-------------------------------
/// @name Creating an Error Notice
///-------------------------------
/**
Creates and returns an error notice in the given view with the specified title and message.
@param view The view to display the notice in.
@param title The title of the notice.
@param message The message of the notice.
@return The newly created error notice object.
*/
+ (WBErrorNoticeView *)errorNoticeInView:(UIView *)view title:(NSString *)title message:(NSString *)message;
@property (nonatomic, strong) NSString *message; // default: @"Information not provided."
@end

View File

@@ -7,7 +7,8 @@
//
#import "WBErrorNoticeView.h"
#import "WBNoticeView_Private.h"
#import "WBNoticeView+ForSubclassEyesOnly.h"
#import "WBRedGradientView.h"
@implementation WBErrorNoticeView
@@ -25,14 +26,91 @@
- (void)show
{
[self _showNoticeOfType:WBNoticeViewTypeError
view:self.view
title:self.title
message:self.message
duration:self.duration
delay:self.delay
alpha:self.alpha
yOrigin:self.originY];
// Obtain the screen width
CGFloat viewWidth = self.view.frame.size.width;
// Locate the images
NSString *path = [[[NSBundle mainBundle]resourcePath]stringByAppendingPathComponent:@"NoticeView.bundle"];
NSString *noticeIconImageName = [path stringByAppendingPathComponent:@"notice_error_icon.png"];
// Make and add the title label
float titleYOrigin = 10.0;
self.titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(55.0, titleYOrigin, viewWidth - 70.0, 16.0)];
self.titleLabel.textColor = [UIColor whiteColor];
self.titleLabel.shadowOffset = CGSizeMake(0.0, -1.0);
self.titleLabel.shadowColor = [UIColor blackColor];
self.titleLabel.font = [UIFont boldSystemFontOfSize:14.0];
self.titleLabel.backgroundColor = [UIColor clearColor];
self.titleLabel.text = self.title;
// Make the message label
self.messageLabel = [[UILabel alloc]initWithFrame:CGRectMake(55.0, 20.0 + 10.0, viewWidth - 70.0, 12.0)];
self.messageLabel.font = [UIFont systemFontOfSize:13.0];
self.messageLabel.textColor = [UIColor colorWithRed:239.0/255.0 green:167.0/255.0 blue:163.0/255.0 alpha:1.0];
self.messageLabel.backgroundColor = [UIColor clearColor];
self.messageLabel.text = message;
// Calculate the number of lines it'll take to display the text
NSInteger numberOfLines = [[self.messageLabel lines]count];
self.messageLabel.numberOfLines = numberOfLines;
[self.messageLabel sizeToFit];
CGFloat messageLabelHeight = self.messageLabel.frame.size.height;
CGRect r = self.messageLabel.frame;
r.origin.y = self.titleLabel.frame.origin.y + self.titleLabel.frame.size.height;
float noticeViewHeight = 0.0;
double currOsVersion = [[[UIDevice currentDevice]systemVersion]doubleValue];
if (currOsVersion >= 6.0f) {
noticeViewHeight = messageLabelHeight;
} else {
// Now we can determine the height of one line of text
r.size.height = self.messageLabel.frame.size.height * numberOfLines;
r.size.width = viewWidth - 70.0;
self.messageLabel.frame = r;
// Calculate the notice view height
noticeViewHeight = 10.0;
if (numberOfLines > 1) {
noticeViewHeight += ((numberOfLines - 1) * messageLabelHeight);
}
}
// Add some bottom margin for the notice view
noticeViewHeight += 30.0;
// Make sure we hide completely the view, including its shadow
float hiddenYOrigin = -noticeViewHeight - 20.0;
// Make and add the notice view
self.gradientView = [[WBRedGradientView alloc] initWithFrame:CGRectMake(0.0, hiddenYOrigin, viewWidth, noticeViewHeight + 10.0)];
[self.view addSubview:self.gradientView];
// Make and add the icon view
UIImageView *iconView = [[UIImageView alloc]initWithFrame:CGRectMake(10.0, 10.0, 20.0, 30.0)];
iconView.image = [UIImage imageWithContentsOfFile:noticeIconImageName];
iconView.contentMode = UIViewContentModeScaleAspectFit;
iconView.alpha = 0.8;
[self.gradientView addSubview:iconView];
// Add the title label
[self.gradientView addSubview:self.titleLabel];
// Add the message label
[self.gradientView addSubview:self.messageLabel];
// Add the drop shadow to the notice view
CALayer *noticeLayer = self.gradientView.layer;
noticeLayer.shadowColor = [[UIColor blackColor]CGColor];
noticeLayer.shadowOffset = CGSizeMake(0.0, 3);
noticeLayer.shadowOpacity = 0.50;
noticeLayer.masksToBounds = NO;
noticeLayer.shouldRasterize = YES;
self.hiddenYOrigin = hiddenYOrigin;
[self displayNotice];
}
@end

View File

@@ -0,0 +1,25 @@
//
// WBNoticeView+ForSubclassEyesOnly.h
// NoticeView
//
// Created by Blake Watters on 11/12/12.
// Copyright (c) 2012 Tito Ciuro. All rights reserved.
//
#import <QuartzCore/QuartzCore.h>
#import "WBNoticeView.h"
#import "UILabel+WBExtensions.h"
@interface WBNoticeView (ForSubclassEyesOnly)
@property(nonatomic, strong) UIView *gradientView;
@property(nonatomic, strong) UILabel *titleLabel;
@property(nonatomic, strong) UILabel *messageLabel;
@property(nonatomic, assign) CGFloat hiddenYOrigin;
/**
Tells the superclass to display the notice.
*/
- (void)displayNotice;
@end

View File

@@ -8,76 +8,111 @@
#import <Foundation/Foundation.h>
/**
`WBNoticeView` objects provides a lightweight, non-intrusive means for displaying information to the user. The `WBNoticeView` class is an abstract class that encapsulates the interface common to all notice objects.
*/
@interface WBNoticeView : NSObject
typedef enum {
WBNoticeViewTypeError = 0,
WBNoticeViewTypeSuccess,
WBNoticeViewTypeSticky
} WBNoticeViewType;
///----------------------------
/// @name Initializing a Notice
///----------------------------
typedef void (^WBNoticeViewDismissedBlock)(void);
/**
Initializes the receiver with the given origin view and title.
This is the designated initializer.
@param view The view from which the notice will originate when displayed.
@param title The title for the notice.
@return The receiver, initialized with the given view and title.
*/
- (id)initWithView:(UIView *)view title:(NSString *)title;
@property (nonatomic, readwrite) WBNoticeViewType noticeType;
///---------------------------------
/// @name Configuring Notice Display
///---------------------------------
@property (nonatomic, strong) UIView *view;
@property (nonatomic, strong) NSString *title; // default: @"Unknown Error"
/**
The view from which the notice will be displayed.
*/
@property (nonatomic, weak) UIView *view;
@property (nonatomic, readwrite) CGFloat duration; // default: 0.5
@property (nonatomic, readwrite) CGFloat delay; // default: 2.0
@property (nonatomic, readwrite) CGFloat alpha; // default: 1.0
@property (nonatomic, readwrite) CGFloat originY; // default: 0.0
@property (nonatomic, readwrite, getter = isSticky) BOOL sticky; // default NO (Error and Success notice); YES (Sticky notice)
@property (nonatomic, readwrite, strong) WBNoticeViewDismissedBlock dismissedBlock;
/**
The title text for the notice.
**Default**: `"Unknown Error"`
*/
@property (nonatomic, copy) NSString *title;
/**
The message for the notice. Not supported by all notice types.
*/
@property (nonatomic, copy) NSString *message;
+ (WBNoticeView *)defaultManager;
/**
The animation duration for the notice.
**Default**: `0.5`
*/
@property (nonatomic, readwrite) NSTimeInterval duration;
- (id)initWithView:(UIView *)theView title:(NSString *)theTitle; // throws NSInvalidArgumentException is view is nil.
/**
The time interval in seconds that the notice will be displayed before being automatically dismissed.
**Default**: `2.0`
*/
@property (nonatomic, readwrite) NSTimeInterval delay;
- (void)show; // Must be implemented in the subclasses, or else it'll raise an exception.
/**
The amount of transparency applied to the notice. Values can range between `0.0` (transparent) and `1.0` (opaque). Values outside this range are clamped to `0.0` or `1.0`.
**Default**: `1.0`
*/
@property (nonatomic, readwrite) CGFloat alpha;
- (void)dismissNotice; // Only succeeds if the notice is sticky.
/**
The number of points that the notice will be offset vertically from the origin view when being displayed.
**Default**: `0.0`
*/
@property (nonatomic, readwrite) CGFloat originY;
// Error notice methods
/**
A Boolean value that determines if the notice will be automatically dismissed after the time interval specified by the `delay` property expires.
**Default**: `NO`
*/
@property (nonatomic, readwrite, getter = isSticky) BOOL sticky;
- (void)showErrorNoticeInView:(UIView *)view
title:(NSString *)title
message:(NSString *)message;
///----------------------------------------
/// @name Showing and Dismissing the Notice
///----------------------------------------
- (void)showErrorNoticeInView:(UIView *)view
title:(NSString *)title
message:(NSString *)message
duration:(float)duration
delay:(float)delay
alpha:(float)alpha;
/**
Shows the notice.
@warning The `WBNoticeView` class is abstract. Concrete subclasses must provide an implementation of the `show` method or else an exception will be raised.
*/
- (void)show;
- (void)showErrorNoticeInView:(UIView *)view
title:(NSString *)title
message:(NSString *)message
duration:(float)duration
delay:(float)delay
alpha:(float)alpha
yOrigin:(CGFloat)origin;
/**
Dismisses the notice.
Only succeeds if the notice is sticky.
*/
- (void)dismissNotice;
// Success notice methods
///----------------------------------
/// @name Setting the Dismissal Block
///----------------------------------
- (void)showSuccessNoticeInView:(UIView *)view
message:(NSString *)message;
- (void)showSuccessNoticeInView:(UIView *)view
message:(NSString *)message
duration:(float)duration
delay:(float)delay
alpha:(float)alpha
yOrigin:(CGFloat)origin;
// Sticky notice methods
- (void)showStickyNoticeInView:(UIView *)view
message:(NSString *)message
duration:(float)duration
alpha:(float)alpha
yOrigin:(CGFloat)origin;
/**
Sets a block to be executed when the notice is dismissed.
The block accepts a single Boolean value that indicates if the notice was dismissed interactively by the user or was dismissed due to expiration of the display interval.
@param block The block to be executed when the notice is dismissed.
*/
- (void)setDismissalBlock:(void (^)(BOOL dismissedInteractively))block;
@end

View File

@@ -7,13 +7,7 @@
//
#import "WBNoticeView.h"
#import "WBNoticeView_Private.h"
#import "WBRedGradientView.h"
#import "WBBlueGradientView.h"
#import "WBGrayGradientView.h"
#import "UILabel+WBExtensions.h"
#import <QuartzCore/QuartzCore.h>
#import "WBNoticeView+ForSubclassEyesOnly.h"
@interface WBNoticeView ()
@@ -23,41 +17,12 @@
@property(nonatomic, assign) CGFloat hiddenYOrigin;
@property(nonatomic, strong) WBNoticeView *currentNotice;
@property(nonatomic, copy) void (^dismissalBlock)(BOOL dismissedInteractively);
- (void)_showErrorNoticeInView:(UIView *)view
title:(NSString *)title
message:(NSString *)message
duration:(float)duration
delay:(float)delay
alpha:(float)alpha
yOrigin:(CGFloat)origin;
- (void)_showSuccessNoticeInView:(UIView *)view
title:(NSString *)title
message:(NSString *)message
duration:(float)duration
delay:(float)delay
alpha:(float)alpha
yOrigin:(CGFloat)origin;
- (void)_showStickyNoticeInView:(UIView *)view
title:(NSString *)title
message:(NSString *)message
duration:(float)duration
delay:(float)delay
alpha:(float)alpha
yOrigin:(CGFloat)origin;
- (void)displayNoticeWithDuration:(CGFloat)duration
delay:(CGFloat)delay
origin:(CGFloat)origin
hiddenYOrigin:(CGFloat)hiddenYOrigin
alpha:(CGFloat)alpha;
- (void)dismissNoticeWithDuration:(CGFloat)duration
delay:(CGFloat)delay
hiddenYOrigin:(CGFloat)hiddenYOrigin;
- (void)dismissNoticeWithDuration:(NSTimeInterval)duration
delay:(NSTimeInterval)delay
hiddenYOrigin:(CGFloat)hiddenYOrigin
interactively:(BOOL)interactively;
- (void)cleanup;
@end
@@ -69,7 +34,6 @@
@synthesize messageLabel = _messageLabel;
@synthesize hiddenYOrigin = _hiddenYOrigin;
@synthesize currentNotice = _currentNotice;
@synthesize noticeType = _noticeType;
@synthesize view = _view;
@synthesize title = _title;
@synthesize duration = _duration;
@@ -77,29 +41,26 @@
@synthesize alpha = _alpha;
@synthesize originY = _originY;
@synthesize sticky = _sticky;
@synthesize dismissedBlock = _dismissedBlock;
@synthesize dismissalBlock = _dismissalBlock;
+ (WBNoticeView *)defaultManager
- (id)initWithView:(UIView *)view title:(NSString *)title
{
static WBNoticeView *__sWBNoticeView = nil;
if (nil == __sWBNoticeView) {
__sWBNoticeView = [WBNoticeView new];
NSParameterAssert(view);
self = [super init];
if (self) {
self.view = view;
self.title = title ?: @"Unknown Error";
self.message = @"Information not provided";
self.duration = 0.5;
self.alpha = 1.0;
self.delay = 2.0;
}
return __sWBNoticeView;
return self;
}
- (id)initWithView:(UIView *)theView title:(NSString *)theTitle
- (NSTimeInterval)delay
{
[WBNoticeView _raiseIfObjectIsNil:theView named:@"view"];
if (self = [super init]) {
self.view = theView;
self.title = theTitle;
}
return self;
return [self isSticky] ? 0.0 : _delay;
}
- (void)show
@@ -108,421 +69,14 @@
[self doesNotRecognizeSelector:_cmd];
}
#pragma mark - Error Notice Methods
- (void)showErrorNoticeInView:(UIView *)view
title:(NSString *)title
message:(NSString *)message
{
[self _showNoticeOfType:WBNoticeViewTypeError
view:view
title:title
message:message
duration:0.0
delay:0.0
alpha:0.8
yOrigin:0.0];
}
- (void)showErrorNoticeInView:(UIView *)view
title:(NSString *)title
message:(NSString *)message
duration:(float)duration
delay:(float)delay
alpha:(float)alpha
{
[self _showNoticeOfType:WBNoticeViewTypeError
view:view
title:title
message:message
duration:duration
delay:delay
alpha:alpha
yOrigin:0.0];
}
- (void)showErrorNoticeInView:(UIView *)view
title:(NSString *)title
message:(NSString *)message
duration:(float)duration
delay:(float)delay
alpha:(float)alpha
yOrigin:(CGFloat)origin
{
[self _showNoticeOfType:WBNoticeViewTypeError
view:view
title:title
message:message
duration:duration
delay:delay
alpha:alpha
yOrigin:origin];
}
#pragma mark -
- (void)showSuccessNoticeInView:(UIView *)view
message:(NSString *)message
{
[self _showNoticeOfType:WBNoticeViewTypeSuccess
view:view
title:message
message:nil
duration:0.0
delay:0.0
alpha:0.8
yOrigin:0.0];
}
- (void)showSuccessNoticeInView:(UIView *)view
message:(NSString *)message
duration:(float)duration
delay:(float)delay
alpha:(float)alpha
{
[self _showNoticeOfType:WBNoticeViewTypeSuccess
view:view
title:message
message:nil
duration:duration
delay:delay
alpha:alpha
yOrigin:0.0];
}
- (void)showSuccessNoticeInView:(UIView *)view
message:(NSString *)message
duration:(float)duration
delay:(float)delay
alpha:(float)alpha
yOrigin:(CGFloat)origin
{
[self _showNoticeOfType:WBNoticeViewTypeSuccess
view:view
title:message
message:nil
duration:duration
delay:delay
alpha:alpha
yOrigin:origin];
}
#pragma mark -
- (void)showStickyNoticeInView:(UIView *)view
message:(NSString *)message
duration:(float)duration
alpha:(float)alpha
yOrigin:(CGFloat)origin
{
[self _showNoticeOfType:WBNoticeViewTypeSticky
view:view
title:message
message:nil
duration:duration
delay:0.0
alpha:alpha
yOrigin:origin];
}
#pragma mark - Private Section
- (void)_showNoticeOfType:(WBNoticeViewType)noticeType
view:(UIView *)view
title:(NSString *)title
message:(NSString *)message
duration:(float)duration
delay:(float)delay
alpha:(float)alpha
yOrigin:(CGFloat)origin
{
if (nil == self.gradientView) {
// Set default values if needed
if (nil == title) title = @"Unknown Error";
if (nil == message) message = @"";//@"Information not provided.";
if (0.0 == duration) duration = 0.5;
if (0.0 == alpha) alpha = 1.0;
if (origin < 0.0) origin = 0.0;
if (self.isSticky) {
delay = 0.0;
} else {
if (0.0 == delay) delay = 2.0;
}
switch (noticeType) {
case WBNoticeViewTypeError:
[self _showErrorNoticeInView:view title:title message:message duration:duration delay:delay alpha:alpha yOrigin:origin];
break;
case WBNoticeViewTypeSuccess:
[self _showSuccessNoticeInView:view title:title message:message duration:duration delay:delay alpha:alpha yOrigin:origin];
break;
case WBNoticeViewTypeSticky:
[self _showStickyNoticeInView:view title:title message:message duration:duration delay:delay alpha:alpha yOrigin:origin];
break;
}
}
}
#pragma mark -
- (void)_showErrorNoticeInView:(UIView *)view
title:(NSString *)title
message:(NSString *)message
duration:(float)duration
delay:(float)delay
alpha:(float)alpha
yOrigin:(CGFloat)origin
{
// Obtain the screen width
CGFloat viewWidth = view.frame.size.width;
// Locate the images
NSString *path = [[[NSBundle mainBundle]resourcePath]stringByAppendingPathComponent:@"NoticeView.bundle"];
NSString *noticeIconImageName = [path stringByAppendingPathComponent:@"notice_error_icon.png"];
// Make and add the title label
float titleYOrigin = 10.0;
self.titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(55.0, titleYOrigin, viewWidth - 70.0, 16.0)];
self.titleLabel.textColor = [UIColor whiteColor];
self.titleLabel.shadowOffset = CGSizeMake(0.0, -1.0);
self.titleLabel.shadowColor = [UIColor blackColor];
self.titleLabel.font = [UIFont boldSystemFontOfSize:14.0];
self.titleLabel.backgroundColor = [UIColor clearColor];
self.titleLabel.text = title;
// Make the message label
self.messageLabel = [[UILabel alloc]initWithFrame:CGRectMake(55.0, 20.0 + 10.0, viewWidth - 70.0, 12.0)];
self.messageLabel.font = [UIFont systemFontOfSize:13.0];
self.messageLabel.textColor = [UIColor colorWithRed:239.0/255.0 green:167.0/255.0 blue:163.0/255.0 alpha:1.0];
self.messageLabel.backgroundColor = [UIColor clearColor];
self.messageLabel.text = message;
// Calculate the number of lines it'll take to display the text
NSInteger numberOfLines = [[self.messageLabel lines]count];
self.messageLabel.numberOfLines = numberOfLines;
[self.messageLabel sizeToFit];
CGFloat messageLabelHeight = self.messageLabel.frame.size.height;
CGRect r = self.messageLabel.frame;
r.origin.y = self.titleLabel.frame.origin.y + self.titleLabel.frame.size.height;
float noticeViewHeight = 0.0;
double currOsVersion = [[[UIDevice currentDevice]systemVersion]doubleValue];
if (currOsVersion >= 6.0f) {
noticeViewHeight = messageLabelHeight;
} else {
// Now we can determine the height of one line of text
r.size.height = self.messageLabel.frame.size.height * numberOfLines;
r.size.width = viewWidth - 70.0;
self.messageLabel.frame = r;
// Calculate the notice view height
noticeViewHeight = 10.0;
if (numberOfLines > 1) {
noticeViewHeight += ((numberOfLines - 1) * messageLabelHeight);
}
}
// Add some bottom margin for the notice view
noticeViewHeight += 30.0;
// Make sure we hide completely the view, including its shadow
float hiddenYOrigin = -noticeViewHeight - 20.0;
// Make and add the notice view
self.gradientView = [[WBRedGradientView alloc]initWithFrame:CGRectMake(0.0, hiddenYOrigin, viewWidth, noticeViewHeight + 10.0)];
[view addSubview:self.gradientView];
// Make and add the icon view
UIImageView *iconView = [[UIImageView alloc]initWithFrame:CGRectMake(10.0, 10.0, 20.0, 30.0)];
iconView.image = [UIImage imageWithContentsOfFile:noticeIconImageName];
iconView.contentMode = UIViewContentModeScaleAspectFit;
iconView.alpha = 0.8;
[self.gradientView addSubview:iconView];
// Add the title label
[self.gradientView addSubview:self.titleLabel];
// Add the message label
[self.gradientView addSubview:self.messageLabel];
// Add the drop shadow to the notice view
CALayer *noticeLayer = self.gradientView.layer;
noticeLayer.shadowColor = [[UIColor blackColor]CGColor];
noticeLayer.shadowOffset = CGSizeMake(0.0, 3);
noticeLayer.shadowOpacity = 0.50;
noticeLayer.masksToBounds = NO;
noticeLayer.shouldRasterize = YES;
self.duration = duration;
self.delay = delay;
self.alpha = alpha;
self.hiddenYOrigin = hiddenYOrigin;
[self displayNoticeWithDuration:duration delay:delay origin:origin hiddenYOrigin:hiddenYOrigin alpha:alpha];
}
- (void)_showSuccessNoticeInView:(UIView *)view
title:(NSString *)title
message:(NSString *)message
duration:(float)duration
delay:(float)delay
alpha:(float)alpha
yOrigin:(CGFloat)origin
{
// Obtain the screen width
CGFloat viewWidth = view.frame.size.width;
// Locate the images
NSString *path = [[[NSBundle mainBundle]resourcePath]stringByAppendingPathComponent:@"NoticeView.bundle"];
NSString *noticeIconImageName = [path stringByAppendingPathComponent:@"notice_success_icon.png"];
NSInteger numberOfLines = 1;
CGFloat messageLineHeight = 30.0;
// Make and add the title label
float titleYOrigin = 18.0;
self.titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(55.0, titleYOrigin, viewWidth - 70.0, 16.0)];
self.titleLabel.textColor = [UIColor whiteColor];
self.titleLabel.shadowOffset = CGSizeMake(0.0, -1.0);
self.titleLabel.shadowColor = [UIColor blackColor];
self.titleLabel.font = [UIFont boldSystemFontOfSize:14.0];
self.titleLabel.backgroundColor = [UIColor clearColor];
self.titleLabel.text = title;
// Calculate the notice view height
float noticeViewHeight = 40.0;
float hiddenYOrigin = 0.0;
if (numberOfLines > 1) {
noticeViewHeight += (numberOfLines - 1) * messageLineHeight;
}
// Make sure we hide completely the view, including its shadow
hiddenYOrigin = -noticeViewHeight - 20.0;
// Make and add the notice view
self.gradientView = [[WBBlueGradientView alloc]initWithFrame:CGRectMake(0.0, hiddenYOrigin, viewWidth, noticeViewHeight + 10.0)];
[view addSubview:self.gradientView];
// Make and add the icon view
UIImageView *iconView = [[UIImageView alloc]initWithFrame:CGRectMake(10.0, 10.0, 20.0, 30.0)];
iconView.image = [UIImage imageWithContentsOfFile:noticeIconImageName];
iconView.contentMode = UIViewContentModeScaleAspectFit;
iconView.alpha = 0.8;
[self.gradientView addSubview:iconView];
// Add the title label
[self.gradientView addSubview:self.titleLabel];
// Add the drop shadow to the notice view
CALayer *noticeLayer = self.gradientView.layer;
noticeLayer.shadowColor = [[UIColor blackColor]CGColor];
noticeLayer.shadowOffset = CGSizeMake(0.0, 3);
noticeLayer.shadowOpacity = 0.50;
noticeLayer.masksToBounds = NO;
noticeLayer.shouldRasterize = YES;
self.duration = duration;
self.delay = delay;
self.alpha = alpha;
self.hiddenYOrigin = hiddenYOrigin;
[self displayNoticeWithDuration:duration delay:delay origin:origin hiddenYOrigin:hiddenYOrigin alpha:alpha];
}
- (void)_showStickyNoticeInView:(UIView *)view
title:(NSString *)title
message:(NSString *)message
duration:(float)duration
delay:(float)delay
alpha:(float)alpha
yOrigin:(CGFloat)origin
{
// Obtain the screen width
CGFloat viewWidth = view.frame.size.width;
// Locate the images
NSString *path = [[[NSBundle mainBundle]resourcePath]stringByAppendingPathComponent:@"NoticeView.bundle"];
NSString *noticeIconImageName = [path stringByAppendingPathComponent:@"up.png"];
NSInteger numberOfLines = 1;
CGFloat messageLineHeight = 30.0;
// Make and add the title label
self.titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(55.0, 8.0, viewWidth - 70.0, 16.0)];
self.titleLabel.textColor = [UIColor blackColor];
self.titleLabel.shadowOffset = CGSizeMake(0.0, 1.0);
self.titleLabel.shadowColor = [UIColor whiteColor];
self.titleLabel.font = [UIFont boldSystemFontOfSize:12.0];
self.titleLabel.backgroundColor = [UIColor clearColor];
self.titleLabel.text = title;
[self.titleLabel sizeToFit];
CGRect frame = self.titleLabel.frame;
if (frame.size.width > 260) {
frame.size.width = 260;
self.titleLabel.frame = frame;
}
// Calculate the notice view height
float noticeViewHeight = 40.0;
float hiddenYOrigin = 0.0;
if (numberOfLines > 1) {
noticeViewHeight += (numberOfLines - 1) * messageLineHeight;
}
// Make sure we hide completely the view, including its shadow
hiddenYOrigin = -noticeViewHeight - 20.0;
// Make and add the notice view
self.gradientView = [[WBGrayGradientView alloc]initWithFrame:CGRectMake(0.0, hiddenYOrigin, viewWidth, 32)];
[view addSubview:self.gradientView];
// Center the message in the middle of the notice
frame = self.titleLabel.frame;
frame.origin.x = (self.gradientView.frame.size.width - frame.size.width) / 2;
self.titleLabel.frame = frame;
// Make and add the icon view
UIImageView *iconView = nil;
CGFloat labelLeftPos = self.titleLabel.frame.origin.x;
iconView = [[UIImageView alloc]initWithFrame:CGRectMake(labelLeftPos - 25, 9.0, 18, 13.0)];
iconView.image = [UIImage imageWithContentsOfFile:noticeIconImageName];
iconView.contentMode = UIViewContentModeScaleAspectFit;
iconView.alpha = 0.8;
[self.gradientView addSubview:iconView];
// Add the title label
[self.gradientView addSubview:self.titleLabel];
// Add the drop shadow to the notice view
CALayer *noticeLayer = self.gradientView.layer;
noticeLayer.shadowColor = [[UIColor blackColor]CGColor];
noticeLayer.shadowOffset = CGSizeMake(0.0, 3);
noticeLayer.shadowOpacity = 0.50;
noticeLayer.masksToBounds = NO;
noticeLayer.shouldRasterize = YES;
self.duration = duration;
self.delay = delay;
self.alpha = alpha;
self.hiddenYOrigin = hiddenYOrigin;
[self displayNoticeWithDuration:duration delay:delay origin:origin hiddenYOrigin:hiddenYOrigin alpha:alpha];
}
#pragma mark -
- (void)displayNoticeWithDuration:(CGFloat)duration delay:(CGFloat)delay origin:(CGFloat)origin hiddenYOrigin:(CGFloat)hiddenYOrigin alpha:(CGFloat)alpha
- (void)displayNotice
{
// Setup accessiblity on the gradient view
NSString *accessibilityLabel = ([self.messageLabel.text length]) ? [NSString stringWithFormat:@"%@, %@", [self.titleLabel text], [self.messageLabel text]]
: [self.titleLabel text];
self.gradientView.accessibilityTraits = (self.isSticky || self.dismissedBlock) ? (UIAccessibilityTraitStaticText | UIAccessibilityTraitButton) : UIAccessibilityTraitStaticText;
self.gradientView.accessibilityTraits = (self.isSticky || self.dismissalBlock) ? UIAccessibilityTraitButton : UIAccessibilityTraitStaticText;
self.gradientView.accessibilityLabel = accessibilityLabel;
// If the notice is sticky, add tap capabilities
@@ -534,28 +88,28 @@
button.accessibilityLabel = accessibilityLabel;
frame.origin.x = frame.origin.y = 0.0;
button.frame = frame;
[button addTarget:self.currentNotice action:@selector(dismissNotice) forControlEvents:UIControlEventTouchUpInside];
[button addTarget:self.currentNotice action:@selector(dismissNoticeInteractively) forControlEvents:UIControlEventTouchUpInside];
[self.gradientView addSubview:button];
}
// Go ahead, display it
[UIView animateWithDuration:duration animations:^ {
[UIView animateWithDuration:self.duration animations:^ {
CGRect newFrame = self.gradientView.frame;
newFrame.origin.y = origin;
newFrame.origin.y = self.originY;
self.gradientView.frame = newFrame;
self.gradientView.alpha = alpha;
self.gradientView.alpha = self.alpha;
} completion:^ (BOOL finished) {
if (finished) {
// if it's not sticky, hide it automatically
if (NO == self.isSticky) {
// Display for a while, then hide it again
[self dismissNoticeWithDuration:duration delay:delay hiddenYOrigin:hiddenYOrigin];
[self dismissNoticeWithDuration:self.duration delay:self.delay hiddenYOrigin:self.hiddenYOrigin interactively:NO];
}
}
}];
}
- (void)dismissNoticeWithDuration:(CGFloat)duration delay:(CGFloat)delay hiddenYOrigin:(CGFloat)hiddenYOrigin
- (void)dismissNoticeWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay hiddenYOrigin:(CGFloat)hiddenYOrigin interactively:(BOOL)interactively
{
[UIView animateWithDuration:duration delay:delay options:UIViewAnimationOptionCurveEaseOut animations:^ {
CGRect newFrame = self.gradientView.frame;
@@ -563,8 +117,8 @@
self.gradientView.frame = newFrame;
} completion:^ (BOOL finished) {
if (finished) {
if (self.dismissedBlock) {
self.dismissedBlock();
if (self.dismissalBlock) {
self.dismissalBlock(interactively);
}
// Cleanup
[self cleanup];
@@ -575,10 +129,15 @@
- (void)dismissNotice
{
if (self.isSticky) {
[self dismissNoticeWithDuration:self.duration delay:self.delay hiddenYOrigin:self.hiddenYOrigin];
[self dismissNoticeWithDuration:self.duration delay:self.delay hiddenYOrigin:self.hiddenYOrigin interactively:NO];
}
}
- (void)dismissNoticeInteractively
{
[self dismissNoticeWithDuration:self.duration delay:self.delay hiddenYOrigin:self.hiddenYOrigin interactively:YES];
}
#pragma mark -
- (void)cleanup
@@ -595,21 +154,4 @@
[self cleanup];
}
#pragma mark -
+ (void)_raiseIfObjectIsNil:(id)object named:(NSString *)name
{
if (nil == object) {
// If the name has not been supplied, name it generically
if (nil == name) name = @"<name not supplied>";
// Log the stack trace
NSLog(@"%@", [NSThread callStackSymbols]);
[[NSException exceptionWithName:NSInvalidArgumentException
reason:[NSString stringWithFormat:@"*** -[%@ %@]: '%@' cannot be nil.", [self class], NSStringFromSelector(_cmd), name]
userInfo:nil]raise];
}
}
@end

View File

@@ -1,29 +0,0 @@
//
// WBNoticeView_Private.h
// NoticeView
//
// Created by Tito Ciuro on 5/25/12.
// Copyright (c) 2012 Tito Ciuro. All rights reserved.
//
#ifndef NoticeView_WBNoticeView_Private_h
#define NoticeView_WBNoticeView_Private_h
#import "WBNoticeView.h"
@interface WBNoticeView (Private)
+ (void)_raiseIfObjectIsNil:(id)object named:(NSString *)name;
- (void)_showNoticeOfType:(WBNoticeViewType)noticeType
view:(UIView *)view
title:(NSString *)title
message:(NSString *)message
duration:(float)duration
delay:(float)delay
alpha:(float)alpha
yOrigin:(CGFloat)origin;
@end
#endif

View File

@@ -8,8 +8,22 @@
#import "WBNoticeView.h"
/**
The `WBStickyNoticeView` class is a `WBNoticeView` subclass suitable for displaying a sticky informational message to a user. The notice is presented on a gray gradient background with a vertical error icon on the left hand side of the notice. It supports the display of a title only.
*/
@interface WBStickyNoticeView : WBNoticeView
///-------------------------------
/// @name Creating a Sticky Notice
///-------------------------------
/**
Creates and returns a sticky notice in the given view with the specified title.
@param view The view to display the notice in.
@param title The title of the notice.
@return The newly created sticky notice object.
*/
+ (WBStickyNoticeView *)stickyNoticeInView:(UIView *)view title:(NSString *)title;
@end

View File

@@ -7,7 +7,8 @@
//
#import "WBStickyNoticeView.h"
#import "WBNoticeView_Private.h"
#import "WBNoticeView+ForSubclassEyesOnly.h"
#import "WBGrayGradientView.h"
@implementation WBStickyNoticeView
@@ -24,14 +25,73 @@
- (void)show
{
[self _showNoticeOfType:WBNoticeViewTypeSticky
view:self.view
title:self.title
message:nil
duration:self.duration
delay:0.0
alpha:self.alpha
yOrigin:self.originY];
// Obtain the screen width
CGFloat viewWidth = self.view.frame.size.width;
// Locate the images
NSString *path = [[[NSBundle mainBundle]resourcePath]stringByAppendingPathComponent:@"NoticeView.bundle"];
NSString *noticeIconImageName = [path stringByAppendingPathComponent:@"up.png"];
NSInteger numberOfLines = 1;
CGFloat messageLineHeight = 30.0;
// Make and add the title label
self.titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(55.0, 8.0, viewWidth - 70.0, 16.0)];
self.titleLabel.textColor = [UIColor blackColor];
self.titleLabel.shadowOffset = CGSizeMake(0.0, 1.0);
self.titleLabel.shadowColor = [UIColor whiteColor];
self.titleLabel.font = [UIFont boldSystemFontOfSize:12.0];
self.titleLabel.backgroundColor = [UIColor clearColor];
self.titleLabel.text = title;
[self.titleLabel sizeToFit];
CGRect frame = self.titleLabel.frame;
if (frame.size.width > 260) {
frame.size.width = 260;
self.titleLabel.frame = frame;
}
// Calculate the notice view height
float noticeViewHeight = 40.0;
float hiddenYOrigin = 0.0;
if (numberOfLines > 1) {
noticeViewHeight += (numberOfLines - 1) * messageLineHeight;
}
// Make sure we hide completely the view, including its shadow
hiddenYOrigin = -noticeViewHeight - 20.0;
// Make and add the notice view
self.gradientView = [[WBGrayGradientView alloc] initWithFrame:CGRectMake(0.0, hiddenYOrigin, viewWidth, 32)];
[self.view addSubview:self.gradientView];
// Center the message in the middle of the notice
frame = self.titleLabel.frame;
frame.origin.x = (self.gradientView.frame.size.width - frame.size.width) / 2;
self.titleLabel.frame = frame;
// Make and add the icon view
UIImageView *iconView = nil;
CGFloat labelLeftPos = self.titleLabel.frame.origin.x;
iconView = [[UIImageView alloc]initWithFrame:CGRectMake(labelLeftPos - 25, 9.0, 18, 13.0)];
iconView.image = [UIImage imageWithContentsOfFile:noticeIconImageName];
iconView.contentMode = UIViewContentModeScaleAspectFit;
iconView.alpha = 0.8;
[self.gradientView addSubview:iconView];
// Add the title label
[self.gradientView addSubview:self.titleLabel];
// Add the drop shadow to the notice view
CALayer *noticeLayer = self.gradientView.layer;
noticeLayer.shadowColor = [[UIColor blackColor]CGColor];
noticeLayer.shadowOffset = CGSizeMake(0.0, 3);
noticeLayer.shadowOpacity = 0.50;
noticeLayer.masksToBounds = NO;
noticeLayer.shouldRasterize = YES;
self.hiddenYOrigin = hiddenYOrigin;
[self displayNotice];
}
@end

View File

@@ -8,8 +8,22 @@
#import "WBNoticeView.h"
/**
The `WBSuccessNoticeView` class is a `WBNoticeView` subclass suitable for displaying an informational success message to a user. The notice is presented on a blue gradient background with a checkmark icon on the left hand side of the notice. It supports the display of a title only.
*/
@interface WBSuccessNoticeView : WBNoticeView
///-------------------------------
/// @name Creating a Success Notice
///-------------------------------
/**
Creates and returns a success notice in the given view with the specified title.
@param view The view to display the notice in.
@param title The title of the notice.
@return The newly created success notice object.
*/
+ (WBSuccessNoticeView *)successNoticeInView:(UIView *)view title:(NSString *)title;
@end

View File

@@ -7,7 +7,8 @@
//
#import "WBSuccessNoticeView.h"
#import "WBNoticeView_Private.h"
#import "WBNoticeView+ForSubclassEyesOnly.h"
#import "WBBlueGradientView.h"
@implementation WBSuccessNoticeView
@@ -24,14 +25,61 @@
- (void)show
{
[self _showNoticeOfType:WBNoticeViewTypeSuccess
view:self.view
title:self.title
message:nil
duration:self.duration
delay:self.delay
alpha:self.alpha
yOrigin:self.originY];
// Obtain the screen width
CGFloat viewWidth = self.view.frame.size.width;
// Locate the images
NSString *path = [[[NSBundle mainBundle]resourcePath]stringByAppendingPathComponent:@"NoticeView.bundle"];
NSString *noticeIconImageName = [path stringByAppendingPathComponent:@"notice_success_icon.png"];
NSInteger numberOfLines = 1;
CGFloat messageLineHeight = 30.0;
// Make and add the title label
float titleYOrigin = 18.0;
self.titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(55.0, titleYOrigin, viewWidth - 70.0, 16.0)];
self.titleLabel.textColor = [UIColor whiteColor];
self.titleLabel.shadowOffset = CGSizeMake(0.0, -1.0);
self.titleLabel.shadowColor = [UIColor blackColor];
self.titleLabel.font = [UIFont boldSystemFontOfSize:14.0];
self.titleLabel.backgroundColor = [UIColor clearColor];
self.titleLabel.text = title;
// Calculate the notice view height
float noticeViewHeight = 40.0;
float hiddenYOrigin = 0.0;
if (numberOfLines > 1) {
noticeViewHeight += (numberOfLines - 1) * messageLineHeight;
}
// Make sure we hide completely the view, including its shadow
hiddenYOrigin = -noticeViewHeight - 20.0;
// Make and add the notice view
self.gradientView = [[WBBlueGradientView alloc] initWithFrame:CGRectMake(0.0, hiddenYOrigin, viewWidth, noticeViewHeight + 10.0)];
[self.view addSubview:self.gradientView];
// Make and add the icon view
UIImageView *iconView = [[UIImageView alloc]initWithFrame:CGRectMake(10.0, 10.0, 20.0, 30.0)];
iconView.image = [UIImage imageWithContentsOfFile:noticeIconImageName];
iconView.contentMode = UIViewContentModeScaleAspectFit;
iconView.alpha = 0.8;
[self.gradientView addSubview:iconView];
// Add the title label
[self.gradientView addSubview:self.titleLabel];
// Add the drop shadow to the notice view
CALayer *noticeLayer = self.gradientView.layer;
noticeLayer.shadowColor = [[UIColor blackColor]CGColor];
noticeLayer.shadowOffset = CGSizeMake(0.0, 3);
noticeLayer.shadowOpacity = 0.50;
noticeLayer.masksToBounds = NO;
noticeLayer.shouldRasterize = YES;
self.hiddenYOrigin = hiddenYOrigin;
[self displayNotice];
}
@end

View File

@@ -73,9 +73,9 @@
- (IBAction)showLargeErrorNotice:(id)sender
{
WBErrorNoticeView *notice = [WBErrorNoticeView errorNoticeInView:self.view title:@"Network Error" message:@"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book."];
notice.dismissedBlock = ^{
[notice setDismissalBlock:^(BOOL dismissedInteractively) {
NSLog(@"showLargeErrorNotice dismissed!");
};
}];
[notice show];
}
@@ -88,9 +88,9 @@
- (IBAction)showSmallStickyNotice:(id)sender
{
WBStickyNoticeView *notice = [WBStickyNoticeView stickyNoticeInView:self.view title:@"7 New Tweets."];
notice.dismissedBlock = ^{
[notice setDismissalBlock:^(BOOL dismissedInteractively) {
NSLog(@"showSmallStickyNotice dismissed!");
};
}];
[notice show];
}