├── .gitignore ├── Default-568h@2x.png ├── LICENSE ├── NoticeView.podspec ├── NoticeView.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── NoticeView.xccheckout │ └── xcuserdata │ │ └── rle.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── xcshareddata │ └── xcschemes │ │ └── NoticeView.xcscheme └── xcuserdata │ └── rle.xcuserdatad │ └── xcschemes │ ├── NoticeView.xcscheme │ └── xcschememanagement.plist ├── NoticeView ├── Images │ ├── Default.png │ ├── Default@2x.png │ └── navigationBar.png ├── NoticeView-Info.plist ├── NoticeView-Prefix.pch ├── WBAppDelegate.h ├── WBAppDelegate.m ├── WBNoticeView │ ├── NSOperationQueue+WBNoticeExtensions.h │ ├── NSOperationQueue+WBNoticeExtensions.m │ ├── NoticeView.bundle │ │ ├── notice_error_icon.png │ │ ├── notice_error_icon@2x.png │ │ ├── notice_success_icon.png │ │ ├── notice_success_icon@2x.png │ │ ├── up.png │ │ └── up@2x.png │ ├── UILabel+WBExtensions.h │ ├── UILabel+WBExtensions.m │ ├── WBBlueGradientView.h │ ├── WBBlueGradientView.m │ ├── WBErrorNoticeView.h │ ├── WBErrorNoticeView.m │ ├── WBGrayGradientView.h │ ├── WBGrayGradientView.m │ ├── WBNoticeOperation.h │ ├── WBNoticeOperation.m │ ├── WBNoticeView+ForSubclassEyesOnly.h │ ├── WBNoticeView.h │ ├── WBNoticeView.m │ ├── WBRedGradientView.h │ ├── WBRedGradientView.m │ ├── WBStickyNoticeView.h │ ├── WBStickyNoticeView.m │ ├── WBSuccessNoticeView.h │ └── WBSuccessNoticeView.m ├── WBScrollViewController.h ├── WBScrollViewController.m ├── WBViewController.h ├── WBViewController.m ├── en.lproj │ ├── InfoPlist.strings │ └── MainStoryboard.storyboard └── main.m ├── NoticeViewiPad ├── NoticeViewiPad.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata └── NoticeViewiPad │ ├── NoticeViewiPad-Info.plist │ ├── NoticeViewiPad-Prefix.pch │ ├── WBAppDelegate.h │ ├── WBAppDelegate.m │ ├── WBDetailViewController.h │ ├── WBDetailViewController.m │ ├── WBMasterViewController.h │ ├── WBMasterViewController.m │ ├── en.lproj │ ├── InfoPlist.strings │ └── MainStoryboard.storyboard │ └── main.m └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # temp nibs and swap files 2 | *~.nib 3 | *.swp 4 | *.orig 5 | 6 | # OS X folder attributes 7 | .DS_Store 8 | 9 | # user-specific XCode stuff 10 | *.mode1v3 11 | *.mode2v3 12 | *.pbxuser 13 | *.perspectivev3 14 | 15 | NoticeView.xcodeproj/xcuserdata/tito* 16 | NoticeView.xcodeproj/project.xcworkspace/xcuserdata/tito* 17 | 18 | /.gitignore 19 | -------------------------------------------------------------------------------- /Default-568h@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tciuro/NoticeView/aea3a0c1e5c0e7b4140a8d9a0501c8f0f7a91c55/Default-568h@2x.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Webbo, L.L.C. All rights reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE -------------------------------------------------------------------------------- /NoticeView.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'NoticeView' 3 | s.version = '3.0.7' 4 | s.license = 'MIT' 5 | s.summary = 'A TweetBot-like notice component for iOS.' 6 | s.homepage = 'https://github.com/tciuro/NoticeView/' 7 | s.author = { 'Tito Ciuro' => 'tciuro@mac.com' } 8 | s.source = { :git => 'https://github.com/tciuro/NoticeView.git', :tag => '3.0.7' } 9 | s.platform = :ios 10 | s.ios.deployment_target = '5.0' 11 | s.source_files = 'NoticeView/WBNoticeView/*.{m,h}' 12 | s.frameworks = 'Foundation', 'UIKit', 'CoreGraphics', 'QuartzCore' 13 | s.resources = 'NoticeView/WBNoticeView/NoticeView.bundle' 14 | s.requires_arc = true 15 | end 16 | -------------------------------------------------------------------------------- /NoticeView.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 250628781651F7DC00D3443E /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 250628771651F7DB00D3443E /* Default-568h@2x.png */; }; 11 | 3EA90DC617086FF70045E620 /* WBScrollViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3EA90DC517086FF70045E620 /* WBScrollViewController.m */; }; 12 | 592BDDE915645BEE00B78820 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 592BDDE115645BEE00B78820 /* Default.png */; }; 13 | 592BDDEA15645BEE00B78820 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 592BDDE215645BEE00B78820 /* Default@2x.png */; }; 14 | 592BDDEB15645BEE00B78820 /* NoticeView.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 592BDDE415645BEE00B78820 /* NoticeView.bundle */; }; 15 | 592BDDEC15645BEE00B78820 /* UILabel+WBExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 592BDDE615645BEE00B78820 /* UILabel+WBExtensions.m */; }; 16 | 592BDDED15645BEE00B78820 /* WBNoticeView.m in Sources */ = {isa = PBXBuildFile; fileRef = 592BDDE815645BEE00B78820 /* WBNoticeView.m */; }; 17 | 595FE869156F021E004C505B /* navigationBar.png in Resources */ = {isa = PBXBuildFile; fileRef = 595FE868156F021E004C505B /* navigationBar.png */; }; 18 | 59A782331562F5F70001F08D /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 59A782321562F5F70001F08D /* UIKit.framework */; }; 19 | 59A782351562F5F70001F08D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 59A782341562F5F70001F08D /* Foundation.framework */; }; 20 | 59A782371562F5F70001F08D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 59A782361562F5F70001F08D /* CoreGraphics.framework */; }; 21 | 59A7823D1562F5F70001F08D /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 59A7823B1562F5F70001F08D /* InfoPlist.strings */; }; 22 | 59A7823F1562F5F70001F08D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 59A7823E1562F5F70001F08D /* main.m */; }; 23 | 59A782431562F5F70001F08D /* WBAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 59A782421562F5F70001F08D /* WBAppDelegate.m */; }; 24 | 59A782461562F5F80001F08D /* MainStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 59A782441562F5F70001F08D /* MainStoryboard.storyboard */; }; 25 | 59A782491562F5F80001F08D /* WBViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 59A782481562F5F80001F08D /* WBViewController.m */; }; 26 | 59A782591562F8750001F08D /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 59A782581562F8750001F08D /* QuartzCore.framework */; }; 27 | 746CBD001570A1D100B844B1 /* WBErrorNoticeView.m in Sources */ = {isa = PBXBuildFile; fileRef = 746CBCFF1570A1D100B844B1 /* WBErrorNoticeView.m */; }; 28 | 746CBD081570A8FA00B844B1 /* WBSuccessNoticeView.m in Sources */ = {isa = PBXBuildFile; fileRef = 746CBD071570A8FA00B844B1 /* WBSuccessNoticeView.m */; }; 29 | 748A6A59157D0B74003C7655 /* WBGrayGradientView.m in Sources */ = {isa = PBXBuildFile; fileRef = 748A6A58157D0B74003C7655 /* WBGrayGradientView.m */; }; 30 | 748A6A5D157D0D06003C7655 /* WBStickyNoticeView.m in Sources */ = {isa = PBXBuildFile; fileRef = 748A6A5C157D0D06003C7655 /* WBStickyNoticeView.m */; }; 31 | 7492FA45157C8CD700FB834C /* WBBlueGradientView.m in Sources */ = {isa = PBXBuildFile; fileRef = 7492FA42157C8CD700FB834C /* WBBlueGradientView.m */; }; 32 | 7492FA46157C8CD700FB834C /* WBRedGradientView.m in Sources */ = {isa = PBXBuildFile; fileRef = 7492FA44157C8CD700FB834C /* WBRedGradientView.m */; }; 33 | FE07240C16BAB0F000B46F59 /* NSOperationQueue+WBNoticeExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = FE07240B16BAB0F000B46F59 /* NSOperationQueue+WBNoticeExtensions.m */; }; 34 | FED81FA916B97A1C00254985 /* WBNoticeOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = FED81FA816B97A1C00254985 /* WBNoticeOperation.m */; }; 35 | /* End PBXBuildFile section */ 36 | 37 | /* Begin PBXFileReference section */ 38 | 250628731651E78600D3443E /* WBNoticeView+ForSubclassEyesOnly.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "WBNoticeView+ForSubclassEyesOnly.h"; sourceTree = ""; }; 39 | 250628771651F7DB00D3443E /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "../../Default-568h@2x.png"; sourceTree = ""; }; 40 | 3EA90DC417086FF70045E620 /* WBScrollViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WBScrollViewController.h; sourceTree = ""; }; 41 | 3EA90DC517086FF70045E620 /* WBScrollViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WBScrollViewController.m; sourceTree = ""; }; 42 | 592BDDE115645BEE00B78820 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; 43 | 592BDDE215645BEE00B78820 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; 44 | 592BDDE415645BEE00B78820 /* NoticeView.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = NoticeView.bundle; sourceTree = ""; }; 45 | 592BDDE515645BEE00B78820 /* UILabel+WBExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UILabel+WBExtensions.h"; sourceTree = ""; }; 46 | 592BDDE615645BEE00B78820 /* UILabel+WBExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UILabel+WBExtensions.m"; sourceTree = ""; }; 47 | 592BDDE715645BEE00B78820 /* WBNoticeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WBNoticeView.h; sourceTree = ""; }; 48 | 592BDDE815645BEE00B78820 /* WBNoticeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WBNoticeView.m; sourceTree = ""; }; 49 | 595FE868156F021E004C505B /* navigationBar.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = navigationBar.png; sourceTree = ""; }; 50 | 59A7822E1562F5F70001F08D /* NoticeView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NoticeView.app; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | 59A782321562F5F70001F08D /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 52 | 59A782341562F5F70001F08D /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 53 | 59A782361562F5F70001F08D /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 54 | 59A7823A1562F5F70001F08D /* NoticeView-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "NoticeView-Info.plist"; sourceTree = ""; }; 55 | 59A7823C1562F5F70001F08D /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 56 | 59A7823E1562F5F70001F08D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 57 | 59A782401562F5F70001F08D /* NoticeView-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NoticeView-Prefix.pch"; sourceTree = ""; }; 58 | 59A782411562F5F70001F08D /* WBAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WBAppDelegate.h; sourceTree = ""; }; 59 | 59A782421562F5F70001F08D /* WBAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WBAppDelegate.m; sourceTree = ""; }; 60 | 59A782451562F5F80001F08D /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/MainStoryboard.storyboard; sourceTree = ""; }; 61 | 59A782471562F5F80001F08D /* WBViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WBViewController.h; sourceTree = ""; }; 62 | 59A782481562F5F80001F08D /* WBViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WBViewController.m; sourceTree = ""; }; 63 | 59A782581562F8750001F08D /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; 64 | 746CBCFE1570A1D100B844B1 /* WBErrorNoticeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WBErrorNoticeView.h; sourceTree = ""; }; 65 | 746CBCFF1570A1D100B844B1 /* WBErrorNoticeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WBErrorNoticeView.m; sourceTree = ""; }; 66 | 746CBD061570A8FA00B844B1 /* WBSuccessNoticeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WBSuccessNoticeView.h; sourceTree = ""; }; 67 | 746CBD071570A8FA00B844B1 /* WBSuccessNoticeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WBSuccessNoticeView.m; sourceTree = ""; }; 68 | 748A6A57157D0B74003C7655 /* WBGrayGradientView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WBGrayGradientView.h; sourceTree = ""; }; 69 | 748A6A58157D0B74003C7655 /* WBGrayGradientView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WBGrayGradientView.m; sourceTree = ""; }; 70 | 748A6A5B157D0D06003C7655 /* WBStickyNoticeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WBStickyNoticeView.h; sourceTree = ""; }; 71 | 748A6A5C157D0D06003C7655 /* WBStickyNoticeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WBStickyNoticeView.m; sourceTree = ""; }; 72 | 7492FA41157C8CD700FB834C /* WBBlueGradientView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WBBlueGradientView.h; sourceTree = ""; }; 73 | 7492FA42157C8CD700FB834C /* WBBlueGradientView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WBBlueGradientView.m; sourceTree = ""; }; 74 | 7492FA43157C8CD700FB834C /* WBRedGradientView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WBRedGradientView.h; sourceTree = ""; }; 75 | 7492FA44157C8CD700FB834C /* WBRedGradientView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WBRedGradientView.m; sourceTree = ""; }; 76 | FE07240A16BAB0F000B46F59 /* NSOperationQueue+WBNoticeExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSOperationQueue+WBNoticeExtensions.h"; sourceTree = ""; }; 77 | FE07240B16BAB0F000B46F59 /* NSOperationQueue+WBNoticeExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSOperationQueue+WBNoticeExtensions.m"; sourceTree = ""; }; 78 | FED81FA716B97A1C00254985 /* WBNoticeOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WBNoticeOperation.h; sourceTree = ""; }; 79 | FED81FA816B97A1C00254985 /* WBNoticeOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WBNoticeOperation.m; sourceTree = ""; }; 80 | /* End PBXFileReference section */ 81 | 82 | /* Begin PBXFrameworksBuildPhase section */ 83 | 59A7822B1562F5F70001F08D /* Frameworks */ = { 84 | isa = PBXFrameworksBuildPhase; 85 | buildActionMask = 2147483647; 86 | files = ( 87 | 59A782591562F8750001F08D /* QuartzCore.framework in Frameworks */, 88 | 59A782331562F5F70001F08D /* UIKit.framework in Frameworks */, 89 | 59A782351562F5F70001F08D /* Foundation.framework in Frameworks */, 90 | 59A782371562F5F70001F08D /* CoreGraphics.framework in Frameworks */, 91 | ); 92 | runOnlyForDeploymentPostprocessing = 0; 93 | }; 94 | /* End PBXFrameworksBuildPhase section */ 95 | 96 | /* Begin PBXGroup section */ 97 | 592BDDE015645BEE00B78820 /* Images */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 250628771651F7DB00D3443E /* Default-568h@2x.png */, 101 | 595FE868156F021E004C505B /* navigationBar.png */, 102 | 592BDDE115645BEE00B78820 /* Default.png */, 103 | 592BDDE215645BEE00B78820 /* Default@2x.png */, 104 | ); 105 | path = Images; 106 | sourceTree = ""; 107 | }; 108 | 592BDDE315645BEE00B78820 /* WBNoticeView */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | FE07240A16BAB0F000B46F59 /* NSOperationQueue+WBNoticeExtensions.h */, 112 | FE07240B16BAB0F000B46F59 /* NSOperationQueue+WBNoticeExtensions.m */, 113 | 746CBCFE1570A1D100B844B1 /* WBErrorNoticeView.h */, 114 | 746CBCFF1570A1D100B844B1 /* WBErrorNoticeView.m */, 115 | FED81FA716B97A1C00254985 /* WBNoticeOperation.h */, 116 | FED81FA816B97A1C00254985 /* WBNoticeOperation.m */, 117 | 746CBD061570A8FA00B844B1 /* WBSuccessNoticeView.h */, 118 | 746CBD071570A8FA00B844B1 /* WBSuccessNoticeView.m */, 119 | 748A6A5B157D0D06003C7655 /* WBStickyNoticeView.h */, 120 | 748A6A5C157D0D06003C7655 /* WBStickyNoticeView.m */, 121 | 746CBD051570A88800B844B1 /* Private */, 122 | ); 123 | path = WBNoticeView; 124 | sourceTree = ""; 125 | }; 126 | 59A782231562F5F70001F08D = { 127 | isa = PBXGroup; 128 | children = ( 129 | 59A782381562F5F70001F08D /* NoticeView */, 130 | 59A782311562F5F70001F08D /* Frameworks */, 131 | 59A7822F1562F5F70001F08D /* Products */, 132 | ); 133 | sourceTree = ""; 134 | }; 135 | 59A7822F1562F5F70001F08D /* Products */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | 59A7822E1562F5F70001F08D /* NoticeView.app */, 139 | ); 140 | name = Products; 141 | sourceTree = ""; 142 | }; 143 | 59A782311562F5F70001F08D /* Frameworks */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | 59A782581562F8750001F08D /* QuartzCore.framework */, 147 | 59A782321562F5F70001F08D /* UIKit.framework */, 148 | 59A782341562F5F70001F08D /* Foundation.framework */, 149 | 59A782361562F5F70001F08D /* CoreGraphics.framework */, 150 | ); 151 | name = Frameworks; 152 | sourceTree = ""; 153 | }; 154 | 59A782381562F5F70001F08D /* NoticeView */ = { 155 | isa = PBXGroup; 156 | children = ( 157 | 59A782441562F5F70001F08D /* MainStoryboard.storyboard */, 158 | 59A782411562F5F70001F08D /* WBAppDelegate.h */, 159 | 59A782421562F5F70001F08D /* WBAppDelegate.m */, 160 | 3EA90DC417086FF70045E620 /* WBScrollViewController.h */, 161 | 3EA90DC517086FF70045E620 /* WBScrollViewController.m */, 162 | 59A782471562F5F80001F08D /* WBViewController.h */, 163 | 59A782481562F5F80001F08D /* WBViewController.m */, 164 | 592BDDE315645BEE00B78820 /* WBNoticeView */, 165 | 592BDDE015645BEE00B78820 /* Images */, 166 | 59A782391562F5F70001F08D /* Supporting Files */, 167 | ); 168 | path = NoticeView; 169 | sourceTree = ""; 170 | }; 171 | 59A782391562F5F70001F08D /* Supporting Files */ = { 172 | isa = PBXGroup; 173 | children = ( 174 | 59A7823A1562F5F70001F08D /* NoticeView-Info.plist */, 175 | 59A7823B1562F5F70001F08D /* InfoPlist.strings */, 176 | 59A7823E1562F5F70001F08D /* main.m */, 177 | 59A782401562F5F70001F08D /* NoticeView-Prefix.pch */, 178 | ); 179 | name = "Supporting Files"; 180 | sourceTree = ""; 181 | }; 182 | 746CBD051570A88800B844B1 /* Private */ = { 183 | isa = PBXGroup; 184 | children = ( 185 | 592BDDE715645BEE00B78820 /* WBNoticeView.h */, 186 | 592BDDE815645BEE00B78820 /* WBNoticeView.m */, 187 | 592BDDE415645BEE00B78820 /* NoticeView.bundle */, 188 | 592BDDE515645BEE00B78820 /* UILabel+WBExtensions.h */, 189 | 592BDDE615645BEE00B78820 /* UILabel+WBExtensions.m */, 190 | 7492FA41157C8CD700FB834C /* WBBlueGradientView.h */, 191 | 7492FA42157C8CD700FB834C /* WBBlueGradientView.m */, 192 | 748A6A57157D0B74003C7655 /* WBGrayGradientView.h */, 193 | 748A6A58157D0B74003C7655 /* WBGrayGradientView.m */, 194 | 7492FA43157C8CD700FB834C /* WBRedGradientView.h */, 195 | 7492FA44157C8CD700FB834C /* WBRedGradientView.m */, 196 | 250628731651E78600D3443E /* WBNoticeView+ForSubclassEyesOnly.h */, 197 | ); 198 | name = Private; 199 | sourceTree = ""; 200 | }; 201 | /* End PBXGroup section */ 202 | 203 | /* Begin PBXNativeTarget section */ 204 | 59A7822D1562F5F70001F08D /* NoticeView */ = { 205 | isa = PBXNativeTarget; 206 | buildConfigurationList = 59A7824C1562F5F80001F08D /* Build configuration list for PBXNativeTarget "NoticeView" */; 207 | buildPhases = ( 208 | 59A7822A1562F5F70001F08D /* Sources */, 209 | 59A7822B1562F5F70001F08D /* Frameworks */, 210 | 59A7822C1562F5F70001F08D /* Resources */, 211 | ); 212 | buildRules = ( 213 | ); 214 | dependencies = ( 215 | ); 216 | name = NoticeView; 217 | productName = NoticeView; 218 | productReference = 59A7822E1562F5F70001F08D /* NoticeView.app */; 219 | productType = "com.apple.product-type.application"; 220 | }; 221 | /* End PBXNativeTarget section */ 222 | 223 | /* Begin PBXProject section */ 224 | 59A782251562F5F70001F08D /* Project object */ = { 225 | isa = PBXProject; 226 | attributes = { 227 | CLASSPREFIX = WB; 228 | LastUpgradeCheck = 0430; 229 | ORGANIZATIONNAME = "Tito Ciuro"; 230 | }; 231 | buildConfigurationList = 59A782281562F5F70001F08D /* Build configuration list for PBXProject "NoticeView" */; 232 | compatibilityVersion = "Xcode 3.2"; 233 | developmentRegion = English; 234 | hasScannedForEncodings = 0; 235 | knownRegions = ( 236 | en, 237 | ); 238 | mainGroup = 59A782231562F5F70001F08D; 239 | productRefGroup = 59A7822F1562F5F70001F08D /* Products */; 240 | projectDirPath = ""; 241 | projectRoot = ""; 242 | targets = ( 243 | 59A7822D1562F5F70001F08D /* NoticeView */, 244 | ); 245 | }; 246 | /* End PBXProject section */ 247 | 248 | /* Begin PBXResourcesBuildPhase section */ 249 | 59A7822C1562F5F70001F08D /* Resources */ = { 250 | isa = PBXResourcesBuildPhase; 251 | buildActionMask = 2147483647; 252 | files = ( 253 | 59A7823D1562F5F70001F08D /* InfoPlist.strings in Resources */, 254 | 59A782461562F5F80001F08D /* MainStoryboard.storyboard in Resources */, 255 | 592BDDE915645BEE00B78820 /* Default.png in Resources */, 256 | 592BDDEA15645BEE00B78820 /* Default@2x.png in Resources */, 257 | 592BDDEB15645BEE00B78820 /* NoticeView.bundle in Resources */, 258 | 595FE869156F021E004C505B /* navigationBar.png in Resources */, 259 | 250628781651F7DC00D3443E /* Default-568h@2x.png in Resources */, 260 | ); 261 | runOnlyForDeploymentPostprocessing = 0; 262 | }; 263 | /* End PBXResourcesBuildPhase section */ 264 | 265 | /* Begin PBXSourcesBuildPhase section */ 266 | 59A7822A1562F5F70001F08D /* Sources */ = { 267 | isa = PBXSourcesBuildPhase; 268 | buildActionMask = 2147483647; 269 | files = ( 270 | 59A7823F1562F5F70001F08D /* main.m in Sources */, 271 | 59A782431562F5F70001F08D /* WBAppDelegate.m in Sources */, 272 | 59A782491562F5F80001F08D /* WBViewController.m in Sources */, 273 | 592BDDEC15645BEE00B78820 /* UILabel+WBExtensions.m in Sources */, 274 | 592BDDED15645BEE00B78820 /* WBNoticeView.m in Sources */, 275 | 746CBD001570A1D100B844B1 /* WBErrorNoticeView.m in Sources */, 276 | 746CBD081570A8FA00B844B1 /* WBSuccessNoticeView.m in Sources */, 277 | 7492FA45157C8CD700FB834C /* WBBlueGradientView.m in Sources */, 278 | 7492FA46157C8CD700FB834C /* WBRedGradientView.m in Sources */, 279 | 748A6A59157D0B74003C7655 /* WBGrayGradientView.m in Sources */, 280 | 748A6A5D157D0D06003C7655 /* WBStickyNoticeView.m in Sources */, 281 | FED81FA916B97A1C00254985 /* WBNoticeOperation.m in Sources */, 282 | FE07240C16BAB0F000B46F59 /* NSOperationQueue+WBNoticeExtensions.m in Sources */, 283 | 3EA90DC617086FF70045E620 /* WBScrollViewController.m in Sources */, 284 | ); 285 | runOnlyForDeploymentPostprocessing = 0; 286 | }; 287 | /* End PBXSourcesBuildPhase section */ 288 | 289 | /* Begin PBXVariantGroup section */ 290 | 59A7823B1562F5F70001F08D /* InfoPlist.strings */ = { 291 | isa = PBXVariantGroup; 292 | children = ( 293 | 59A7823C1562F5F70001F08D /* en */, 294 | ); 295 | name = InfoPlist.strings; 296 | sourceTree = ""; 297 | }; 298 | 59A782441562F5F70001F08D /* MainStoryboard.storyboard */ = { 299 | isa = PBXVariantGroup; 300 | children = ( 301 | 59A782451562F5F80001F08D /* en */, 302 | ); 303 | name = MainStoryboard.storyboard; 304 | sourceTree = ""; 305 | }; 306 | /* End PBXVariantGroup section */ 307 | 308 | /* Begin XCBuildConfiguration section */ 309 | 59A7824A1562F5F80001F08D /* Debug */ = { 310 | isa = XCBuildConfiguration; 311 | buildSettings = { 312 | ALWAYS_SEARCH_USER_PATHS = NO; 313 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 314 | CLANG_ENABLE_OBJC_ARC = YES; 315 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 316 | COPY_PHASE_STRIP = NO; 317 | GCC_C_LANGUAGE_STANDARD = gnu99; 318 | GCC_DYNAMIC_NO_PIC = NO; 319 | GCC_OPTIMIZATION_LEVEL = 0; 320 | GCC_PREPROCESSOR_DEFINITIONS = ( 321 | "DEBUG=1", 322 | "$(inherited)", 323 | ); 324 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 325 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 326 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 327 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 328 | GCC_WARN_UNUSED_VARIABLE = YES; 329 | IPHONEOS_DEPLOYMENT_TARGET = 5.1; 330 | SDKROOT = iphoneos; 331 | }; 332 | name = Debug; 333 | }; 334 | 59A7824B1562F5F80001F08D /* Release */ = { 335 | isa = XCBuildConfiguration; 336 | buildSettings = { 337 | ALWAYS_SEARCH_USER_PATHS = NO; 338 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 339 | CLANG_ENABLE_OBJC_ARC = YES; 340 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 341 | COPY_PHASE_STRIP = YES; 342 | GCC_C_LANGUAGE_STANDARD = gnu99; 343 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 344 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 345 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 346 | GCC_WARN_UNUSED_VARIABLE = YES; 347 | IPHONEOS_DEPLOYMENT_TARGET = 5.1; 348 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 349 | SDKROOT = iphoneos; 350 | VALIDATE_PRODUCT = YES; 351 | }; 352 | name = Release; 353 | }; 354 | 59A7824D1562F5F80001F08D /* Debug */ = { 355 | isa = XCBuildConfiguration; 356 | buildSettings = { 357 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 358 | GCC_PREFIX_HEADER = "NoticeView/NoticeView-Prefix.pch"; 359 | INFOPLIST_FILE = "NoticeView/NoticeView-Info.plist"; 360 | IPHONEOS_DEPLOYMENT_TARGET = 5.1; 361 | PRODUCT_NAME = "$(TARGET_NAME)"; 362 | TARGETED_DEVICE_FAMILY = "1,2"; 363 | WRAPPER_EXTENSION = app; 364 | }; 365 | name = Debug; 366 | }; 367 | 59A7824E1562F5F80001F08D /* Release */ = { 368 | isa = XCBuildConfiguration; 369 | buildSettings = { 370 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 371 | GCC_PREFIX_HEADER = "NoticeView/NoticeView-Prefix.pch"; 372 | INFOPLIST_FILE = "NoticeView/NoticeView-Info.plist"; 373 | IPHONEOS_DEPLOYMENT_TARGET = 5.1; 374 | PRODUCT_NAME = "$(TARGET_NAME)"; 375 | TARGETED_DEVICE_FAMILY = "1,2"; 376 | WRAPPER_EXTENSION = app; 377 | }; 378 | name = Release; 379 | }; 380 | /* End XCBuildConfiguration section */ 381 | 382 | /* Begin XCConfigurationList section */ 383 | 59A782281562F5F70001F08D /* Build configuration list for PBXProject "NoticeView" */ = { 384 | isa = XCConfigurationList; 385 | buildConfigurations = ( 386 | 59A7824A1562F5F80001F08D /* Debug */, 387 | 59A7824B1562F5F80001F08D /* Release */, 388 | ); 389 | defaultConfigurationIsVisible = 0; 390 | defaultConfigurationName = Release; 391 | }; 392 | 59A7824C1562F5F80001F08D /* Build configuration list for PBXNativeTarget "NoticeView" */ = { 393 | isa = XCConfigurationList; 394 | buildConfigurations = ( 395 | 59A7824D1562F5F80001F08D /* Debug */, 396 | 59A7824E1562F5F80001F08D /* Release */, 397 | ); 398 | defaultConfigurationIsVisible = 0; 399 | defaultConfigurationName = Release; 400 | }; 401 | /* End XCConfigurationList section */ 402 | }; 403 | rootObject = 59A782251562F5F70001F08D /* Project object */; 404 | } 405 | -------------------------------------------------------------------------------- /NoticeView.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /NoticeView.xcodeproj/project.xcworkspace/xcshareddata/NoticeView.xccheckout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDESourceControlProjectFavoriteDictionaryKey 6 | 7 | IDESourceControlProjectIdentifier 8 | 411B52F3-B12B-4A73-B420-42CF7491ACE1 9 | IDESourceControlProjectName 10 | NoticeView 11 | IDESourceControlProjectOriginsDictionary 12 | 13 | 9ADE7216-4C90-4E47-810B-399387C11BA4 14 | https://github.com/tciuro/NoticeView 15 | 16 | IDESourceControlProjectPath 17 | NoticeView.xcodeproj/project.xcworkspace 18 | IDESourceControlProjectRelativeInstallPathDictionary 19 | 20 | 9ADE7216-4C90-4E47-810B-399387C11BA4 21 | ../.. 22 | 23 | IDESourceControlProjectURL 24 | https://github.com/tciuro/NoticeView 25 | IDESourceControlProjectVersion 26 | 110 27 | IDESourceControlProjectWCCIdentifier 28 | 9ADE7216-4C90-4E47-810B-399387C11BA4 29 | IDESourceControlProjectWCConfigurations 30 | 31 | 32 | IDESourceControlRepositoryExtensionIdentifierKey 33 | public.vcs.git 34 | IDESourceControlWCCIdentifierKey 35 | 9ADE7216-4C90-4E47-810B-399387C11BA4 36 | IDESourceControlWCCName 37 | NoticeView 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /NoticeView.xcodeproj/project.xcworkspace/xcuserdata/rle.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tciuro/NoticeView/aea3a0c1e5c0e7b4140a8d9a0501c8f0f7a91c55/NoticeView.xcodeproj/project.xcworkspace/xcuserdata/rle.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /NoticeView.xcodeproj/xcshareddata/xcschemes/NoticeView.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 51 | 52 | 58 | 59 | 60 | 61 | 62 | 63 | 69 | 70 | 76 | 77 | 78 | 79 | 81 | 82 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /NoticeView.xcodeproj/xcuserdata/rle.xcuserdatad/xcschemes/NoticeView.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 51 | 52 | 58 | 59 | 60 | 61 | 62 | 63 | 69 | 70 | 76 | 77 | 78 | 79 | 81 | 82 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /NoticeView.xcodeproj/xcuserdata/rle.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | NoticeView.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 59A7822D1562F5F70001F08D 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /NoticeView/Images/Default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tciuro/NoticeView/aea3a0c1e5c0e7b4140a8d9a0501c8f0f7a91c55/NoticeView/Images/Default.png -------------------------------------------------------------------------------- /NoticeView/Images/Default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tciuro/NoticeView/aea3a0c1e5c0e7b4140a8d9a0501c8f0f7a91c55/NoticeView/Images/Default@2x.png -------------------------------------------------------------------------------- /NoticeView/Images/navigationBar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tciuro/NoticeView/aea3a0c1e5c0e7b4140a8d9a0501c8f0f7a91c55/NoticeView/Images/navigationBar.png -------------------------------------------------------------------------------- /NoticeView/NoticeView-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIcons 12 | 13 | CFBundlePrimaryIcon 14 | 15 | CFBundleIconFiles 16 | 17 | Default.png 18 | Default@2x.png 19 | Default@2x.png 20 | Default.png 21 | 22 | 23 | 24 | CFBundleIdentifier 25 | com.webbo.${PRODUCT_NAME:rfc1034identifier} 26 | CFBundleInfoDictionaryVersion 27 | 6.0 28 | CFBundleName 29 | ${PRODUCT_NAME} 30 | CFBundlePackageType 31 | APPL 32 | CFBundleShortVersionString 33 | 1.0 34 | CFBundleSignature 35 | ???? 36 | CFBundleVersion 37 | 1.0 38 | LSRequiresIPhoneOS 39 | 40 | UIMainStoryboardFile 41 | MainStoryboard 42 | UIRequiredDeviceCapabilities 43 | 44 | armv7 45 | 46 | UISupportedInterfaceOrientations 47 | 48 | UIInterfaceOrientationPortrait 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /NoticeView/NoticeView-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'NoticeView' target in the 'NoticeView' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_5_0 8 | #warning "This project uses features only available in iOS SDK 5.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /NoticeView/WBAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // WBAppDelegate.h 3 | // NoticeView 4 | // 5 | // Created by Tito Ciuro on 5/15/12. 6 | // 7 | // Copyright (c) 2012 Webbo, L.L.C. All rights reserved. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE 26 | // 27 | 28 | #import 29 | 30 | @interface WBAppDelegate : UIResponder 31 | 32 | @property (strong, nonatomic) UIWindow *window; 33 | 34 | @end 35 | -------------------------------------------------------------------------------- /NoticeView/WBAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // WBAppDelegate.m 3 | // NoticeView 4 | // 5 | // Created by Tito Ciuro on 5/15/12. 6 | // 7 | // Copyright (c) 2012 Webbo, L.L.C. All rights reserved. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE 26 | // 27 | 28 | #import "WBAppDelegate.h" 29 | 30 | @implementation WBAppDelegate 31 | 32 | @synthesize window = _window; 33 | 34 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 35 | { 36 | [[UINavigationBar appearance]setTintColor:[UIColor darkGrayColor]]; 37 | 38 | return YES; 39 | } 40 | 41 | - (void)applicationWillResignActive:(UIApplication *)application 42 | { 43 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 44 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 45 | } 46 | 47 | - (void)applicationDidEnterBackground:(UIApplication *)application 48 | { 49 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 50 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 51 | } 52 | 53 | - (void)applicationWillEnterForeground:(UIApplication *)application 54 | { 55 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 56 | } 57 | 58 | - (void)applicationDidBecomeActive:(UIApplication *)application 59 | { 60 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 61 | } 62 | 63 | - (void)applicationWillTerminate:(UIApplication *)application 64 | { 65 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 66 | } 67 | 68 | @end 69 | -------------------------------------------------------------------------------- /NoticeView/WBNoticeView/NSOperationQueue+WBNoticeExtensions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSOperationQueue+WBNoticeExtensions.h 3 | // NoticeView 4 | // 5 | // Created by Johannes Plunien on 1/31/13. 6 | // Copyright (c) 2013 Tito Ciuro. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "WBNoticeView.h" 11 | #import "WBNoticeOperation.h" 12 | 13 | @interface NSOperationQueue (WBNoticeExtensions) 14 | 15 | + (WBNoticeOperation *)addNoticeView:(WBNoticeView *)noticeView; 16 | + (WBNoticeOperation *)addNoticeView:(WBNoticeView *)noticeView 17 | filterDuplicates:(BOOL)filterDuplicates; 18 | + (void)cancelAllNoticeViews; 19 | + (void)cancelAndDismissAllNoticeViews; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /NoticeView/WBNoticeView/NSOperationQueue+WBNoticeExtensions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSOperationQueue+WBNoticeExtensions.m 3 | // NoticeView 4 | // 5 | // Created by Johannes Plunien on 1/31/13. 6 | // Copyright (c) 2013 Tito Ciuro. All rights reserved. 7 | // 8 | 9 | #import "NSOperationQueue+WBNoticeExtensions.h" 10 | 11 | @implementation NSOperationQueue (WBNoticeExtensions) 12 | 13 | static NSOperationQueue *_noticeQueue = nil; 14 | 15 | + (NSOperationQueue *)noticeQueue { 16 | static dispatch_once_t onceToken; 17 | dispatch_once(&onceToken, ^{ 18 | if (_noticeQueue == nil) { 19 | _noticeQueue = [[NSOperationQueue alloc] init]; 20 | _noticeQueue.maxConcurrentOperationCount = 1; 21 | } 22 | }); 23 | return _noticeQueue; 24 | } 25 | 26 | #pragma mark - Public methods 27 | 28 | + (void)cancelAllNoticeViews 29 | { 30 | [[self noticeQueue] cancelAllOperations]; 31 | } 32 | 33 | + (void)cancelAndDismissAllNoticeViews 34 | { 35 | for (WBNoticeOperation *operation in [[self noticeQueue] operations]) { 36 | [operation.noticeView dismissNotice]; 37 | [operation cancel]; 38 | } 39 | } 40 | 41 | + (WBNoticeOperation *)addNoticeView:(WBNoticeView *)noticeView 42 | { 43 | return [self addNoticeView:noticeView filterDuplicates:NO]; 44 | } 45 | 46 | + (WBNoticeOperation *)addNoticeView:(WBNoticeView *)noticeView 47 | filterDuplicates:(BOOL)filterDuplicates 48 | { 49 | if (filterDuplicates) { 50 | for (NSOperation *operation in [[self noticeQueue] operations]) { 51 | WBNoticeOperation *noticeOperation = (WBNoticeOperation *)operation; 52 | if ([noticeOperation.noticeView isEqual:noticeView]) { 53 | return nil; 54 | } 55 | } 56 | } 57 | WBNoticeOperation *operation = [[WBNoticeOperation alloc] init]; 58 | operation.noticeView = noticeView; 59 | [[self noticeQueue] addOperation:operation]; 60 | return operation; 61 | } 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /NoticeView/WBNoticeView/NoticeView.bundle/notice_error_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tciuro/NoticeView/aea3a0c1e5c0e7b4140a8d9a0501c8f0f7a91c55/NoticeView/WBNoticeView/NoticeView.bundle/notice_error_icon.png -------------------------------------------------------------------------------- /NoticeView/WBNoticeView/NoticeView.bundle/notice_error_icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tciuro/NoticeView/aea3a0c1e5c0e7b4140a8d9a0501c8f0f7a91c55/NoticeView/WBNoticeView/NoticeView.bundle/notice_error_icon@2x.png -------------------------------------------------------------------------------- /NoticeView/WBNoticeView/NoticeView.bundle/notice_success_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tciuro/NoticeView/aea3a0c1e5c0e7b4140a8d9a0501c8f0f7a91c55/NoticeView/WBNoticeView/NoticeView.bundle/notice_success_icon.png -------------------------------------------------------------------------------- /NoticeView/WBNoticeView/NoticeView.bundle/notice_success_icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tciuro/NoticeView/aea3a0c1e5c0e7b4140a8d9a0501c8f0f7a91c55/NoticeView/WBNoticeView/NoticeView.bundle/notice_success_icon@2x.png -------------------------------------------------------------------------------- /NoticeView/WBNoticeView/NoticeView.bundle/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tciuro/NoticeView/aea3a0c1e5c0e7b4140a8d9a0501c8f0f7a91c55/NoticeView/WBNoticeView/NoticeView.bundle/up.png -------------------------------------------------------------------------------- /NoticeView/WBNoticeView/NoticeView.bundle/up@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tciuro/NoticeView/aea3a0c1e5c0e7b4140a8d9a0501c8f0f7a91c55/NoticeView/WBNoticeView/NoticeView.bundle/up@2x.png -------------------------------------------------------------------------------- /NoticeView/WBNoticeView/UILabel+WBExtensions.h: -------------------------------------------------------------------------------- 1 | // 2 | // UILabel+WBExtensions.h 3 | // NoticeView 4 | // 5 | // Created by Tito Ciuro on 5/15/12. 6 | // Copyright (c) 2012 Tito Ciuro. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface UILabel (WBExtensions) 12 | 13 | - (NSArray *)lines; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /NoticeView/WBNoticeView/UILabel+WBExtensions.m: -------------------------------------------------------------------------------- 1 | // 2 | // UILabel+WBExtensions.m 3 | // NoticeView 4 | // 5 | // Created by Tito Ciuro on 5/15/12. 6 | // Copyright (c) 2012 Tito Ciuro. All rights reserved. 7 | // 8 | 9 | #import "UILabel+WBExtensions.h" 10 | 11 | @implementation UILabel (WBExtensions) 12 | 13 | - (NSArray *)lines 14 | { 15 | if (! [self.text length]) return nil; 16 | 17 | NSMutableArray *lines = [NSMutableArray arrayWithCapacity:10]; 18 | NSCharacterSet *wordSeparators = [NSCharacterSet whitespaceAndNewlineCharacterSet]; 19 | NSString *currentLine = self.text; 20 | int textLength = [self.text length]; 21 | 22 | NSRange rCurrentLine = NSMakeRange(0, textLength); 23 | NSRange rWhitespace = NSMakeRange(0, 0); 24 | NSRange rRemainingText = NSMakeRange(0, textLength); 25 | BOOL done = NO; 26 | 27 | while (NO == done) { 28 | // determine the next whitespace word separator position 29 | rWhitespace.location = rWhitespace.location + rWhitespace.length; 30 | rWhitespace.length = textLength - rWhitespace.location; 31 | rWhitespace = [self.text rangeOfCharacterFromSet:wordSeparators options:NSCaseInsensitiveSearch range:rWhitespace]; 32 | 33 | if (NSNotFound == rWhitespace.location) { 34 | rWhitespace.location = textLength; 35 | done = YES; 36 | } 37 | 38 | NSRange rTest = NSMakeRange(rRemainingText.location, rWhitespace.location - rRemainingText.location); 39 | NSString *textTest = [self.text substringWithRange:rTest]; 40 | CGSize sizeTest = [textTest sizeWithFont:self.font forWidth:1024.0 lineBreakMode:NSLineBreakByWordWrapping]; 41 | 42 | if (sizeTest.width > self.bounds.size.width) { 43 | [lines addObject:[currentLine stringByTrimmingCharactersInSet:wordSeparators]]; 44 | rRemainingText.location = rCurrentLine.location + rCurrentLine.length; 45 | rRemainingText.length = textLength-rRemainingText.location; 46 | continue; 47 | } 48 | 49 | rCurrentLine = rTest; 50 | currentLine = textTest; 51 | } 52 | 53 | [lines addObject:[currentLine stringByTrimmingCharactersInSet:wordSeparators]]; 54 | 55 | return lines; 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /NoticeView/WBNoticeView/WBBlueGradientView.h: -------------------------------------------------------------------------------- 1 | // 2 | // WBBlueGradientView.h 3 | // GradientView 4 | // 5 | // Created by Tito Ciuro on 6/3/12. 6 | // Copyright (c) 2012 Webbo, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface WBBlueGradientView : UIView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /NoticeView/WBNoticeView/WBBlueGradientView.m: -------------------------------------------------------------------------------- 1 | // 2 | // WBBlueGradientView.m 3 | // GradientView 4 | // 5 | // Created by Tito Ciuro on 6/3/12. 6 | // Copyright (c) 2012 Webbo, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "WBBlueGradientView.h" 12 | 13 | @implementation WBBlueGradientView 14 | 15 | - (void)drawRect:(CGRect)rect 16 | { 17 | 18 | UIColor *redTop = [UIColor colorWithRed:37/255.0f green:122/255.0f blue:185/255.0f alpha:1.0]; 19 | UIColor *redBot = [UIColor colorWithRed:18/255.0f green:96/255.0f blue:154/255.0f alpha:1.0]; 20 | 21 | CAGradientLayer *gradient = [CAGradientLayer layer]; 22 | gradient.frame = self.bounds; 23 | gradient.colors = [NSArray arrayWithObjects: 24 | (id)redTop.CGColor, 25 | (id)redBot.CGColor, 26 | nil]; 27 | gradient.locations = [NSArray arrayWithObjects: 28 | [NSNumber numberWithFloat:0.0f], 29 | [NSNumber numberWithFloat:0.7], 30 | nil]; 31 | 32 | [self.layer insertSublayer:gradient atIndex:0]; 33 | self.layer.needsDisplayOnBoundsChange = YES; 34 | 35 | UIView *firstTopBlueLine = [[UIView alloc]initWithFrame:CGRectMake(0.0, 0.0, self.bounds.size.width, 1.0)]; 36 | firstTopBlueLine.backgroundColor = [UIColor colorWithRed:105/255.0f green:163/255.0f blue:208/255.0f alpha:1.0]; 37 | [self addSubview:firstTopBlueLine]; 38 | 39 | UIView *secondTopBlueLine = [[UIView alloc]initWithFrame:CGRectMake(0.0, 1.0, self.bounds.size.width, 1.0)]; 40 | secondTopBlueLine.backgroundColor = [UIColor colorWithRed:46/255.0f green:126/255.0f blue:188/255.0f alpha:1.0]; 41 | [self addSubview:secondTopBlueLine]; 42 | 43 | UIView *firstBotBlueLine = [[UIView alloc]initWithFrame:CGRectMake(0.0, self.bounds.size.height - 1, self.frame.size.width, 1.0)]; 44 | firstBotBlueLine.backgroundColor = [UIColor colorWithRed:18/255.0f green:92/255.0f blue:149/255.0f alpha:1.0]; 45 | [self addSubview:firstBotBlueLine]; 46 | 47 | UIView *secondBotDarkLine = [[UIView alloc]initWithFrame:CGRectMake(0.0, self.bounds.size.height, self.frame.size.width, 1.0)]; 48 | secondBotDarkLine.backgroundColor = [UIColor colorWithRed:4/255.0f green:45/255.0f blue:75/255.0f alpha:1.0]; 49 | [self addSubview:secondBotDarkLine]; 50 | } 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /NoticeView/WBNoticeView/WBErrorNoticeView.h: -------------------------------------------------------------------------------- 1 | // 2 | // WBErrorNoticeView.h 3 | // NoticeView 4 | // 5 | // Created by Tito Ciuro on 5/25/12. 6 | // Copyright (c) 2012 Tito Ciuro. All rights reserved. 7 | // 8 | 9 | #import "WBNoticeView.h" 10 | 11 | /** 12 | The `WBErrorNoticeView` class is a `WBNoticeView` subclass suitable for displaying an error to a user. The notice is presented on a red gradient background with an error icon on the left hand side of the notice. It supports the display of a title and a message. 13 | */ 14 | @interface WBErrorNoticeView : WBNoticeView 15 | 16 | ///------------------------------- 17 | /// @name Creating an Error Notice 18 | ///------------------------------- 19 | 20 | /** 21 | Creates and returns an error notice in the given view with the specified title and message. 22 | 23 | @param view The view to display the notice in. 24 | @param title The title of the notice. 25 | @param message The message of the notice. 26 | @return The newly created error notice object. 27 | */ 28 | + (WBErrorNoticeView *)errorNoticeInView:(UIView *)view title:(NSString *)title message:(NSString *)message; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /NoticeView/WBNoticeView/WBErrorNoticeView.m: -------------------------------------------------------------------------------- 1 | // 2 | // WBErrorNoticeView.m 3 | // NoticeView 4 | // 5 | // Created by Tito Ciuro on 5/25/12. 6 | // Copyright (c) 2012 Tito Ciuro. All rights reserved. 7 | // 8 | 9 | #import "WBErrorNoticeView.h" 10 | #import "WBNoticeView+ForSubclassEyesOnly.h" 11 | #import "WBRedGradientView.h" 12 | 13 | @implementation WBErrorNoticeView 14 | 15 | + (WBErrorNoticeView *)errorNoticeInView:(UIView *)view title:(NSString *)title message:(NSString *)message 16 | { 17 | WBErrorNoticeView *notice = [[WBErrorNoticeView alloc]initWithView:view title:title]; 18 | 19 | notice.message = message; 20 | notice.sticky = NO; 21 | 22 | return notice; 23 | } 24 | 25 | - (void)show 26 | { 27 | // Obtain the screen width 28 | CGFloat viewWidth = self.view.bounds.size.width; 29 | 30 | // Locate the images 31 | NSString *path = [[[NSBundle mainBundle]resourcePath]stringByAppendingPathComponent:@"NoticeView.bundle"]; 32 | NSString *noticeIconImageName = [path stringByAppendingPathComponent:@"notice_error_icon.png"]; 33 | 34 | // Make and add the title label 35 | float titleYOrigin = 10.0; 36 | 37 | self.titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(55.0 + (self.contentInset.left), titleYOrigin + (self.contentInset.top), viewWidth - 70.0 - (self.contentInset.right+self.contentInset.left) , 16.0)]; 38 | // NSLog(@"titleLabelFrame: %@", NSStringFromCGRect(self.titleLabel.frame)); 39 | // self.titleLabel.layer.borderWidth = 1; 40 | // self.titleLabel.layer.borderColor = [UIColor greenColor].CGColor; 41 | self.titleLabel.textColor = [UIColor whiteColor]; 42 | self.titleLabel.shadowOffset = CGSizeMake(0.0, -1.0); 43 | self.titleLabel.shadowColor = [UIColor blackColor]; 44 | self.titleLabel.font = [UIFont boldSystemFontOfSize:14.0]; 45 | self.titleLabel.backgroundColor = [UIColor clearColor]; 46 | self.titleLabel.text = self.title; 47 | 48 | // Make the message label 49 | self.messageLabel = [[UILabel alloc]initWithFrame:CGRectMake(55.0 + (self.contentInset.left), 20.0 + 10.0 + (self.contentInset.top), viewWidth - 70.0 - (self.contentInset.right+self.contentInset.left), 12.0)]; 50 | self.messageLabel.font = [UIFont systemFontOfSize:13.0]; 51 | self.messageLabel.textColor = [UIColor colorWithRed:239.0/255.0 green:167.0/255.0 blue:163.0/255.0 alpha:1.0]; 52 | self.messageLabel.backgroundColor = [UIColor clearColor]; 53 | self.messageLabel.text = self.message; 54 | 55 | // Calculate the number of lines it'll take to display the text 56 | NSInteger numberOfLines = [[self.messageLabel lines]count]; 57 | self.messageLabel.numberOfLines = numberOfLines; 58 | [self.messageLabel sizeToFit]; 59 | CGFloat messageLabelHeight = self.messageLabel.frame.size.height; 60 | 61 | CGRect r = self.messageLabel.frame; 62 | r.origin.y = self.titleLabel.frame.origin.y + self.titleLabel.frame.size.height; 63 | 64 | float noticeViewHeight = 0.0; 65 | double currOsVersion = [[[UIDevice currentDevice]systemVersion]doubleValue]; 66 | if (currOsVersion >= 6.0f) { 67 | noticeViewHeight = messageLabelHeight; 68 | } else { 69 | // Now we can determine the height of one line of text 70 | r.size.height = self.messageLabel.frame.size.height * numberOfLines; 71 | r.size.width = viewWidth - 70.0; 72 | self.messageLabel.frame = r; 73 | 74 | // Calculate the notice view height 75 | noticeViewHeight = 10.0; 76 | if (numberOfLines > 1) { 77 | noticeViewHeight += ((numberOfLines - 1) * messageLabelHeight); 78 | } 79 | } 80 | 81 | // Add some bottom margin for the notice view 82 | noticeViewHeight += 30.0; 83 | 84 | // Make sure we hide completely the view, including its shadow 85 | float hiddenYOrigin = self.slidingMode == WBNoticeViewSlidingModeDown ? -noticeViewHeight - 20.0: self.view.bounds.size.height; 86 | 87 | // Make and add the notice view 88 | self.gradientView = [[WBRedGradientView alloc] initWithFrame:CGRectMake(0.0, hiddenYOrigin, viewWidth, noticeViewHeight + 10.0)]; 89 | [self.view addSubview:self.gradientView]; 90 | 91 | // Make and add the icon view 92 | UIImageView *iconView = [[UIImageView alloc]initWithFrame:CGRectMake(10.0, 10.0, 20.0, 30.0)]; 93 | iconView.image = [UIImage imageWithContentsOfFile:noticeIconImageName]; 94 | iconView.contentMode = UIViewContentModeScaleAspectFit; 95 | iconView.alpha = 0.8; 96 | [self.gradientView addSubview:iconView]; 97 | 98 | // Add the title label 99 | [self.gradientView addSubview:self.titleLabel]; 100 | 101 | // Add the message label 102 | [self.gradientView addSubview:self.messageLabel]; 103 | 104 | // Add the drop shadow to the notice view 105 | CALayer *noticeLayer = self.gradientView.layer; 106 | noticeLayer.shadowColor = [[UIColor blackColor]CGColor]; 107 | noticeLayer.shadowOffset = CGSizeMake(0.0, 3); 108 | noticeLayer.shadowOpacity = 0.50; 109 | noticeLayer.masksToBounds = NO; 110 | noticeLayer.shouldRasterize = YES; 111 | 112 | self.hiddenYOrigin = hiddenYOrigin; 113 | 114 | [self displayNotice]; 115 | } 116 | 117 | @end 118 | -------------------------------------------------------------------------------- /NoticeView/WBNoticeView/WBGrayGradientView.h: -------------------------------------------------------------------------------- 1 | // 2 | // WBGrayGradientView.h 3 | // GradientView 4 | // 5 | // Created by Tito Ciuro on 6/3/12. 6 | // Copyright (c) 2012 Webbo, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface WBGrayGradientView : UIView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /NoticeView/WBNoticeView/WBGrayGradientView.m: -------------------------------------------------------------------------------- 1 | // 2 | // WBGrayGradientView.m 3 | // GradientView 4 | // 5 | // Created by Tito Ciuro on 6/3/12. 6 | // Copyright (c) 2012 Webbo, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "WBGrayGradientView.h" 12 | 13 | @implementation WBGrayGradientView 14 | 15 | - (void)drawRect:(CGRect)rect 16 | { 17 | UIColor *redTop = [UIColor colorWithRed:198/255.0f green:200/255.0f blue:202/255.0f alpha:1.0]; 18 | UIColor *redBot = [UIColor colorWithRed:225/255.0f green:228/255.0f blue:229/255.0f alpha:1.0]; 19 | 20 | CAGradientLayer *gradient = [CAGradientLayer layer]; 21 | gradient.frame = self.bounds; 22 | gradient.colors = [NSArray arrayWithObjects: 23 | (id)redTop.CGColor, 24 | (id)redBot.CGColor, 25 | nil]; 26 | gradient.locations = [NSArray arrayWithObjects: 27 | [NSNumber numberWithFloat:0.0f], 28 | [NSNumber numberWithFloat:0.7], 29 | nil]; 30 | 31 | [self.layer insertSublayer:gradient atIndex:0]; 32 | self.layer.needsDisplayOnBoundsChange = YES; 33 | 34 | UIView *firstBotWhiteLine = [[UIView alloc]initWithFrame:CGRectMake(0.0, self.bounds.size.height - 1, self.frame.size.width, 1.0)]; 35 | firstBotWhiteLine.backgroundColor = [UIColor colorWithRed:236/255.0f green:238/255.0f blue:239/255.0f alpha:1.0]; 36 | [self addSubview:firstBotWhiteLine]; 37 | 38 | UIView *secondBotDarkLine = [[UIView alloc]initWithFrame:CGRectMake(0.0, self.bounds.size.height, self.frame.size.width, 1.0)]; 39 | secondBotDarkLine.backgroundColor = [UIColor colorWithRed:113/255.0f green:114/255.0f blue:115/255.0f alpha:1.0]; 40 | [self addSubview:secondBotDarkLine]; 41 | } 42 | 43 | @end 44 | -------------------------------------------------------------------------------- /NoticeView/WBNoticeView/WBNoticeOperation.h: -------------------------------------------------------------------------------- 1 | // 2 | // WBNoticeOperation.h 3 | // NoticeView 4 | // 5 | // Created by Johannes Plunien on 1/30/13. 6 | // Copyright (c) 2013 Tito Ciuro. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "WBNoticeView.h" 11 | 12 | @interface WBNoticeOperation : NSOperation 13 | 14 | @property (nonatomic, assign) BOOL dismissedInteractively; 15 | @property (nonatomic, strong) WBNoticeView *noticeView; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /NoticeView/WBNoticeView/WBNoticeOperation.m: -------------------------------------------------------------------------------- 1 | // 2 | // WBNoticeOperation.m 3 | // NoticeView 4 | // 5 | // Created by Johannes Plunien on 1/30/13. 6 | // Copyright (c) 2013 Tito Ciuro. All rights reserved. 7 | // 8 | 9 | #import "WBNoticeOperation.h" 10 | 11 | @interface WBNoticeOperation () 12 | 13 | @property (nonatomic, assign, getter = isExecuting) BOOL executing; 14 | @property (nonatomic, assign, getter = isCancelled) BOOL cancelled; 15 | @property (nonatomic, assign, getter = isFinished) BOOL finished; 16 | 17 | @end 18 | 19 | @implementation WBNoticeOperation 20 | 21 | - (void)start 22 | { 23 | if (self.cancelled) { 24 | return; 25 | } 26 | 27 | self.executing = YES; 28 | self.finished = NO; 29 | 30 | __weak __typeof(&*self)weakSelf = self; 31 | self.noticeView.dismissalBlock = ^(BOOL dismissedInteractively) { 32 | [weakSelf willChangeValueForKey:@"isFinished"]; 33 | [weakSelf willChangeValueForKey:@"isExecuting"]; 34 | weakSelf.executing = NO; 35 | weakSelf.finished = YES; 36 | weakSelf.dismissedInteractively = dismissedInteractively; 37 | [weakSelf didChangeValueForKey:@"isExecuting"]; 38 | [weakSelf didChangeValueForKey:@"isFinished"]; 39 | }; 40 | 41 | dispatch_sync(dispatch_get_main_queue(), ^{ 42 | [self.noticeView show]; 43 | }); 44 | } 45 | 46 | - (void)cancel 47 | { 48 | self.cancelled = YES; 49 | } 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /NoticeView/WBNoticeView/WBNoticeView+ForSubclassEyesOnly.h: -------------------------------------------------------------------------------- 1 | // 2 | // WBNoticeView+ForSubclassEyesOnly.h 3 | // NoticeView 4 | // 5 | // Created by Blake Watters on 11/12/12. 6 | // Copyright (c) 2012 Tito Ciuro. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "WBNoticeView.h" 11 | #import "UILabel+WBExtensions.h" 12 | 13 | @interface WBNoticeView (ForSubclassEyesOnly) 14 | 15 | @property(nonatomic, strong) UIButton *dismissButton; 16 | @property(nonatomic, strong) UIView *gradientView; 17 | @property(nonatomic, strong) UILabel *titleLabel; 18 | @property(nonatomic, strong) UILabel *messageLabel; 19 | @property(nonatomic, assign) CGFloat hiddenYOrigin; 20 | 21 | /** 22 | Tells the superclass to display the notice. 23 | */ 24 | - (void)displayNotice; 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /NoticeView/WBNoticeView/WBNoticeView.h: -------------------------------------------------------------------------------- 1 | // 2 | // WBNoticeView.h 3 | // NoticeView 4 | // 5 | // Created by Tito Ciuro on 5/16/12. 6 | // Copyright (c) 2012 Tito Ciuro. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef enum WBNoticeViewSlidingMode { 12 | WBNoticeViewSlidingModeUp, 13 | WBNoticeViewSlidingModeDown, 14 | } WBNoticeViewSlidingMode; 15 | 16 | /** 17 | `WBNoticeView` objects provides a lightweight, non-intrusive means for displaying information to the user. The `WBNoticeView` class is an abstract class that encapsulates the interface common to all notice objects. 18 | */ 19 | @interface WBNoticeView : NSObject 20 | 21 | ///---------------------------- 22 | /// @name Initializing a Notice 23 | ///---------------------------- 24 | 25 | /** 26 | Initializes the receiver with the given origin view and title. 27 | 28 | This is the designated initializer. 29 | 30 | @param view The view from which the notice will originate when displayed. 31 | @param title The title for the notice. 32 | @return The receiver, initialized with the given view and title. 33 | */ 34 | - (id)initWithView:(UIView *)view title:(NSString *)title; 35 | 36 | ///--------------------------------- 37 | /// @name Configuring Notice Display 38 | ///--------------------------------- 39 | 40 | /** 41 | The view from which the notice will be displayed. 42 | */ 43 | @property (nonatomic, weak) UIView *view; 44 | 45 | /** 46 | Content insets 47 | */ 48 | @property (nonatomic) UIEdgeInsets contentInset; 49 | 50 | /** 51 | The title text for the notice. 52 | 53 | **Default**: `"Unknown Error"` 54 | */ 55 | @property (nonatomic, copy) NSString *title; 56 | 57 | /** 58 | The message for the notice. Not supported by all notice types. 59 | */ 60 | @property (nonatomic, copy) NSString *message; 61 | 62 | /** 63 | The animation duration for the notice. 64 | 65 | **Default**: `0.5` 66 | */ 67 | @property (nonatomic, readwrite) NSTimeInterval duration; 68 | 69 | /** 70 | The time interval in seconds that the notice will be displayed before being automatically dismissed. 71 | 72 | **Default**: `2.0` 73 | */ 74 | @property (nonatomic, readwrite) NSTimeInterval delay; 75 | 76 | /** 77 | The amount of transparency applied to the notice. Values can range between `0.0` (transparent) and `1.0` (opaque). Values outside this range are clamped to `0.0` or `1.0`. 78 | 79 | **Default**: `1.0` 80 | */ 81 | @property (nonatomic, readwrite) CGFloat alpha; 82 | 83 | /** 84 | The number of points that the notice will be offset vertically from the origin view when being displayed. 85 | 86 | **Default**: `0.0` 87 | */ 88 | @property (nonatomic, readwrite) CGFloat originY; 89 | 90 | /** 91 | A Boolean value that determines if the notice supports tap to dismiss. 92 | 93 | **Default**: `YES` 94 | */ 95 | @property (nonatomic, assign, getter = isTapToDismissEnabled) BOOL tapToDismissEnabled; 96 | 97 | /** 98 | A Boolean value that determines if the notice will be automatically dismissed after the time interval specified by the `delay` property expires. 99 | 100 | **Default**: `NO` 101 | */ 102 | @property (nonatomic, readwrite, getter = isSticky) BOOL sticky; 103 | 104 | /** 105 | Decides if the notice is shown sliding up from the bottom or down from the top 106 | 107 | **Default**: 'WBNoticeViewSlidingModeDown' 108 | */ 109 | @property WBNoticeViewSlidingMode slidingMode; 110 | 111 | /** 112 | A Boolean value that determines if the notice will be floating. If added to a `UIScrollView`, it 113 | will have a fixed position while scrolling. 114 | 115 | **Default**: `NO` 116 | */ 117 | @property (nonatomic, readwrite, getter = isFloating) BOOL floating; 118 | 119 | ///---------------------------------------- 120 | /// @name Showing and Dismissing the Notice 121 | ///---------------------------------------- 122 | 123 | /** 124 | Shows the notice. 125 | 126 | @warning The `WBNoticeView` class is abstract. Concrete subclasses must provide an implementation of the `show` method or else an exception will be raised. 127 | */ 128 | - (void)show; 129 | 130 | /** 131 | Dismisses the notice. 132 | */ 133 | - (void)dismissNotice; 134 | 135 | ///---------------------------------- 136 | /// @name Setting the Dismissal Block 137 | ///---------------------------------- 138 | 139 | /** 140 | Sets a block to be executed when the notice is dismissed. 141 | 142 | The block accepts a single Boolean value that indicates if the notice was dismissed interactively by the user or was dismissed due to expiration of the display interval. 143 | 144 | @param block The block to be executed when the notice receives a dismissal. 145 | */ 146 | - (void)setDismissalBlock:(void (^)(BOOL dismissedInteractively))block; 147 | 148 | /** 149 | Sets a block to be executed when the notice is dismissed. 150 | 151 | The block accepts a Boolean value that indicates if the notice was dismissed interactively by the user or was dismissed due to expiration of the display interval. 152 | The block also accepts a boolean pointer that makes it possible to make the dismissal optional depending on the resulting block. (Good for putting up an AlertView in the block to check if the user is sure) 153 | 154 | @param block The block to be executed when the notice receives a dismissal. 155 | */ 156 | - (void)setDismissalBlockWithOptionalDismiss:(void (^)(BOOL dismissedInteractively, BOOL *dismissAfterBlock))block; 157 | 158 | @end 159 | -------------------------------------------------------------------------------- /NoticeView/WBNoticeView/WBNoticeView.m: -------------------------------------------------------------------------------- 1 | // 2 | // WBNoticeView.m 3 | // NoticeView 4 | // 5 | // Created by Tito Ciuro on 5/16/12. 6 | // Copyright (c) 2012 Tito Ciuro. All rights reserved. 7 | // 8 | 9 | #import "WBNoticeView.h" 10 | #import "WBNoticeView+ForSubclassEyesOnly.h" 11 | 12 | @interface WBNoticeView () 13 | 14 | @property(nonatomic, strong) UIButton *dismissButton; 15 | @property(nonatomic, strong) UIView *gradientView; 16 | @property(nonatomic, strong) UILabel *titleLabel; 17 | @property(nonatomic, strong) UILabel *messageLabel; 18 | 19 | @property(nonatomic, assign, getter = isObserving) BOOL observing; 20 | @property(nonatomic, assign) CGFloat hiddenYOrigin; 21 | @property(nonatomic, strong) WBNoticeView *currentNotice; 22 | @property(nonatomic, copy) void (^dismissalBlock)(BOOL dismissedInteractively); 23 | @property(nonatomic, copy) void (^dismissalBlockWithOptionalDismiss)(BOOL dismissedInteractively, BOOL *dismissAfterBlock); 24 | @property(nonatomic, strong) NSTimer *displayTimer; 25 | 26 | - (void)dismissNoticeWithDuration:(NSTimeInterval)duration 27 | delay:(NSTimeInterval)delay 28 | hiddenYOrigin:(CGFloat)hiddenYOrigin; 29 | - (void)cleanup; 30 | 31 | @end 32 | 33 | @implementation WBNoticeView 34 | 35 | @synthesize gradientView = _gradientView; 36 | @synthesize titleLabel = _titleLabel; 37 | @synthesize messageLabel = _messageLabel; 38 | @synthesize hiddenYOrigin = _hiddenYOrigin; 39 | @synthesize currentNotice = _currentNotice; 40 | @synthesize view = _view; 41 | @synthesize title = _title; 42 | @synthesize duration = _duration; 43 | @synthesize delay = _delay; 44 | @synthesize alpha = _alpha; 45 | @synthesize originY = _originY; 46 | @synthesize sticky = _sticky; 47 | @synthesize dismissalBlock = _dismissalBlock; 48 | @synthesize dismissalBlockWithOptionalDismiss = _dismissalBlockWithOptionalDismiss; 49 | @synthesize floating = _floating; 50 | 51 | - (id)initWithView:(UIView *)view title:(NSString *)title 52 | { 53 | NSParameterAssert(view); 54 | self = [super init]; 55 | if (self) { 56 | _view = view; 57 | _title = title ?: @"Unknown Error"; 58 | _message = @"Information not provided"; 59 | _duration = 0.5; 60 | _alpha = 1.0; 61 | _delay = 2.0; 62 | _tapToDismissEnabled = YES; 63 | _slidingMode = WBNoticeViewSlidingModeDown; 64 | _floating = NO; 65 | _contentInset = UIEdgeInsetsMake(0,0,0,0); // No insets as default 66 | } 67 | return self; 68 | } 69 | 70 | - (void)show 71 | { 72 | // Subclasses need to override this method... 73 | [self doesNotRecognizeSelector:_cmd]; 74 | } 75 | 76 | - (BOOL)isEqual:(WBNoticeView *)object 77 | { 78 | return [self.title isEqual:object.title] && [self.message isEqual:object.message]; 79 | } 80 | 81 | #pragma mark - 82 | 83 | - (void)displayNotice 84 | { 85 | self.gradientView.userInteractionEnabled = YES; 86 | 87 | // Add tap to dismiss capabilities if desired 88 | if (self.isTapToDismissEnabled) { 89 | // Add an invisible button that responds to a manual dismiss 90 | self.currentNotice = self; 91 | self.dismissButton = [UIButton buttonWithType:UIButtonTypeCustom]; 92 | self.dismissButton.frame = self.gradientView.bounds; 93 | [self.dismissButton addTarget:self action:@selector(dismissNoticeInteractively) forControlEvents:UIControlEventTouchUpInside]; 94 | [self.gradientView addSubview:self.dismissButton]; 95 | } 96 | 97 | [self updateAccessibilityLabels]; 98 | 99 | //set default originY if WBNoticeViewSlidingModeUp 100 | if ((self.slidingMode == WBNoticeViewSlidingModeUp) && (self.originY == 0)) { 101 | self.originY = self.view.bounds.size.height - self.gradientView.bounds.size.height; 102 | self.gradientView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleWidth; 103 | } else 104 | { 105 | self.gradientView.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleWidth; 106 | } 107 | 108 | // Go ahead, display it 109 | [UIView animateWithDuration:self.duration animations:^ { 110 | CGRect newFrame = self.gradientView.frame; 111 | //add scroll offset if scrolled 112 | double scrollOffsetY = 0.0f; 113 | if ([self.view isKindOfClass:[UIScrollView class]]) { 114 | UIScrollView *scrollView = (UIScrollView *)self.view; 115 | scrollOffsetY = scrollView.contentOffset.y; 116 | } 117 | newFrame.origin.y = self.originY + scrollOffsetY; 118 | self.gradientView.frame = newFrame; 119 | self.gradientView.alpha = self.alpha; 120 | [self registerObserver]; 121 | } completion:^ (BOOL finished) { 122 | // if it's not sticky, hide it automatically 123 | if ((self.tapToDismissEnabled && !self.isSticky) || (!self.tapToDismissEnabled && self.isSticky)) { 124 | // Schedule a timer 125 | self.displayTimer = [NSTimer scheduledTimerWithTimeInterval:self.delay target:self selector:@selector(dismissAfterTimerExpiration) userInfo:nil repeats:NO]; 126 | } else if (!self.isSticky) { 127 | // Display for a while, then hide it again 128 | [self dismissNoticeWithDuration:self.duration delay:self.delay hiddenYOrigin:self.hiddenYOrigin]; 129 | } 130 | }]; 131 | } 132 | 133 | - (void)dismissNoticeWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay hiddenYOrigin:(CGFloat)hiddenYOrigin 134 | { 135 | [UIView animateWithDuration:duration delay:delay options:UIViewAnimationOptionCurveEaseOut animations:^ { 136 | [self unregisterObserver]; 137 | CGRect newFrame = self.gradientView.frame; 138 | if (self.slidingMode == WBNoticeViewSlidingModeUp) { 139 | newFrame.origin.y = self.gradientView.frame.origin.y + self.gradientView.bounds.size.height; 140 | } else 141 | { 142 | newFrame.origin.y = hiddenYOrigin; 143 | } 144 | self.gradientView.frame = newFrame; 145 | } completion:^ (BOOL finished) { 146 | 147 | if (self.dismissalBlock) { 148 | self.dismissalBlock(NO); 149 | } 150 | 151 | if (self.dismissalBlockWithOptionalDismiss) { 152 | BOOL dismissAfterBlock = YES; 153 | self.dismissalBlockWithOptionalDismiss(NO, &dismissAfterBlock); 154 | } 155 | 156 | // Cleanup 157 | [self cleanup]; 158 | }]; 159 | } 160 | 161 | - (void)dismissNotice 162 | { 163 | [self.displayTimer invalidate]; 164 | [self dismissNoticeWithDuration:self.duration delay:self.delay hiddenYOrigin:self.hiddenYOrigin]; 165 | } 166 | 167 | - (void)dismissNoticeInteractively 168 | { 169 | // TODO: Should this timer invalidate here or when the dismissalBlock is set to nil further down? 170 | [self.displayTimer invalidate]; 171 | 172 | if (self.dismissalBlock) { 173 | self.dismissalBlock(YES); 174 | } 175 | 176 | // By default we want to dismiss after the block has been called 177 | BOOL dismissAfterBlock = YES; 178 | if (self.dismissalBlockWithOptionalDismiss) { 179 | self.dismissalBlockWithOptionalDismiss(YES, &dismissAfterBlock); 180 | } 181 | 182 | // Lets check if the block wanted us to dismiss 183 | if (dismissAfterBlock) { 184 | // Clear the reference to the dismissal block so that the animation does invoke the block a second time 185 | self.dismissalBlock = nil; 186 | self.dismissalBlockWithOptionalDismiss = nil; 187 | 188 | [self dismissNoticeWithDuration:self.duration delay:0 hiddenYOrigin:self.hiddenYOrigin]; 189 | } 190 | } 191 | 192 | - (void)dismissAfterTimerExpiration 193 | { 194 | [self dismissNoticeWithDuration:self.duration delay:0.0 hiddenYOrigin:self.hiddenYOrigin]; 195 | } 196 | 197 | - (void)updateAccessibilityLabels 198 | { 199 | // Setup accessiblity on the gradient view 200 | NSString *accessibilityLabel = ([self.messageLabel.text length]) ? [NSString stringWithFormat:@"%@, %@", [self.titleLabel text], [self.messageLabel text]] 201 | : [self.titleLabel text]; 202 | self.gradientView.accessibilityTraits = (self.isTapToDismissEnabled) ? UIAccessibilityTraitButton : UIAccessibilityTraitStaticText; 203 | self.gradientView.accessibilityLabel = accessibilityLabel; 204 | self.dismissButton.accessibilityLabel = accessibilityLabel; 205 | } 206 | 207 | #pragma mark - KVO 208 | 209 | - (void)observeValueForKeyPath:(NSString *)keyPath 210 | ofObject:(id)object 211 | change:(NSDictionary *)change 212 | context:(void *)context 213 | { 214 | if (self.floating && object && [object isKindOfClass:[UIScrollView class]] && [keyPath isEqualToString:@"contentOffset"]) { 215 | CGRect newFrame = self.gradientView.frame; 216 | newFrame.origin.y = ((UIScrollView *)object).contentOffset.y; 217 | self.hiddenYOrigin += (newFrame.origin.y - self.gradientView.frame.origin.y); 218 | self.gradientView.frame = newFrame; 219 | [self.view bringSubviewToFront:self.gradientView]; 220 | } 221 | if (self.floating && self.view.window == nil) { 222 | [self unregisterObserver]; 223 | } 224 | } 225 | 226 | - (void)registerObserver 227 | { 228 | if (self.floating && [self.view isKindOfClass:[UIScrollView class]]) { 229 | [self.view addObserver:self 230 | forKeyPath:@"contentOffset" 231 | options:NSKeyValueObservingOptionNew 232 | context:NULL]; 233 | self.observing = YES; 234 | } 235 | } 236 | 237 | - (void)unregisterObserver 238 | { 239 | if (self.floating && self.isObserving && [self.view isKindOfClass:[UIScrollView class]]) { 240 | [self.view removeObserver:self forKeyPath:@"contentOffset"]; 241 | self.observing = NO; 242 | } 243 | } 244 | 245 | #pragma mark - 246 | 247 | - (void)cleanup 248 | { 249 | [self.displayTimer invalidate]; 250 | [self.gradientView removeFromSuperview]; 251 | self.gradientView = nil; 252 | self.titleLabel = nil; 253 | self.messageLabel = nil; 254 | self.currentNotice = nil; 255 | } 256 | 257 | - (void)dealloc 258 | { 259 | [self cleanup]; 260 | } 261 | 262 | @end 263 | -------------------------------------------------------------------------------- /NoticeView/WBNoticeView/WBRedGradientView.h: -------------------------------------------------------------------------------- 1 | // 2 | // WBRedGradientView.h 3 | // GradientView 4 | // 5 | // Created by Tito Ciuro on 6/3/12. 6 | // Copyright (c) 2012 Webbo, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface WBRedGradientView : UIView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /NoticeView/WBNoticeView/WBRedGradientView.m: -------------------------------------------------------------------------------- 1 | // 2 | // WBRedGradientView.m 3 | // GradientView 4 | // 5 | // Created by Tito Ciuro on 6/3/12. 6 | // Copyright (c) 2012 Webbo, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "WBRedGradientView.h" 12 | 13 | @implementation WBRedGradientView 14 | 15 | - (void)drawRect:(CGRect)rect 16 | { 17 | 18 | UIColor *redTop = [UIColor colorWithRed:167/255.0f green:26/255.0f blue:20/255.0f alpha:1.0]; 19 | UIColor *redBot = [UIColor colorWithRed:134/255.0f green:9/255.0f blue:7/255.0f alpha:1.0]; 20 | 21 | CAGradientLayer *gradient = [CAGradientLayer layer]; 22 | gradient.frame = self.bounds; 23 | gradient.colors = [NSArray arrayWithObjects: 24 | (id)redTop.CGColor, 25 | (id)redBot.CGColor, 26 | nil]; 27 | gradient.locations = [NSArray arrayWithObjects: 28 | [NSNumber numberWithFloat:0.0f], 29 | [NSNumber numberWithFloat:0.7], 30 | nil]; 31 | 32 | [self.layer insertSublayer:gradient atIndex:0]; 33 | self.layer.needsDisplayOnBoundsChange = YES; 34 | 35 | UIView *firstTopPinkLine = [[UIView alloc]initWithFrame:CGRectMake(0.0, 0.0, self.bounds.size.width, 1.0)]; 36 | firstTopPinkLine.backgroundColor = [UIColor colorWithRed:211/255.0f green:82/255.0f blue:80/255.0f alpha:1.0]; 37 | [self addSubview:firstTopPinkLine]; 38 | 39 | UIView *secondTopRedLine = [[UIView alloc]initWithFrame:CGRectMake(0.0, 1.0, self.bounds.size.width, 1.0)]; 40 | secondTopRedLine.backgroundColor = [UIColor colorWithRed:193/255.0f green:30/255.0f blue:23/255.0f alpha:1.0]; 41 | [self addSubview:secondTopRedLine]; 42 | 43 | UIView *firstBotRedLine = [[UIView alloc]initWithFrame:CGRectMake(0.0, self.bounds.size.height - 1, self.frame.size.width, 1.0)]; 44 | firstBotRedLine.backgroundColor = [UIColor colorWithRed:134/255.0f green:9/255.0f blue:7/255.0f alpha:1.0]; 45 | [self addSubview:firstBotRedLine]; 46 | 47 | UIView *secondBotDarkLine = [[UIView alloc]initWithFrame:CGRectMake(0.0, self.bounds.size.height, self.frame.size.width, 1.0)]; 48 | secondBotDarkLine.backgroundColor = [UIColor colorWithRed:52/255.0f green:4/255.0f blue:3/255.0f alpha:1.0]; 49 | [self addSubview:secondBotDarkLine]; 50 | } 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /NoticeView/WBNoticeView/WBStickyNoticeView.h: -------------------------------------------------------------------------------- 1 | // 2 | // WBStickyNoticeView.h 3 | // NoticeView 4 | // 5 | // Created by Tito Ciuro on 6/4/12. 6 | // Copyright (c) 2012 Tito Ciuro. All rights reserved. 7 | // 8 | 9 | #import "WBNoticeView.h" 10 | 11 | /** 12 | The `WBStickyNoticeView` class is a `WBNoticeView` subclass suitable for displaying a sticky informational message to a user. The notice is presented on a gray gradient background with a vertical error icon on the left hand side of the notice. It supports the display of a title only. 13 | */ 14 | @interface WBStickyNoticeView : WBNoticeView 15 | 16 | ///------------------------------- 17 | /// @name Creating a Sticky Notice 18 | ///------------------------------- 19 | 20 | /** 21 | Creates and returns a sticky notice in the given view with the specified title. 22 | 23 | @param view The view to display the notice in. 24 | @param title The title of the notice. 25 | @return The newly created sticky notice object. 26 | */ 27 | + (WBStickyNoticeView *)stickyNoticeInView:(UIView *)view title:(NSString *)title; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /NoticeView/WBNoticeView/WBStickyNoticeView.m: -------------------------------------------------------------------------------- 1 | // 2 | // WBStickyNoticeView.m 3 | // NoticeView 4 | // 5 | // Created by Tito Ciuro on 6/4/12. 6 | // Copyright (c) 2012 Tito Ciuro. All rights reserved. 7 | // 8 | 9 | #import "WBStickyNoticeView.h" 10 | #import "WBNoticeView+ForSubclassEyesOnly.h" 11 | #import "WBGrayGradientView.h" 12 | 13 | @implementation WBStickyNoticeView 14 | 15 | + (WBStickyNoticeView *)stickyNoticeInView:(UIView *)view title:(NSString *)title 16 | { 17 | WBStickyNoticeView *notice = [[WBStickyNoticeView alloc]initWithView:view title:title]; 18 | 19 | notice.sticky = YES; 20 | 21 | return notice; 22 | } 23 | 24 | - (void)show 25 | { 26 | // Obtain the screen width 27 | CGFloat viewWidth = self.view.bounds.size.width; 28 | 29 | // Locate the images 30 | NSString *path = [[[NSBundle mainBundle]resourcePath]stringByAppendingPathComponent:@"NoticeView.bundle"]; 31 | NSString *noticeIconImageName = [path stringByAppendingPathComponent:@"up.png"]; 32 | 33 | NSInteger numberOfLines = 1; 34 | CGFloat messageLineHeight = 30.0; 35 | 36 | // Make and add the title label 37 | self.titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(55.0 + (self.contentInset.left), 8.0 + (self.contentInset.top), viewWidth - 70.0 - (self.contentInset.right+self.contentInset.left) , 16.0)]; 38 | self.titleLabel.textColor = [UIColor blackColor]; 39 | self.titleLabel.shadowOffset = CGSizeMake(0.0, 1.0); 40 | self.titleLabel.shadowColor = [UIColor whiteColor]; 41 | self.titleLabel.font = [UIFont boldSystemFontOfSize:12.0]; 42 | self.titleLabel.backgroundColor = [UIColor clearColor]; 43 | self.titleLabel.text = self.title; 44 | [self.titleLabel sizeToFit]; 45 | CGRect frame = self.titleLabel.frame; 46 | if (frame.size.width > 260) { 47 | frame.size.width = 260; 48 | self.titleLabel.frame = frame; 49 | } 50 | 51 | // Calculate the notice view height 52 | float noticeViewHeight = 40.0; 53 | if (numberOfLines > 1) { 54 | noticeViewHeight += (numberOfLines - 1) * messageLineHeight; 55 | } 56 | 57 | // Make sure we hide completely the view, including its shadow 58 | float hiddenYOrigin = self.slidingMode == WBNoticeViewSlidingModeDown ? -noticeViewHeight - 20.0: self.view.bounds.size.height; 59 | 60 | // Make and add the notice view 61 | self.gradientView = [[WBGrayGradientView alloc] initWithFrame:CGRectMake(0.0, hiddenYOrigin, viewWidth, 32)]; 62 | [self.view addSubview:self.gradientView]; 63 | 64 | // Center the message in the middle of the notice 65 | frame = self.titleLabel.frame; 66 | frame.origin.x = (self.gradientView.frame.size.width - frame.size.width) / 2; 67 | self.titleLabel.frame = frame; 68 | 69 | // Make and add the icon view 70 | UIImageView *iconView = nil; 71 | CGFloat labelLeftPos = self.titleLabel.frame.origin.x; 72 | iconView = [[UIImageView alloc]initWithFrame:CGRectMake(labelLeftPos - 25, 9.0, 18, 13.0)]; 73 | iconView.image = [UIImage imageWithContentsOfFile:noticeIconImageName]; 74 | iconView.contentMode = UIViewContentModeScaleAspectFit; 75 | iconView.alpha = 0.8; 76 | [self.gradientView addSubview:iconView]; 77 | 78 | // Add the title label 79 | [self.gradientView addSubview:self.titleLabel]; 80 | 81 | // Add the drop shadow to the notice view 82 | CALayer *noticeLayer = self.gradientView.layer; 83 | noticeLayer.shadowColor = [[UIColor blackColor]CGColor]; 84 | noticeLayer.shadowOffset = CGSizeMake(0.0, 3); 85 | noticeLayer.shadowOpacity = 0.50; 86 | noticeLayer.masksToBounds = NO; 87 | noticeLayer.shouldRasterize = YES; 88 | 89 | self.hiddenYOrigin = hiddenYOrigin; 90 | 91 | [self displayNotice]; 92 | } 93 | 94 | @end 95 | -------------------------------------------------------------------------------- /NoticeView/WBNoticeView/WBSuccessNoticeView.h: -------------------------------------------------------------------------------- 1 | // 2 | // WBSuccessNoticeView.h 3 | // NoticeView 4 | // 5 | // Created by Tito Ciuro on 5/25/12. 6 | // Copyright (c) 2012 Tito Ciuro. All rights reserved. 7 | // 8 | 9 | #import "WBNoticeView.h" 10 | 11 | /** 12 | The `WBSuccessNoticeView` class is a `WBNoticeView` subclass suitable for displaying an informational success message to a user. The notice is presented on a blue gradient background with a checkmark icon on the left hand side of the notice. It supports the display of a title only. 13 | */ 14 | @interface WBSuccessNoticeView : WBNoticeView 15 | 16 | ///------------------------------- 17 | /// @name Creating a Success Notice 18 | ///------------------------------- 19 | 20 | /** 21 | Creates and returns a success notice in the given view with the specified title. 22 | 23 | @param view The view to display the notice in. 24 | @param title The title of the notice. 25 | @return The newly created success notice object. 26 | */ 27 | + (WBSuccessNoticeView *)successNoticeInView:(UIView *)view title:(NSString *)title; 28 | 29 | @end 30 | -------------------------------------------------------------------------------- /NoticeView/WBNoticeView/WBSuccessNoticeView.m: -------------------------------------------------------------------------------- 1 | // 2 | // WBSuccessNoticeView.m 3 | // NoticeView 4 | // 5 | // Created by Tito Ciuro on 5/25/12. 6 | // Copyright (c) 2012 Tito Ciuro. All rights reserved. 7 | // 8 | 9 | #import "WBSuccessNoticeView.h" 10 | #import "WBNoticeView+ForSubclassEyesOnly.h" 11 | #import "WBBlueGradientView.h" 12 | 13 | @implementation WBSuccessNoticeView 14 | 15 | + (WBSuccessNoticeView *)successNoticeInView:(UIView *)view title:(NSString *)title 16 | { 17 | WBSuccessNoticeView *notice = [[WBSuccessNoticeView alloc]initWithView:view title:title]; 18 | 19 | notice.sticky = NO; 20 | 21 | return notice; 22 | } 23 | 24 | - (void)show 25 | { 26 | // Obtain the screen width 27 | CGFloat viewWidth = self.view.bounds.size.width; 28 | 29 | // Locate the images 30 | NSString *path = [[[NSBundle mainBundle]resourcePath]stringByAppendingPathComponent:@"NoticeView.bundle"]; 31 | NSString *noticeIconImageName = [path stringByAppendingPathComponent:@"notice_success_icon.png"]; 32 | 33 | NSInteger numberOfLines = 1; 34 | CGFloat messageLineHeight = 30.0; 35 | 36 | // Make and add the title label 37 | float titleYOrigin = 18.0; 38 | self.titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(55.0 + (self.contentInset.left), titleYOrigin + (self.contentInset.top), viewWidth - 70.0 - (self.contentInset.right+self.contentInset.left) , 16.0)]; 39 | self.titleLabel.textColor = [UIColor whiteColor]; 40 | self.titleLabel.shadowOffset = CGSizeMake(0.0, -1.0); 41 | self.titleLabel.shadowColor = [UIColor blackColor]; 42 | self.titleLabel.font = [UIFont boldSystemFontOfSize:14.0]; 43 | self.titleLabel.backgroundColor = [UIColor clearColor]; 44 | self.titleLabel.text = self.title; 45 | 46 | // Calculate the notice view height 47 | float noticeViewHeight = 40.0; 48 | if (numberOfLines > 1) { 49 | noticeViewHeight += (numberOfLines - 1) * messageLineHeight; 50 | } 51 | 52 | // Make sure we hide completely the view, including its shadow 53 | float hiddenYOrigin = self.slidingMode == WBNoticeViewSlidingModeDown ? -noticeViewHeight - 20.0: self.view.bounds.size.height; 54 | 55 | // Make and add the notice view 56 | self.gradientView = [[WBBlueGradientView alloc] initWithFrame:CGRectMake(0.0, hiddenYOrigin, viewWidth, noticeViewHeight + 10.0)]; 57 | [self.view addSubview:self.gradientView]; 58 | 59 | // Make and add the icon view 60 | UIImageView *iconView = [[UIImageView alloc]initWithFrame:CGRectMake(10.0, 10.0, 20.0, 30.0)]; 61 | iconView.image = [UIImage imageWithContentsOfFile:noticeIconImageName]; 62 | iconView.contentMode = UIViewContentModeScaleAspectFit; 63 | iconView.alpha = 0.8; 64 | [self.gradientView addSubview:iconView]; 65 | 66 | // Add the title label 67 | [self.gradientView addSubview:self.titleLabel]; 68 | 69 | // Add the drop shadow to the notice view 70 | CALayer *noticeLayer = self.gradientView.layer; 71 | noticeLayer.shadowColor = [[UIColor blackColor]CGColor]; 72 | noticeLayer.shadowOffset = CGSizeMake(0.0, 3); 73 | noticeLayer.shadowOpacity = 0.50; 74 | noticeLayer.masksToBounds = NO; 75 | noticeLayer.shouldRasterize = YES; 76 | 77 | self.hiddenYOrigin = hiddenYOrigin; 78 | 79 | [self displayNotice]; 80 | } 81 | 82 | @end 83 | -------------------------------------------------------------------------------- /NoticeView/WBScrollViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // WBScrollViewController.h 3 | // NoticeView 4 | // 5 | // Created by Johannes Plunien on 3/31/13. 6 | // Copyright (c) 2013 Tito Ciuro. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface WBScrollViewController : UITableViewController 12 | 13 | @property (nonatomic, assign) BOOL floating; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /NoticeView/WBScrollViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // WBScrollViewController.m 3 | // NoticeView 4 | // 5 | // Created by Johannes Plunien on 3/31/13. 6 | // Copyright (c) 2013 Tito Ciuro. All rights reserved. 7 | // 8 | 9 | #import "WBErrorNoticeView.h" 10 | #import "WBScrollViewController.h" 11 | 12 | @interface WBScrollViewController () 13 | 14 | @end 15 | 16 | @implementation WBScrollViewController 17 | 18 | - (void)viewDidAppear:(BOOL)animated 19 | { 20 | [super viewDidAppear:animated]; 21 | 22 | [self scrollToRow:2]; 23 | [self performBlock:^{ 24 | [self showNoticeView]; 25 | [self performBlock:^{ 26 | [self scrollToRow:1]; 27 | } afterDelay:1.0]; 28 | } afterDelay:0.31]; 29 | } 30 | 31 | - (void)performBlock:(void (^)(void))block afterDelay:(NSTimeInterval)delay 32 | { 33 | int64_t delta = (int64_t)(1.0e9 * delay); 34 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delta), dispatch_get_main_queue(), block); 35 | } 36 | 37 | - (void)scrollToRow:(NSInteger)row 38 | { 39 | NSIndexPath *indexPath = [NSIndexPath indexPathForRow:row inSection:0]; 40 | [self.tableView scrollToRowAtIndexPath:indexPath 41 | atScrollPosition:UITableViewScrollPositionTop 42 | animated:YES]; 43 | } 44 | 45 | - (void)showNoticeView 46 | { 47 | WBErrorNoticeView *notice = [WBErrorNoticeView errorNoticeInView:self.view 48 | title:@"Network Error" 49 | message:@"Check your network connection."]; 50 | notice.sticky = YES; 51 | notice.tapToDismissEnabled = YES; 52 | notice.floating = self.floating; 53 | [notice show]; 54 | } 55 | 56 | @end 57 | -------------------------------------------------------------------------------- /NoticeView/WBViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // WBViewController.h 3 | // NoticeView 4 | // 5 | // Created by Tito Ciuro on 5/15/12. 6 | // 7 | // Copyright (c) 2012 Webbo, L.L.C. All rights reserved. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE 26 | // 27 | 28 | #import 29 | 30 | @interface WBViewController : UIViewController 31 | 32 | @property(nonatomic, strong) IBOutlet UIImageView *headerView; 33 | 34 | - (IBAction)showSmallErrorNotice:(id)sender; 35 | - (IBAction)showLargeErrorNotice:(id)sender; 36 | - (IBAction)showSmallSuccessNotice:(id)sender; 37 | - (IBAction)showSmallErrorNoticeBelow:(id)sender; 38 | - (IBAction)showLargeErrorNoticeBelow:(id)sender; 39 | - (IBAction)showSmallSuccessNoticeBelow:(id)sender; 40 | - (IBAction)showSmallStickyNoticeBelow:(id)sender; 41 | - (IBAction)showSmallErrorNoticeAndPush:(id)sender; 42 | - (IBAction)showStickyNoticeAndPush:(id)sender; 43 | 44 | 45 | - (IBAction)dismissStickyNotice:(id)sender; 46 | 47 | @end 48 | -------------------------------------------------------------------------------- /NoticeView/WBViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // WBViewController.m 3 | // NoticeView 4 | // 5 | // Created by Tito Ciuro on 5/15/12. 6 | // 7 | // Copyright (c) 2012 Webbo, L.L.C. All rights reserved. 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in 17 | // all copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | // THE SOFTWARE 26 | // 27 | 28 | #import "WBScrollViewController.h" 29 | #import "WBViewController.h" 30 | #import "WBNoticeView.h" 31 | #import "WBErrorNoticeView.h" 32 | #import "WBSuccessNoticeView.h" 33 | #import "WBStickyNoticeView.h" 34 | #import "NSOperationQueue+WBNoticeExtensions.h" 35 | 36 | @interface WBViewController () 37 | @property (nonatomic, readwrite, weak) WBNoticeView *currentNoticeView; 38 | @end 39 | 40 | @implementation WBViewController 41 | 42 | @synthesize headerView; 43 | 44 | - (void)viewDidLoad 45 | { 46 | UIImage *bkgImage = [UIImage imageNamed:@"Default.png"]; 47 | self.view.backgroundColor = [UIColor colorWithPatternImage:bkgImage]; 48 | self.view.contentMode = UIViewContentModeScaleAspectFill; 49 | [self.view setOpaque:YES]; 50 | } 51 | 52 | - (void)viewWillAppear:(BOOL)animated 53 | { 54 | [super viewWillAppear:animated]; 55 | } 56 | 57 | - (void)viewWillDisappear:(BOOL)animated 58 | { 59 | [super viewDidUnload]; 60 | } 61 | 62 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation 63 | { 64 | return YES; 65 | } 66 | 67 | #pragma mark - Action Methods 68 | 69 | - (IBAction)showSmallErrorNotice:(id)sender 70 | { 71 | WBErrorNoticeView *notice = [WBErrorNoticeView errorNoticeInView:self.view title:@"Network Error" message:@"Check your network connection."]; 72 | [notice show]; 73 | } 74 | 75 | - (IBAction)showQueuedErrorNotice:(id)sender 76 | { 77 | WBErrorNoticeView *notice = [WBErrorNoticeView errorNoticeInView:self.view title:@"Queued Network Error" message:@"Check your network connection."]; 78 | WBNoticeOperation *operation = [NSOperationQueue addNoticeView:notice filterDuplicates:YES]; 79 | __block WBNoticeOperation *weakOperation = operation; 80 | operation.completionBlock = ^{ 81 | NSLog(@"Queued notice operation dismissed! Interactively: %@", weakOperation.dismissedInteractively ? @"YES" : @"NO"); 82 | }; 83 | } 84 | 85 | - (IBAction)showLargeErrorNotice:(id)sender 86 | { 87 | WBErrorNoticeView *notice = [WBErrorNoticeView errorNoticeInView:self.view title:@"Network Error" message:@"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book."]; 88 | [notice setDismissalBlock:^(BOOL dismissedInteractively) { 89 | NSLog(@"showLargeErrorNotice dismissed!"); 90 | }]; 91 | [notice show]; 92 | } 93 | 94 | - (IBAction)showSmallSuccessNotice:(id)sender 95 | { 96 | WBSuccessNoticeView *notice = [WBSuccessNoticeView successNoticeInView:self.view title:@"Link Saved Successfully"]; 97 | [notice show]; 98 | } 99 | 100 | - (IBAction)showSmallStickyNotice:(id)sender 101 | { 102 | WBStickyNoticeView *notice = [WBStickyNoticeView stickyNoticeInView:self.view title:@"7 New Tweets."]; 103 | [notice setDismissalBlock:^(BOOL dismissedInteractively) { 104 | NSLog(@"showSmallStickyNotice dismissed!"); 105 | }]; 106 | [notice show]; 107 | } 108 | 109 | - (IBAction)showSmallErrorNoticeBelow:(id)sender 110 | { 111 | WBErrorNoticeView *notice = [WBErrorNoticeView errorNoticeInView:self.view title:@"Network Error" message:@"Check your network connection."]; 112 | 113 | notice.alpha = 0.8; 114 | notice.originY = self.headerView.frame.size.height; 115 | 116 | [notice show]; 117 | } 118 | 119 | - (IBAction)showLargeErrorNoticeBelow:(id)sender 120 | { 121 | WBErrorNoticeView *notice = [WBErrorNoticeView errorNoticeInView:self.view title:@"Network Error" message:@"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book."]; 122 | 123 | notice.alpha = 0.8; 124 | notice.originY = self.headerView.frame.size.height; 125 | 126 | [notice show]; 127 | } 128 | 129 | - (IBAction)showSmallSuccessNoticeBelow:(id)sender 130 | { 131 | WBSuccessNoticeView *notice = [WBSuccessNoticeView successNoticeInView:self.view title:@"Link Saved Successfully"]; 132 | 133 | notice.alpha = 0.8; 134 | notice.originY = self.headerView.frame.size.height; 135 | 136 | [notice show]; 137 | } 138 | 139 | - (IBAction)showSmallStickyNoticeBelow:(id)sender 140 | { 141 | WBStickyNoticeView *notice = [WBStickyNoticeView stickyNoticeInView:self.view title:@"7 New Tweets arrived somewhere from the intertubes."]; 142 | 143 | notice.originY = self.headerView.frame.size.height; 144 | 145 | [notice show]; 146 | } 147 | 148 | - (IBAction)showSmallErrorNoticeAndPush:(id)sender 149 | { 150 | WBErrorNoticeView *notice = [WBErrorNoticeView errorNoticeInView:self.view title:@"Network Error" message:@"Check your network connection."]; 151 | [notice show]; 152 | 153 | [self.navigationController pushViewController: [[WBViewController alloc] init] animated:YES]; 154 | } 155 | 156 | - (IBAction)showStickyNoticeAndPush:(id)sender 157 | { 158 | WBStickyNoticeView *notice = [WBStickyNoticeView stickyNoticeInView:self.view title:@"7 New Tweets arrived somewhere from the intertubes."]; 159 | [notice show]; 160 | 161 | [self.navigationController pushViewController: [[WBViewController alloc] init] animated:YES]; 162 | } 163 | 164 | - (IBAction)showStickyError:(id)sender 165 | { 166 | if (nil == self.currentNoticeView) { 167 | WBErrorNoticeView *notice = [WBErrorNoticeView errorNoticeInView:self.view title:@"Network Error" message:@"Check your network connection."]; 168 | notice.sticky = YES; 169 | [notice show]; 170 | self.currentNoticeView = notice; 171 | } 172 | } 173 | 174 | - (IBAction)showStickyErrorNoticeAndPush:(id)sender 175 | { 176 | WBErrorNoticeView *notice = [WBErrorNoticeView errorNoticeInView:self.view title:@"Network Error" message:@"Check your network connection."]; 177 | notice.sticky = YES; 178 | [notice show]; 179 | 180 | [self.navigationController pushViewController: [[WBViewController alloc] init] animated:YES]; 181 | } 182 | 183 | #pragma mark - 184 | 185 | - (IBAction)dismissStickyNotice:(id)sender 186 | { 187 | [self.currentNoticeView dismissNotice]; 188 | } 189 | 190 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 191 | if ([segue.identifier isEqualToString:@"Floating"]) { 192 | WBScrollViewController *controller = [segue destinationViewController]; 193 | controller.floating = YES; 194 | } 195 | } 196 | 197 | @end 198 | -------------------------------------------------------------------------------- /NoticeView/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /NoticeView/en.lproj/MainStoryboard.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 31 | 46 | 61 | 62 | 63 | 64 | 65 | 80 | 95 | 110 | 125 | 140 | 155 | 170 | 185 | 200 | 215 | 230 | 245 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | -------------------------------------------------------------------------------- /NoticeView/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // NoticeView 4 | // 5 | // Created by Tito Ciuro on 5/15/12. 6 | // Copyright (c) 2012 Tito Ciuro. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "WBAppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([WBAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /NoticeViewiPad/NoticeViewiPad.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 7477C26D1570573300D0E620 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7477C26C1570573300D0E620 /* UIKit.framework */; }; 11 | 7477C26F1570573300D0E620 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7477C26E1570573300D0E620 /* Foundation.framework */; }; 12 | 7477C2711570573300D0E620 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7477C2701570573300D0E620 /* CoreGraphics.framework */; }; 13 | 7477C2771570573300D0E620 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7477C2751570573300D0E620 /* InfoPlist.strings */; }; 14 | 7477C2791570573300D0E620 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 7477C2781570573300D0E620 /* main.m */; }; 15 | 7477C27D1570573300D0E620 /* WBAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7477C27C1570573300D0E620 /* WBAppDelegate.m */; }; 16 | 7477C2801570573300D0E620 /* MainStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7477C27E1570573300D0E620 /* MainStoryboard.storyboard */; }; 17 | 7477C2831570573300D0E620 /* WBMasterViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7477C2821570573300D0E620 /* WBMasterViewController.m */; }; 18 | 7477C2861570573300D0E620 /* WBDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7477C2851570573300D0E620 /* WBDetailViewController.m */; }; 19 | 74D33BFD157057A000225A76 /* NoticeView.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 74D33BF8157057A000225A76 /* NoticeView.bundle */; }; 20 | 74D33BFE157057A000225A76 /* UILabel+WBExtensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 74D33BFA157057A000225A76 /* UILabel+WBExtensions.m */; }; 21 | 74D33BFF157057A000225A76 /* WBNoticeView.m in Sources */ = {isa = PBXBuildFile; fileRef = 74D33BFC157057A000225A76 /* WBNoticeView.m */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXFileReference section */ 25 | 7477C2681570573300D0E620 /* NoticeViewiPad.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NoticeViewiPad.app; sourceTree = BUILT_PRODUCTS_DIR; }; 26 | 7477C26C1570573300D0E620 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 27 | 7477C26E1570573300D0E620 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 28 | 7477C2701570573300D0E620 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 29 | 7477C2741570573300D0E620 /* NoticeViewiPad-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "NoticeViewiPad-Info.plist"; sourceTree = ""; }; 30 | 7477C2761570573300D0E620 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 31 | 7477C2781570573300D0E620 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 32 | 7477C27A1570573300D0E620 /* NoticeViewiPad-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NoticeViewiPad-Prefix.pch"; sourceTree = ""; }; 33 | 7477C27B1570573300D0E620 /* WBAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WBAppDelegate.h; sourceTree = ""; }; 34 | 7477C27C1570573300D0E620 /* WBAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WBAppDelegate.m; sourceTree = ""; }; 35 | 7477C27F1570573300D0E620 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/MainStoryboard.storyboard; sourceTree = ""; }; 36 | 7477C2811570573300D0E620 /* WBMasterViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WBMasterViewController.h; sourceTree = ""; }; 37 | 7477C2821570573300D0E620 /* WBMasterViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WBMasterViewController.m; sourceTree = ""; }; 38 | 7477C2841570573300D0E620 /* WBDetailViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WBDetailViewController.h; sourceTree = ""; }; 39 | 7477C2851570573300D0E620 /* WBDetailViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = WBDetailViewController.m; sourceTree = ""; }; 40 | 74D33BF8157057A000225A76 /* NoticeView.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = NoticeView.bundle; sourceTree = ""; }; 41 | 74D33BF9157057A000225A76 /* UILabel+WBExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UILabel+WBExtensions.h"; sourceTree = ""; }; 42 | 74D33BFA157057A000225A76 /* UILabel+WBExtensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UILabel+WBExtensions.m"; sourceTree = ""; }; 43 | 74D33BFB157057A000225A76 /* WBNoticeView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WBNoticeView.h; sourceTree = ""; }; 44 | 74D33BFC157057A000225A76 /* WBNoticeView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WBNoticeView.m; sourceTree = ""; }; 45 | /* End PBXFileReference section */ 46 | 47 | /* Begin PBXFrameworksBuildPhase section */ 48 | 7477C2651570573300D0E620 /* Frameworks */ = { 49 | isa = PBXFrameworksBuildPhase; 50 | buildActionMask = 2147483647; 51 | files = ( 52 | 7477C26D1570573300D0E620 /* UIKit.framework in Frameworks */, 53 | 7477C26F1570573300D0E620 /* Foundation.framework in Frameworks */, 54 | 7477C2711570573300D0E620 /* CoreGraphics.framework in Frameworks */, 55 | ); 56 | runOnlyForDeploymentPostprocessing = 0; 57 | }; 58 | /* End PBXFrameworksBuildPhase section */ 59 | 60 | /* Begin PBXGroup section */ 61 | 7477C25D1570573300D0E620 = { 62 | isa = PBXGroup; 63 | children = ( 64 | 7477C2721570573300D0E620 /* NoticeViewiPad */, 65 | 7477C26B1570573300D0E620 /* Frameworks */, 66 | 7477C2691570573300D0E620 /* Products */, 67 | ); 68 | sourceTree = ""; 69 | }; 70 | 7477C2691570573300D0E620 /* Products */ = { 71 | isa = PBXGroup; 72 | children = ( 73 | 7477C2681570573300D0E620 /* NoticeViewiPad.app */, 74 | ); 75 | name = Products; 76 | sourceTree = ""; 77 | }; 78 | 7477C26B1570573300D0E620 /* Frameworks */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 7477C26C1570573300D0E620 /* UIKit.framework */, 82 | 7477C26E1570573300D0E620 /* Foundation.framework */, 83 | 7477C2701570573300D0E620 /* CoreGraphics.framework */, 84 | ); 85 | name = Frameworks; 86 | sourceTree = ""; 87 | }; 88 | 7477C2721570573300D0E620 /* NoticeViewiPad */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | 7477C27E1570573300D0E620 /* MainStoryboard.storyboard */, 92 | 7477C27B1570573300D0E620 /* WBAppDelegate.h */, 93 | 7477C27C1570573300D0E620 /* WBAppDelegate.m */, 94 | 7477C2811570573300D0E620 /* WBMasterViewController.h */, 95 | 7477C2821570573300D0E620 /* WBMasterViewController.m */, 96 | 7477C2841570573300D0E620 /* WBDetailViewController.h */, 97 | 7477C2851570573300D0E620 /* WBDetailViewController.m */, 98 | 74D33BF7157057A000225A76 /* WBNoticeView */, 99 | 7477C2731570573300D0E620 /* Supporting Files */, 100 | ); 101 | path = NoticeViewiPad; 102 | sourceTree = ""; 103 | }; 104 | 7477C2731570573300D0E620 /* Supporting Files */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 7477C2741570573300D0E620 /* NoticeViewiPad-Info.plist */, 108 | 7477C2751570573300D0E620 /* InfoPlist.strings */, 109 | 7477C2781570573300D0E620 /* main.m */, 110 | 7477C27A1570573300D0E620 /* NoticeViewiPad-Prefix.pch */, 111 | ); 112 | name = "Supporting Files"; 113 | sourceTree = ""; 114 | }; 115 | 74D33BF7157057A000225A76 /* WBNoticeView */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | 74D33BF8157057A000225A76 /* NoticeView.bundle */, 119 | 74D33BF9157057A000225A76 /* UILabel+WBExtensions.h */, 120 | 74D33BFA157057A000225A76 /* UILabel+WBExtensions.m */, 121 | 74D33BFB157057A000225A76 /* WBNoticeView.h */, 122 | 74D33BFC157057A000225A76 /* WBNoticeView.m */, 123 | ); 124 | name = WBNoticeView; 125 | path = ../../NoticeView/WBNoticeView; 126 | sourceTree = ""; 127 | }; 128 | /* End PBXGroup section */ 129 | 130 | /* Begin PBXNativeTarget section */ 131 | 7477C2671570573300D0E620 /* NoticeViewiPad */ = { 132 | isa = PBXNativeTarget; 133 | buildConfigurationList = 7477C2891570573300D0E620 /* Build configuration list for PBXNativeTarget "NoticeViewiPad" */; 134 | buildPhases = ( 135 | 7477C2641570573300D0E620 /* Sources */, 136 | 7477C2651570573300D0E620 /* Frameworks */, 137 | 7477C2661570573300D0E620 /* Resources */, 138 | ); 139 | buildRules = ( 140 | ); 141 | dependencies = ( 142 | ); 143 | name = NoticeViewiPad; 144 | productName = NoticeViewiPad; 145 | productReference = 7477C2681570573300D0E620 /* NoticeViewiPad.app */; 146 | productType = "com.apple.product-type.application"; 147 | }; 148 | /* End PBXNativeTarget section */ 149 | 150 | /* Begin PBXProject section */ 151 | 7477C25F1570573300D0E620 /* Project object */ = { 152 | isa = PBXProject; 153 | attributes = { 154 | CLASSPREFIX = WB; 155 | LastUpgradeCheck = 0430; 156 | ORGANIZATIONNAME = "Webbo, LLC"; 157 | }; 158 | buildConfigurationList = 7477C2621570573300D0E620 /* Build configuration list for PBXProject "NoticeViewiPad" */; 159 | compatibilityVersion = "Xcode 3.2"; 160 | developmentRegion = English; 161 | hasScannedForEncodings = 0; 162 | knownRegions = ( 163 | en, 164 | ); 165 | mainGroup = 7477C25D1570573300D0E620; 166 | productRefGroup = 7477C2691570573300D0E620 /* Products */; 167 | projectDirPath = ""; 168 | projectRoot = ""; 169 | targets = ( 170 | 7477C2671570573300D0E620 /* NoticeViewiPad */, 171 | ); 172 | }; 173 | /* End PBXProject section */ 174 | 175 | /* Begin PBXResourcesBuildPhase section */ 176 | 7477C2661570573300D0E620 /* Resources */ = { 177 | isa = PBXResourcesBuildPhase; 178 | buildActionMask = 2147483647; 179 | files = ( 180 | 7477C2771570573300D0E620 /* InfoPlist.strings in Resources */, 181 | 7477C2801570573300D0E620 /* MainStoryboard.storyboard in Resources */, 182 | 74D33BFD157057A000225A76 /* NoticeView.bundle in Resources */, 183 | ); 184 | runOnlyForDeploymentPostprocessing = 0; 185 | }; 186 | /* End PBXResourcesBuildPhase section */ 187 | 188 | /* Begin PBXSourcesBuildPhase section */ 189 | 7477C2641570573300D0E620 /* Sources */ = { 190 | isa = PBXSourcesBuildPhase; 191 | buildActionMask = 2147483647; 192 | files = ( 193 | 7477C2791570573300D0E620 /* main.m in Sources */, 194 | 7477C27D1570573300D0E620 /* WBAppDelegate.m in Sources */, 195 | 7477C2831570573300D0E620 /* WBMasterViewController.m in Sources */, 196 | 7477C2861570573300D0E620 /* WBDetailViewController.m in Sources */, 197 | 74D33BFE157057A000225A76 /* UILabel+WBExtensions.m in Sources */, 198 | 74D33BFF157057A000225A76 /* WBNoticeView.m in Sources */, 199 | ); 200 | runOnlyForDeploymentPostprocessing = 0; 201 | }; 202 | /* End PBXSourcesBuildPhase section */ 203 | 204 | /* Begin PBXVariantGroup section */ 205 | 7477C2751570573300D0E620 /* InfoPlist.strings */ = { 206 | isa = PBXVariantGroup; 207 | children = ( 208 | 7477C2761570573300D0E620 /* en */, 209 | ); 210 | name = InfoPlist.strings; 211 | sourceTree = ""; 212 | }; 213 | 7477C27E1570573300D0E620 /* MainStoryboard.storyboard */ = { 214 | isa = PBXVariantGroup; 215 | children = ( 216 | 7477C27F1570573300D0E620 /* en */, 217 | ); 218 | name = MainStoryboard.storyboard; 219 | sourceTree = ""; 220 | }; 221 | /* End PBXVariantGroup section */ 222 | 223 | /* Begin XCBuildConfiguration section */ 224 | 7477C2871570573300D0E620 /* Debug */ = { 225 | isa = XCBuildConfiguration; 226 | buildSettings = { 227 | ALWAYS_SEARCH_USER_PATHS = NO; 228 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 229 | CLANG_ENABLE_OBJC_ARC = YES; 230 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 231 | COPY_PHASE_STRIP = NO; 232 | GCC_C_LANGUAGE_STANDARD = gnu99; 233 | GCC_DYNAMIC_NO_PIC = NO; 234 | GCC_OPTIMIZATION_LEVEL = 0; 235 | GCC_PREPROCESSOR_DEFINITIONS = ( 236 | "DEBUG=1", 237 | "$(inherited)", 238 | ); 239 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 240 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 241 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 242 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 243 | GCC_WARN_UNUSED_VARIABLE = YES; 244 | IPHONEOS_DEPLOYMENT_TARGET = 5.1; 245 | SDKROOT = iphoneos; 246 | TARGETED_DEVICE_FAMILY = 2; 247 | }; 248 | name = Debug; 249 | }; 250 | 7477C2881570573300D0E620 /* Release */ = { 251 | isa = XCBuildConfiguration; 252 | buildSettings = { 253 | ALWAYS_SEARCH_USER_PATHS = NO; 254 | ARCHS = "$(ARCHS_STANDARD_32_BIT)"; 255 | CLANG_ENABLE_OBJC_ARC = YES; 256 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 257 | COPY_PHASE_STRIP = YES; 258 | GCC_C_LANGUAGE_STANDARD = gnu99; 259 | GCC_VERSION = com.apple.compilers.llvm.clang.1_0; 260 | GCC_WARN_ABOUT_RETURN_TYPE = YES; 261 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 262 | GCC_WARN_UNUSED_VARIABLE = YES; 263 | IPHONEOS_DEPLOYMENT_TARGET = 5.1; 264 | OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1"; 265 | SDKROOT = iphoneos; 266 | TARGETED_DEVICE_FAMILY = 2; 267 | VALIDATE_PRODUCT = YES; 268 | }; 269 | name = Release; 270 | }; 271 | 7477C28A1570573300D0E620 /* Debug */ = { 272 | isa = XCBuildConfiguration; 273 | buildSettings = { 274 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 275 | GCC_PREFIX_HEADER = "NoticeViewiPad/NoticeViewiPad-Prefix.pch"; 276 | INFOPLIST_FILE = "NoticeViewiPad/NoticeViewiPad-Info.plist"; 277 | PRODUCT_NAME = "$(TARGET_NAME)"; 278 | WRAPPER_EXTENSION = app; 279 | }; 280 | name = Debug; 281 | }; 282 | 7477C28B1570573300D0E620 /* Release */ = { 283 | isa = XCBuildConfiguration; 284 | buildSettings = { 285 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 286 | GCC_PREFIX_HEADER = "NoticeViewiPad/NoticeViewiPad-Prefix.pch"; 287 | INFOPLIST_FILE = "NoticeViewiPad/NoticeViewiPad-Info.plist"; 288 | PRODUCT_NAME = "$(TARGET_NAME)"; 289 | WRAPPER_EXTENSION = app; 290 | }; 291 | name = Release; 292 | }; 293 | /* End XCBuildConfiguration section */ 294 | 295 | /* Begin XCConfigurationList section */ 296 | 7477C2621570573300D0E620 /* Build configuration list for PBXProject "NoticeViewiPad" */ = { 297 | isa = XCConfigurationList; 298 | buildConfigurations = ( 299 | 7477C2871570573300D0E620 /* Debug */, 300 | 7477C2881570573300D0E620 /* Release */, 301 | ); 302 | defaultConfigurationIsVisible = 0; 303 | defaultConfigurationName = Release; 304 | }; 305 | 7477C2891570573300D0E620 /* Build configuration list for PBXNativeTarget "NoticeViewiPad" */ = { 306 | isa = XCConfigurationList; 307 | buildConfigurations = ( 308 | 7477C28A1570573300D0E620 /* Debug */, 309 | 7477C28B1570573300D0E620 /* Release */, 310 | ); 311 | defaultConfigurationIsVisible = 0; 312 | defaultConfigurationName = Release; 313 | }; 314 | /* End XCConfigurationList section */ 315 | }; 316 | rootObject = 7477C25F1570573300D0E620 /* Project object */; 317 | } 318 | -------------------------------------------------------------------------------- /NoticeViewiPad/NoticeViewiPad.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /NoticeViewiPad/NoticeViewiPad/NoticeViewiPad-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.webbo.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIMainStoryboardFile 28 | MainStoryboard 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /NoticeViewiPad/NoticeViewiPad/NoticeViewiPad-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'NoticeViewiPad' target in the 'NoticeViewiPad' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_5_0 8 | #warning "This project uses features only available in iOS SDK 5.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /NoticeViewiPad/NoticeViewiPad/WBAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // WBAppDelegate.h 3 | // NoticeViewiPad 4 | // 5 | // Created by Tito Ciuro on 5/25/12. 6 | // Copyright (c) 2012 Webbo, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface WBAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /NoticeViewiPad/NoticeViewiPad/WBAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // WBAppDelegate.m 3 | // NoticeViewiPad 4 | // 5 | // Created by Tito Ciuro on 5/25/12. 6 | // Copyright (c) 2012 Webbo, LLC. All rights reserved. 7 | // 8 | 9 | #import "WBAppDelegate.h" 10 | 11 | @implementation WBAppDelegate 12 | 13 | @synthesize window = _window; 14 | 15 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 16 | { 17 | // Override point for customization after application launch. 18 | UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController; 19 | UINavigationController *navigationController = [splitViewController.viewControllers lastObject]; 20 | splitViewController.delegate = (id)navigationController.topViewController; 21 | return YES; 22 | } 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application 25 | { 26 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 27 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 28 | } 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application 31 | { 32 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 33 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 34 | } 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application 37 | { 38 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 39 | } 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application 42 | { 43 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 44 | } 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application 47 | { 48 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 49 | } 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /NoticeViewiPad/NoticeViewiPad/WBDetailViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // WBDetailViewController.h 3 | // NoticeViewiPad 4 | // 5 | // Created by Tito Ciuro on 5/25/12. 6 | // Copyright (c) 2012 Webbo, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface WBDetailViewController : UIViewController 12 | 13 | @property (strong, nonatomic) id detailItem; 14 | 15 | - (IBAction)showNotice:(id)sender; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /NoticeViewiPad/NoticeViewiPad/WBDetailViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // WBDetailViewController.m 3 | // NoticeViewiPad 4 | // 5 | // Created by Tito Ciuro on 5/25/12. 6 | // Copyright (c) 2012 Webbo, LLC. All rights reserved. 7 | // 8 | 9 | #import "WBDetailViewController.h" 10 | #import "WBNoticeView.h" 11 | 12 | @interface WBDetailViewController () 13 | @property (strong, nonatomic) UIPopoverController *masterPopoverController; 14 | - (void)configureView; 15 | @end 16 | 17 | @implementation WBDetailViewController 18 | 19 | @synthesize detailItem = _detailItem; 20 | @synthesize masterPopoverController = _masterPopoverController; 21 | 22 | #pragma mark - Managing the detail item 23 | 24 | - (void)setDetailItem:(id)newDetailItem 25 | { 26 | if (_detailItem != newDetailItem) { 27 | _detailItem = newDetailItem; 28 | 29 | // Update the view. 30 | [self configureView]; 31 | } 32 | 33 | if (self.masterPopoverController != nil) { 34 | [self.masterPopoverController dismissPopoverAnimated:YES]; 35 | } 36 | } 37 | 38 | - (void)configureView 39 | { 40 | // Update the user interface for the detail item. 41 | 42 | if (self.detailItem) { 43 | //self.detailDescriptionLabel.text = [self.detailItem description]; 44 | } 45 | } 46 | 47 | - (void)viewDidLoad 48 | { 49 | [super viewDidLoad]; 50 | // Do any additional setup after loading the view, typically from a nib. 51 | [self configureView]; 52 | } 53 | 54 | - (void)viewDidUnload 55 | { 56 | [super viewDidUnload]; 57 | // Release any retained subviews of the main view. 58 | } 59 | 60 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation 61 | { 62 | return YES; 63 | } 64 | 65 | #pragma mark - Split view 66 | 67 | - (void)splitViewController:(UISplitViewController *)splitController willHideViewController:(UIViewController *)viewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)popoverController 68 | { 69 | barButtonItem.title = NSLocalizedString(@"Master", @"Master"); 70 | [self.navigationItem setLeftBarButtonItem:barButtonItem animated:YES]; 71 | self.masterPopoverController = popoverController; 72 | } 73 | 74 | - (void)splitViewController:(UISplitViewController *)splitController willShowViewController:(UIViewController *)viewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem 75 | { 76 | // Called when the view is shown again in the split view, invalidating the button and popover controller. 77 | [self.navigationItem setLeftBarButtonItem:nil animated:YES]; 78 | self.masterPopoverController = nil; 79 | } 80 | 81 | #pragma mark - Action Methods 82 | 83 | - (IBAction)showNotice:(id)sender 84 | { 85 | WBNoticeView *nm = [WBNoticeView defaultManager]; 86 | [nm showErrorNoticeInView:self.view title:@"Network Error" message:@"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book."]; 87 | } 88 | 89 | @end 90 | -------------------------------------------------------------------------------- /NoticeViewiPad/NoticeViewiPad/WBMasterViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // WBMasterViewController.h 3 | // NoticeViewiPad 4 | // 5 | // Created by Tito Ciuro on 5/25/12. 6 | // Copyright (c) 2012 Webbo, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class WBDetailViewController; 12 | 13 | @interface WBMasterViewController : UITableViewController 14 | 15 | @property (strong, nonatomic) WBDetailViewController *detailViewController; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /NoticeViewiPad/NoticeViewiPad/WBMasterViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // WBMasterViewController.m 3 | // NoticeViewiPad 4 | // 5 | // Created by Tito Ciuro on 5/25/12. 6 | // Copyright (c) 2012 Webbo, LLC. All rights reserved. 7 | // 8 | 9 | #import "WBMasterViewController.h" 10 | 11 | #import "WBDetailViewController.h" 12 | 13 | @interface WBMasterViewController () { 14 | NSMutableArray *_objects; 15 | } 16 | @end 17 | 18 | @implementation WBMasterViewController 19 | 20 | @synthesize detailViewController = _detailViewController; 21 | 22 | - (void)awakeFromNib 23 | { 24 | self.clearsSelectionOnViewWillAppear = NO; 25 | self.contentSizeForViewInPopover = CGSizeMake(320.0, 600.0); 26 | [super awakeFromNib]; 27 | } 28 | 29 | - (void)viewDidLoad 30 | { 31 | [super viewDidLoad]; 32 | // Do any additional setup after loading the view, typically from a nib. 33 | self.navigationItem.leftBarButtonItem = self.editButtonItem; 34 | 35 | UIBarButtonItem *addButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(insertNewObject:)]; 36 | self.navigationItem.rightBarButtonItem = addButton; 37 | self.detailViewController = (WBDetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController]; 38 | } 39 | 40 | - (void)viewDidUnload 41 | { 42 | [super viewDidUnload]; 43 | // Release any retained subviews of the main view. 44 | } 45 | 46 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation 47 | { 48 | return YES; 49 | } 50 | 51 | - (void)insertNewObject:(id)sender 52 | { 53 | if (!_objects) { 54 | _objects = [[NSMutableArray alloc] init]; 55 | } 56 | [_objects insertObject:[NSDate date] atIndex:0]; 57 | NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0]; 58 | [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; 59 | } 60 | 61 | #pragma mark - Table View 62 | 63 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 64 | { 65 | return 1; 66 | } 67 | 68 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 69 | { 70 | return _objects.count; 71 | } 72 | 73 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 74 | { 75 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"]; 76 | 77 | NSDate *object = [_objects objectAtIndex:indexPath.row]; 78 | cell.textLabel.text = [object description]; 79 | return cell; 80 | } 81 | 82 | - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath 83 | { 84 | // Return NO if you do not want the specified item to be editable. 85 | return YES; 86 | } 87 | 88 | - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath 89 | { 90 | if (editingStyle == UITableViewCellEditingStyleDelete) { 91 | [_objects removeObjectAtIndex:indexPath.row]; 92 | [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; 93 | } else if (editingStyle == UITableViewCellEditingStyleInsert) { 94 | // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view. 95 | } 96 | } 97 | 98 | /* 99 | // Override to support rearranging the table view. 100 | - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath 101 | { 102 | } 103 | */ 104 | 105 | /* 106 | // Override to support conditional rearranging of the table view. 107 | - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath 108 | { 109 | // Return NO if you do not want the item to be re-orderable. 110 | return YES; 111 | } 112 | */ 113 | 114 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 115 | { 116 | NSDate *object = [_objects objectAtIndex:indexPath.row]; 117 | self.detailViewController.detailItem = object; 118 | } 119 | 120 | @end 121 | -------------------------------------------------------------------------------- /NoticeViewiPad/NoticeViewiPad/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /NoticeViewiPad/NoticeViewiPad/en.lproj/MainStoryboard.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | -------------------------------------------------------------------------------- /NoticeViewiPad/NoticeViewiPad/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // NoticeViewiPad 4 | // 5 | // Created by Tito Ciuro on 5/25/12. 6 | // Copyright (c) 2012 Webbo, LLC. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "WBAppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([WBAppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## NoticeView 2 | 3 | A TweetBot-like notice component for iOS. 4 | 5 |
6 | ![Alt text](http://cloud.github.com/downloads/tciuro/NoticeView/screenshot_2.0.1.png) 7 |
8 |
9 | 10 | ## Usage 11 | 12 | * Drop the WBNoticeView folder in your project 13 | * Add QuartzCore.framework to your project 14 | 15 | ### New in 3.0: Cleaner API, easier customization and more 16 | 17 | First and foremost, please note that in v3.0 the API has changed. Instead of deprecating and complicating the API, it was decided to replace some of the methods. NoticeView is a tiny library, so the impact should be minimal and the code easy to adapt. 18 | 19 | Why the change? Prior to v3.0, it was fairly difficult to customize the look and feel given the current structure of the notice classes. This version simplifies that task, improves accessibility, and enhances the utility of the tap to dismiss functionality. 20 | 21 | Here is the brief of the changes introduced in v3.0: 22 | 23 | - Adds a .podspec file to the repository. 24 | - Updated the internals of WBNoticeView to set up accessibility labels and attributes for the notice. These changes make it easy to target the notice views inside of tests while performing functional testing (e.g. KIF https://github.com/square/KIF). 25 | - It's now easier to customize NoticeView. 26 | - Tap to dismiss behaviors are much more flexible. It's possible to determine if a notice was dismissed interactively in the dismissal block, allowing for Growl style notifications that can be responded to on tap or automatically dismissed. Notices can also be optionally dismissed via tap before the display duration has expired. 27 | 28 | Acknowledgments: big thanks to @blakewatters for taking the time to refactor, document and cleanup the code. 29 | 30 | ### New in 2.4: Specify a completion block when a notice is dismissed 31 | 32 | Starting with 2.4, any notice can have a completion block associated with it. This block will be invoked when the notice has been dismissed (works on sticky and non-sticky notices): 33 | 34 | WBErrorNoticeView *notice = [WBErrorNoticeView errorNoticeInView:self.view title:NSLocalizedString(@"Signup Error", nil) message:NSLocalizedString(@"You need to fill out all entries in this screen to signup.", nil)]; 35 | notice.sticky = YES; 36 | notice.dismissedBlock = ^{ 37 | NSLog(@"The notice has been dismissed!"); 38 | }; 39 | 40 | ### New in 2.3.1: Dismissing a Notice Manually 41 | 42 | Starting with 2.3.1, any notice with the 'sticky' property set can be dismissed at will. This could happen for example when an specific event is detected. Just invoke 'dismissNotice' on the notice whenever you're ready to dismiss it: 43 | 44 | [myNotice dismissNotice]; 45 | 46 | Check the demo project to see it in action. 47 | 48 | ### New in 2.3: Make any Notice Sticky 49 | 50 | Some users have asked whether 'sticky' could be a property of any notice view. Well, I'm happy to report that starting with version 2.3, all notice types can be made sticky. The usage follows the regular pattern, only this time we set the 'sticky' property accordingly: 51 | 52 | WBErrorNoticeView *notice = [WBErrorNoticeView errorNoticeInView:self.view title:@"Network Error" message:@"Check your network connection."]; 53 | notice.sticky = YES; 54 | [notice show]; 55 | 56 | ### New in 2.1: Sticky Notice 57 | 58 | New in 2.1 is a different type of notice: Sticky. As it name implies, the notice will remain visible until the user taps on it to dismiss it. The usage follows the Error and Success notice pattern: 59 | 60 | WBStickyNoticeView *notice = [WBStickyNoticeView stickyNoticeInView:self.view title:@"7 New Tweets."]; 61 | [notice show]; 62 | 63 | ### NoticeView 1.0 vs 2.0 64 | 65 | The behavior in version 1 was "fire and forget". Calling *showErrorNoticeInView* or *showSuccessNoticeInView* displayed the notice, but there was no way to retain it for later use. Version 2 allows the developer to instantiate a notice, customize it (optional) and show it. Not only it's possible to retain it, but also customize it anytime with say, a different title and message. Oh, yeah… and it's cleaner too. 66 | 67 | ### Examples 68 | 69 | Since version 2 is more flexible, I have eliminated the older examples and replaced them with the new API. Please note that the older API is still there, for backward compatibility. 70 |
71 | 72 | To display a small error notice: 73 | 74 | WBErrorNoticeView *notice = [WBErrorNoticeView errorNoticeInView:self.view title:@"Network Error" message:@"Check your network connection."]; 75 | [notice show]; 76 | 77 | If the message provided doesn't fit in one line, the notice will be enlarged to accommodate the text: 78 | 79 | WBErrorNoticeView *notice = [WBErrorNoticeView errorNoticeInView:self.view title:@"Network Error" message:@"Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book."]; 80 | [notice show]; 81 | 82 | To display a small success notice: 83 | 84 | WBSuccessNoticeView *notice = [WBSuccessNoticeView successNoticeInView:self.view title:@"Link Saved Successfully"]; 85 | [notice show]; 86 | 87 | ### Customizing the Notice 88 | 89 | Instead of piling up a bunch of arguments in a method call, I decided to use properties instead. This way, new properties can be added easily without having to clutter the API with specialized methods. 90 | 91 | Example: customize a success notice with a bit of transparency and placing the notice at a specific Y coordinate: 92 | 93 | WBSuccessNoticeView *notice = [WBSuccessNoticeView successNoticeInView:self.view title:@"Link Saved Successfully"]; 94 | 95 | notice.alpha = 0.8; 96 | notice.originY = self.headerView.frame.size.height; 97 | 98 | [notice show]; 99 | 100 | 101 | ## Notes 102 | 103 | > If you pass nil instead of a valid UIView, an NSInvalidArgumentException exception will be raised. 104 | 105 | The default values are the following: 106 | 107 | if (nil == title) title = @"Unknown Error"; 108 | if (nil == message) message = @"Information not provided."; 109 | if (0.0 == duration) duration = 0.5; 110 | if (0.0 == delay) delay = 2.0; 111 | if (0.0 == alpha) alpha = 1.0; 112 | if (origin < 0.0) origin = 0.0; 113 | 114 | ## NoticeView SLOCCount Stats 115 | 116 | Who doesn't like stats? ;-) Here are some as reported by SLOCCount: 117 | 118 | SLOC Directory SLOC-by-Language (Sorted) 119 | 797 NoticeView objc=797 120 | 245 NoticeViewiPad objc=245 121 | 0 NoticeView.xcode proj (none) 122 | 0 top_dir (none) 123 | 124 | Totals grouped by language (dominant language first): 125 | 126 | objc: 1042 (100.00%) 127 | 128 | Total Physical Source Lines of Code (SLOC) = 1,042 129 | Development Effort Estimate, Person-Years (Person-Months) = 0.21 (2.51) (*) 130 | Schedule Estimate, Years (Months) = 0.30 (3.54) (**) 131 | Estimated Average Number of Developers (Effort/Schedule) = 0.71 132 | Total Estimated Cost to Develop = $ 50,119 (***) 133 | 134 | (*) Basic COCOMO model, Person-Months = 2.4 * (KSLOC**1.05) 135 | (**) Basic COCOMO model, Months = 2.5 * (person-months**0.38) 136 | (***) Average salary = $100,000/year, overhead = 2.40 137 | 138 | SLOCCount, Copyright (C) 2001-2004 David A. Wheeler 139 |
http://www.dwheeler.com/sloccount/ 140 | 141 | Latest salary information 142 |
http://www.indeed.com/salary/Software-Engineer.html 143 | 144 | ## Contribute 145 | 146 | I'd love to include your contributions. Feel free to improve it, send comments or suggestions. If you have improvements please [send me a pull request](https://github.com/tciuro/NoticeView/pull/new/master). 147 | 148 | ## Contact Me 149 | 150 | You can ping me on Twitter — [@titusmagnus](http://twitter.com/titusmagnus). --------------------------------------------------------------------------------