From 6286f992197b395fee35356f803cffa54469f6b7 Mon Sep 17 00:00:00 2001 From: Blake Watters Date: Mon, 12 Nov 2012 23:10:13 -0500 Subject: [PATCH] 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. --- Default-568h@2x.png | Bin 0 -> 18594 bytes NoticeView.xcodeproj/project.pbxproj | 8 +- NoticeView/WBNoticeView/WBErrorNoticeView.h | 17 +- NoticeView/WBNoticeView/WBErrorNoticeView.m | 96 +++- .../WBNoticeView+ForSubclassEyesOnly.h | 25 + NoticeView/WBNoticeView/WBNoticeView.h | 143 +++-- NoticeView/WBNoticeView/WBNoticeView.m | 530 ++---------------- .../WBNoticeView/WBNoticeView_Private.h | 29 - NoticeView/WBNoticeView/WBStickyNoticeView.h | 14 + NoticeView/WBNoticeView/WBStickyNoticeView.m | 78 ++- NoticeView/WBNoticeView/WBSuccessNoticeView.h | 14 + NoticeView/WBNoticeView/WBSuccessNoticeView.m | 66 ++- NoticeView/WBViewController.m | 8 +- 13 files changed, 416 insertions(+), 612 deletions(-) create mode 100644 Default-568h@2x.png create mode 100644 NoticeView/WBNoticeView/WBNoticeView+ForSubclassEyesOnly.h delete mode 100644 NoticeView/WBNoticeView/WBNoticeView_Private.h diff --git a/Default-568h@2x.png b/Default-568h@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0891b7aabfcf3422423b109c8beed2bab838c607 GIT binary patch literal 18594 zcmeI4X;f257Jx&9fS`ixvS;&$x8J@slQFSel)6zJN=?13FB7H(lQjRkSy8x_-S~tvu2gzn1oS+dLcF#eqtq$ z%tf9TTvX?`)R@}3uBI;jzS-=ZR-Td&MHaS&;!0?Ni*#$#`n*~CcQK)Q9vAQ~TUpnI!j)a2biYK^R)M~A5wUDZhx?ULMX z3x1P&qt=trOY6P2U67L=m=U?F|5#Uj(eCueNTZaHs_ceWiHeET+j+tp3Jt9g(ekqP z2WOvfR{qV+9r+o4J5?qK>7;;^+I7tGv-i)es$X_D=EoKF+S?zsyj^oRFElP}c}JT< zd8SUs-?O?}2YD#ngKbnHgzHBcboxK_2r9l(?eNCl-pEzkJm}fY?WC*jnS?VBE4EpY zO$fEejz6fU;W2Kl>JeQBZBl-%Irg`obSlg*@4QB;Dd1H7^Oi5wvt4d{RZ!8Og?^aE z)k0$1g+V3fd(gdQ3d&q2q-FL*uy#}|bc^=VhFsl0jBgUGJ+-s3U8MK9A!YJJMxpci z5hJ%|{DwV48fZn0{n5l$N_KcSb#NKE4plB`9I6Zt=Z!~-zw0{9tg$L&Ju1F0X)Cy8 zKF;(&lJ>x)Jw(=;p~sF(Sd9VWGwFE2rnyS9!f^DZ8+aCLq zQ};>lcJ1GDLqjm6Hd>|Eabno@P`~Bn(~6^aD_#yoEH(a?Nm1S<;S+hSxI5d16^<1lEM3NPFi zkqPrpL)+ zgnseFikg`gJVBha1&7C4;O6>h=dt~`ND+;Zd?W(4v2JIb7Pt>Td42%M-Ju-XAH#Pns762L}K3 zDhvsRqN0Ni(1UrishD2YvV?4*h2iFj$+&N||Fn$4n|^NSU+o?~jq`0jVQt8T9l{7b zXiwwODFh2V!Q6sqP9S>WH$oOf$N~=d0-bqTlD61!=`&0eAP-F>XN?*|gtOXX{ zQVTWyYo4ZK0GAw!GHf|pz9`D;-bbb*5LBX*{bnz|+)$@&P9|ORM2o?95{;ejvo&r- zq8cBhTN6nn)7~W>54U)%-F_-b?YKdfk5I8MHcuzBD5)!;yv#Z&R&^y=@=>VTIMy#r zX&U<=BsPkdqcMe<_}2+>H%XKyrr5ZR8_KVe>ZqYN z^=^~TFD};;rHJ$U;{~w^hYojl4hRI@SH$^K{YEo=sg)WY87r!*7blQK&qnpDo0`Vn zkl)9u9g=mCh&ZCJS(L4yN3k0kQ zuvg$h2KEEk51T+O0JQ+r0`R>g{jvqM0Mr6d3qUOZwE!?PI7HY@CE|dr sfw?Q;rAv?G4&^^8-z_>&sWXMxvD*gPOU4CBe-*@OtE+wfmVJNyHv)PfH~;_u literal 0 HcmV?d00001 diff --git a/NoticeView.xcodeproj/project.pbxproj b/NoticeView.xcodeproj/project.pbxproj index 3db4061..b8ca471 100644 --- a/NoticeView.xcodeproj/project.pbxproj +++ b/NoticeView.xcodeproj/project.pbxproj @@ -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 = ""; }; + 250628771651F7DB00D3443E /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "../../Default-568h@2x.png"; sourceTree = ""; }; 592BDDE115645BEE00B78820 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; 592BDDE215645BEE00B78820 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; 592BDDE415645BEE00B78820 /* NoticeView.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = NoticeView.bundle; sourceTree = ""; }; @@ -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 = ""; }; 746CBCFF1570A1D100B844B1 /* WBErrorNoticeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WBErrorNoticeView.m; sourceTree = ""; }; - 746CBD011570A65D00B844B1 /* WBNoticeView_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WBNoticeView_Private.h; sourceTree = ""; }; 746CBD061570A8FA00B844B1 /* WBSuccessNoticeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WBSuccessNoticeView.h; sourceTree = ""; }; 746CBD071570A8FA00B844B1 /* WBSuccessNoticeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WBSuccessNoticeView.m; sourceTree = ""; }; 748A6A57157D0B74003C7655 /* WBGrayGradientView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WBGrayGradientView.h; sourceTree = ""; }; @@ -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 = ""; @@ -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; }; diff --git a/NoticeView/WBNoticeView/WBErrorNoticeView.h b/NoticeView/WBNoticeView/WBErrorNoticeView.h index 9998841..135630a 100644 --- a/NoticeView/WBNoticeView/WBErrorNoticeView.h +++ b/NoticeView/WBNoticeView/WBErrorNoticeView.h @@ -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 diff --git a/NoticeView/WBNoticeView/WBErrorNoticeView.m b/NoticeView/WBNoticeView/WBErrorNoticeView.m index b5bdf14..02561f7 100644 --- a/NoticeView/WBNoticeView/WBErrorNoticeView.m +++ b/NoticeView/WBNoticeView/WBErrorNoticeView.m @@ -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 diff --git a/NoticeView/WBNoticeView/WBNoticeView+ForSubclassEyesOnly.h b/NoticeView/WBNoticeView/WBNoticeView+ForSubclassEyesOnly.h new file mode 100644 index 0000000..728be1b --- /dev/null +++ b/NoticeView/WBNoticeView/WBNoticeView+ForSubclassEyesOnly.h @@ -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 +#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 diff --git a/NoticeView/WBNoticeView/WBNoticeView.h b/NoticeView/WBNoticeView/WBNoticeView.h index b2ecf9d..a71dc3b 100644 --- a/NoticeView/WBNoticeView/WBNoticeView.h +++ b/NoticeView/WBNoticeView/WBNoticeView.h @@ -8,76 +8,111 @@ #import +/** + `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 diff --git a/NoticeView/WBNoticeView/WBNoticeView.m b/NoticeView/WBNoticeView/WBNoticeView.m index ac54fcf..9f3bd05 100644 --- a/NoticeView/WBNoticeView/WBNoticeView.m +++ b/NoticeView/WBNoticeView/WBNoticeView.m @@ -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 +#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 = @""; - - // 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 diff --git a/NoticeView/WBNoticeView/WBNoticeView_Private.h b/NoticeView/WBNoticeView/WBNoticeView_Private.h deleted file mode 100644 index e4a409b..0000000 --- a/NoticeView/WBNoticeView/WBNoticeView_Private.h +++ /dev/null @@ -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 diff --git a/NoticeView/WBNoticeView/WBStickyNoticeView.h b/NoticeView/WBNoticeView/WBStickyNoticeView.h index f257877..20f7464 100644 --- a/NoticeView/WBNoticeView/WBStickyNoticeView.h +++ b/NoticeView/WBNoticeView/WBStickyNoticeView.h @@ -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 diff --git a/NoticeView/WBNoticeView/WBStickyNoticeView.m b/NoticeView/WBNoticeView/WBStickyNoticeView.m index 7c36e8a..e5c4202 100644 --- a/NoticeView/WBNoticeView/WBStickyNoticeView.m +++ b/NoticeView/WBNoticeView/WBStickyNoticeView.m @@ -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 diff --git a/NoticeView/WBNoticeView/WBSuccessNoticeView.h b/NoticeView/WBNoticeView/WBSuccessNoticeView.h index 1b0abd2..5e47e4c 100644 --- a/NoticeView/WBNoticeView/WBSuccessNoticeView.h +++ b/NoticeView/WBNoticeView/WBSuccessNoticeView.h @@ -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 diff --git a/NoticeView/WBNoticeView/WBSuccessNoticeView.m b/NoticeView/WBNoticeView/WBSuccessNoticeView.m index d16ace6..d890a4b 100644 --- a/NoticeView/WBNoticeView/WBSuccessNoticeView.m +++ b/NoticeView/WBNoticeView/WBSuccessNoticeView.m @@ -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 diff --git a/NoticeView/WBViewController.m b/NoticeView/WBViewController.m index 9982649..547693a 100644 --- a/NoticeView/WBViewController.m +++ b/NoticeView/WBViewController.m @@ -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]; }