diff --git a/Resources/Placeholder.png b/Resources/Placeholder.png new file mode 100644 index 0000000..8d9329e Binary files /dev/null and b/Resources/Placeholder.png differ diff --git a/Stage1st.xcodeproj/project.pbxproj b/Stage1st.xcodeproj/project.pbxproj index bcc2166..ab7b5ca 100644 --- a/Stage1st.xcodeproj/project.pbxproj +++ b/Stage1st.xcodeproj/project.pbxproj @@ -90,6 +90,8 @@ C096CD3D16EADD3300D4FF7F /* SVWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C096CD3716EADD3300D4FF7F /* SVWebViewController.m */; }; C096CD3F16EADE0000D4FF7F /* MessageUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C096CD3E16EADE0000D4FF7F /* MessageUI.framework */; }; C0AEAE8116F5F88D00CB0E71 /* S1BaseURLViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = C0AEAE8016F5F88D00CB0E71 /* S1BaseURLViewController.m */; }; + C0AEAE8316F60D7900CB0E71 /* Placeholder.png in Resources */ = {isa = PBXBuildFile; fileRef = C0AEAE8216F60D7900CB0E71 /* Placeholder.png */; }; + C0AEAE8716F6267900CB0E71 /* MTStatusBarOverlay.m in Sources */ = {isa = PBXBuildFile; fileRef = C0AEAE8616F6267900CB0E71 /* MTStatusBarOverlay.m */; }; C0B1919716F0A80F0053E0C5 /* content_ipad.css in Resources */ = {isa = PBXBuildFile; fileRef = C0B1919616F0A80F0053E0C5 /* content_ipad.css */; }; /* End PBXBuildFile section */ @@ -231,6 +233,9 @@ C096CD3E16EADE0000D4FF7F /* MessageUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MessageUI.framework; path = System/Library/Frameworks/MessageUI.framework; sourceTree = SDKROOT; }; C0AEAE7F16F5F88D00CB0E71 /* S1BaseURLViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = S1BaseURLViewController.h; path = Stage1st/S1BaseURLViewController.h; sourceTree = ""; }; C0AEAE8016F5F88D00CB0E71 /* S1BaseURLViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = S1BaseURLViewController.m; path = Stage1st/S1BaseURLViewController.m; sourceTree = ""; }; + C0AEAE8216F60D7900CB0E71 /* Placeholder.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = Placeholder.png; path = Resources/Placeholder.png; sourceTree = ""; }; + C0AEAE8516F6267900CB0E71 /* MTStatusBarOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MTStatusBarOverlay.h; sourceTree = ""; }; + C0AEAE8616F6267900CB0E71 /* MTStatusBarOverlay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTStatusBarOverlay.m; sourceTree = ""; }; C0B1919616F0A80F0053E0C5 /* content_ipad.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = content_ipad.css; sourceTree = ""; }; /* End PBXFileReference section */ @@ -384,6 +389,7 @@ C095875416CA8A3E007E0119 /* Vendors */ = { isa = PBXGroup; children = ( + C0AEAE8416F6267800CB0E71 /* MTStatusBarOverlay */, C096CD2D16EADD3300D4FF7F /* SVWebViewController */, C083353B16E647A70035C1AA /* GoogleToolbox */, C083352216E4E1550035C1AA /* REComposeViewController */, @@ -397,6 +403,7 @@ C095875516CA8A43007E0119 /* Resources */ = { isa = PBXGroup; children = ( + C0AEAE8216F60D7900CB0E71 /* Placeholder.png */, C08B602116E1B5CE00591827 /* Logo.png */, C08B602216E1B5CE00591827 /* Logo@2x.png */, C002D2B916EC665D00A5414E /* Icon-72.png */, @@ -551,6 +558,16 @@ path = Vendors/SVWebViewController; sourceTree = ""; }; + C0AEAE8416F6267800CB0E71 /* MTStatusBarOverlay */ = { + isa = PBXGroup; + children = ( + C0AEAE8516F6267900CB0E71 /* MTStatusBarOverlay.h */, + C0AEAE8616F6267900CB0E71 /* MTStatusBarOverlay.m */, + ); + name = MTStatusBarOverlay; + path = Vendors/MTStatusBarOverlay; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -643,6 +660,7 @@ C002D2BB16EC665D00A5414E /* Icon-72.png in Resources */, C002D2BC16EC665D00A5414E /* Icon-72@2x.png in Resources */, C0B1919716F0A80F0053E0C5 /* content_ipad.css in Resources */, + C0AEAE8316F60D7900CB0E71 /* Placeholder.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -693,6 +711,7 @@ C096CD3B16EADD3300D4FF7F /* SVModalWebViewController.m in Sources */, C096CD3D16EADD3300D4FF7F /* SVWebViewController.m in Sources */, C0AEAE8116F5F88D00CB0E71 /* S1BaseURLViewController.m in Sources */, + C0AEAE8716F6267900CB0E71 /* MTStatusBarOverlay.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Stage1st/S1ContentViewController.m b/Stage1st/S1ContentViewController.m index c33ec15..147fecf 100644 --- a/Stage1st/S1ContentViewController.m +++ b/Stage1st/S1ContentViewController.m @@ -18,6 +18,7 @@ #import "S1HUD.h" #import "REComposeViewController.h" #import "SVModalWebViewController.h" +#import "MTStatusBarOverlay.h" #define _REPLY_PER_PAGE 50 @@ -255,11 +256,12 @@ - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger [replyController setCompletionHandler:^(REComposeViewController *composeViewController, REComposeResult result){ if (result == REComposeResultCancelled) { - [composeViewController dismissViewControllerAnimated:NO completion:nil]; + [composeViewController dismissViewControllerAnimated:YES completion:nil]; } else if (result == REComposeResultPosted) { if (composeViewController.text.length > 0) { - [self replyWithReplyController:composeViewController]; + [self replyWithReplyText:composeViewController.text]; + [composeViewController dismissViewControllerAnimated:YES completion:nil]; } } }]; @@ -285,7 +287,7 @@ - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger if (2 == buttonIndex) { [self rootViewController].modalPresentationStyle = UIModalPresentationFullScreen; - SVModalWebViewController *controller = [[SVModalWebViewController alloc] initWithAddress:[NSString stringWithFormat:@"http://bbs.saraba1st.com/2b/read-htm-tid-%@.html", self.topic.topicID]]; + SVModalWebViewController *controller = [[SVModalWebViewController alloc] initWithAddress:[NSString stringWithFormat:@"%@/2b/read-htm-tid-%@.html",[[NSUserDefaults standardUserDefaults] valueForKey:@"BaseURL"], self.topic.topicID]]; [self presentViewController:controller animated:YES completion:nil]; } } @@ -358,29 +360,36 @@ - (void)fetchContent }]; } -- (void)replyWithReplyController:(REComposeViewController *)composeViewController +- (void)replyWithReplyText:(NSString *)text { NSString *suffix = @"\n\n——— 来自Stage1st Reader Evolution"; - NSString *replyWithSuffix = [composeViewController.text stringByAppendingString:suffix]; + NSString *replyWithSuffix = [text stringByAppendingString:suffix]; NSString *timestamp = [[NSString stringWithFormat:@"%ld", (long)[[NSDate date] timeIntervalSince1970]] substringToIndex:10]; NSString *path = [NSString stringWithFormat:@"m/post.php?action=reply&tid=%@&tmp=%@", self.topic.topicID, timestamp]; + + MTStatusBarOverlay *overlay = [MTStatusBarOverlay sharedInstance]; + overlay.animation = MTStatusBarOverlayAnimationShrink; + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; [self.HTTPClient postPath:path parameters:@{ @"fid":self.topic.fID, @"content":replyWithSuffix } success:^(AFHTTPRequestOperation *operation, id responseObject) { - NSString *HTMLString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]; - NSLog(@"Reply Response: %@", HTMLString); - if (_currentPage == _totalPages) { - _needToScrollToBottom = YES; - [self fetchContent]; - } - [composeViewController dismissViewControllerAnimated:NO completion:nil]; - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; - + NSString *HTMLString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]; + NSLog(@"Reply Response: %@", HTMLString); + + if (HTMLString.length > 0) { + [overlay postFinishMessage:@"回复成功" duration:2.5 animated:YES]; + _needToScrollToBottom = YES; + [self fetchContent]; + } else { + [overlay postErrorMessage:@"回复很可能未成功" duration:2.5 animated:YES]; + } + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { - NSLog(@"%@", error); - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; + NSLog(@"%@", error); + [overlay postErrorMessage:@"回复失败" duration:2.5 animated:YES]; + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; }]; } diff --git a/Stage1st/S1Parser.m b/Stage1st/S1Parser.m index 28ce243..09f9cf2 100644 --- a/Stage1st/S1Parser.m +++ b/Stage1st/S1Parser.m @@ -14,7 +14,7 @@ static NSString * const topicPattern = @"
  • (.*?).*?\\((\\d+)"; static NSString * const cssPattern = @""; -static NSString * const cleanupPattern = @"(?:
    (
    )?\\r\\n
    .*?
    )|(?:.*?
    )|(?:src=\"http://.*?/images/back\\.gif\")|(?:onload=\"if\\(this.offsetWidth>'600'\\)this.width='600';\")"; +static NSString * const cleanupPattern = @"(?:
    (
    )?\\r\\n
    .*?
    )|(?:.*?
    )|(?:src=\"http://[-.0-9a-zA-Z]+/2b/images/back\\.gif\")|(?:onload=\"if\\(this.offsetWidth>'600'\\)this.width='600';\")"; static NSString * const indexPattern = @"td>(.*?)\\r\\n tabbarDelegate; - -//@property (nonatomic, copy) void (^eventHandler)(NSString *key); +@property (nonatomic, weak) id tabbarDelegate; @property (nonatomic, strong) NSArray *keys; +@property (nonatomic, assign) BOOL enabled; + @end diff --git a/Stage1st/S1TabBar.m b/Stage1st/S1TabBar.m index 1346087..4196bed 100644 --- a/Stage1st/S1TabBar.m +++ b/Stage1st/S1TabBar.m @@ -30,6 +30,7 @@ - (id)initWithFrame:(CGRect)frame andKeys:(NSArray *)keys if (!self) return nil; _keys = keys; _index = -1; + _enabled = YES; _buttons = [NSMutableArray arrayWithCapacity:keys.count]; self.backgroundColor = [UIColor blackColor]; self.bounces = NO; @@ -98,6 +99,7 @@ - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL - (void)tapped:(UIButton *)sender { + if (!_enabled) return; if (_index >= 0) [_buttons[_index] setSelected:NO]; _index = sender.tag; diff --git a/Stage1st/S1TopicListCell.m b/Stage1st/S1TopicListCell.m index 54da5ea..417d942 100644 --- a/Stage1st/S1TopicListCell.m +++ b/Stage1st/S1TopicListCell.m @@ -112,7 +112,7 @@ - (void)drawRect:(CGRect)rect [titleContent drawInRect: titleRect withFont:[UIFont boldSystemFontOfSize:15.0f] lineBreakMode: NSLineBreakByTruncatingTail alignment: NSTextAlignmentLeft]; } if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) { - [titleContent drawAtPoint:titlePoint forWidth:self.bounds.size.width-90.0f withFont:[UIFont boldSystemFontOfSize:18.0] fontSize:17.0 lineBreakMode:NSLineBreakByTruncatingTail baselineAdjustment:UIBaselineAdjustmentAlignCenters]; + [titleContent drawAtPoint:titlePoint forWidth:self.bounds.size.width-120.0f withFont:[UIFont boldSystemFontOfSize:18.0] fontSize:17.0 lineBreakMode:NSLineBreakByTruncatingTail baselineAdjustment:UIBaselineAdjustmentAlignCenters]; } diff --git a/Stage1st/S1TopicListViewController.m b/Stage1st/S1TopicListViewController.m index cb90569..d5375f0 100644 --- a/Stage1st/S1TopicListViewController.m +++ b/Stage1st/S1TopicListViewController.m @@ -183,12 +183,17 @@ - (void)recent:(id)sender } [self.scrollTabBar deselectAll]; - self.currentKey = nil; + self.currentKey = @""; } - (void)refresh:(id)sender { - if (self.scrollTabBar.userInteractionEnabled) { + if (self.refreshControl.hidden) { + [self.refreshControl endRefreshing]; + return; + } + + if (self.scrollTabBar.enabled) { [self fetchTopicsForKeyFromServer:self.currentKey scrollToTop:NO]; } else { [self.refreshControl endRefreshing]; @@ -200,12 +205,14 @@ - (void)refresh:(id)sender - (void)tabbar:(S1TabBar *)tabbar didSelectedKey:(NSString *)key { self.naviItem.title = @"Stage1st"; + if (self.tableView.hidden) { self.tableView.hidden = NO; } if (self.refreshControl.hidden) { self.refreshControl.hidden = NO; } + if (![self.currentKey isEqualToString:key]) { self.currentKey = key; [self fetchTopicsForKey:key]; @@ -232,39 +239,39 @@ - (void)fetchTopicsForKey:(NSString *)key - (void)fetchTopicsForKeyFromServer:(NSString *)key scrollToTop:(BOOL)toTop { - self.scrollTabBar.userInteractionEnabled = NO; + self.scrollTabBar.enabled = NO; S1HUD *HUD = [S1HUD showHUDInView:self.view]; [HUD showActivityIndicator]; NSString *path = [NSString stringWithFormat:@"simple/?f%@.html", self.threadsInfo[key]]; [self.HTTPClient getPath:path parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { - NSString *HTMLString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]; - if (HTMLString) { - NSArray *topics = [S1Parser topicsFromHTMLString:HTMLString withContext:@{@"FID": self.threadsInfo[self.currentKey]}]; - if (topics.count > 0) { - self.topics = topics; - self.cache[key] = topics; - [self.tableView reloadData]; - if (toTop) { - [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO]; - } - } - } - if (self.refreshControl.refreshing) { - [self.refreshControl endRefreshing]; - } - [HUD hideWithDelay:0.3]; - self.scrollTabBar.userInteractionEnabled = YES; + NSString *HTMLString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]; + if (HTMLString) { + NSArray *topics = [S1Parser topicsFromHTMLString:HTMLString withContext:@{@"FID": self.threadsInfo[self.currentKey]}]; + if (topics.count > 0) { + self.topics = topics; + self.cache[key] = topics; + [self.tableView reloadData]; + if (toTop) { + [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0] atScrollPosition:UITableViewScrollPositionTop animated:NO]; + } + } + } + if (self.refreshControl.refreshing) { + [self.refreshControl endRefreshing]; + } + [HUD hideWithDelay:0.3]; + self.scrollTabBar.enabled = YES; } failure:^(AFHTTPRequestOperation *operation, NSError *error) { - [HUD setText:@"Request Failed"]; - [HUD hideWithDelay:0.3]; - self.scrollTabBar.userInteractionEnabled = YES; - if (self.refreshControl.refreshing) { - [self.refreshControl endRefreshing]; - } + [HUD setText:@"Request Failed"]; + [HUD hideWithDelay:0.3]; + self.scrollTabBar.enabled = YES; + if (self.refreshControl.refreshing) { + [self.refreshControl endRefreshing]; + } }]; } @@ -327,10 +334,8 @@ - (NSArray *)keys - (void)updateTabbar:(id)sender { - self.currentKey = @""; - self.topics = [NSArray array]; - [self.tableView reloadData]; [self.scrollTabBar setKeys:[self keys]]; + self.tableView.hidden = YES; } - (void)updateHTTPClient:(id)sender diff --git a/Stage1st/S1URLCache.m b/Stage1st/S1URLCache.m index d0f22fd..ebeea57 100644 --- a/Stage1st/S1URLCache.m +++ b/Stage1st/S1URLCache.m @@ -41,7 +41,7 @@ - (NSCachedURLResponse *)cachedResponseForRequest:(NSURLRequest *)request return [super cachedResponseForRequest:request]; } else { - NSData *imageData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"placeholder" ofType:@"png"]]; + NSData *imageData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Placeholder" ofType:@"png"]]; NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL MIMEType:@"image/png" diff --git a/Vendors/MTStatusBarOverlay/MTStatusBarOverlay.h b/Vendors/MTStatusBarOverlay/MTStatusBarOverlay.h new file mode 100755 index 0000000..5a7ca0b --- /dev/null +++ b/Vendors/MTStatusBarOverlay/MTStatusBarOverlay.h @@ -0,0 +1,200 @@ +// +// MTStatusBarOverlay.m +// +// Created by Matthias Tretter on 27.09.10. +// Copyright (c) 2009-2011 Matthias Tretter, @myell0w. All rights reserved. +// +// 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. +// +// Credits go to: +// ------------------------------- +// http://stackoverflow.com/questions/2833724/adding-view-on-statusbar-in-iphone +// http://www.cocoabyss.com/uikit/custom-status-bar-ios/ +// @reederapp for inspiration +// ------------------------------- + +#import + + +//=========================================================== +#pragma mark - +#pragma mark Definitions and Types +//=========================================================== + +// Animation that happens, when the user touches the status bar overlay +typedef enum MTStatusBarOverlayAnimation { + MTStatusBarOverlayAnimationNone, // nothing happens + MTStatusBarOverlayAnimationShrink, // the status bar shrinks to the right side and only shows the activity indicator + MTStatusBarOverlayAnimationFallDown // the status bar falls down and displays more information +} MTStatusBarOverlayAnimation; + + +// Mode of the detail view +typedef enum MTDetailViewMode { + MTDetailViewModeHistory, // History of messages is recorded and displayed in detailView + MTDetailViewModeDetailText, // a text can be displayed easily + MTDetailViewModeCustom // the detailView can be customized in the way the developer wants +} MTDetailViewMode; + +// indicates the type of a message +typedef enum MTMessageType { + MTMessageTypeActivity, // shows actvity indicator + MTMessageTypeFinish, // shows checkmark + MTMessageTypeError // shows error-mark +} MTMessageType; + + +// keys used in the dictionary-representation of a status message +#define kMTStatusBarOverlayMessageKey @"MessageText" +#define kMTStatusBarOverlayMessageTypeKey @"MessageType" +#define kMTStatusBarOverlayDurationKey @"MessageDuration" +#define kMTStatusBarOverlayAnimationKey @"MessageAnimation" +#define kMTStatusBarOverlayImmediateKey @"MessageImmediate" + +// keys used for saving state to NSUserDefaults +#define kMTStatusBarOverlayStateShrinked @"kMTStatusBarOverlayStateShrinked" + + +// forward-declaration of delegate-protocol +@protocol MTStatusBarOverlayDelegate; + + +//=========================================================== +#pragma mark - +#pragma mark MTStatusBarOverlay Interface +//=========================================================== + +/** + This class provides an overlay over the iOS Status Bar that can display information + and perform an animation when you touch it: + + it can either shrink and only overlap the battery-icon (like in Reeder) or it can display + a detail-view that shows additional information. You can show a history of all the previous + messages for free by setting historyEnabled to YES + */ +@interface MTStatusBarOverlay : UIWindow + +// the view that holds all the components of the overlay (except for the detailView) +@property (nonatomic, strong) UIView *backgroundView; +// the detailView is shown when animation is set to "FallDown" +@property (nonatomic, strong) UIView *detailView; +// the current progress +@property (nonatomic, assign) double progress; +// the frame of the status bar when animation is set to "Shrink" and it is shrinked +@property (nonatomic, assign) CGRect smallFrame; +// the current active animation +@property (nonatomic, assign) MTStatusBarOverlayAnimation animation; +// the label that holds the finished-indicator (either a checkmark, or a error-sign per default) +@property (nonatomic, strong) UILabel *finishedLabel; +// if this flag is set to YES, neither activityIndicator nor finishedLabel are shown +@property (nonatomic, assign) BOOL hidesActivity; +// the image used when the Status Bar Style is Default +@property (nonatomic, strong) UIImage *defaultStatusBarImage; +// the image used when the Status Bar Style is Default and the Overlay is shrinked +@property (nonatomic, strong) UIImage *defaultStatusBarImageShrinked; +// detect if status bar is currently shrinked +@property (nonatomic, readonly, getter=isShrinked) BOOL shrinked; +// detect if detailView is currently hidden +@property (nonatomic, readonly, getter=isDetailViewHidden) BOOL detailViewHidden; +// all messages that were displayed since the last finish-call +@property (nonatomic, strong, readonly) NSMutableArray *messageHistory; +// DEPRECATED: enable/disable history-tracking of messages +@property (nonatomic, assign, getter=isHistoryEnabled) BOOL historyEnabled; +// the last message that was visible +@property (nonatomic, copy) NSString *lastPostedMessage; +// determines if immediate messages in the queue get removed or stay in the queue, when a new immediate message gets posted +@property (nonatomic, assign) BOOL canRemoveImmediateMessagesFromQueue; +// the mode of the detailView +@property (nonatomic, assign) MTDetailViewMode detailViewMode; +// the text displayed in the detailView (alternative to history) +@property (nonatomic, copy) NSString *detailText; +// the delegate of the overlay +@property (nonatomic, unsafe_unretained) id delegate; + + +//=========================================================== +#pragma mark - +#pragma mark Class Methods +//=========================================================== + +// Singleton Instance ++ (MTStatusBarOverlay *)sharedInstance; ++ (MTStatusBarOverlay *)sharedOverlay; + +//=========================================================== +#pragma mark - +#pragma mark Instance Methods +//=========================================================== + +// for customizing appearance, automatically disabled userInteractionEnabled on view +- (void)addSubviewToBackgroundView:(UIView *)view; +- (void)addSubviewToBackgroundView:(UIView *)view atIndex:(NSInteger)index; + +// Method to re-post a cleared message +- (void)postMessageDictionary:(NSDictionary *)messageDictionary; + +// shows an activity indicator and the given message +- (void)postMessage:(NSString *)message; +- (void)postMessage:(NSString *)message duration:(NSTimeInterval)duration; +- (void)postMessage:(NSString *)message duration:(NSTimeInterval)duration animated:(BOOL)animated; +- (void)postMessage:(NSString *)message animated:(BOOL)animated; +// clears the message queue and shows this message instantly +- (void)postImmediateMessage:(NSString *)message animated:(BOOL)animated; +- (void)postImmediateMessage:(NSString *)message duration:(NSTimeInterval)duration; +- (void)postImmediateMessage:(NSString *)message duration:(NSTimeInterval)duration animated:(BOOL)animated; + +// shows a checkmark instead of the activity indicator and hides the status bar after the specified duration +- (void)postFinishMessage:(NSString *)message duration:(NSTimeInterval)duration; +- (void)postFinishMessage:(NSString *)message duration:(NSTimeInterval)duration animated:(BOOL)animated; +// clears the message queue and shows this message instantly +- (void)postImmediateFinishMessage:(NSString *)message duration:(NSTimeInterval)duration animated:(BOOL)animated; + +// shows a error-sign instead of the activity indicator and hides the status bar after the specified duration +- (void)postErrorMessage:(NSString *)message duration:(NSTimeInterval)duration; +- (void)postErrorMessage:(NSString *)message duration:(NSTimeInterval)duration animated:(BOOL)animated; +// clears the message queue and shows this message instantly +- (void)postImmediateErrorMessage:(NSString *)message duration:(NSTimeInterval)duration animated:(BOOL)animated; + +// hides the status bar overlay and resets it +- (void)hide; +// hides the status bar overlay but doesn't reset it's values +// this is useful if e.g. you have a screen where you don't have +// a status bar, but the other screens have one +// then you can hide it temporary and show it again afterwards +- (void)hideTemporary; +// this shows the status bar overlay, if there is text to show +- (void)show; + +// saves the state in NSUserDefaults and synchronizes them +- (void)saveState; +- (void)saveStateSynchronized:(BOOL)synchronizeAtEnd; +// restores the state from NSUserDefaults +- (void)restoreState; + +@end + + + +//=========================================================== +#pragma mark - +#pragma mark Delegate Protocol +//=========================================================== + +@protocol MTStatusBarOverlayDelegate +@optional +// is called, when a gesture on the overlay is recognized +- (void)statusBarOverlayDidRecognizeGesture:(UIGestureRecognizer *)gestureRecognizer; +// is called when the status bar overlay gets hidden +- (void)statusBarOverlayDidHide; +// is called, when the status bar overlay changed it's displayed message from one message to another +- (void)statusBarOverlayDidSwitchFromOldMessage:(NSString *)oldMessage toNewMessage:(NSString *)newMessage; +// is called when an immediate message gets posted and therefore messages in the queue get lost +// it tells the delegate the lost messages and the delegate can then enqueue the messages again +- (void)statusBarOverlayDidClearMessageQueue:(NSArray *)messageQueue; +@end diff --git a/Vendors/MTStatusBarOverlay/MTStatusBarOverlay.m b/Vendors/MTStatusBarOverlay/MTStatusBarOverlay.m new file mode 100755 index 0000000..884bc2e --- /dev/null +++ b/Vendors/MTStatusBarOverlay/MTStatusBarOverlay.m @@ -0,0 +1,1737 @@ +// +// MTStatusBarOverlay.m +// +// Created by Matthias Tretter on 27.09.10. +// Copyright (c) 2009-2011 Matthias Tretter, @myell0w. All rights reserved. +// +// 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. +// +// Credits go to: +// ------------------------------- +// http://stackoverflow.com/questions/2833724/adding-view-on-statusbar-in-iphone +// http://www.cocoabyss.com/uikit/custom-status-bar-ios/ +// @reederapp for inspiration +// ------------------------------- + +#import "MTStatusBarOverlay.h" +#import + + +//////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark Functions +//////////////////////////////////////////////////////////////////////// + +NSData* MTStatusBarBackgroundImageData(BOOL shrinked); +unsigned char* MTStatusBarBackgroundImageArray(BOOL shrinked); +unsigned int MTStatusBarBackgroundImageLength(BOOL shrinked); + +void mt_dispatch_sync_on_main_thread(dispatch_block_t block); + +//////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark Defines +//////////////////////////////////////////////////////////////////////// + +// the height of the status bar +#define kStatusBarHeight 20.f +// width of the screen in portrait-orientation +#define kScreenWidth [UIScreen mainScreen].bounds.size.width +// height of the screen in portrait-orientation +#define kScreenHeight [UIScreen mainScreen].bounds.size.height +// macro for checking if we are on the iPad +#define IsIPad (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) +// macro for checking if we are on the iPad in iPhone-Emulation mode +#define IsIPhoneEmulationMode (!IsIPad && \ +MAX([UIApplication sharedApplication].statusBarFrame.size.width, [UIApplication sharedApplication].statusBarFrame.size.height) > 480.f) + + + +//////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark Customization +//////////////////////////////////////////////////////////////////////// + +/////////////////////////////////////////////////////// +// Light Theme (for UIStatusBarStyleDefault) +/////////////////////////////////////////////////////// + +#define kLightThemeTextColor [UIColor blackColor] +#define kLightThemeErrorMessageTextColor [UIColor blackColor] // [UIColor colorWithRed:0.494898f green:0.330281f blue:0.314146f alpha:1.0f] +#define kLightThemeFinishedMessageTextColor [UIColor blackColor] // [UIColor colorWithRed:0.389487f green:0.484694f blue:0.38121f alpha:1.0f] +#define kLightThemeShadowColor [UIColor whiteColor] +#define kLightThemeErrorMessageShadowColor [UIColor whiteColor] +#define kLightThemeFinishedMessageShadowColor [UIColor whiteColor] +#define kLightThemeActivityIndicatorViewStyle UIActivityIndicatorViewStyleGray +#define kLightThemeDetailViewBackgroundColor [UIColor blackColor] +#define kLightThemeDetailViewBorderColor [UIColor darkGrayColor] +#define kLightThemeHistoryTextColor [UIColor colorWithRed:0.749f green:0.749f blue:0.749f alpha:1.0f] + + +/////////////////////////////////////////////////////// +// Dark Theme (for UIStatusBarStyleBlackOpaque) +/////////////////////////////////////////////////////// + +#define kDarkThemeTextColor [UIColor colorWithRed:0.749f green:0.749f blue:0.749f alpha:1.0f] +#define kDarkThemeErrorMessageTextColor [UIColor colorWithRed:0.749f green:0.749f blue:0.749f alpha:1.0f] // [UIColor colorWithRed:0.918367f green:0.48385f blue:0.423895f alpha:1.0f] +#define kDarkThemeFinishedMessageTextColor [UIColor colorWithRed:0.749f green:0.749f blue:0.749f alpha:1.0f] // [UIColor colorWithRed:0.681767f green:0.918367f blue:0.726814f alpha:1.0f] +#define kDarkThemeActivityIndicatorViewStyle UIActivityIndicatorViewStyleWhite +#define kDarkThemeDetailViewBackgroundColor [UIColor colorWithRed:0.3f green:0.3f blue:0.3f alpha:1.0f] +#define kDarkThemeDetailViewBorderColor [UIColor whiteColor] +#define kDarkThemeHistoryTextColor [UIColor whiteColor] + +/////////////////////////////////////////////////////// +// Progress +/////////////////////////////////////////////////////// + +#define kProgressViewAlpha 0.4f +#define kProgressViewBackgroundColor [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:1.0f] + + +/////////////////////////////////////////////////////// +// Animations +/////////////////////////////////////////////////////// + +// minimum time that a message is shown, when messages are queued +#define kMinimumMessageVisibleTime 0.4f + +// duration of the animation to show next status message in seconds +#define kNextStatusAnimationDuration 0.6f + +// duration the statusBarOverlay takes to appear when it was hidden +#define kAppearAnimationDuration 0.5f + +// animation duration of animation mode shrink +#define kAnimationDurationShrink 0.3f + +// animation duration of animation mode fallDown +#define kAnimationDurationFallDown 0.4f + +// animation duration of change of progressView-size +#define kUpdateProgressViewDuration 0.2f + +// delay after that the status bar gets visible again after rotation +#define kRotationAppearDelay [UIApplication sharedApplication].statusBarOrientationAnimationDuration + + +/////////////////////////////////////////////////////// +// Text +/////////////////////////////////////////////////////// + +// Text that is displayed in the finished-Label when the finish was successful +#define kFinishedText @"✓" +#define kFinishedFontSize 22.f + +// Text that is displayed when an error occured +#define kErrorText @"✗" +#define kErrorFontSize 19.f + + + +/////////////////////////////////////////////////////// +// Detail View +/////////////////////////////////////////////////////// + +#define kHistoryTableRowHeight 25.f +#define kMaxHistoryTableRowCount 5 + +#define kDetailViewAlpha 0.9f +#define kDetailViewWidth (IsIPad ? 400.f : 280.f) +// default frame of detail view when it is hidden +#define kDefaultDetailViewFrame CGRectMake((kScreenWidth - kDetailViewWidth)/2, -(kHistoryTableRowHeight*kMaxHistoryTableRowCount + kStatusBarHeight),\ +kDetailViewWidth, kHistoryTableRowHeight*kMaxHistoryTableRowCount + kStatusBarHeight) + + +/////////////////////////////////////////////////////// +// Size +/////////////////////////////////////////////////////// + +// Size of the text in the status labels +#define kStatusLabelSize 12.f + +// default-width of the small-mode +#define kWidthSmall 26.f + + + +//////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark Class Extension +//////////////////////////////////////////////////////////////////////// + +@interface MTStatusBarOverlay () + +@property (nonatomic, strong) UIActivityIndicatorView *activityIndicator; +@property (nonatomic, strong) UIImageView *statusBarBackgroundImageView; +@property (nonatomic, strong) UILabel *statusLabel1; +@property (nonatomic, strong) UILabel *statusLabel2; +@property (nonatomic, unsafe_unretained) UILabel *hiddenStatusLabel; +@property (unsafe_unretained, nonatomic, readonly) UILabel *visibleStatusLabel; +@property (nonatomic, strong) UIImageView *progressView; +@property (nonatomic, assign) CGRect oldBackgroundViewFrame; +// overwrite property for read-write-access +@property (assign, getter=isHideInProgress) BOOL hideInProgress; +@property (assign, getter=isActive) BOOL active; +// read out hidden-state using alpha-value and hidden-property +@property (nonatomic, readonly, getter=isReallyHidden) BOOL reallyHidden; +@property (nonatomic, strong) UITextView *detailTextView; +@property (nonatomic, strong) NSMutableArray *messageQueue; +// overwrite property for read-write-access +@property (nonatomic, strong) NSMutableArray *messageHistory; +@property (nonatomic, strong) UITableView *historyTableView; +@property (nonatomic, assign) BOOL forcedToHide; + +// intern method that posts a new entry to the message-queue +- (void)postMessage:(NSString *)message type:(MTMessageType)messageType duration:(NSTimeInterval)duration animated:(BOOL)animated immediate:(BOOL)immediate; +// intern method that clears the messageQueue and then posts a new entry to it +- (void)postImmediateMessage:(NSString *)message type:(MTMessageType)messageType duration:(NSTimeInterval)duration animated:(BOOL)animated; +// intern method that does all the work of showing the next message in the queue +- (void)showNextMessage; + +// is called when the user touches the statusbar +- (void)contentViewClicked:(UIGestureRecognizer *)gestureRecognizer; +// is called when the user swipes down the statusbar +- (void)contentViewSwipedUp:(UIGestureRecognizer *)gestureRecognizer; +- (void)contentViewSwipedDown:(UIGestureRecognizer *)gestureRecognizer; + +// updates the current status bar background image for the given style and current size +- (void)setStatusBarBackgroundForStyle:(UIStatusBarStyle)style; +// updates the text-colors of the labels for the given style and message type +- (void)setColorSchemeForStatusBarStyle:(UIStatusBarStyle)style messageType:(MTMessageType)messageType; +// updates the visiblity of the activity indicator and finished-label depending on the type +- (void)updateUIForMessageType:(MTMessageType)messageType duration:(NSTimeInterval)duration; +// updates the size of the progressView to always cover only the displayed text-frame +- (void)updateProgressViewSizeForLabel:(UILabel *)label; +// calls the delegate when a switch from one message to another one occured +- (void)callDelegateWithNewMessage:(NSString *)newMessage; +// update the height of the detail text view according to new text +- (void)updateDetailTextViewHeight; +// shrink/expand the overlay +- (void)setShrinked:(BOOL)shrinked animated:(BOOL)animated; + +// set hidden-state using alpha-value instead of hidden-property +- (void)setHidden:(BOOL)hidden useAlpha:(BOOL)useAlpha; +// used for performSelector:withObject: +- (void)setHiddenUsingAlpha:(BOOL)hidden; +// set hidden-state of detailView +- (void)setDetailViewHidden:(BOOL)hidden animated:(BOOL)animated; + +// History-tracking +- (void)addMessageToHistory:(NSString *)message; +- (void)clearHistory; + +// selectors +- (void)rotateToStatusBarFrame:(NSValue *)statusBarFrameValue; +- (void)didChangeStatusBarFrame:(NSNotification *)notification; + +// Fix to not overlay Notification Center +- (void)applicationDidBecomeActive:(NSNotification *)notifaction; +- (void)applicationWillResignActive:(NSNotification *)notifaction; + +// returns the current frame for the detail view depending on the interface orientation +- (CGRect)backgroundViewFrameForStatusBarInterfaceOrientation; + +@end + + + +@implementation MTStatusBarOverlay + +@synthesize backgroundView = backgroundView_; +@synthesize detailView = detailView_; +@synthesize statusBarBackgroundImageView = statusBarBackgroundImageView_; +@synthesize statusLabel1 = statusLabel1_; +@synthesize statusLabel2 = statusLabel2_; +@synthesize hiddenStatusLabel = hiddenStatusLabel_; +@synthesize progress = progress_; +@synthesize progressView = progressView_; +@synthesize activityIndicator = activityIndicator_; +@synthesize finishedLabel = finishedLabel_; +@synthesize hidesActivity = hidesActivity_; +@synthesize defaultStatusBarImage = defaultStatusBarImage_; +@synthesize defaultStatusBarImageShrinked = defaultStatusBarImageShrinked_; +@synthesize smallFrame = smallFrame_; +@synthesize oldBackgroundViewFrame = oldBackgroundViewFrame_; +@synthesize animation = animation_; +@synthesize hideInProgress = hideInProgress_; +@synthesize active = active_; +@synthesize messageQueue = messageQueue_; +@synthesize canRemoveImmediateMessagesFromQueue = canRemoveImmediateMessagesFromQueue_; +@synthesize detailViewMode = detailViewMode_; +@synthesize detailText = detailText_; +@synthesize detailTextView = detailTextView_; +@synthesize messageHistory = messageHistory_; +@synthesize historyTableView = historyTableView_; +@synthesize delegate = delegate_; +@synthesize forcedToHide = forcedToHide_; +@synthesize lastPostedMessage = lastPostedMessage_; + +//////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark Lifecycle +//////////////////////////////////////////////////////////////////////// + +- (id)initWithFrame:(CGRect)frame { + if ((self = [super initWithFrame:frame])) { + CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame; + + // only use height of 20px even is status bar is doubled + statusBarFrame.size.height = statusBarFrame.size.height == 2*kStatusBarHeight ? kStatusBarHeight : statusBarFrame.size.height; + // if we are on the iPad but in iPhone-Mode (non-universal-app) correct the width + if(IsIPhoneEmulationMode) { + statusBarFrame.size.width = 320.f; + } + + // Place the window on the correct level and position + self.windowLevel = UIWindowLevelStatusBar+1.f; + self.frame = statusBarFrame; + self.alpha = 0.f; + self.hidden = NO; + + // Default Small size: just show Activity Indicator + smallFrame_ = CGRectMake(statusBarFrame.size.width - kWidthSmall, 0.f, kWidthSmall, statusBarFrame.size.height); + + // Default-values + animation_ = MTStatusBarOverlayAnimationNone; + active_ = NO; + hidesActivity_ = NO; + forcedToHide_ = NO; + + // the detail view that is shown when the user touches the status bar in animation mode "FallDown" + detailView_ = [[UIView alloc] initWithFrame:kDefaultDetailViewFrame]; + detailView_.backgroundColor = [UIColor blackColor]; + detailView_.alpha = kDetailViewAlpha; + detailView_.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin; + detailViewMode_ = MTDetailViewModeCustom; + + // add rounded corners to detail-view + detailView_.layer.masksToBounds = YES; + detailView_.layer.cornerRadius = 10.f; + detailView_.layer.borderWidth = 2.5f; + // add shadow + /*detailView_.layer.shadowColor = [UIColor blackColor].CGColor; + detailView_.layer.shadowOpacity = 1.0f; + detailView_.layer.shadowRadius = 6.0f; + detailView_.layer.shadowOffset = CGSizeMake(0, 3);*/ + + // Detail Text label + detailTextView_ = [[UITextView alloc] initWithFrame:CGRectMake(0, kStatusBarHeight, + kDefaultDetailViewFrame.size.width, kDefaultDetailViewFrame.size.height - kStatusBarHeight)]; + detailTextView_.backgroundColor = [UIColor clearColor]; + detailTextView_.userInteractionEnabled = NO; + detailTextView_.hidden = detailViewMode_ != MTDetailViewModeDetailText; + [detailView_ addSubview:detailTextView_]; + + // Message History + messageHistory_ = [[NSMutableArray alloc] init]; + + historyTableView_ = [[UITableView alloc] initWithFrame:CGRectMake(0, kStatusBarHeight, + kDefaultDetailViewFrame.size.width, kDefaultDetailViewFrame.size.height - kStatusBarHeight)]; + historyTableView_.dataSource = self; + historyTableView_.delegate = nil; + historyTableView_.rowHeight = kHistoryTableRowHeight; + historyTableView_.separatorStyle = UITableViewCellSeparatorStyleNone; + // make table view-background transparent + historyTableView_.backgroundColor = [UIColor clearColor]; + historyTableView_.opaque = NO; + historyTableView_.hidden = detailViewMode_ != MTDetailViewModeHistory; + historyTableView_.backgroundView = nil; + + [detailView_ addSubview:historyTableView_]; + [self addSubview:detailView_]; + + // Create view that stores all the content + CGRect backgroundFrame = [self backgroundViewFrameForStatusBarInterfaceOrientation]; + backgroundView_ = [[UIView alloc] initWithFrame:backgroundFrame]; + backgroundView_.clipsToBounds = YES; + backgroundView_.autoresizingMask = UIViewAutoresizingFlexibleWidth; + oldBackgroundViewFrame_ = backgroundView_.frame; + + // Add gesture recognizers + UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(contentViewClicked:)]; + //UISwipeGestureRecognizer *upGestureRecognizer = [[[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(contentViewSwipedUp:)] autorelease]; + //UISwipeGestureRecognizer *downGestureRecognizer = [[[UISwipeGestureRecognizer alloc] initWithTarget:self action:@selector(contentViewSwipedDown:)] autorelease]; + + //upGestureRecognizer.direction = UISwipeGestureRecognizerDirectionUp; + //downGestureRecognizer.direction = UISwipeGestureRecognizerDirectionDown; + + [backgroundView_ addGestureRecognizer:tapGestureRecognizer]; + //[detailView_ addGestureRecognizer:upGestureRecognizer]; + //[self addGestureRecognizer:downGestureRecognizer]; + + // Images used as background when status bar style is Default + defaultStatusBarImage_ = [UIImage imageWithData:MTStatusBarBackgroundImageData(NO)]; + defaultStatusBarImageShrinked_ = [UIImage imageWithData:MTStatusBarBackgroundImageData(YES)]; + + // Background-Image of the Content View + statusBarBackgroundImageView_ = [[UIImageView alloc] initWithFrame:backgroundView_.frame]; + statusBarBackgroundImageView_.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + [self addSubviewToBackgroundView:statusBarBackgroundImageView_]; + + // Activity Indicator + activityIndicator_ = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; + activityIndicator_.frame = CGRectMake(6.f, 3.f, backgroundView_.frame.size.height - 6.f, backgroundView_.frame.size.height - 6.f); + activityIndicator_.hidesWhenStopped = YES; + + // iOS 5 doesn't correctly resize the activityIndicator. Bug? + if ([activityIndicator_ respondsToSelector:@selector(setColor:)]) { + [activityIndicator_.layer setValue:[NSNumber numberWithFloat:0.75f] forKeyPath:@"transform.scale"]; + } + + [self addSubviewToBackgroundView:activityIndicator_]; + + // Finished-Label + finishedLabel_ = [[UILabel alloc] initWithFrame:CGRectMake(4.f,1.f,backgroundView_.frame.size.height, backgroundView_.frame.size.height-1.f)]; + finishedLabel_.shadowOffset = CGSizeMake(0.f, 1.f); + finishedLabel_.backgroundColor = [UIColor clearColor]; + finishedLabel_.hidden = YES; + finishedLabel_.text = kFinishedText; + finishedLabel_.textAlignment = UITextAlignmentCenter; + finishedLabel_.font = [UIFont fontWithName:@"HelveticaNeue-Bold" size:kFinishedFontSize]; + finishedLabel_.adjustsFontSizeToFitWidth = YES; + [self addSubviewToBackgroundView:finishedLabel_]; + + // Status Label 1 is first visible + statusLabel1_ = [[UILabel alloc] initWithFrame:CGRectMake(30.f, 0.f, backgroundView_.frame.size.width - 60.f,backgroundView_.frame.size.height-1.f)]; + statusLabel1_.backgroundColor = [UIColor clearColor]; + statusLabel1_.shadowOffset = CGSizeMake(0.f, 1.f); + statusLabel1_.font = [UIFont boldSystemFontOfSize:kStatusLabelSize]; + statusLabel1_.textAlignment = UITextAlignmentCenter; + statusLabel1_.numberOfLines = 1; + statusLabel1_.lineBreakMode = UILineBreakModeTailTruncation; + statusLabel1_.autoresizingMask = UIViewAutoresizingFlexibleWidth; + [self addSubviewToBackgroundView:statusLabel1_]; + + // Status Label 2 is hidden + statusLabel2_ = [[UILabel alloc] initWithFrame:CGRectMake(30.f, backgroundView_.frame.size.height,backgroundView_.frame.size.width - 60.f , backgroundView_.frame.size.height-1.f)]; + statusLabel2_.shadowOffset = CGSizeMake(0.f, 1.f); + statusLabel2_.backgroundColor = [UIColor clearColor]; + statusLabel2_.font = [UIFont boldSystemFontOfSize:kStatusLabelSize]; + statusLabel2_.textAlignment = UITextAlignmentCenter; + statusLabel2_.numberOfLines = 1; + statusLabel2_.lineBreakMode = UILineBreakModeTailTruncation; + statusLabel2_.autoresizingMask = UIViewAutoresizingFlexibleWidth; + [self addSubviewToBackgroundView:statusLabel2_]; + + // the hidden status label at the beginning + hiddenStatusLabel_ = statusLabel2_; + + progress_ = 1.0; + progressView_ = [[UIImageView alloc] initWithFrame:statusBarBackgroundImageView_.frame]; + progressView_.opaque = NO; + progressView_.hidden = YES; + progressView_.alpha = kProgressViewAlpha; + [self addSubviewToBackgroundView:progressView_]; + + messageQueue_ = [[NSMutableArray alloc] init]; + canRemoveImmediateMessagesFromQueue_ = YES; + + [self addSubview:backgroundView_]; + + // listen for changes of status bar frame + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(didChangeStatusBarFrame:) + name:UIApplicationWillChangeStatusBarFrameNotification object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(applicationDidBecomeActive:) + name:UIApplicationDidBecomeActiveNotification object:nil]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(applicationWillResignActive:) + name:UIApplicationWillResignActiveNotification object:nil]; + + // initial rotation, fixes the issue with a wrong bar appearance in landscape only mode + [self rotateToStatusBarFrame:nil]; + } + + return self; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + delegate_ = nil; +} + +//////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark Status Bar Appearance +//////////////////////////////////////////////////////////////////////// + +- (void)addSubviewToBackgroundView:(UIView *)view { + view.userInteractionEnabled = NO; + [self.backgroundView addSubview:view]; +} + +- (void)addSubviewToBackgroundView:(UIView *)view atIndex:(NSInteger)index { + view.userInteractionEnabled = NO; + [self.backgroundView insertSubview:view atIndex:index]; +} + + +//////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark Save/Restore current State +//////////////////////////////////////////////////////////////////////// + +- (void)saveState { + [self saveStateSynchronized:YES]; +} + +- (void)saveStateSynchronized:(BOOL)synchronizeAtEnd { + // TODO: save more state + [[NSUserDefaults standardUserDefaults] setBool:self.shrinked forKey:kMTStatusBarOverlayStateShrinked]; + + if (synchronizeAtEnd) { + [[NSUserDefaults standardUserDefaults] synchronize]; + } +} + +- (void)restoreState { + // restore shrinked-state + [self setShrinked:[[NSUserDefaults standardUserDefaults] boolForKey:kMTStatusBarOverlayStateShrinked] animated:NO]; +} + +//////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark Message Posting +//////////////////////////////////////////////////////////////////////// + +- (void)postMessage:(NSString *)message { + [self postMessage:message animated:YES]; +} + +- (void)postMessage:(NSString *)message animated:(BOOL)animated { + [self postMessage:message type:MTMessageTypeActivity duration:0 animated:animated immediate:NO]; +} + +- (void)postMessage:(NSString *)message duration:(NSTimeInterval)duration { + [self postMessage:message type:MTMessageTypeActivity duration:duration animated:YES immediate:NO]; +} + +- (void)postMessage:(NSString *)message duration:(NSTimeInterval)duration animated:(BOOL)animated { + [self postMessage:message type:MTMessageTypeActivity duration:duration animated:animated immediate:NO]; +} + +- (void)postImmediateMessage:(NSString *)message animated:(BOOL)animated { + [self postImmediateMessage:message type:MTMessageTypeActivity duration:0 animated:animated]; +} + +- (void)postImmediateMessage:(NSString *)message duration:(NSTimeInterval)duration { + [self postImmediateMessage:message type:MTMessageTypeActivity duration:duration animated:YES]; +} + +- (void)postImmediateMessage:(NSString *)message duration:(NSTimeInterval)duration animated:(BOOL)animated { + [self postImmediateMessage:message type:MTMessageTypeActivity duration:duration animated:animated]; +} + +- (void)postFinishMessage:(NSString *)message duration:(NSTimeInterval)duration { + [self postFinishMessage:message duration:duration animated:YES]; +} + +- (void)postFinishMessage:(NSString *)message duration:(NSTimeInterval)duration animated:(BOOL)animated { + [self postMessage:message type:MTMessageTypeFinish duration:duration animated:animated immediate:NO]; +} + +- (void)postImmediateFinishMessage:(NSString *)message duration:(NSTimeInterval)duration animated:(BOOL)animated { + [self postImmediateMessage:message type:MTMessageTypeFinish duration:duration animated:animated]; +} + +- (void)postErrorMessage:(NSString *)message duration:(NSTimeInterval)duration { + [self postErrorMessage:message duration:duration animated:YES]; +} + +- (void)postErrorMessage:(NSString *)message duration:(NSTimeInterval)duration animated:(BOOL)animated { + [self postMessage:message type:MTMessageTypeError duration:duration animated:animated immediate:NO]; +} + +- (void)postImmediateErrorMessage:(NSString *)message duration:(NSTimeInterval)duration animated:(BOOL)animated { + [self postImmediateMessage:message type:MTMessageTypeError duration:duration animated:animated]; +} + +- (void)postMessageDictionary:(NSDictionary *)messageDictionary { + [self postMessage:[messageDictionary valueForKey:kMTStatusBarOverlayMessageKey] + type:[[messageDictionary valueForKey:kMTStatusBarOverlayMessageTypeKey] intValue] + duration:[[messageDictionary valueForKey:kMTStatusBarOverlayDurationKey] doubleValue] + animated:[[messageDictionary valueForKey:kMTStatusBarOverlayAnimationKey] boolValue] + immediate:[[messageDictionary valueForKey:kMTStatusBarOverlayImmediateKey] boolValue]]; +} + +- (void)postMessage:(NSString *)message type:(MTMessageType)messageType duration:(NSTimeInterval)duration animated:(BOOL)animated immediate:(BOOL)immediate { + mt_dispatch_sync_on_main_thread(^{ + // don't add to queue when message is empty + if (message.length == 0) { + return; + } + + NSDictionary *messageDictionaryRepresentation = [NSDictionary dictionaryWithObjectsAndKeys:message, kMTStatusBarOverlayMessageKey, + [NSNumber numberWithInt:messageType], kMTStatusBarOverlayMessageTypeKey, + [NSNumber numberWithDouble:duration], kMTStatusBarOverlayDurationKey, + [NSNumber numberWithBool:animated], kMTStatusBarOverlayAnimationKey, + [NSNumber numberWithBool:immediate], kMTStatusBarOverlayImmediateKey, nil]; + + @synchronized (self.messageQueue) { + [self.messageQueue insertObject:messageDictionaryRepresentation atIndex:0]; + } + + // if the overlay is currently not active, begin with showing of messages + if (!self.active) { + [self showNextMessage]; + } + }); +} + +- (void)postImmediateMessage:(NSString *)message type:(MTMessageType)messageType duration:(NSTimeInterval)duration animated:(BOOL)animated { + @synchronized(self.messageQueue) { + NSMutableArray *clearedMessages = [NSMutableArray array]; + + for (id messageDictionary in self.messageQueue) { + if (messageDictionary != [self.messageQueue lastObject] && + (self.canRemoveImmediateMessagesFromQueue || [[messageDictionary valueForKey:kMTStatusBarOverlayImmediateKey] boolValue] == NO)) { + [clearedMessages addObject:messageDictionary]; + } + } + + [self.messageQueue removeObjectsInArray:clearedMessages]; + + // call delegate + if ([self.delegate respondsToSelector:@selector(statusBarOverlayDidClearMessageQueue:)] && clearedMessages.count > 0) { + [self.delegate statusBarOverlayDidClearMessageQueue:clearedMessages]; + } + } + + [self postMessage:message type:messageType duration:duration animated:animated immediate:YES]; +} + +//////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark Showing Next Message +//////////////////////////////////////////////////////////////////////// + +- (void)showNextMessage { + if (self.forcedToHide) { + return; + } + + // if there is no next message to show overlay is not active anymore + @synchronized(self.messageQueue) { + if([self.messageQueue count] < 1) { + self.active = NO; + return; + } + } + + // there is a next message, overlay is active + self.active = YES; + + NSDictionary *nextMessageDictionary = nil; + + // read out next message + @synchronized(self.messageQueue) { + nextMessageDictionary = [self.messageQueue lastObject]; + } + + NSString *message = [nextMessageDictionary valueForKey:kMTStatusBarOverlayMessageKey]; + MTMessageType messageType = (MTMessageType)[[nextMessageDictionary valueForKey:kMTStatusBarOverlayMessageTypeKey] intValue]; + NSTimeInterval duration = (NSTimeInterval)[[nextMessageDictionary valueForKey:kMTStatusBarOverlayDurationKey] doubleValue]; + BOOL animated = [[nextMessageDictionary valueForKey:kMTStatusBarOverlayAnimationKey] boolValue]; + + // don't show anything if status bar is hidden (queue gets cleared) + if([UIApplication sharedApplication].statusBarHidden) { + @synchronized(self.messageQueue) { + [self.messageQueue removeAllObjects]; + } + + self.active = NO; + + return; + } + + // don't duplicate animation if already displaying with text + if (!self.reallyHidden && [self.visibleStatusLabel.text isEqualToString:message]) { + // remove unneccesary message + @synchronized(self.messageQueue) { + if (self.messageQueue.count > 0) + [self.messageQueue removeLastObject]; + } + + // show the next message w/o delay + [self showNextMessage]; + + return; + } + + // cancel previous hide- and clear requests + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hide) object:nil]; + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(clearHistory) object:nil]; + + // update UI depending on current status bar style + UIStatusBarStyle statusBarStyle = [UIApplication sharedApplication].statusBarStyle; + [self setStatusBarBackgroundForStyle:statusBarStyle]; + [self setColorSchemeForStatusBarStyle:statusBarStyle messageType:messageType]; + [self updateUIForMessageType:messageType duration:duration]; + + // if status bar is currently hidden, show it unless it is forced to hide + if (self.reallyHidden) { + // clear currently visible status label + self.visibleStatusLabel.text = @""; + + // show status bar overlay with animation + [UIView animateWithDuration:self.shrinked ? 0 : kAppearAnimationDuration + animations:^{ + [self setHidden:NO useAlpha:YES]; + }]; + } + + + if (animated) { + // set text of currently not visible label to new text + self.hiddenStatusLabel.text = message; + // update progressView to only cover displayed text + [self updateProgressViewSizeForLabel:self.hiddenStatusLabel]; + + // position hidden status label under visible status label + self.hiddenStatusLabel.frame = CGRectMake(self.hiddenStatusLabel.frame.origin.x, + kStatusBarHeight, + self.hiddenStatusLabel.frame.size.width, + self.hiddenStatusLabel.frame.size.height); + + + // animate hidden label into user view and visible status label out of view + [UIView animateWithDuration:kNextStatusAnimationDuration + delay:0 + options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction + animations:^{ + // move both status labels up + self.statusLabel1.frame = CGRectMake(self.statusLabel1.frame.origin.x, + self.statusLabel1.frame.origin.y - kStatusBarHeight, + self.statusLabel1.frame.size.width, + self.statusLabel1.frame.size.height); + self.statusLabel2.frame = CGRectMake(self.statusLabel2.frame.origin.x, + self.statusLabel2.frame.origin.y - kStatusBarHeight, + self.statusLabel2.frame.size.width, + self.statusLabel2.frame.size.height); + } + completion:^(BOOL finished) { + // add old message to history + [self addMessageToHistory:self.visibleStatusLabel.text]; + + // after animation, set new hidden status label indicator + if (self.hiddenStatusLabel == self.statusLabel1) { + self.hiddenStatusLabel = self.statusLabel2; + } else { + self.hiddenStatusLabel = self.statusLabel1; + } + + // remove the message from the queue + @synchronized(self.messageQueue) { + if (self.messageQueue.count > 0) + [self.messageQueue removeLastObject]; + } + + // inform delegate about message-switch + [self callDelegateWithNewMessage:message]; + + // show the next message + [self performSelector:@selector(showNextMessage) withObject:nil afterDelay:kMinimumMessageVisibleTime]; + }]; + } + + // w/o animation just save old text and set new one + else { + // add old message to history + [self addMessageToHistory:self.visibleStatusLabel.text]; + // set new text + self.visibleStatusLabel.text = message; + // update progressView to only cover displayed text + [self updateProgressViewSizeForLabel:self.visibleStatusLabel]; + + // remove the message from the queue + @synchronized(self.messageQueue) { + if (self.messageQueue.count > 0) + [self.messageQueue removeLastObject]; + } + + // inform delegate about message-switch + [self callDelegateWithNewMessage:message]; + + // show next message + [self performSelector:@selector(showNextMessage) withObject:nil afterDelay:kMinimumMessageVisibleTime]; + } + + self.lastPostedMessage = message; +} + +- (void)hide { + [self.activityIndicator stopAnimating]; + self.statusLabel1.text = @""; + self.statusLabel2.text = @""; + + self.hideInProgress = NO; + // cancel previous hide- and clear requests + [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(hide) object:nil]; + + // hide detailView + [self setDetailViewHidden:YES animated:YES]; + + // hide status bar overlay with animation + [UIView animateWithDuration:self.shrinked ? 0. : kAppearAnimationDuration + delay:0 + options:UIViewAnimationOptionAllowUserInteraction + animations:^{ + [self setHidden:YES useAlpha:YES]; + } completion:^(BOOL finished) { + // call delegate + if ([self.delegate respondsToSelector:@selector(statusBarOverlayDidHide)]) { + [self.delegate statusBarOverlayDidHide]; + } + }]; +} + +- (void)hideTemporary { + self.forcedToHide = YES; + + // hide status bar overlay with animation + [UIView animateWithDuration:self.shrinked ? 0. : kAppearAnimationDuration animations:^{ + [self setHidden:YES useAlpha:YES]; + }]; +} +// this shows the status bar overlay, if there is text to show +- (void)show { + self.forcedToHide = NO; + + if (self.reallyHidden) { + if (self.visibleStatusLabel.text.length > 0) { + // show status bar overlay with animation + [UIView animateWithDuration:self.shrinked ? 0. : kAppearAnimationDuration animations:^{ + [self setHidden:NO useAlpha:YES]; + }]; + } + + [self showNextMessage]; + } +} + +//////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark Rotation +//////////////////////////////////////////////////////////////////////// + +- (void)didChangeStatusBarFrame:(NSNotification *)notification { + NSValue * statusBarFrameValue = [notification.userInfo valueForKey:UIApplicationStatusBarFrameUserInfoKey]; + + // TODO: react on changes of status bar height (e.g. incoming call, tethering, ...) + // NSLog(@"Status bar frame changed: %@", NSStringFromCGRect([statusBarFrameValue CGRectValue])); + + // have to use performSelector to prohibit animation of rotation + [self performSelector:@selector(rotateToStatusBarFrame:) withObject:statusBarFrameValue afterDelay:0]; +} + +- (void)rotateToStatusBarFrame:(NSValue *)statusBarFrameValue { + // current interface orientation + UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation; + // is the statusBar visible before rotation? + BOOL visibleBeforeTransformation = !self.reallyHidden; + // store a flag, if the StatusBar is currently shrinked + BOOL shrinkedBeforeTransformation = self.shrinked; + + + // hide and then unhide after rotation + if (visibleBeforeTransformation) { + [self setHidden:YES useAlpha:YES]; + [self setDetailViewHidden:YES animated:NO]; + } + + CGFloat pi = (CGFloat)M_PI; + if (orientation == UIDeviceOrientationPortrait) { + self.transform = CGAffineTransformIdentity; + self.frame = CGRectMake(0.f,0.f,kScreenWidth,kStatusBarHeight); + self.smallFrame = CGRectMake(self.frame.size.width - kWidthSmall, 0.0f, kWidthSmall, self.frame.size.height); + }else if (orientation == UIDeviceOrientationLandscapeLeft) { + self.transform = CGAffineTransformMakeRotation(pi * (90.f) / 180.0f); + self.frame = CGRectMake(kScreenWidth - kStatusBarHeight,0, kStatusBarHeight, kScreenHeight); + self.smallFrame = CGRectMake(kScreenHeight-kWidthSmall,0,kWidthSmall,kStatusBarHeight); + } else if (orientation == UIDeviceOrientationLandscapeRight) { + self.transform = CGAffineTransformMakeRotation(pi * (-90.f) / 180.0f); + self.frame = CGRectMake(0.f,0.f, kStatusBarHeight, kScreenHeight); + self.smallFrame = CGRectMake(kScreenHeight-kWidthSmall,0.f, kWidthSmall, kStatusBarHeight); + } else if (orientation == UIDeviceOrientationPortraitUpsideDown) { + self.transform = CGAffineTransformMakeRotation(pi); + self.frame = CGRectMake(0.f,kScreenHeight - kStatusBarHeight,kScreenWidth,kStatusBarHeight); + self.smallFrame = CGRectMake(self.frame.size.width - kWidthSmall, 0.f, kWidthSmall, self.frame.size.height); + } + + self.backgroundView.frame = [self backgroundViewFrameForStatusBarInterfaceOrientation]; + + // if the statusBar is currently shrinked, update the frames for the new rotation state + if (shrinkedBeforeTransformation) { + // the oldBackgroundViewFrame is the frame of the whole StatusBar + self.oldBackgroundViewFrame = CGRectMake(0.f,0.f,UIInterfaceOrientationIsPortrait(orientation) ? kScreenWidth : kScreenHeight,kStatusBarHeight); + // the backgroundView gets the newly computed smallFrame + self.backgroundView.frame = self.smallFrame; + } + + // make visible after given time + if (visibleBeforeTransformation) { + // TODO: + // somehow this doesn't work anymore since rotation-method was changed from + // DeviceDidRotate-Notification to StatusBarFrameChanged-Notification + // therefore iplemented it with a UIView-Animation instead + //[self performSelector:@selector(setHiddenUsingAlpha:) withObject:[NSNumber numberWithBool:NO] afterDelay:kRotationAppearDelay]; + + [UIView animateWithDuration:kAppearAnimationDuration + delay:kRotationAppearDelay + options:UIViewAnimationOptionCurveEaseInOut + animations:^{ + [self setHiddenUsingAlpha:NO]; + } + completion:NULL]; + } +} + +//////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark Setter/Getter +//////////////////////////////////////////////////////////////////////// + +- (void)setProgress:(double)progress { + // bound progress to 0.0 - 1.0 + progress = MAX(0.0, MIN(progress, 1.0)); + + // do not decrease progress if it is no reset + if (progress == 0.0 || progress > progress_) { + progress_ = progress; + } + + // update UI on main thread + [self performSelectorOnMainThread:@selector(updateProgressViewSizeForLabel:) withObject:self.visibleStatusLabel waitUntilDone:NO]; +} + +- (void)setDetailText:(NSString *)detailText { + if (detailText_ != detailText) { + detailText_ = [detailText copy]; + + // update text in label + self.detailTextView.text = detailText; + // update height of detailText-View + [self updateDetailTextViewHeight]; + + // update height of detailView + [self setDetailViewHidden:self.detailViewHidden animated:YES]; + } +} + +- (void)setDetailViewMode:(MTDetailViewMode)detailViewMode { + detailViewMode_ = detailViewMode; + + // update UI + self.historyTableView.hidden = detailViewMode != MTDetailViewModeHistory; + self.detailTextView.hidden = detailViewMode != MTDetailViewModeDetailText; +} + +- (void)setAnimation:(MTStatusBarOverlayAnimation)animation { + animation_ = animation; + + // update appearance according to new animation-mode + + // if new animation mode is shrink or none, the detailView mustn't be visible + if (animation == MTStatusBarOverlayAnimationShrink || animation == MTStatusBarOverlayAnimationNone) { + // detailView currently visible -> hide it + if (!self.detailViewHidden) { + [self setDetailViewHidden:YES animated:YES]; + } + } + + // if new animation mode is fallDown, the overlay must be extended + if (animation == MTStatusBarOverlayAnimationFallDown) { + if (self.shrinked) { + [self setShrinked:NO animated:YES]; + } + } +} + +- (BOOL)isShrinked { + return self.backgroundView.frame.size.width == self.smallFrame.size.width; +} + +- (void)setShrinked:(BOOL)shrinked animated:(BOOL)animated { + [UIView animateWithDuration:animated ? kAnimationDurationShrink : 0. + animations:^{ + // shrink the overlay + if (shrinked) { + self.oldBackgroundViewFrame = self.backgroundView.frame; + self.backgroundView.frame = self.smallFrame; + + self.statusLabel1.hidden = YES; + self.statusLabel2.hidden = YES; + } + // expand the overlay + else { + self.backgroundView.frame = self.oldBackgroundViewFrame; + + self.statusLabel1.hidden = NO; + self.statusLabel2.hidden = NO; + + if ([activityIndicator_ respondsToSelector:@selector(setColor:)]) { + CGRect frame = self.statusLabel1.frame; + frame.size.width = self.backgroundView.frame.size.width-60.f; + self.statusLabel1.frame = frame; + + frame = self.statusLabel2.frame; + frame.size.width = self.backgroundView.frame.size.width-60.f; + self.statusLabel2.frame = frame; + } + } + + // update status bar background + [self setStatusBarBackgroundForStyle:[UIApplication sharedApplication].statusBarStyle]; + }]; +} + + +- (BOOL)isDetailViewHidden { + return self.detailView.hidden == YES || self.detailView.alpha == 0.f || + self.detailView.frame.origin.y + self.detailView.frame.size.height < kStatusBarHeight; +} + +- (void)setDetailViewHidden:(BOOL)hidden animated:(BOOL)animated { + // hide detail view + if (hidden) { + [UIView animateWithDuration:animated ? kAnimationDurationFallDown : 0. + delay:0. + options:UIViewAnimationOptionCurveEaseOut + animations: ^{ + self.detailView.frame = CGRectMake(self.detailView.frame.origin.x, - self.detailView.frame.size.height, + self.detailView.frame.size.width, self.detailView.frame.size.height); + } + completion:NULL]; + } + // show detail view + else { + [UIView animateWithDuration:animated ? kAnimationDurationFallDown : 0. + delay:0. + options:UIViewAnimationOptionCurveEaseIn + animations: ^{ + int y = 0; + + // if history is enabled let the detailView "grow" with + // the number of messages in the history up until the set maximum + if (self.detailViewMode == MTDetailViewModeHistory) { + y = -(kMaxHistoryTableRowCount - MIN(self.messageHistory.count, kMaxHistoryTableRowCount)) * kHistoryTableRowHeight; + + self.historyTableView.frame = CGRectMake(self.historyTableView.frame.origin.x, kStatusBarHeight - y, + self.historyTableView.frame.size.width, self.historyTableView.frame.size.height); + } + + if (self.detailViewMode == MTDetailViewModeDetailText) { + self.detailView.frame = CGRectMake(self.detailView.frame.origin.x, y, + self.detailView.frame.size.width, self.detailTextView.frame.size.height + kStatusBarHeight); + } else { + self.detailView.frame = CGRectMake(self.detailView.frame.origin.x, y, + self.detailView.frame.size.width, self.detailView.frame.size.height); + } + } + completion:NULL]; + } + +} + +- (UILabel *)visibleStatusLabel { + if (self.hiddenStatusLabel == self.statusLabel1) { + return self.statusLabel2; + } + + return self.statusLabel1; +} + +//////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark UITableViewDataSource +//////////////////////////////////////////////////////////////////////// + +- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { + return self.messageHistory.count; +} + +- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { + static NSString *cellID = @"MTStatusBarOverlayHistoryCellID"; + UITableViewCell *cell = nil; + + // step 1: is there a reusable cell? + cell = [tableView dequeueReusableCellWithIdentifier:cellID]; + + // step 2: no? -> create new cell + if (cell == nil) { + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellID]; + + cell.textLabel.font = [UIFont boldSystemFontOfSize:10]; + cell.textLabel.textColor = [UIApplication sharedApplication].statusBarStyle == UIStatusBarStyleDefault ? kLightThemeHistoryTextColor : kDarkThemeHistoryTextColor; + + cell.detailTextLabel.font = [UIFont boldSystemFontOfSize:12]; + cell.detailTextLabel.textColor = [UIApplication sharedApplication].statusBarStyle == UIStatusBarStyleDefault ? kLightThemeHistoryTextColor : kDarkThemeHistoryTextColor; + } + + // step 3: set up cell value + cell.textLabel.text = [self.messageHistory objectAtIndex:indexPath.row]; + cell.detailTextLabel.text = kFinishedText; + + return cell; +} + +//////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark Gesture Recognizer +//////////////////////////////////////////////////////////////////////// + +- (void)contentViewClicked:(UIGestureRecognizer *)gestureRecognizer { + if (gestureRecognizer.state == UIGestureRecognizerStateEnded) { + // if we are currently in a special state, restore to normal + // and ignore current set animation in that case + if (self.shrinked) { + [self setShrinked:NO animated:YES]; + } else if (!self.detailViewHidden) { + [self setDetailViewHidden:YES animated:YES]; + } else { + // normal case/status, do what's specified in animation-state + switch (self.animation) { + case MTStatusBarOverlayAnimationShrink: + [self setShrinked:!self.shrinked animated:YES]; + break; + + case MTStatusBarOverlayAnimationFallDown: + // detailView currently visible -> hide it + [self setDetailViewHidden:!self.detailViewHidden animated:YES]; + break; + case MTStatusBarOverlayAnimationNone: + // ignore + break; + } + } + + if ([self.delegate respondsToSelector:@selector(statusBarOverlayDidRecognizeGesture:)]) { + [self.delegate statusBarOverlayDidRecognizeGesture:gestureRecognizer]; + } + } +} + +- (void)contentViewSwipedUp:(UIGestureRecognizer *)gestureRecognizer { + if (gestureRecognizer.state == UIGestureRecognizerStateEnded) { + [self setDetailViewHidden:YES animated:YES]; + + if ([self.delegate respondsToSelector:@selector(statusBarOverlayDidRecognizeGesture:)]) { + [self.delegate statusBarOverlayDidRecognizeGesture:gestureRecognizer]; + } + } +} + +- (void)contentViewSwipedDown:(UIGestureRecognizer *)gestureRecognizer { + if (gestureRecognizer.state == UIGestureRecognizerStateEnded) { + [self setDetailViewHidden:NO animated:YES]; + + if ([self.delegate respondsToSelector:@selector(statusBarOverlayDidRecognizeGesture:)]) { + [self.delegate statusBarOverlayDidRecognizeGesture:gestureRecognizer]; + } + } +} + +//////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark UIApplication Notifications +//////////////////////////////////////////////////////////////////////// + +- (void)applicationWillResignActive:(NSNotification *)notifaction { + // We hide temporary when the application resigns active s.t the overlay + // doesn't overlay the Notification Center. Let's hope this helps AppStore + // Approval ... + [self hideTemporary]; +} + +- (void)applicationDidBecomeActive:(NSNotification *)notifaction { + [self show]; +} + +//////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark Private Methods +//////////////////////////////////////////////////////////////////////// + +- (void)setStatusBarBackgroundForStyle:(UIStatusBarStyle)style { + // gray status bar? + // on iPad the Default Status Bar Style is black too + if (style == UIStatusBarStyleDefault && !IsIPad && !IsIPhoneEmulationMode) { + // choose image depending on size + if (self.shrinked) { + self.statusBarBackgroundImageView.image = [self.defaultStatusBarImageShrinked stretchableImageWithLeftCapWidth:2.0f topCapHeight:0.0f]; + } else { + self.statusBarBackgroundImageView.image = [self.defaultStatusBarImage stretchableImageWithLeftCapWidth:2.0f topCapHeight:0.0f]; + } + statusBarBackgroundImageView_.backgroundColor = [UIColor clearColor]; + } + // black status bar? -> no image + else { + self.statusBarBackgroundImageView.image = nil; + statusBarBackgroundImageView_.backgroundColor = [UIColor blackColor]; + } +} + +- (void)setColorSchemeForStatusBarStyle:(UIStatusBarStyle)style messageType:(MTMessageType)messageType { + // gray status bar? + // on iPad the Default Status Bar Style is black too + if (style == UIStatusBarStyleDefault && !IsIPad && !IsIPhoneEmulationMode) { + // set color of labels depending on messageType + switch(messageType) { + case MTMessageTypeFinish: + self.statusLabel1.textColor = kLightThemeFinishedMessageTextColor; + self.statusLabel2.textColor = kLightThemeFinishedMessageTextColor; + self.finishedLabel.textColor = kLightThemeFinishedMessageTextColor; + self.statusLabel1.shadowColor = kLightThemeFinishedMessageShadowColor; + self.statusLabel2.shadowColor = kLightThemeFinishedMessageShadowColor; + self.finishedLabel.shadowColor = kLightThemeFinishedMessageShadowColor; + break; + case MTMessageTypeError: + self.statusLabel1.textColor = kLightThemeErrorMessageTextColor; + self.statusLabel2.textColor = kLightThemeErrorMessageTextColor; + self.finishedLabel.textColor = kLightThemeErrorMessageTextColor; + self.statusLabel1.shadowColor = kLightThemeErrorMessageShadowColor; + self.statusLabel2.shadowColor = kLightThemeErrorMessageShadowColor; + self.finishedLabel.shadowColor = kLightThemeErrorMessageShadowColor; + break; + default: + self.statusLabel1.textColor = kLightThemeTextColor; + self.statusLabel2.textColor = kLightThemeTextColor; + self.finishedLabel.textColor = kLightThemeTextColor; + self.statusLabel1.shadowColor = kLightThemeShadowColor; + self.statusLabel2.shadowColor = kLightThemeShadowColor; + self.finishedLabel.shadowColor = kLightThemeShadowColor; + break; + } + + self.activityIndicator.activityIndicatorViewStyle = kLightThemeActivityIndicatorViewStyle; + + if ([self.activityIndicator respondsToSelector:@selector(setColor:)]) { + [self.activityIndicator setColor:kLightThemeTextColor]; + } + + self.detailView.backgroundColor = kLightThemeDetailViewBackgroundColor; + self.detailView.layer.borderColor = [kLightThemeDetailViewBorderColor CGColor]; + self.historyTableView.separatorColor = kLightThemeDetailViewBorderColor; + self.detailTextView.textColor = kLightThemeHistoryTextColor; + + self.progressView.backgroundColor = [UIColor clearColor]; + self.progressView.image = [self.defaultStatusBarImageShrinked stretchableImageWithLeftCapWidth:2.0f topCapHeight:0.0f]; + } else { + // set color of labels depending on messageType + switch(messageType) { + case MTMessageTypeFinish: + self.statusLabel1.textColor = kDarkThemeFinishedMessageTextColor; + self.statusLabel2.textColor = kDarkThemeFinishedMessageTextColor; + self.finishedLabel.textColor = kDarkThemeFinishedMessageTextColor; + break; + case MTMessageTypeError: + self.statusLabel1.textColor = kDarkThemeErrorMessageTextColor; + self.statusLabel2.textColor = kDarkThemeErrorMessageTextColor; + self.finishedLabel.textColor = kDarkThemeErrorMessageTextColor; + break; + default: + self.statusLabel1.textColor = kDarkThemeTextColor; + self.statusLabel2.textColor = kDarkThemeTextColor; + self.finishedLabel.textColor = kDarkThemeTextColor; + break; + } + self.statusLabel1.shadowColor = nil; + self.statusLabel2.shadowColor = nil; + self.finishedLabel.shadowColor = nil; + + self.activityIndicator.activityIndicatorViewStyle = kDarkThemeActivityIndicatorViewStyle; + + if ([self.activityIndicator respondsToSelector:@selector(setColor:)]) { + [self.activityIndicator setColor:nil]; + } + + self.detailView.backgroundColor = kDarkThemeDetailViewBackgroundColor; + self.detailView.layer.borderColor = [kDarkThemeDetailViewBorderColor CGColor]; + self.historyTableView.separatorColor = kDarkThemeDetailViewBorderColor; + self.detailTextView.textColor = kDarkThemeHistoryTextColor; + + self.progressView.backgroundColor = kProgressViewBackgroundColor; + self.progressView.image = nil; + } +} + +- (void)updateUIForMessageType:(MTMessageType)messageType duration:(NSTimeInterval)duration { + // set properties depending on message-type + switch (messageType) { + case MTMessageTypeActivity: + // will not call hide after delay + self.hideInProgress = NO; + // show activity indicator, hide finished-label + self.finishedLabel.hidden = YES; + self.activityIndicator.hidden = self.hidesActivity; + + // start activity indicator + if (!self.hidesActivity) { + [self.activityIndicator startAnimating]; + } + break; + case MTMessageTypeFinish: + // will call hide after delay + self.hideInProgress = YES; + // show finished-label, hide acitvity indicator + self.finishedLabel.hidden = self.hidesActivity; + self.activityIndicator.hidden = YES; + + // stop activity indicator + [self.activityIndicator stopAnimating]; + + // update font and text + self.finishedLabel.font = [UIFont fontWithName:@"HelveticaNeue-Bold" size:kFinishedFontSize]; + self.finishedLabel.text = kFinishedText; + self.progress = 1.0; + break; + case MTMessageTypeError: + // will call hide after delay + self.hideInProgress = YES; + // show finished-label, hide activity indicator + self.finishedLabel.hidden = self.hidesActivity; + self.activityIndicator.hidden = YES; + + // stop activity indicator + [self.activityIndicator stopAnimating]; + + // update font and text + self.finishedLabel.font = [UIFont boldSystemFontOfSize:kErrorFontSize]; + self.finishedLabel.text = kErrorText; + self.progress = 1.0; + break; + } + + // if a duration is specified, hide after given duration + if (duration > 0.) { + // hide after duration + [self performSelector:@selector(hide) withObject:nil afterDelay:duration]; + // clear history after duration + [self performSelector:@selector(clearHistory) withObject:nil afterDelay:duration]; + } +} + +- (void)callDelegateWithNewMessage:(NSString *)newMessage { + if ([self.delegate respondsToSelector:@selector(statusBarOverlayDidSwitchFromOldMessage:toNewMessage:)]) { + NSString *oldMessage = nil; + + if (self.messageHistory.count > 0) { + oldMessage = [self.messageHistory lastObject]; + } + + [self.delegate statusBarOverlayDidSwitchFromOldMessage:oldMessage + toNewMessage:newMessage]; + } +} + +- (void)updateDetailTextViewHeight { + CGRect f = self.detailTextView.frame; + f.size.height = self.detailTextView.contentSize.height; + self.detailTextView.frame = f; +} + +- (void)updateProgressViewSizeForLabel:(UILabel *)label { + if (self.progress < 1.) { + CGSize size = [label sizeThatFits:label.frame.size]; + CGFloat width = size.width * (float)(1. - self.progress); + CGFloat x = label.center.x + size.width/2.f - width; + + // if we werent able to determine a size, do nothing + if (size.width == 0.f) { + return; + } + + // progressView always covers only the visible portion of the text + // it "shrinks" to the right with increased progress to reveal more + // text under it + self.progressView.hidden = NO; + //[UIView animateWithDuration:self.progress > 0.0 ? kUpdateProgressViewDuration : 0.0 + // animations:^{ + self.progressView.frame = CGRectMake(x, self.progressView.frame.origin.y, + self.backgroundView.frame.size.width-x, self.progressView.frame.size.height); + // }]; + } else { + self.progressView.hidden = YES; + } +} + +- (CGRect)backgroundViewFrameForStatusBarInterfaceOrientation{ + + UIInterfaceOrientation interfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation]; + + return (UIInterfaceOrientationIsLandscape(interfaceOrientation) ? + CGRectMake(0, 0, kScreenHeight, kStatusBarHeight) : + CGRectMake(0, 0, kScreenWidth, kStatusBarHeight)); +} + +//////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark History Tracking +//////////////////////////////////////////////////////////////////////// + +- (BOOL)isHistoryEnabled { + return self.detailViewMode == MTDetailViewModeHistory; +} + +- (void)setHistoryEnabled:(BOOL)historyEnabled { + if (historyEnabled) { + self.detailViewMode = MTDetailViewModeHistory; + } else { + self.detailViewMode = MTDetailViewModeCustom; + } + + self.historyTableView.hidden = !historyEnabled; +} + +- (void)addMessageToHistory:(NSString *)message { + if (message != nil + && [message stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]].length > 0) { + // add message to history-array + [self.messageHistory addObject:message]; + + if (self.historyEnabled) { + NSIndexPath *newHistoryMessageIndexPath = [NSIndexPath indexPathForRow:self.messageHistory.count-1 inSection:0]; + [self setDetailViewHidden:self.detailViewHidden animated:YES]; + + // update history table-view + [self.historyTableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newHistoryMessageIndexPath] + withRowAnimation:UITableViewRowAnimationFade]; + [self.historyTableView scrollToRowAtIndexPath:newHistoryMessageIndexPath + atScrollPosition:UITableViewScrollPositionTop animated:YES]; + } + } +} + +- (void)clearHistory { + [self.messageHistory removeAllObjects]; + [self.historyTableView reloadData]; +} + +//////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark Custom Hide Methods +//////////////////////////////////////////////////////////////////////// + +// used for performSelector:withObject +- (void)setHiddenUsingAlpha:(BOOL)hidden { + [self setHidden:hidden useAlpha:YES]; +} + +- (void)setHidden:(BOOL)hidden useAlpha:(BOOL)useAlpha { + if (useAlpha) { + self.alpha = hidden ? 0.f : 1.f; + } else { + self.hidden = hidden; + } +} + +- (BOOL)isReallyHidden { + return self.alpha == 0.f || self.hidden; +} + +//////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark Singleton Definitions +//////////////////////////////////////////////////////////////////////// + ++ (MTStatusBarOverlay *)sharedInstance { + static dispatch_once_t pred; + __strong static MTStatusBarOverlay *sharedOverlay = nil; + + dispatch_once(&pred, ^{ + sharedOverlay = [[MTStatusBarOverlay alloc] init]; + }); + + return sharedOverlay; +} + ++ (MTStatusBarOverlay *)sharedOverlay { + return [self sharedInstance]; +} + +@end + + +//////////////////////////////////////////////////////////////////////// +#pragma mark - +#pragma mark Encoded images +//////////////////////////////////////////////////////////////////////// + +unsigned char Silver_Base_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x14, + 0x08, 0x02, 0x00, 0x00, 0x00, 0xca, 0x87, 0x60, 0x8c, 0x00, 0x00, 0x00, + 0x66, 0x49, 0x44, 0x41, 0x54, 0x18, 0x19, 0x63, 0xc8, 0xca, 0xc9, 0xbd, + 0x7c, 0xe3, 0xf6, 0xfb, 0x6f, 0x3f, 0x81, 0x24, 0x90, 0xcd, 0x78, 0xf5, + 0xe6, 0x5d, 0x09, 0x19, 0x29, 0x06, 0x30, 0x78, 0xf1, 0xe4, 0x19, 0xe3, + 0xeb, 0xcf, 0xdf, 0x20, 0x1c, 0x08, 0xc9, 0xf2, 0xef, 0xef, 0x3f, 0x54, + 0xfe, 0xbf, 0xbf, 0x28, 0xfc, 0xbf, 0x7f, 0x51, 0xf9, 0x7f, 0xd0, 0xf8, + 0xff, 0xd0, 0xf8, 0x7f, 0xff, 0xe0, 0x57, 0xff, 0x07, 0xcd, 0xfc, 0x3f, + 0x68, 0xea, 0xff, 0xa1, 0xf1, 0xff, 0xfe, 0xfd, 0x83, 0xe2, 0x9e, 0x7f, + 0xe8, 0xfa, 0xd1, 0xed, 0xff, 0x87, 0xe6, 0x1f, 0x0c, 0xff, 0xa1, 0xca, + 0xcf, 0x9f, 0x37, 0x1f, 0xd9, 0x7c, 0x00, 0x5e, 0xf4, 0x44, 0x69, 0xf0, + 0x03, 0xee, 0x97, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, + 0x42, 0x60, 0x82 +}; +unsigned int Silver_Base_png_len = 159; + +unsigned char Silver_Base_Shrinked_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x14, + 0x08, 0x02, 0x00, 0x00, 0x00, 0xca, 0x87, 0x60, 0x8c, 0x00, 0x00, 0x00, + 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, + 0x65, 0x00, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x49, 0x6d, 0x61, 0x67, + 0x65, 0x52, 0x65, 0x61, 0x64, 0x79, 0x71, 0xc9, 0x65, 0x3c, 0x00, 0x00, + 0x03, 0x22, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, + 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, + 0x74, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x3d, 0x22, 0xef, 0xbb, 0xbf, + 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x57, 0x35, 0x4d, 0x30, 0x4d, 0x70, + 0x43, 0x65, 0x68, 0x69, 0x48, 0x7a, 0x72, 0x65, 0x53, 0x7a, 0x4e, 0x54, + 0x63, 0x7a, 0x6b, 0x63, 0x39, 0x64, 0x22, 0x3f, 0x3e, 0x20, 0x3c, 0x78, + 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, + 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, + 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, + 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x41, 0x64, 0x6f, 0x62, 0x65, + 0x20, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, + 0x30, 0x2d, 0x63, 0x30, 0x36, 0x30, 0x20, 0x36, 0x31, 0x2e, 0x31, 0x33, + 0x34, 0x37, 0x37, 0x37, 0x2c, 0x20, 0x32, 0x30, 0x31, 0x30, 0x2f, 0x30, + 0x32, 0x2f, 0x31, 0x32, 0x2d, 0x31, 0x37, 0x3a, 0x33, 0x32, 0x3a, 0x30, + 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x3e, 0x20, + 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, + 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, + 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, + 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, + 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, + 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x20, + 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, + 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, + 0x2e, 0x30, 0x2f, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, + 0x6d, 0x70, 0x4d, 0x4d, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x6d, 0x6d, + 0x2f, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x73, 0x74, 0x52, + 0x65, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, + 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x73, 0x54, 0x79, 0x70, + 0x65, 0x2f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, + 0x66, 0x23, 0x22, 0x20, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3d, 0x22, 0x41, 0x64, 0x6f, + 0x62, 0x65, 0x20, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, + 0x20, 0x43, 0x53, 0x35, 0x20, 0x4d, 0x61, 0x63, 0x69, 0x6e, 0x74, 0x6f, + 0x73, 0x68, 0x22, 0x20, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x49, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, + 0x70, 0x2e, 0x69, 0x69, 0x64, 0x3a, 0x31, 0x37, 0x37, 0x36, 0x31, 0x30, + 0x43, 0x42, 0x32, 0x33, 0x33, 0x34, 0x31, 0x31, 0x45, 0x30, 0x38, 0x45, + 0x42, 0x44, 0x43, 0x42, 0x33, 0x39, 0x37, 0x38, 0x33, 0x31, 0x39, 0x45, + 0x45, 0x35, 0x22, 0x20, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x44, 0x6f, + 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, + 0x70, 0x2e, 0x64, 0x69, 0x64, 0x3a, 0x31, 0x37, 0x37, 0x36, 0x31, 0x30, + 0x43, 0x43, 0x32, 0x33, 0x33, 0x34, 0x31, 0x31, 0x45, 0x30, 0x38, 0x45, + 0x42, 0x44, 0x43, 0x42, 0x33, 0x39, 0x37, 0x38, 0x33, 0x31, 0x39, 0x45, + 0x45, 0x35, 0x22, 0x3e, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, + 0x44, 0x65, 0x72, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x20, + 0x73, 0x74, 0x52, 0x65, 0x66, 0x3a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x69, 0x69, + 0x64, 0x3a, 0x31, 0x37, 0x37, 0x36, 0x31, 0x30, 0x43, 0x39, 0x32, 0x33, + 0x33, 0x34, 0x31, 0x31, 0x45, 0x30, 0x38, 0x45, 0x42, 0x44, 0x43, 0x42, + 0x33, 0x39, 0x37, 0x38, 0x33, 0x31, 0x39, 0x45, 0x45, 0x35, 0x22, 0x20, + 0x73, 0x74, 0x52, 0x65, 0x66, 0x3a, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, + 0x6e, 0x74, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x64, 0x69, + 0x64, 0x3a, 0x31, 0x37, 0x37, 0x36, 0x31, 0x30, 0x43, 0x41, 0x32, 0x33, + 0x33, 0x34, 0x31, 0x31, 0x45, 0x30, 0x38, 0x45, 0x42, 0x44, 0x43, 0x42, + 0x33, 0x39, 0x37, 0x38, 0x33, 0x31, 0x39, 0x45, 0x45, 0x35, 0x22, 0x2f, + 0x3e, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x20, 0x3c, 0x2f, 0x72, + 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x20, 0x3c, 0x2f, 0x78, 0x3a, + 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0x20, 0x3c, 0x3f, 0x78, + 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x65, 0x6e, 0x64, 0x3d, 0x22, + 0x72, 0x22, 0x3f, 0x3e, 0x6a, 0xe1, 0x0d, 0xf2, 0x00, 0x00, 0x00, 0x65, + 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x62, 0x7c, 0xff, 0xed, 0x27, 0x03, + 0x18, 0x3c, 0x79, 0xf4, 0x68, 0xfa, 0x94, 0x49, 0x8c, 0x6f, 0xbf, 0x7e, + 0x67, 0x80, 0x81, 0x17, 0x4f, 0x9e, 0xb1, 0xfc, 0xfb, 0xf7, 0x1f, 0xce, + 0x17, 0x93, 0x92, 0x64, 0xf9, 0xf7, 0xf7, 0x1f, 0x03, 0x12, 0x00, 0xca, + 0xff, 0x45, 0xe1, 0xff, 0xfd, 0x8b, 0xca, 0xff, 0x83, 0xc6, 0xff, 0x87, + 0xc6, 0xff, 0xfb, 0x07, 0xbf, 0xfa, 0x3f, 0x68, 0xe6, 0xff, 0x41, 0x53, + 0xff, 0xef, 0x0f, 0xba, 0xfd, 0x7f, 0xf0, 0xba, 0x0f, 0xdd, 0xfc, 0xbf, + 0xff, 0xd0, 0xfc, 0x83, 0xe1, 0x3f, 0x54, 0xfe, 0xfc, 0x79, 0xf3, 0x91, + 0xf9, 0x00, 0x01, 0x06, 0x00, 0xfe, 0x10, 0x41, 0xb0, 0x8a, 0x17, 0x69, + 0xf0, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, + 0x82 +}; +unsigned int Silver_Base_Shrinked_png_len = 1009; + +unsigned char Silver_Base_2x_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x28, + 0x08, 0x02, 0x00, 0x00, 0x00, 0x48, 0x34, 0xfc, 0xd7, 0x00, 0x00, 0x00, + 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x16, 0x25, 0x00, 0x00, 0x16, + 0x25, 0x01, 0x49, 0x52, 0x24, 0xf0, 0x00, 0x00, 0x00, 0xb9, 0x49, 0x44, + 0x41, 0x54, 0x38, 0x11, 0x63, 0xcc, 0xca, 0xc9, 0x15, 0x11, 0x15, 0xf5, + 0xf6, 0xf3, 0x57, 0x54, 0x52, 0x66, 0x65, 0x65, 0x65, 0x60, 0x60, 0xf8, + 0xf5, 0xeb, 0xe7, 0x83, 0xfb, 0xf7, 0xb7, 0x6e, 0xda, 0xf8, 0xe6, 0xf5, + 0x6b, 0x66, 0x4f, 0x6f, 0x9f, 0xdc, 0xc2, 0x62, 0xa0, 0x0a, 0x26, 0x26, + 0x26, 0xa0, 0x1c, 0x10, 0x30, 0x33, 0x33, 0x0b, 0x0b, 0x8b, 0x18, 0x99, + 0x98, 0x5c, 0xb9, 0x74, 0x89, 0xf1, 0xd4, 0x85, 0x4b, 0x2a, 0xaa, 0x6a, + 0x10, 0x09, 0x34, 0xf2, 0xda, 0x95, 0x2b, 0x8c, 0xaf, 0x3e, 0x7e, 0x01, + 0x2a, 0x47, 0x93, 0x80, 0x70, 0x7f, 0xfd, 0xfa, 0xc5, 0x02, 0x34, 0xf3, + 0xff, 0xff, 0xff, 0x58, 0xa5, 0x81, 0x4e, 0x61, 0xc1, 0x25, 0x07, 0xd1, + 0x40, 0xa1, 0xf4, 0x3f, 0x1c, 0x16, 0xc3, 0x0c, 0xff, 0xf7, 0x0f, 0xab, + 0xbb, 0x60, 0xd2, 0xf8, 0x75, 0x13, 0x34, 0x1c, 0xbb, 0xa7, 0xe1, 0x86, + 0xe3, 0xb5, 0x9b, 0xa0, 0xe1, 0x83, 0x57, 0xf7, 0x3f, 0xbc, 0xfe, 0x1e, + 0xc2, 0x1e, 0xc3, 0x9f, 0x1c, 0xfe, 0xff, 0xc7, 0x1b, 0x25, 0xff, 0x29, + 0x0b, 0x16, 0x02, 0x76, 0xe3, 0x4f, 0xc8, 0x84, 0xc2, 0x9c, 0x80, 0xe1, + 0xb4, 0x94, 0x3e, 0x77, 0xf6, 0x1c, 0xbe, 0x2c, 0x78, 0xfe, 0xfc, 0x79, + 0x3c, 0xd2, 0x00, 0x60, 0xe2, 0x89, 0x27, 0x38, 0x98, 0xa8, 0xf6, 0x00, + 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; +unsigned int Silver_Base_2x_png_len = 263; + +unsigned char Silver_Base_Shrinked_2x_png[] = { + 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, + 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x28, + 0x08, 0x02, 0x00, 0x00, 0x00, 0x48, 0x34, 0xfc, 0xd7, 0x00, 0x00, 0x00, + 0x19, 0x74, 0x45, 0x58, 0x74, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, + 0x65, 0x00, 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x49, 0x6d, 0x61, 0x67, + 0x65, 0x52, 0x65, 0x61, 0x64, 0x79, 0x71, 0xc9, 0x65, 0x3c, 0x00, 0x00, + 0x03, 0x22, 0x69, 0x54, 0x58, 0x74, 0x58, 0x4d, 0x4c, 0x3a, 0x63, 0x6f, + 0x6d, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x78, 0x6d, 0x70, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3f, 0x78, 0x70, 0x61, 0x63, 0x6b, 0x65, + 0x74, 0x20, 0x62, 0x65, 0x67, 0x69, 0x6e, 0x3d, 0x22, 0xef, 0xbb, 0xbf, + 0x22, 0x20, 0x69, 0x64, 0x3d, 0x22, 0x57, 0x35, 0x4d, 0x30, 0x4d, 0x70, + 0x43, 0x65, 0x68, 0x69, 0x48, 0x7a, 0x72, 0x65, 0x53, 0x7a, 0x4e, 0x54, + 0x63, 0x7a, 0x6b, 0x63, 0x39, 0x64, 0x22, 0x3f, 0x3e, 0x20, 0x3c, 0x78, + 0x3a, 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x20, 0x78, 0x6d, 0x6c, + 0x6e, 0x73, 0x3a, 0x78, 0x3d, 0x22, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x3a, + 0x6e, 0x73, 0x3a, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x22, 0x20, 0x78, 0x3a, + 0x78, 0x6d, 0x70, 0x74, 0x6b, 0x3d, 0x22, 0x41, 0x64, 0x6f, 0x62, 0x65, + 0x20, 0x58, 0x4d, 0x50, 0x20, 0x43, 0x6f, 0x72, 0x65, 0x20, 0x35, 0x2e, + 0x30, 0x2d, 0x63, 0x30, 0x36, 0x30, 0x20, 0x36, 0x31, 0x2e, 0x31, 0x33, + 0x34, 0x37, 0x37, 0x37, 0x2c, 0x20, 0x32, 0x30, 0x31, 0x30, 0x2f, 0x30, + 0x32, 0x2f, 0x31, 0x32, 0x2d, 0x31, 0x37, 0x3a, 0x33, 0x32, 0x3a, 0x30, + 0x30, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x3e, 0x20, + 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x20, 0x78, 0x6d, 0x6c, + 0x6e, 0x73, 0x3a, 0x72, 0x64, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, + 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72, + 0x67, 0x2f, 0x31, 0x39, 0x39, 0x39, 0x2f, 0x30, 0x32, 0x2f, 0x32, 0x32, + 0x2d, 0x72, 0x64, 0x66, 0x2d, 0x73, 0x79, 0x6e, 0x74, 0x61, 0x78, 0x2d, + 0x6e, 0x73, 0x23, 0x22, 0x3e, 0x20, 0x3c, 0x72, 0x64, 0x66, 0x3a, 0x44, + 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x72, + 0x64, 0x66, 0x3a, 0x61, 0x62, 0x6f, 0x75, 0x74, 0x3d, 0x22, 0x22, 0x20, + 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, 0x6d, 0x70, 0x3d, 0x22, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, + 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, + 0x2e, 0x30, 0x2f, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x78, + 0x6d, 0x70, 0x4d, 0x4d, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x6e, 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x6d, 0x6d, + 0x2f, 0x22, 0x20, 0x78, 0x6d, 0x6c, 0x6e, 0x73, 0x3a, 0x73, 0x74, 0x52, + 0x65, 0x66, 0x3d, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6e, + 0x73, 0x2e, 0x61, 0x64, 0x6f, 0x62, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x78, 0x61, 0x70, 0x2f, 0x31, 0x2e, 0x30, 0x2f, 0x73, 0x54, 0x79, 0x70, + 0x65, 0x2f, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, + 0x66, 0x23, 0x22, 0x20, 0x78, 0x6d, 0x70, 0x3a, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x6f, 0x72, 0x54, 0x6f, 0x6f, 0x6c, 0x3d, 0x22, 0x41, 0x64, 0x6f, + 0x62, 0x65, 0x20, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x73, 0x68, 0x6f, 0x70, + 0x20, 0x43, 0x53, 0x35, 0x20, 0x4d, 0x61, 0x63, 0x69, 0x6e, 0x74, 0x6f, + 0x73, 0x68, 0x22, 0x20, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x49, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, + 0x70, 0x2e, 0x69, 0x69, 0x64, 0x3a, 0x42, 0x30, 0x37, 0x30, 0x30, 0x45, + 0x33, 0x38, 0x32, 0x33, 0x33, 0x34, 0x31, 0x31, 0x45, 0x30, 0x38, 0x45, + 0x42, 0x44, 0x43, 0x42, 0x33, 0x39, 0x37, 0x38, 0x33, 0x31, 0x39, 0x45, + 0x45, 0x35, 0x22, 0x20, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, 0x44, 0x6f, + 0x63, 0x75, 0x6d, 0x65, 0x6e, 0x74, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, + 0x70, 0x2e, 0x64, 0x69, 0x64, 0x3a, 0x42, 0x30, 0x37, 0x30, 0x30, 0x45, + 0x33, 0x39, 0x32, 0x33, 0x33, 0x34, 0x31, 0x31, 0x45, 0x30, 0x38, 0x45, + 0x42, 0x44, 0x43, 0x42, 0x33, 0x39, 0x37, 0x38, 0x33, 0x31, 0x39, 0x45, + 0x45, 0x35, 0x22, 0x3e, 0x20, 0x3c, 0x78, 0x6d, 0x70, 0x4d, 0x4d, 0x3a, + 0x44, 0x65, 0x72, 0x69, 0x76, 0x65, 0x64, 0x46, 0x72, 0x6f, 0x6d, 0x20, + 0x73, 0x74, 0x52, 0x65, 0x66, 0x3a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x69, 0x69, + 0x64, 0x3a, 0x31, 0x37, 0x37, 0x36, 0x31, 0x30, 0x43, 0x44, 0x32, 0x33, + 0x33, 0x34, 0x31, 0x31, 0x45, 0x30, 0x38, 0x45, 0x42, 0x44, 0x43, 0x42, + 0x33, 0x39, 0x37, 0x38, 0x33, 0x31, 0x39, 0x45, 0x45, 0x35, 0x22, 0x20, + 0x73, 0x74, 0x52, 0x65, 0x66, 0x3a, 0x64, 0x6f, 0x63, 0x75, 0x6d, 0x65, + 0x6e, 0x74, 0x49, 0x44, 0x3d, 0x22, 0x78, 0x6d, 0x70, 0x2e, 0x64, 0x69, + 0x64, 0x3a, 0x31, 0x37, 0x37, 0x36, 0x31, 0x30, 0x43, 0x45, 0x32, 0x33, + 0x33, 0x34, 0x31, 0x31, 0x45, 0x30, 0x38, 0x45, 0x42, 0x44, 0x43, 0x42, + 0x33, 0x39, 0x37, 0x38, 0x33, 0x31, 0x39, 0x45, 0x45, 0x35, 0x22, 0x2f, + 0x3e, 0x20, 0x3c, 0x2f, 0x72, 0x64, 0x66, 0x3a, 0x44, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3e, 0x20, 0x3c, 0x2f, 0x72, + 0x64, 0x66, 0x3a, 0x52, 0x44, 0x46, 0x3e, 0x20, 0x3c, 0x2f, 0x78, 0x3a, + 0x78, 0x6d, 0x70, 0x6d, 0x65, 0x74, 0x61, 0x3e, 0x20, 0x3c, 0x3f, 0x78, + 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x20, 0x65, 0x6e, 0x64, 0x3d, 0x22, + 0x72, 0x22, 0x3f, 0x3e, 0xff, 0x12, 0x6f, 0xeb, 0x00, 0x00, 0x00, 0x7a, + 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x62, 0x7c, 0xff, 0xed, 0x27, 0x03, + 0x2a, 0xf8, 0xf5, 0xeb, 0xe7, 0x83, 0xfb, 0xf7, 0xb7, 0x6e, 0xda, 0xf8, + 0xe6, 0xf5, 0x6b, 0xc6, 0x77, 0x5f, 0x7f, 0x30, 0x60, 0x03, 0x40, 0x45, + 0x53, 0x27, 0x4c, 0xc0, 0x29, 0x0d, 0x04, 0xd7, 0xae, 0x5c, 0x61, 0x7c, + 0xfb, 0xe5, 0x3b, 0x2e, 0xe9, 0x5f, 0xbf, 0x7e, 0xb1, 0xfc, 0xff, 0xff, + 0x1f, 0x97, 0x34, 0x2b, 0x2b, 0x2b, 0x3e, 0x69, 0x20, 0xa0, 0x50, 0xfa, + 0x1f, 0x01, 0xdd, 0xff, 0xfe, 0xd1, 0xd2, 0x70, 0x02, 0x2e, 0xff, 0x47, + 0x3b, 0x97, 0x0f, 0xa8, 0xee, 0x7f, 0xff, 0x87, 0xa9, 0xc7, 0xfe, 0x53, + 0x12, 0xa1, 0xff, 0x29, 0x0b, 0x16, 0xda, 0xba, 0x7c, 0xe0, 0xa4, 0xcf, + 0x9d, 0x3d, 0x87, 0x4f, 0xfa, 0xfc, 0xf9, 0xf3, 0x78, 0xa4, 0x01, 0x02, + 0x0c, 0x00, 0x3d, 0x65, 0x8a, 0xd2, 0x20, 0x85, 0xdc, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82 +}; +unsigned int Silver_Base_Shrinked_2x_png_len = 1030; + + + +NSData* MTStatusBarBackgroundImageData(BOOL shrinked) { + return [NSData dataWithBytesNoCopy:MTStatusBarBackgroundImageArray(shrinked) length:MTStatusBarBackgroundImageLength(shrinked) freeWhenDone:NO]; +} + +unsigned char* MTStatusBarBackgroundImageArray(BOOL shrinked) { + if (shrinked) { + if ([UIScreen mainScreen].scale == 1.0) { + return Silver_Base_Shrinked_png; + } else { + return Silver_Base_Shrinked_2x_png; + } + } else { + if ([UIScreen mainScreen].scale == 1.0) { + return Silver_Base_png; + } else { + return Silver_Base_2x_png; + } + } +} + +unsigned int MTStatusBarBackgroundImageLength(BOOL shrinked) { + if (shrinked) { + if ([UIScreen mainScreen].scale == 1.0) { + return Silver_Base_Shrinked_png_len; + } else { + return Silver_Base_Shrinked_2x_png_len; + } + } else { + if ([UIScreen mainScreen].scale == 1.0) { + return Silver_Base_png_len; + } else { + return Silver_Base_2x_png_len; + } + } +} + +void mt_dispatch_sync_on_main_thread(dispatch_block_t block) { + if ([NSThread isMainThread]) { + block(); + } else { + dispatch_sync(dispatch_get_main_queue(), block); + } +} diff --git a/Vendors/REComposeViewController/REComposeViewController.m b/Vendors/REComposeViewController/REComposeViewController.m index 8bef451..407895b 100755 --- a/Vendors/REComposeViewController/REComposeViewController.m +++ b/Vendors/REComposeViewController/REComposeViewController.m @@ -65,7 +65,6 @@ - (void)viewDidLoad _backView.layer.shadowOpacity = 0.7; _backView.layer.shadowColor = [UIColor blackColor].CGColor; _backView.layer.shadowOffset = CGSizeMake(3, 5); - _backView.layer.shadowPath = [[UIBezierPath bezierPathWithRect:_backView.bounds] CGPath]; _sheetView.frame = _backView.bounds; _sheetView.layer.cornerRadius = _cornerRadius; @@ -91,6 +90,7 @@ - (void)viewDidLoad - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; + [_sheetView.textView becomeFirstResponder]; [UIView animateWithDuration:0.4 animations:^{ @@ -175,6 +175,9 @@ - (void)layoutWithOrientation:(UIInterfaceOrientation)interfaceOrientation width textViewContainerFrame.origin.y = _sheetView.navigationBar.frame.size.height; textViewContainerFrame.size.height = _sheetView.frame.size.height - _sheetView.navigationBar.frame.size.height; _sheetView.textViewContainer.frame = textViewContainerFrame; + + _backView.layer.shadowPath = [[UIBezierPath bezierPathWithRect:_backView.bounds] CGPath]; + } - (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion